Comment calculer efficacement la «direction» moyenne des pixels dans une image en niveaux de gris?

J’ai donc compris que je pouvais convertir une image en niveaux de gris comme ceci:

public static Bitmap GrayScale(this Image img) { var bmp = new Bitmap(img.Width, img.Height); using(var g = Graphics.FromImage(bmp)) { var colorMasortingx = new ColorMasortingx( new[] { new[] {.30f, .30f, .30f, 0, 0}, new[] {.59f, .59f, .59f, 0, 0}, new[] {.11f, .11f, .11f, 0, 0}, new[] {0, 0, 0, 1.0f, 0}, new[] {0, 0, 0, 0, 1.0f} }); using(var attrs = new ImageAtsortingbutes()) { attrs.SetColorMasortingx(colorMasortingx); g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs); } } return bmp; } 

Maintenant, je veux calculer la “direction” moyenne des pixels.

Ce que je veux dire par là, c’est que je veux regarder disons une région 3×3, puis si le côté gauche est plus sombre que le côté droit, la direction serait vers la droite, si le bas est plus foncé que le haut, le la direction serait vers le haut, si le coin inférieur gauche est plus sombre que le coin supérieur droit, alors la direction sera en haut à droite. (Pensez aux petites flèches vectorielles sur chaque région 3×3). Un meilleur exemple est peut-être si vous dessinez un dégradé de niveaux de gris dans Photoshop et que vous voulez calculer à quel angle ils l’ont dessiné.

J’ai fait des choses comme ce MatLab, mais c’était il y a des années. Je pense que je pourrais utiliser une masortingce similaire à ColorMasortingx pour calculer cela, mais je ne sais pas trop comment. On dirait que cette fonction pourrait être ce que je veux; pourrais-je le convertir en niveaux de gris (comme ci-dessus), puis faire quelque chose avec la masortingce en niveaux de gris pour calculer ces directions?

IIRC, ce que je veux est assez similaire à la détection de bord .

Après avoir calculé ces vecteurs de direction, je vais simplement les parcourir et calculer la direction moyenne de l’image.

L’objective final est de faire pivoter les images de manière à ce que leur direction moyenne soit toujours orientée vers le haut. De cette façon, si j’ai deux images identiques, sauf qu’une est tournée (90,180 ou 270 degrés), elles seront orientées de la même façon (je ne suis pas inquiet si une personne se retrouve à l’envers).


* snip * Suppression de spam. Vous pouvez voir les révisions de vous voulez lire le rest de mes tentatives.

Calculer la moyenne des angles est généralement une mauvaise idée:

 ... sum += Math.Atan2(yi, xi); } } double avg = sum / (img.Width * img.Height); 

La moyenne d’un ensemble d’angles n’a pas de signification claire: par exemple, la moyenne d’un angle dirigé vers le haut et d’un angle dirigé vers le bas est un angle dirigé vers la droite. Est-ce que c’est ce que tu veux? En supposant que “up” est + PI, la moyenne entre deux angles presque orientés vers le haut serait un angle pointant vers le bas, si l’un des angles est PI- [une petite valeur], l’autre -PI + [une petite valeur]. Ce n’est probablement pas ce que vous voulez. En outre, vous ignorez totalement la force du contour – la plupart des pixels de vos images réelles ne sont pas du tout des contours, de sorte que la direction du dégradé est principalement composée de bruit.

Si vous voulez calculer quelque chose comme une “direction moyenne”, vous devez additionner les vecteurs au lieu des angles, puis calculer Atan2 après la boucle. Le problème est que: la sum des vecteurs ne vous dit rien sur les objects à l’intérieur de l’image, car les dégradés qui se dirigent dans des directions opposées s’annulent. Il vous dit seulement quelque chose sur la différence de luminosité entre la première / dernière ligne et la première / dernière colonne de l’image. Ce n’est probablement pas ce que vous voulez.

Je pense que le moyen le plus simple d’orienter les images est de créer un histogramme d’angle: Créez un tableau avec (par exemple) 360 cases pour 360 degrés de directions de dégradé. Calculez ensuite l’angle et la magnitude du gradient pour chaque pixel. Ajoutez chaque degré de dégradé à la corbeille d’angle droite. Cela ne vous donnera pas un seul angle, mais un histogramme d’angle, qui peut ensuite être utilisé pour orienter deux images l’une vers l’autre à l’aide d’une simple corrélation cyclique.

