web-dev-qa-db-fra.com

Algorithme de comparaison d'images

J'essaie de comparer des images les unes aux autres pour savoir si elles sont différentes. J'ai d'abord essayé de faire une corrélation de Pearson des valeurs RVB, ce qui fonctionne également très bien à moins que les images soient légèrement décalées. Donc, si vous avez des images 100% identiques mais que l'une d'entre elles 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'imgages ...

Edit: Voici un exemple de mes images (microscopiques):

im1: 

enter image description here

im2: 

enter image description here

im3: 

enter image description here

im1 et im2 sont les mêmes mais un peu décalés/coupés, im3 devrait ê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! Certains résultats peuvent être trouvés ici http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

39
snowflake

Une question similaire a été posée il y a un an et a de nombreuses réponses, dont une concernant la pixellisation des images, que j'allais suggérer au moins comme une étape de pré-qualification (car elle exclurait des images très non similaires assez rapidement).

Vous y trouverez également des liens vers des questions encore antérieures, qui contiennent encore plus de références et de bonnes réponses.

Voici une implémentation utilisant certaines des idées de Scipy, en utilisant les trois images ci-dessus (enregistrées respectivement sous les noms im1.jpg, im2.jpg, im3.jpg). La sortie finale montre im1 comparée à elle-même, sous forme de ligne de base, puis chaque image comparée 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)

Notez donc que im1 par rapport à lui-même donne un score de 42105, im2 par rapport à im1 n’est pas très éloigné de cela, mais im3 par rapport aux autres donne bien moins de la moitié de cette valeur. Vous devrez expérimenter d'autres images pour voir si cela peut fonctionner et comment l'améliorer.

Le temps d'exécution est long ... plusieurs minutes sur ma machine. Je voudrais essayer un filtrage préalable pour éviter de perdre du temps à comparer des images très différentes, peut-être avec l’astuce "comparer la taille du fichier jpg" mentionnée dans les réponses à l’autre question, ou avec la pixellisation. Le fait que vous ayez des images de différentes tailles complique les choses, mais vous ne donnez pas assez d'informations sur l'ampleur de la dépeçage à laquelle on pourrait s'attendre. Il est donc difficile de donner une réponse spécifique tenant compte de cela.

37
Peter Hansen

J'ai un fait cela avec une comparaison d'histogramme d'image. Mon algorithme de base était le suivant:

  1. Fractionner l'image en rouge, vert et bleu
  2. Créer des histogrammes normalisés pour les canaux rouge, vert et bleu et les concaténer dans un vecteur (r0...rn, g0...gn, b0...bn) où n est le nombre de "compartiments", 256 devraient suffire
  3. soustraire cet histogramme de l'histogramme d'une autre image et calculer la distance

voici du 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.

Cela a très bien fonctionné pour les photos pour moi mais a échoué pour les graphiques comme les textes et les logos.

13
Otto Allmendinger

Si votre problème concerne les pixels décalés, vous devriez peut-être comparer à une transformation de fréquence.

La FFT devrait être OK ( numpy a une implémentation pour les matrices 2D ), mais j'entends toujours dire que les ondelettes sont meilleures pour ce genre de tâches ^ _ ^ 

À propos des performances, si toutes les images ont 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, afin que vous puissiez obtenir un boost de performances Nice en réutilisant le même code ... Je ne sais pas si numpy est basé sur FFTW, mais si ce n’est pas le cas, vous pourriez peut-être essayer d’enquêter un peu là-bas.

Ici vous avez un prototype ... vous pouvez en jouer un peu 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 __== "__main__":
    sys.exit(main())

Une autre façon de procéder pourrait être de rendre les images floues, puis de soustraire les valeurs en pixels des deux images. Si la différence est non nulle, vous pouvez déplacer l'une des images de 1 px dans chaque direction et comparer à nouveau. Si la différence est inférieure à l'étape précédente, vous pouvez répéter le décalage dans la direction du dégradé et la soustraction jusqu'à la différence. est inférieur à un certain seuil ou augmente à nouveau. Cela devrait fonctionner si le rayon du noyau flou est plus grand que le décalage des images.

En outre, vous pouvez essayer avec certains des outils couramment utilisés dans le flux de production photographique pour mélanger plusieurs expositions ou créer des panoramas, comme le Pano Tools .

