Noyau de filtre Sobel de grande taille
j'utilise un filtre sobel de taille 3x3 pour calculer la dérivée de l'image. En regardant certains articles sur internet, il semble que les noyaux pour Sobel filter pour taille 5x5 et 7x7 sont également communs, mais je ne suis pas en mesure de trouver leurs valeurs de noyau.
est-ce que quelqu'un pourrait me faire savoir les valeurs du noyau pour le filtre de sobel de taille 5x5 et 7x7? Aussi, si quelqu'un peut partager une méthode pour générer les valeurs du noyau, cela sera très utile.
Merci à l'avance.
8 réponses
mise à jour 23-Apr-2018: il semble que les noyaux définis dans le lien ci - dessous ne sont pas de vrais noyaux Sobel (pour 5x5 et plus) - ils peuvent faire un travail raisonnable de détection de bord, mais ils ne doivent pas être appelés noyaux Sobel. Voir réponse de Daniel pour un résumé plus précis et plus complet. (Je vais laisser cette réponse ici car (a) elle est liée à de divers endroits et (B) les réponses acceptées ne peuvent pas être facilement effacées.)
Google semble obtenir beaucoup de résultats, par exemple
http://rsbweb.nih.gov/nih-image/download/user-macros/slowsobel.macro suggère les noyaux suivants pour 3x3, 5x5, 7x7 et 9x9:
3x3:
1 0 -1
2 0 -2
1 0 -1
5x5:
2 1 0 -1 -2
3 2 0 -2 -3
4 3 0 -3 -4
3 2 0 -2 -3
2 1 0 -1 -2
7x7:
3 2 1 0 -1 -2 -3
4 3 2 0 -2 -3 -4
5 4 3 0 -3 -4 -5
6 5 4 0 -4 -5 -6
5 4 3 0 -3 -4 -5
4 3 2 0 -2 -3 -4
3 2 1 0 -1 -2 -3
9x9:
4 3 2 1 0 -1 -2 -3 -4
5 4 3 2 0 -2 -3 -4 -5
6 5 4 3 0 -3 -4 -5 -6
7 6 5 4 0 -4 -5 -6 -7
8 7 6 5 0 -5 -6 -7 -8
7 6 5 4 0 -4 -5 -6 -7
6 5 4 3 0 -3 -4 -5 -6
5 4 3 2 0 -2 -3 -4 -5
4 3 2 1 0 -1 -2 -3 -4
D'autres sources semblent donner des définitions différentes des noyaux plus gros. La bibliothèque Intel IPP, par exemple, donne le noyau 5x5 comme
1 2 0 -2 -1
4 8 0 -8 -4
6 12 0 -12 -6
4 8 0 -8 -4
1 2 0 -2 -1
intuitivement, cela a plus de sens pour moi parce que vous portez plus d'attention aux éléments plus près du centre. Il a également une définition naturelle en termes de noyau 3x3 qui est facile à étendre pour générer des noyaux plus grands. Cela dit, dans ma brève recherche j'ai trouvé 3 définitions différentes du noyau 5x5 - donc je soupçonne que (comme le dit Paul) les noyaux plus grands sont ad hoc, et donc ce n'est en aucun cas la réponse définitive.
le noyau 3x3 est le produit extérieur d'un noyau lissant et d'un noyau gradient, dans Matlab c'est quelque chose comme
sob3x3 = [ 1 2 1 ]' * [1 0 -1]
les noyaux plus gros peuvent être définis en convolvant le noyau 3x3 avec un autre noyau de lissage
sob5x5 = conv2( [ 1 2 1 ]' * [1 2 1], sob3x3 )
vous pouvez répéter le processus pour obtenir des noyaux de plus en plus grands
sob7x7 = conv2( [ 1 2 1 ]' * [1 2 1], sob5x5 )
sob9x9 = conv2( [ 1 2 1 ]' * [1 2 1], sob7x7 )
...
il y a beaucoup d'autres façons de l'écrire, mais je pense que cela explique exactement ce qui se passe le mieux. Fondamentalement, vous commencez avec un noyau de lissage dans une direction et une estimation des différences finies de la dérivée dans l'autre et ensuite juste appliquer lissage jusqu'à ce que vous obtenez la taille du noyau que vous voulez.
parce que c'est juste une série de convolutions, toutes les belles propriétés tiennent, (la commutativité, l'associativité et ainsi de suite) qui peut être utile pour votre application. Par exemple, vous pouvez trivialement séparer le noyau 5x5 en ses composants lissants et dérivés:
sob5x5 = conv([1 2 1],[1 2 1])' * conv([1 2 1],[-1 0 1])
noter que pour être un estimateur" correct "de la dérivée, le 3x3 Sobel doit être mis à l'échelle par un facteur de 1/8:
sob3x3 = 1/8 * [ 1 2 1 ]' * [1 0 -1]
et chaque noyau plus gros doit être gradué d'un facteur supplémentaire de 1/16 (parce que le les cerneaux de lissage ne sont pas normalisés):
sob5x5 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob3x3 )
sob7x7 = 1/16 * conv2( [ 1 2 1 ]' * [1 2 1], sob5x5 )
...
solution Complète pour arbitraire Sobel noyau tailles et les angles
tl;dr: passez à la section 'Exemples'
pour ajouter une autre solution, en développant sur ce document (ce n'est pas particulièrement de haute qualité, mais il montre quelques graphiques et matrices utilisables à partir du bas de la page 2).
Objectif
ce que nous essayons de faire est estimer le gradient local de l'image à la position (x,y). Le gradient est un vecteur composé des composantes dans les directions x et y, gx et GY.
maintenant, imaginez que nous voulons approcher le gradient basé sur notre pixel (x,y) et ses voisins comme une opération du noyau (3x3, 5x5, ou n'importe quelle taille).
"1519180920 Solution" idéenous pouvons approximer le gradient en sommant sur les projections de tous les couples Centre-voisinage sur le la direction de gradient. (Le noyau de Sobel n'est qu'une méthode particulière pour pondérer les différentes contributions, et Prewitt aussi, en gros).
étapes intermédiaires explicites pour 3x3
c'est l'image locale, pixel central (x,y) marqué comme 'o' (centre)
a b c
d o f
g h i
disons que nous voulons le gradient dans la direction X positive. Le vecteur unitaire dans la direction x positive est (1,0) [je vais utiliser plus tard la convention que le Y positif la direction est vers le bas, i.e. (0,1), et cela (0,0) est en haut à gauche de l'image).]
le vecteur de o à f ('de' en abrégé) est (1,0). Le gradient de direction "de" est (f-o) / 1 (la valeur de l'image au pixel dénote ici la valeur f moins au centre o, divisée par la distance entre ces pixels). Si nous projetons le vecteur unitaire de ce gradient de voisinage particulier sur notre direction de gradient désirée (1,0) via un produit de point nous obtenons 1. Voici un petit tableau avec les contributions de tous les les voisins, en commençant par les affaires les plus faciles. Notez que pour les diagonales, leur distance est sqrt2, et les vecteurs d'unité dans les directions diagonales sont 1 / sqrt2 * (+/-1, +/-1)
f: (f-o)/1 * 1
d: (d-o)/1 * -1 because (-1, 0) dot (1, 0) = -1
b: (b-o)/1 * 0 because (0, -1) dot (1, 0) = 0
h: (h-o)/1 * 0 (as per b)
a: (a-o)/sqrt2 * -1/sqrt2 distance is sqrt2, and 1/sqrt2*(-1,-1) dot (1,0) = -1/sqrt2
c: (c-o)/sqrt2 * +1/sqrt2 ...
g: (g-o)/sqrt2 * -1/sqrt2 ...
i: (i-o)/sqrt2 * +1/sqrt2 ...
Modifier pour clarification: Il y a deux facteurs de 1 / sqrt (2) pour la raison suivante:
-
nous sommes intéressés par la contribution à la pente dans une direction spécifique (ici x), nous devons donc projeter le gradient directionnel du pixel central au pixel voisin sur la direction qui nous intéresse. Ceci est accompli en prenant le produit scalaire des vecteurs unitaires dans les directions respectives, qui introduit le premier facteur 1/L (ici 1/sqrt(2) pour les diagonales).
-
le gradient mesure le changement infinitésimal en un point, que nous approximons par des différences finies. En termes d'une équation linéaire, m = (y2-y1)/(x2-x1). Pour cette raison, la différence de valeur entre le pixel central et le pixel voisin (y2-y1) doit être distribuée sur leur distance (correspond à x2-x1) afin d'obtenir les unités ascendantes par unité de distance. Cela donne un deuxième facteur de 1 / L (ici 1 / sqrt (2) pour les diagonales)
1519560920"
Ok, maintenant nous connaissons les contributions. Simplifions cette expression en combinant des paires opposées de contributions pixel. Je commence par d et f:
{(f-o)/1 * 1} + {(d-o)/1 * -1}
= f - o - (d - o)
= f - d
Maintenant, la première diagonale:
{(c-o)/sqrt2 * 1/sqrt2} + {(g-o)/sqrt2 * -1/sqrt2}
= (c - o)/2 - (g - o)/2
= (c - g)/2
la deuxième diagonale contribue (i - a)/2. La direction perpendiculaire contribue à zéro. Notez que toutes les contributions du pixel central' o ' disparaissent.
nous avons maintenant calculé les contributions de tous les voisins les plus proches au gradient en X-direction positive au pixel (x, y), de sorte que notre approximation totale du gradient en x-direction est simplement leur somme:
gx(x,y) = f - d + (c - g)/2 + (i - a)/2
nous pouvons obtenir le même résultat en utilisant un noyau de convolution où les coefficients sont écrits à la place du pixel voisin correspondant:
-1/2 0 1/2
-1 0 1
-1/2 0 1/2
si vous ne voulez pas traiter les fractions, vous les multipliez par 2 et vous obtenez le célèbre noyau Sobel 3x3.
-1 0 1
G_x = -2 0 2
-1 0 1
la multiplication par deux ne sert qu'à obtenir des entiers commodes. La mise à l'échelle de votre image de sortie est fondamentalement arbitraire, la plupart du temps vous le normalisez à votre gamme d'image, de toute façon (pour obtenir des résultats clairement visibles).
par le même raisonnement que ci-dessus, vous obtenez le noyau pour le gradient vertical gy en projetant les contributions du voisin sur le vecteur unité dans la direction positive y (0,1)
-1 -2 -1
G_y = 0 0 0
1 2 1
formule pour noyaux de calibre arbitraire
si vous voulez des noyaux 5x5 ou plus, vous n'avez qu'à faire attention aux distances, p.ex.
A B 2 B A
B C 1 C B
2 1 - 1 2
B C 1 C B
A B 2 B A
où
A = 2 * sqrt2
B = sqrt5
C = sqrt2.
si la longueur du vecteur reliant deux pixels Est L, le vecteur unitaire dans cette direction a un facteur de 1 / L. pour cette raison, les contributions de n'importe quel pixel 'k' à (disons) le gradient x (1,0) peuvent être simplifiées à "(différence de valeur sur la distance au carré) fois (produit non de direction non normalisée vecteur 'ok' avec vecteur de gradient, par exemple (1,0)) "
gx_k = (k - o)/(pixel distance^2) ['ok' dot (1,0)].
parce que le produit dot du vecteur de connexion avec le vecteur unité x sélectionne l'entrée vectorielle correspondante, l'entrée du noyau g_x correspondante à la position k est juste
i / (i*i + j*j)
où i et j sont le nombre de pas entre le pixel central et le pixel k dans la direction x et Y. Dans le ci-dessus 3x3 calcul, le pixel 'a' aurait i = -1 (1 à gauche), j = -1 (1 en haut), et donc le noyau de l'entrée est -1 / (1 + 1) = -1/2.
les entrées pour le noyau G_y sont
j/(i*i + j*j).
si je veux des valeurs entières pour mon noyau, je dois suivre les étapes suivantes:
- vérifier la plage disponible de l'image de sortie
- calcule le résultat le plus élevé possible en appliquant le noyau flottant (c'est-à-dire en supposant une valeur d'entrée max sous toutes les entrées positives du noyau, donc la valeur de sortie est (somme sur toutes les valeurs positives du noyau) * (valeur d'image d'entrée max possible). Si vous avez signé d'entrée, vous devez considérer les valeurs négatives. Le pire des cas est alors la somme de toutes les valeurs positives + la somme de toutes les valeurs abs des entrées négatives (si l'entrée max est positive, -l'entrée max est négative). edit: la somme de toutes les valeurs de l'abs a également été appelée à juste titre le poids du noyau
- calculer la mise à l'échelle maximale permise pour le noyau (sans Plage de débordement de l'image de sortie)
- pour tous multiples entiers (de 2 à au-dessus du maximum) du noyau flottant: vérifiez qui a la somme la plus basse des erreurs absolues d'arrondi et utilisez ce noyau
Donc, en résumé:
Gx_ij = i / (i*i + j*j)
Gy_ij = j / (i*i + j*j)
où i, j est la position dans le noyau comptée à partir du centre. Scale les entrées du noyau selon les besoins pour obtenir des nombres entiers (ou au moins des approximations proches).
ces formules sont valables pour toutes noyau tailles.
exemples
-2/8 -1/5 0 1/5 2/8 -5 -4 0 4 5
-2/5 -1/2 0 1/2 2/5 -8 -10 0 10 8
G_x (5x5) -2/4 -1/1 0 1/1 2/4 (*20) = -10 -20 0 20 10
-2/5 -1/2 0 1/2 2/5 -8 -10 0 10 8
-2/8 -1/5 0 1/5 2/8 -5 -4 0 4 5
notez que les 3x3 pixels centraux du noyau 5x5 en notation float ne sont que le noyau 3x3, c'est-à-dire que les noyaux plus grands représentent une approximation continue avec des données supplémentaires mais moins pondérées. Cela se poursuit avec des tailles de noyau plus grandes:
-3/18 -2/13 -1/10 0 1/10 2/13 3/18
-3/13 -2/8 -1/5 0 1/5 2/8 3/13
-3/10 -2/5 -1/2 0 1/2 2/5 3/10
G_x (7x7) -3/9 -2/4 -1/1 0 1/1 2/4 3/9
-3/10 -2/5 -1/2 0 1/2 2/5 3/10
-3/13 -2/8 -1/5 0 1/5 2/8 3/13
-3/18 -2/13 -1/10 0 1/10 2/13 3/18
représentations entières exactes deviennent impraticables à ce point.
jusqu'à comme je peux le dire (je n'ai pas accès au papier original), la partie "Sobel" à cela pondère correctement les contributions. La solution Prewitt peut être obtenue en laissant de côté la pondération de la distance et en saisissant i et j dans le noyau selon le cas.
Bonus: noyaux Sobel pour des directions arbitraires
pour que nous puissions approximer les composantes x et y du gradient d'image (qui est en fait un vecteur, comme indiqué au tout début). Le le gradient alpha dans toute direction arbitraire (mesuré mathématiquement positif, dans ce cas dans le sens des aiguilles d'une montre puisque le y positif est vers le bas) peut être obtenu en projetant le vecteur du gradient sur le vecteur de l'Unité de gradient alpha.
le vecteur unité alpha est (cos alpha, sin alpha). Pour alpha = 0°, vous pouvez obtenir le résultat pour gx, pour alpha = 90°, vous obtenez gy.
g_alpha = (alpha-unit vector) dot (gx, gy)
= (cos a, sin a) dot (gx, gy)
= cos a * gx + sin a * gy
si vous prenez la peine d'écrire gx et gy comme sommes des contributions de voisins, vous réalisez que vous pouvez grouper la longue expression résultante par des termes qui s'appliquent au même pixel voisin, puis réécrivez ceci comme un noyau de convolution simple avec des entrées
G_alpha_ij = (i * cos a + j * sin a)/(i*i + j*j)
si vous voulez l'approximation d'entier la plus proche, suivez les étapes décrites ci-dessus.
j'ai rapidement piraté un algorithme pour générer un noyau Sobel de n'importe quelle taille impaire > 1, basé sur les exemples donnés par @Paul R:
public static void CreateSobelKernel(int n, ref float[][] Kx, ref float[][] Ky)
{
int side = n * 2 + 3;
int halfSide = side / 2;
for (int i = 0; i < side; i++)
{
int k = (i <= halfSide) ? (halfSide + i) : (side + halfSide - i - 1);
for (int j = 0; j < side; j++)
{
if (j < halfSide)
Kx[i][j] = Ky[j][i] = j - k;
else if (j > halfSide)
Kx[i][j] = Ky[j][i] = k - (side - j - 1);
else
Kx[i][j] = Ky[j][i] = 0;
}
}
}
J'espère que ça aidera.
gradient de Sobel filtre générateur
(cette réponse renvoie à l '" analyse 1519100920 donnée par @Daniel, ci-dessus.)
Gx[i,j] = i / (i*i + j*j)
Gy[i,j] = j / (i*i + j*j)
c'est un résultat important, et une meilleure explication que peut être trouvé dans le papier original . Il devrait être écrit dans Wikipedia , ou quelque part, parce qu'il semble également supérieur à toute autre discussion de la question disponible sur le Internet.
cependant, il n'est pas vrai que les représentations à valeur entière ne sont pas pratiques pour les filtres de taille supérieure à 5*5, comme l'affirme la revendication. En utilisant des nombres entiers de 64 bits, les tailles de filtre de Sobel jusqu'à 15*15 peuvent être exprimées exactement.
ici sont les quatre premiers; le résultat doit être divisé par le "poids", de sorte que le gradient d'une région d'image comme le suivant, est normalisé à une valeur de 1.
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Gx(3) :
-1/2 0/1 1/2 -1 0 1
-1/1 0 1/1 * 2 = -2 0 2
-1/2 0/1 1/2 -1 0 1
weight = 4 weight = 8
Gx (5):
-2/8 -1/5 0/4 1/5 2/8 -5 -4 0 4 5
-2/5 -1/2 0/1 1/2 2/5 -8 -10 0 10 8
-2/4 -1/1 0 1/1 2/4 * 20 = -10 -20 0 20 10
-2/5 -1/2 0/1 1/2 2/5 -8 -10 0 10 8
-2/8 -1/5 0/4 1/5 2/8 -5 -4 0 4 5
weight = 12 weight = 240
Gx (7):
-3/18 -2/13 -1/10 0/9 1/10 2/13 3/18 -130 -120 -78 0 78 120 130
-3/13 -2/8 -1/5 0/4 1/5 2/8 3/13 -180 -195 -156 0 156 195 180
-3/10 -2/5 -1/2 0/1 1/2 2/5 3/10 -234 -312 -390 0 390 312 234
-3/9 -2/4 -1/1 0 1/1 2/4 3/9 * 780 = -260 -390 -780 0 780 390 260
-3/10 -2/5 -1/2 0/1 1/2 2/5 3/10 -234 -312 -390 0 390 312 234
-3/13 -2/8 -1/5 0/4 1/5 2/8 3/13 -180 -195 -156 0 156 195 180
-3/18 -2/13 -1/10 0/9 1/10 2/13 3/18 -130 -120 -78 0 78 120 130
weight = 24 weight = 18720
Gx (9):
-4/32 -3/25 -2/20 -1/17 0/16 1/17 2/20 3/25 4/32 -16575 -15912 -13260 -7800 0 7800 13260 15912 16575
-4/25 -3/18 -2/13 -1/10 0/9 1/10 2/13 3/18 4/25 -21216 -22100 -20400 -13260 0 13260 20400 22100 21216
-4/20 -3/13 -2/8 -1/5 0/4 1/5 2/8 3/13 4/20 -26520 -30600 -33150 -26520 0 26520 33150 30600 26520
-4/17 -3/10 -2/5 -1/2 0/1 1/2 2/5 3/10 4/17 -31200 -39780 -53040 -66300 0 66300 53040 39780 31200
-4/16 -3/9 -2/4 -1/1 0 1/1 2/4 3/9 4/16 * 132600 = -33150 -44200 -66300 -132600 0 132600 66300 44200 33150
-4/17 -3/10 -2/5 -1/2 0/1 1/2 2/5 3/10 4/17 -31200 -39780 -53040 -66300 0 66300 53040 39780 31200
-4/20 -3/13 -2/8 -1/5 0/4 1/5 2/8 3/13 4/20 -26520 -30600 -33150 -26520 0 26520 33150 30600 26520
-4/25 -3/18 -2/13 -1/10 0/9 1/10 2/13 3/18 4/25 -21216 -22100 -20400 -13260 0 13260 20400 22100 21216
-4/32 -3/25 -2/20 -1/17 0/16 1/17 2/20 3/25 4/32 -16575 -15912 -13260 -7800 0 7800 13260 15912 16575
weight = 40 weight = 5304000
le programme Ruby ci-dessous calcule les filtres Sobel et les poids correspondants de n'importe quelle taille, bien que les filtres à valeur entière ne soient pas susceptibles d'être utiles pour des tailles supérieures à 15*15.
#!/usr/bin/ruby
# Sobel image gradient filter generator
# by <ian_bruce@mail.ru> -- Sept 2017
# reference:
# /q/sobel-filter-kernel-of-large-size-43248/"invalid size"
exit false
end
s /= 2
n = 1
# find least-common-multiple of all fractional denominators
(0..s).each { |j|
(1..s).each { |i|
d = i*i + j*j
n = n.lcm(d / d.gcd(i))
}
}
fw1 = format("%d/%d", s, 2*s*s).size + 2
fw2 = format("%d", n).size + 2
weight = 0
s1 = ""
s2 = ""
(-s..s).each { |y|
(-s..s).each { |x|
i, j = x, y # "i, j = y, x" for transpose
d = i*i + j*j
if (i != 0)
if (n * i % d) != 0 # this should never happen
$stderr.puts "inexact division: #{n} * #{i} / ((#{i})^2 + (#{j})^2)"
exit false
end
w = n * i / d
weight += i * w
else
w = 0
end
s1 += "%*s" % [fw1, d > 0 ? "%d/%d" % [i, d] : "0"]
s2 += "%*d" % [fw2, w]
}
s1 += "\n" ; s2 += "\n"
}
f = n.gcd(weight)
puts s1
puts "\nweight = %d%s" % [weight/f, f < n ? "/%d" % (n/f) : ""]
puts "\n* #{n} =\n\n"
puts s2
puts "\nweight = #{weight}"
comme Adam Bowen a expliqué dans sa réponse , le noyau de Sobel est une combinaison d'un lissage le long d'un axe, et une dérivée de différence centrale le long de l'autre axe:
sob3x3 = [1 2 1]' * [1 0 -1]
Le lissage ajoute la régularisation (réduit la sensibilité au bruit).
(je laisse de côté tous les facteurs 1/8
dans ce post, comme L'a fait Sobel lui-même , ce qui signifie que l'opérateur détermine dérivé jusqu'à la mise à l'échelle. Aussi, *
signifie toujours convolution dans ce post.)
généralisons ceci:
deriv_kernel = smoothing_kernel * d/dx
une des propriétés de la convolution est que
d/dx f = d/dx * f
C'est-à-dire convolvant une image avec l'opérateur dérivé élémentaire fournit la dérivée de l'image. Notant également que la convolution est commutative,
deriv_kernel = d/dx * smoothing_kernel = d/dx smoothing_kernel
C'est-à-dire, le dérivé le noyau est la dérivée d'un noyau lissant.
noter que l'application d'un tel noyau à une image par convolution:
image * deriv_kernel = image * smoothing_kernel * d/dx = d/dx (image * smoothing_kernel)
C'est-à-dire qu'avec ce noyau dérivé généralisé et idéalisé on peut calculer le véritable dérivé de l'image lissée. Ce n'est évidemment pas le cas avec le noyau Sobel, car il utilise une approximation de différence centrale par rapport à la dérivée.
Mais choisir un meilleur smoothing_kernel
, cela peut être atteindre. Le noyau gaussien est l'option idéale ici, car il offre le meilleur compromis entre la compacité dans le domaine spatial (faible encombrement du noyau) et la compacité dans le domaine de la fréquence (bon lissage). De plus, le gaussien est parfaitement isotrope et séparable. L'utilisation d'un noyau dérivé gaussien fournit le meilleur opérateur de dérivé régularisé possible.
ainsi, si vous cherchez un plus grand opérateur Sobel, parce que vous avez besoin de plus régularisation, utilisez un opérateur gaussien dérivé à la place.
analysons un peu plus le noyau de Sobel.
le grain lissant est triangulaire, avec les échantillons [1 2 1]
. Il s'agit d'une fonction triangulaire, qui, échantillonnée, conduit à ces trois valeurs:
2 + x , if -2 < x < 0
h = { 2 , if x = 0
2 - x , if 0 < x < 2
son dérivé est:
1 , if -2 < x < 0
d/dx h = { 0 , if x = 0 (not really, but it's the sensible solution)
-1 , if 0 < x < 2
ainsi, nous pouvons voir que le centre l'approximation de la différence de la dérivée peut être considérée comme un échantillon de la dérivée analytique de la même fonction triangulaire utilisée pour le lissage. Ainsi nous avons:
sob3x3 = [1 2 1]' * d/dx [1 2 1] = d/dx ( [1 2 1]' * [1 2 1] )
donc, si vous voulez agrandir ce noyau, agrandissez simplement le noyau lissant:
sob5x5 = d/dx ( [1 2 3 2 1]' * [1 2 3 2 1] ) = [1 2 3 2 1]' * [1 1 0 -1 -1]
sob7x7 = d/dx ( [1 2 3 4 3 2 1]' * [1 2 3 4 3 2 1] ) = [1 2 3 4 3 2 1]' * [1 1 1 0 -1 -1 -1]
c'est tout à fait différent du Conseil donné par Adam Bowen , qui suggère de convoler le noyau avec le noyau triangulaire 3-tab le long de chaque dimension: [1 2 1] * [1 2 1] = [1 4 6 4 1]
, et [1 2 1] * [1 0 -1] = [1 2 0 -2 -1]
. Notez que, en raison du théorème de limite centrale, convolving ce noyau triangulaire avec lui-même conduit à un filtre qui se rapproche du gaussien un peu plus. Plus le noyau que nous créons par des convolutions répétées avec lui-même est grand, plus nous nous rapprochons de ce gaussien. Donc, au lieu d'utiliser cette méthode, vous pourriez aussi bien échantillonner directement la fonction gaussienne.
Daniel a un long billet dans lequel il suggère d'étendre le noyau Sobel d'une autre manière. La forme du grain lissant ici diverge de l'approximation gaussienne, Je n'ai pas essayé d'étudier ses propriétés.
notez qu'aucune de ces trois extensions possibles du noyau Sobel ne sont en fait des noyaux Sobel, puisque le noyau Sobel est explicitement un noyau 3x3 ( voir une note historique de Sobel sur son opérateur , qu'il n'a jamais publiée).
Notez aussi que je ne préconise pas le noyau étendu de Sobel dérivé ici. Utilisez des dérivés gaussiens!
merci à tous, je vais essayer la deuxième variante par @Adam Bowen, prenez le code C pour Sobel5x5, 7x7, 9x9... génération de matrice pour cette variante (peut - être avec des bogues, si vous trouvez un bogue ou pouvez optimiser le code-écrivez-le là):
static void Main(string[] args)
{
float[,] Sobel3x3 = new float[,] {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}};
float[,] Sobel5x5 = Conv2DforSobelOperator(Sobel3x3);
float[,] Sobel7x7 = Conv2DforSobelOperator(Sobel5x5);
Console.ReadKey();
}
public static float[,] Conv2DforSobelOperator(float[,] Kernel)
{
if (Kernel == null)
throw new Exception("Kernel = null");
if (Kernel.GetLength(0) != Kernel.GetLength(1))
throw new Exception("Kernel matrix must be Square matrix!");
float[,] BaseMatrix = new float[,] {
{1, 2, 1},
{2, 4, 2},
{1, 2, 1}};
int KernelSize = Kernel.GetLength(0);
int HalfKernelSize = KernelSize / 2;
int OutSize = KernelSize + 2;
if ((KernelSize & 1) == 0) // Kernel_Size must be: 3, 5, 7, 9 ...
throw new Exception("Kernel size must be odd (3x3, 5x5, 7x7...)");
float[,] Out = new float[OutSize, OutSize];
float[,] InMatrix = new float[OutSize, OutSize];
for (int x = 0; x < BaseMatrix.GetLength(0); x++)
for (int y = 0; y < BaseMatrix.GetLength(1); y++)
InMatrix[HalfKernelSize + x, HalfKernelSize + y] = BaseMatrix[x, y];
for (int x = 0; x < OutSize; x++)
for (int y = 0; y < OutSize; y++)
for (int Kx = 0; Kx < KernelSize; Kx++)
for (int Ky = 0; Ky < KernelSize; Ky++)
{
int X = x + Kx - HalfKernelSize;
int Y = y + Ky - HalfKernelSize;
if (X >= 0 && Y >= 0 && X < OutSize && Y < OutSize)
Out[x, y] += InMatrix[X, Y] * Kernel[KernelSize - 1 - Kx, KernelSize - 1 - Ky];
}
return Out;
}
Résultats (NormalMap) ou copie , où ce metod - №2, @Paul R metod - №1. Maintenant j'utilise last, becouse il donne un résultat plus lisse et il est facile de générer des noyaux avec ce code .
Voici une solution simple faite avec python 3 en utilisant numpy et la réponse @Daniel.
def custom_sobel(shape, axis):
"""
shape must be odd: eg. (5,5)
axis is the direction, with 0 to positive x and 1 to positive y
"""
k = np.zeros(shape)
p = [(j,i) for j in range(shape[0])
for i in range(shape[1])
if not (i == (shape[1] -1)/2. and j == (shape[0] -1)/2.)]
for j, i in p:
j_ = int(j - (shape[0] -1)/2.)
i_ = int(i - (shape[1] -1)/2.)
k[j,i] = (i_ if axis==0 else j_)/float(i_*i_ + j_*j_)
return k
renvoie le noyau (5,5) comme ceci:
Sobel x:
[[-0.25 -0.2 0. 0.2 0.25]
[-0.4 -0.5 0. 0.5 0.4 ]
[-0.5 -1. 0. 1. 0.5 ]
[-0.4 -0.5 0. 0.5 0.4 ]
[-0.25 -0.2 0. 0.2 0.25]]
Sobel y:
[[-0.25 -0.4 -0.5 -0.4 -0.25]
[-0.2 -0.5 -1. -0.5 -0.2 ]
[ 0. 0. 0. 0. 0. ]
[ 0.2 0.5 1. 0.5 0.2 ]
[ 0.25 0.4 0.5 0.4 0.25]]
si quelqu'un connaît une meilleure façon de faire cela en python, s'il vous plaît faites le moi savoir. Je suis encore un débutant ;)