Voici une implémentation de validation de concept de Mathematica que j’ai développée pour voir si cela fonctionnerait:

 angleHistogram[src_] := ( Lx = GaussianFilter[ImageData[src], 2, {0, 1}]; Ly = GaussianFilter[ImageData[src], 2, {1, 0}]; angleAndOrientation = MapThread[{Round[ArcTan[#1, #2]*180/\[Pi]], Sqrt[#1^2 + #2^2]} &, {Lx, Ly}, 2]; angleAndOrientationFlat = Flatten[angleAndOrientation, 1]; bins = BinLists[angleAndOrientationFlat , 1, 5]; histogram = Total /@ Flatten[bins[[All, All, All, 2]], {{1}, {2, 3}}]; maxIndex = Position[histogram, Max[histogram]][[1, 1]]; Labeled[ Show[ ListLinePlot[histogram, PlotRange -> All], Graphics[{Red, Point[{maxIndex, histogram[[maxIndex]]}]}] ], "Maximum at " <> ToString[maxIndex] <> "\[Degree]"] ) 

Résultats avec des exemples d’images:

entrez la description de l'image ici

Les histogrammes des angles montrent également pourquoi l’angle moyen ne peut pas fonctionner: l’histogramme est essentiellement un seul pic net, les autres angles sont à peu près uniformes. La moyenne de cet histogramme sera toujours dominée par l’uniformité du “bruit de fond”. C’est pourquoi vous avez presque le même angle (environ 180 °) pour chacune des images “réelles” avec votre algorithme actuel.

L’image arborescente a un seul angle dominant (l’horizon). Dans ce cas, vous pouvez utiliser le mode de l’histogramme (angle le plus fréquent). Mais cela ne fonctionnera pas pour chaque image:

entrez la description de l'image ici

Ici vous avez deux pics. La corrélation cyclique devrait toujours orienter deux images l’une par rapport à l’autre, mais l’utilisation du mode ne suffit probablement pas.

Notez également que le pic de l’histogramme d’angle n’est pas “haut”: dans l’image d’arborescence ci-dessus, le pic de l’histogramme d’angle correspond probablement à l’horizon. Donc, il pointe vers le haut. Dans l’image Lena, il s’agit de la barre blanche verticale à l’arrière-plan – elle pointe donc vers la droite. En orientant simplement les images en utilisant l’angle le plus fréquent, toutes les images ne seront pas orientées vers le haut.

entrez la description de l'image ici

Cette image a encore plus de pics: L’utilisation du mode (ou probablement d’un seul angle) ne serait pas fiable pour orienter cette image. Mais l’histogramme d’angle dans son ensemble devrait vous donner une orientation fiable.

Remarque: je n’ai pas prétraité les images, ni essayé les opérateurs de dégradé à différentes échelles, ni post-traité l’histogramme obtenu. Dans une application réelle, vous devez ajuster toutes ces choses pour obtenir le meilleur algorithme possible pour un grand nombre d’images test. Ceci est juste un test rapide pour voir si l’idée pourrait fonctionner du tout.

Ajouter: pour orienter deux images à l’aide de cet histogramme,

  1. Normalisez tous les histogrammes afin que la zone située sous l’histogramme soit la même pour chaque image (même si certaines sont plus claires, plus sombres ou plus floues).
  2. Prenez les histogrammes des images et comparez-les pour chaque rotation qui vous intéresse:

Par exemple, en C #:

 for (int rotationAngle = 0; rotationAngle < 360; rotationAngle++) { int difference = 0; for (int i = 0; i < 360; i++) difference += Math.Abs(histogram1[i] - histogram2[(i+rotationAngle) % 360]); if (difference < bestDifferenceSoFar) { bestDifferenceSoFar = difference; foundRotation = rotationAngle; } } 

(Vous pouvez accélérer le processus en utilisant FFT si la longueur de votre histogramme est une puissance de 2. Mais le code serait beaucoup plus complexe et, pour 256 bacs, cela n'aurait pas beaucoup d'importance)

Eh bien, je peux vous donner une autre façon de le faire. Bien que ne sera pas jolie mais espérons que cela fonctionne pour vous.

Il est probable que vos calculs sont corrects. Juste que le gradient une fois la moyenne aboutit à une valeur moyenne différente de celle attendue. Je suppose donc qu’en regardant l’image, vous estimez qu’il doit y avoir un angle moyen différent. Donc;

  • Convertir l’image en binary.
  • Trouver des lignes en utilisant la transformation hough
  • Prenez la plus longue ligne et calculez son angle. Cela devrait vous donner l’angle qui est le plus important.
  • Vous aurez peut-être besoin de pré / post-traitement pour obtenir les bonnes lignes.

Et comme d’une approche de plus. Try GIST Ceci est essentiellement une implémentation la plus largement utilisée dans la reconnaissance de scène. Je trouve que vos images sont de véritables scènes et par conséquent, je suggérerais de suivre cette approche. Cette méthode vous donnera un vecteur que vous comparerez à différents vecteurs d’orientation de la même image. Ceci est une technique très bien connue et devrait certainement être applicable dans votre cas.

Pensez à utiliser le dégradé de votre image pour calculer la direction souhaitée: en.wikipedia.org/wiki/Image_gradient

Vous devez convoluer votre image avec deux kernelx dérivés gaussiens (un en X et un en Y). C’est en fait le Lx et le Ly dans la réponse ci-dessus.

Soustrayez au préalable l’intensité moyenne des pixels avant de calculer le produit sommé entre la fenêtre glissante (sous-image de votre image d’origine) et les fonctions dérivées gaussiennes du premier ordre.

Voir par exemple ce tutoriel: http://bmia.bmt.tue.nl/people/bromeny/MICCAI2008/Materials/05%20Gaussian%20derivatives%20MMA6.pdf

Choisissez le facteur de lissage optimal sigma> = 1.

Pour calculer les kernelx gaussiens, différenciez une fois la fonction 2D-gaussienne (connue de la dissortingbution normale) avec la variable 1d ‘(x-0) ^ 2’ remplacée par (x ^ 2 + y ^ 2). Vous pouvez le dessiner en 2D, par exemple en MS Excel.

Bonne chance!

Michael