Algorithme de comparaison d'images
j'essaie de comparer les images les unes aux autres pour savoir si elles sont différentes. Tout d'abord, J'ai essayé de faire une correction Pearson des valeurs RGB, qui fonctionne aussi très bien à moins que les images sont un peu décalé. Donc si a a une image 100% identique mais une est un peu déplacée, j'obtiens une mauvaise valeur de corrélation.
des suggestions pour un meilleur algorithme?
BTW, je parle de comparer des milliers d'images...
Edit: Voici un exemple de mes photos (microscopiques):
im1:
im2:
im3:
im1 et im2 sont les mêmes, mais un peu décalé/découpé, im3 doit être reconnu comme complètement différent...
Edit: le problème est résolu avec les suggestions de Peter Hansen! Fonctionne très bien! Merci à toutes les réponses! Quelques résultats sont disponibles ici http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf
8 réponses
A question similaire a été posée il y a un an et a de nombreuses réponses, y compris une concernant la pixélisation des images, que j'allais suggérer comme au moins une étape de pré-qualification (car elle exclurait très rapidement les images très non similaires).
il y a également des liens là-bas à des questions encore plus tôt qui ont encore plus de références et de bonnes réponses.
Voici une implémentation utilisant certaines des idées avec Scipy, en utilisant vos trois images ci-dessus (enregistré en tant que im1.jpg, im2.jpg, im3.jpg, respectivement). La sortie finale montre im1 par rapport à lui-même, comme une ligne de base, puis chaque image par rapport aux autres.
>>> import scipy as sp
>>> from scipy.misc import imread
>>> from scipy.signal.signaltools import correlate2d as c2d
>>>
>>> def get(i):
... # get JPG image as Scipy array, RGB (3 layer)
... data = imread('im%s.jpg' % i)
... # convert to grey-scale using W3C luminance calc
... data = sp.inner(data, [299, 587, 114]) / 1000.0
... # normalize per http://en.wikipedia.org/wiki/Cross-correlation
... return (data - data.mean()) / data.std()
...
>>> im1 = get(1)
>>> im2 = get(2)
>>> im3 = get(3)
>>> im1.shape
(105, 401)
>>> im2.shape
(109, 373)
>>> im3.shape
(121, 457)
>>> c11 = c2d(im1, im1, mode='same') # baseline
>>> c12 = c2d(im1, im2, mode='same')
>>> c13 = c2d(im1, im3, mode='same')
>>> c23 = c2d(im2, im3, mode='same')
>>> c11.max(), c12.max(), c13.max(), c23.max()
(42105.00000000259, 39898.103896795357, 16482.883608327804, 15873.465425120798)
ainsi notez que im1 par rapport à lui-même donne une note de 42105, im2 par rapport à im1 n'est pas loin de cela, mais im3 par rapport à l'un ou l'autre des autres donne bien moins de la moitié de cette valeur. Vous devriez expérimenter avec d'autres images à voir comment cela pourrait effectuer et comment vous pourriez améliorer.
est long... plusieurs minutes sur mon répondeur. Je voudrais essayer un pré-filtrage pour éviter de perdre du temps en comparant des images très dissemblables, peut-être avec le truc "comparer la taille du fichier jpg" mentionné dans les réponses à l'autre question, ou avec la pixélisation. Le fait que vous ayez des images de différentes tailles complique les choses, mais vous n'avez pas donné assez d'informations sur l'étendue de la boucherie à laquelle on pourrait s'attendre, il est donc difficile de donner une réponse précise qui tient compte de cela.
j'ai fait ceci avec une comparaison d'histogramme d'image. Mon algorithme de base était ceci:
- image dédoublée en rouge, vert et bleu
- créer des histogrammes normalisés pour les canaux rouge, vert et bleu et les concaténer en un vecteur
(r0...rn, g0...gn, b0...bn)
où n est le nombre de "seaux", 256 devrait être suffisant - soustraire cet histogramme de l'histogramme d'une autre image et calculer la distance
voici un code avec numpy
et pil
r = numpy.asarray(im.convert( "RGB", (1,0,0,0, 1,0,0,0, 1,0,0,0) ))
g = numpy.asarray(im.convert( "RGB", (0,1,0,0, 0,1,0,0, 0,1,0,0) ))
b = numpy.asarray(im.convert( "RGB", (0,0,1,0, 0,0,1,0, 0,0,1,0) ))
hr, h_bins = numpy.histogram(r, bins=256, new=True, normed=True)
hg, h_bins = numpy.histogram(g, bins=256, new=True, normed=True)
hb, h_bins = numpy.histogram(b, bins=256, new=True, normed=True)
hist = numpy.array([hr, hg, hb]).ravel()
si vous avez deux histogrammes, vous pouvez obtenir la distance comme ceci:
diff = hist1 - hist2
distance = numpy.sqrt(numpy.dot(diff, diff))
si les deux images sont identiques, la distance est 0, plus elles divergent, plus la distance est grande.
il a fonctionné assez bien pour les photos pour moi, mais a échoué sur les graphiques comme les textes et les logos.
vous devez vraiment préciser la question mieux, mais, en regardant ces 5 images, les organismes semblent tous être orientés de la même manière. Si c'est toujours le cas, vous pouvez essayer de faire une corrélation croisée normalisée entre les deux images et de prendre la valeur de pointe comme votre degré de similitude. Je ne connais pas de fonction normalisée de corrélation croisée en Python, mais il existe une fonction similaire fftconvolve () et vous pouvez faire la circulaire corrélation croisée de vous-même:
a = asarray(Image.open('c603225337.jpg').convert('L'))
b = asarray(Image.open('9b78f22f42.jpg').convert('L'))
f1 = rfftn(a)
f2 = rfftn(b)
g = f1 * f2
c = irfftn(g)
cela ne fonctionnera pas comme écrit car les images sont de tailles différentes, et la sortie n'est pas pondérée ou normalisée du tout.
L'emplacement de la valeur de crête de la sortie indique le décalage entre les deux images, et l'amplitude de la pointe indique la similarité. Il devrait y avoir un moyen de le pondérer/le normaliser afin que vous puissiez faire la différence entre un bon match et un mauvais match.
ce n'est pas une bonne réponse que je veux, puisque je n'ai pas encore trouvé comment la normaliser, mais je vais la mettre à jour si je le trouve, et il vous donnera une idée à examiner.
si votre problème est sur les pixels décalés, peut-être que vous devriez comparer avec une transformation de fréquence.
The FFT should be OK ( numpy has an implementation for 2D matrices ), mais j'entends toujours que les ondelettes sont mieux pour ce genre de tâches ^ _ ^
à propos de la performance, si toutes les images sont de la même taille, si je me souviens bien, le paquet FFTW a créé une fonction spécialisée pour chaque taille d'entrée FFT, donc vous peut obtenir un boost performance agréable réutilisant le même code... Je ne sais pas si numpy est basé sur FFTW, mais si ce n'est pas peut-être vous pourriez essayer d'étudier un peu.
ici vous avez un prototype... vous pouvez jouer un peu avec pour voir quel seuil correspond à vos images.
import Image
import numpy
import sys
def main():
img1 = Image.open(sys.argv[1])
img2 = Image.open(sys.argv[2])
if img1.size != img2.size or img1.getbands() != img2.getbands():
return -1
s = 0
for band_index, band in enumerate(img1.getbands()):
m1 = numpy.fft.fft2(numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size))
m2 = numpy.fft.fft2(numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size))
s += numpy.sum(numpy.abs(m1-m2))
print s
if __name__ == "__main__":
sys.exit(main())
une autre façon de procéder pourrait être de brouiller les images, puis de soustraire les valeurs des pixels des deux images. Si la différence n'est pas nulle, vous pouvez déplacez une des images de 1 px dans chaque direction et comparez de nouveau, si la différence est plus faible que dans l'étape précédente, vous pouvez répéter le déplacement dans la direction du gradient et de soustraire jusqu'à ce que la différence est inférieure à un certain seuil ou augmente de nouveau. Cela devrait fonctionner si le rayon du noyau flou est plus grand que le déplacement des images.
aussi, vous pouvez essayer avec certains des outils qui sont couramment utilisés dans le flux de photographie pour mélanger expositions multiples ou faire des panoramas, comme le Pano Tools .
j'ai fait un cours de traitement d'image il y a longtemps, et rappelez-vous que lors de la correspondance j'ai normalement commencé par faire l'image en échelle de gris, puis en aiguisant les bords de l'image de sorte que vous voyez seulement les bords. Vous (le logiciel) pouvez alors déplacer et soustraire les images jusqu'à ce que la différence soit minime.
Si cette différence est supérieure au seuil défini, les images ne sont pas égaux et vous pouvez passer à la suivante. Les Images avec un seuil plus petit peuvent alors être analysés suivant.
je pense qu'au mieux on peut réduire radicalement les possibles matches, mais il faudra comparer personnellement les possibles matches pour déterminer qu'ils sont vraiment égaux.
Je ne peux pas vraiment montrer le code comme il était il y a longtemps, et J'ai utilisé Khoros/Cantata pour ce cours.
tout d'abord, la corrélation est une mesure de la similarité très intensive en CPU plutôt inexacte. Pourquoi ne pas simplement aller pour la somme des carrés si les différences entre les pixels individuels?
une solution simple, si le décalage maximum est limité: générer toutes les images décalées possibles et trouver celle qui correspond le mieux. Assurez-vous de calculer votre variable de correspondance (c.-à-d. la corrélation) seulement sur le sous-ensemble de pixels qui peuvent être appariés dans toutes les images déplacées. Aussi, votre le décalage maximum devrait être significativement plus petit que la taille de vos images.
si vous voulez utiliser des techniques de traitement d'image plus avancées, je vous suggère de regarder SIFT c'est une méthode très puissante qui (théoriquement de toute façon) peut faire correspondre correctement les éléments dans les images indépendamment de la traduction, la rotation et l'échelle.
je suppose que vous pourriez faire quelque chose comme ça:
-
estimation déplacement vertical / horizontal de l'image de référence par rapport à l'image de comparaison. un simple SAD (somme de la différence absolue) avec des vecteurs de mouvement ferait à.
-
déplacer l'image de comparaison en conséquence
- calculez la corrélation de pearson que vous essayiez de faire
La mesure du travail posté n'est pas difficile.
- prenez une région (disons environ 32x32) en Image de comparaison.
- déplacez - le par x pixels en horizontal et y pixels en direction verticale.
- calculer la dau (somme de la différence absolue) W. R. T. image originale
- Faites ceci pour plusieurs valeurs de x et y dans un petit intervalle (-10, +10)
- Trouver l'endroit où la différence est minimale
- choisir cette valeur comme vecteur de déplacement
Note:
si le SAD est très élevé pour toutes les valeurs de x et y alors vous pouvez de toute façon supposer que les images sont très dissemblables et la mesure de décalage n'est pas nécessaire.
pour faire fonctionner correctement les importations sur mon Ubuntu 16.04 (à partir d'avril 2017), j'ai installé python 2.7 et ceux-ci:
sudo apt-get install python-dev
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
sudo apt-get install python-scipy
sudo pip install pillow
puis j'ai changé les importations de Snowflake en:
import scipy as sp
from scipy.ndimage import imread
from scipy.signal.signaltools import correlate2d as c2d
comme ce flocon de neige scénarisé a fonctionné pour moi 8 ans plus tard!