6
fortran

Vous devez vraiment mieux préciser la question, mais, en regardant ces 5 images, les organismes semblent tous être orientés de la même manière. Si cela est toujours le cas, vous pouvez essayer d'effectuer une corrélation croisée normalisée entre les deux images et de prendre la valeur maximale en tant que votre degré de similarité. Je ne connais pas de fonction de corrélation croisée normalisée en Python, mais il existe une fonction similaire fftconvolve () et vous pouvez effectuer la corrélation croisée circulaire 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 tel qu'écrit car les images ont des tailles différentes et que la sortie n'est ni pondérée ni normalisée.

L'emplacement de la valeur de crête de la sortie indique le décalage entre les deux images et la magnitude de la crête 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 réponse aussi satisfaisante que je le voudrais, car je n’ai pas encore trouvé le moyen de le normaliser, mais je le mettrai à jour si je le découvre, ce qui vous donnera une idée à examiner.

6
endolith

J'ai suivi des cours de traitement d'images il y a longtemps et je me souviens qu'en débutant, je commençais normalement à rendre les niveaux de gris de l'image, puis à accentuer les contours de l'image pour ne voir que les bords. Vous (le logiciel) pouvez ensuite décaler et soustraire les images jusqu'à ce que la différence soit minimale. 

Si cette différence est supérieure au seuil que vous avez défini, les images ne sont pas égales et vous pouvez passer au suivant. Les images avec un seuil inférieur peuvent ensuite être analysées.

Je pense que, au mieux, vous pouvez radicalement éliminer les correspondances possibles, mais vous devrez personnellement comparer les correspondances possibles pour déterminer si elles sont vraiment égales.

Je ne peux pas vraiment montrer le code tel qu'il était il y a longtemps, et j'ai utilisé Khoros/Cantata pour ce cours.

2
extraneon

Tout d’abord, la corrélation est une mesure assez imprécise de la similarité qui nécessite beaucoup de temps de calcul. 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érez toutes les images décalées possibles et recherchez celle qui correspond le mieux. Assurez-vous de calculer votre variable de correspondance (corrélation, par exemple) uniquement sur le sous-ensemble de pixels pouvant être mis en correspondance dans toutes les images décalées. En outre, votre décalage maximum doit être nettement inférieur à la taille de vos images.

Si vous souhaitez utiliser des techniques de traitement d'image plus avancées, je vous suggère de regarder SIFT . Il s'agit d'une méthode très puissante qui (théoriquement en tout cas) peut bien faire correspondre les éléments des images indépendamment de la translation, de la rotation et de l'échelle.

1
jilles de wit

Pour que les importations fonctionnent correctement sur mon Ubuntu 16.04 (à compter d'avril 2017), j'ai installé Python 2.7 et les suivantes:

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

Ensuite, j'ai modifié les importations de Snowflake par:

import scipy as sp
from scipy.ndimage import imread
from scipy.signal.signaltools import correlate2d as c2d

Quelle merveille que le script de Snowflake ait fonctionné pour moi huit ans plus tard!

0
Doug Null

Je suppose que vous pourriez faire quelque chose comme ça:

  • estimer le déplacement vertical/horizontal de l'image de référence par rapport à l'image de comparaison. un simple. SAD (somme de différence absolue) avec des vecteurs de mouvement ferait à. 

  • décaler l'image de comparaison en conséquence 

  • calculer la corrélation de pearson que vous essayiez de faire

La mesure de décalage n'est pas difficile. 

  • Prenez une région (disons environ 32x32) dans l’image de comparaison. 
  • Déplacez-le de x pixels en horizontal et de y pixels en direction verticale. 
  • Calculez le SAD (somme de la différence absolue) w.r.t. image originale
  • Faites cela pour plusieurs valeurs de x et y dans une petite plage (-10, +10)
  • Trouvez l'endroit où la différence est minimale
  • Choisissez cette valeur comme vecteur de déplacement 

Remarque:

Si le SAD arrive très haut pour toutes les valeurs de x et y, vous pouvez néanmoins supposer que les images sont très différentes et que la mesure par décalage n'est pas nécessaire.

0
Shailesh Kumar