web-dev-qa-db-fra.com

Comparaison d'images - algorithme rapide

Je cherche à créer une table d'images de base, puis à comparer toute nouvelle image avec celle-ci afin de déterminer si la nouvelle image est une copie exacte (ou proche) de la base.

Par exemple: si vous souhaitez réduire le stockage de la même image plusieurs centaines de fois, vous pouvez en stocker une copie et lui fournir des liens de référence. Quand une nouvelle image est entrée, vous voulez comparer avec une image existante pour vous assurer qu'il ne s'agit pas d'un duplicata… d'idées?

Une de mes idées était de réduire à une petite vignette, puis de choisir au hasard 100 emplacements de pixels et de les comparer.

377
meade

Voici trois approches pour résoudre ce problème (et il y en a beaucoup d'autres).

  • La première est une approche standard en vision par ordinateur, la correspondance de points-clés. Cela peut nécessiter des connaissances de base à mettre en œuvre et peut être lent.

  • La deuxième méthode utilise uniquement un traitement d'image élémentaire, et est potentiellement plus rapide que la première approche, et est simple à mettre en œuvre. Cependant, ce qui gagne en compréhension, manque de robustesse - la correspondance échoue sur les images redimensionnées, pivotées ou décolorées.

  • La troisième méthode est à la fois rapide et robuste, mais est potentiellement la plus difficile à mettre en œuvre.

Correspondance des points clés

Mieux que 100 points au hasard, c'est 100 points importants . Certaines parties d’une image contiennent plus d’informations que d’autres (en particulier les bords et les angles), et il s’agit de celles que vous souhaitez utiliser pour la correspondance d’image intelligente. Google " extraction de points-clés " et " correspondance de points-clés " et vous trouverez de nombreux articles scientifiques sur le sujet. De nos jours, points-clés SIFT sont sans doute les plus populaires, car ils peuvent associer des images à différentes échelles, rotations et éclairages. Certaines implémentations SIFT peuvent être trouvées ici .

Le point négatif de la correspondance des points-clés est la durée d'exécution d'une implémentation naïve: O (n ^ 2m), où n est le nombre de points-clés dans chaque image et m le nombre d'images de la base de données. Certains algorithmes intelligents pourraient trouver la correspondance la plus proche plus rapidement, comme les arbres quadrilatères ou le partitionnement d’espace binaire.


Solution alternative: méthode par histogramme

Une autre solution moins robuste mais potentiellement plus rapide consiste à créer des histogrammes de caractéristiques pour chaque image et à choisir l'image avec l'histogramme le plus proche de l'histogramme de l'image d'entrée. J'ai implémenté cela en tant qu'étudiant de premier cycle et nous avons utilisé 3 histogrammes de couleurs (rouge, vert et bleu) et deux histogrammes de texture, direction et échelle. Je donnerai les détails ci-dessous, mais je dois noter que cela ne fonctionnait que pour les images correspondantes TRES similaire aux images de la base de données. Les images redimensionnées, pivotées ou décolorées peuvent échouer avec cette méthode, mais de petites modifications telles que le recadrage ne casseront pas l'algorithme.

Le calcul des histogrammes de couleur est simple: il vous suffit de choisir la plage de vos compartiments d'histogramme et, pour chaque plage, de totaliser le nombre de pixels avec une couleur comprise dans cette plage. Par exemple, considérons l'histogramme "vert" et supposons que nous choisissions 4 compartiments pour notre histogramme: 0-63, 64-127, 128-191 et 192-255. Ensuite, pour chaque pixel, nous examinons la valeur verte et ajoutons un décompte au compartiment approprié. Lorsque nous avons terminé le décompte, nous divisons le total de chaque segment par le nombre de pixels de l'image entière pour obtenir un histogramme normalisé pour le canal vert.

Pour l'histogramme de direction de la texture, nous avons commencé par effectuer la détection des contours sur l'image. Chaque point de bord a un vecteur normal pointant dans la direction perpendiculaire au bord. Nous avons quantifié l'angle du vecteur normal dans l'un des 6 compartiments compris entre 0 et PI (les arêtes ayant une symétrie de 180 degrés, nous avons converti les angles entre -PI et 0 pour qu'ils soient compris entre 0 et PI). Après avoir totalisé le nombre de points de bord dans chaque direction, nous obtenons un histogramme non normalisé représentant la direction de la texture, que nous avons normalisé en divisant chaque seau par le nombre total de points de bord de l'image.

Pour calculer l'histogramme d'échelle de texture, nous avons mesuré la distance jusqu'au prochain point Edge avec la même direction pour chaque point d'arête. Par exemple, si le point d'arête A a une direction de 45 degrés, l'algorithme avance dans cette direction jusqu'à ce qu'il trouve un autre point d'arête ayant une direction de 45 degrés (ou avec un écart raisonnable). Après avoir calculé cette distance pour chaque point Edge, nous vidons ces valeurs dans un histogramme et nous le normalisons en divisant par le nombre total de points Edge.

Vous avez maintenant 5 histogrammes pour chaque image. Pour comparer deux images, prenez la valeur absolue de la différence entre chaque compartiment de l'histogramme, puis additionnez ces valeurs. Par exemple, pour comparer les images A et B, nous calculons

|A.green_histogram.bucket_1 - B.green_histogram.bucket_1| 

pour chaque intervalle de l'histogramme vert, répétez l'opération pour les autres histogrammes, puis récapitulez tous les résultats. Plus le résultat est petit, meilleur est le match. Répétez l'opération pour toutes les images de la base de données, et la correspondance avec le résultat le plus petit gagne. Vous voudrez probablement avoir un seuil, au-dessus duquel l'algorithme conclut qu'aucune correspondance n'a été trouvée.


Troisième choix - Points clés + arbres de décision

Une troisième approche qui est probablement beaucoup plus rapide que les deux autres utilise forêts de texton sémantique (PDF). Cela implique l'extraction de points-clés simples et l'utilisation d'un arbre de décision de collection pour classifier l'image. Cette procédure est plus rapide que la simple correspondance de points-clés SIFT, car elle évite le processus de correspondance coûteux, et les points-clés étant beaucoup plus simples que SIFT, l'extraction de points-clés est donc beaucoup plus rapide. Cependant, il préserve l'invariance de la méthode SIFT vis-à-vis de la rotation, de l'échelle et de la lumière, une caractéristique importante de la méthode histogramme.

Mise à jour :

Mon erreur: le document Semantic Texton Forests ne traite pas spécifiquement de la correspondance d'images, mais plutôt de l'étiquetage de région. Le document original qui fait correspondre est celui-ci: Reconnaissance de points-clés en utilisant des arbres aléatoires . De plus, les documents ci-dessous continuent à développer les idées et à représenter l'état de l'art (c. 2010):

441
Kyle Simek

La meilleure méthode que je connaisse consiste à utiliser un hachage perceptuel. Il semble y avoir une bonne implémentation open source d’un tel hachage disponible sur:

http://phash.org/

L'idée principale est que chaque image est réduite à un petit code de hachage ou à une "empreinte digitale" en identifiant les principales caractéristiques du fichier d'image d'origine et en hachant une représentation compacte de ces caractéristiques (plutôt qu'en hachant directement les données de l'image). Cela signifie que le taux de faux positifs est considérablement réduit par rapport à une approche simpliste telle que la réduction d'images à une image de la taille d'une empreinte minuscule et la comparaison d'empreintes digitales.

phash propose plusieurs types de hash et peut être utilisé pour des images, de l'audio ou de la vidéo.

80
redcalx

Ce message a été le point de départ de ma solution. Beaucoup de bonnes idées ici, donc je voulais partager mes résultats. La principale idée est que j'ai trouvé un moyen de contourner la lenteur de la correspondance d'images basée sur des points-clés en exploitant la vitesse de phash.

Pour la solution générale, il est préférable d’utiliser plusieurs stratégies. Chaque algorithme convient le mieux à certains types de transformations d’images et vous pouvez en tirer parti.

Au sommet, les algorithmes les plus rapides; au bas le plus lent (bien que plus précis). Vous pouvez sauter les plus lents si une bonne correspondance est trouvée au niveau le plus rapide.

  • base de hachage de fichier (md5, sha1, etc.) pour les doublons exacts
  • hachage perceptuel (phash) pour les images redimensionnées
  • fonction basée sur les caractéristiques (SIFT) pour les images modifiées

J'ai de très bons résultats avec phash. La précision est bonne pour les images redimensionnées. Ce n'est pas bon pour les images modifiées (perceptuellement) (rognées, pivotées, reflétées, etc.). Pour gérer la vitesse de hachage, nous devons utiliser un cache disque/une base de données pour gérer les hachages de la botte de foin.

Le truc vraiment sympa à propos de phash est qu’une fois que vous avez construit votre base de données de hachage (qui est pour moi environ 1000 images/s), les recherches peuvent être très, très rapides, en particulier lorsque vous pouvez conserver l’ensemble de la base de données de hachage en mémoire. C'est assez pratique car un hachage ne fait que 8 octets.

Par exemple, si vous avez 1 million d'images, vous aurez besoin d'un tableau de 1 million de valeurs de hachage 64 bits (8 Mo). Sur certains processeurs, cela correspond au cache L2/L3! Dans la pratique, j'ai vu un corei7 comparer à plus de 1 Giga-hamm/s, ce n'est qu'une question de bande passante mémoire pour le processeur. Une base de données de 1 milliard d'images est pratique sur un processeur 64 bits (8 Go RAM nécessaires) et les recherches ne dépasseront pas 1 seconde!

Pour les images modifiées/recadrées, il semblerait qu'un détecteur de fonction ou de point-clé invariable à la transformation, comme SIFT, soit la solution. SIFT produira de bons points-clés qui détecteront le recadrage/la rotation/le miroir, etc. Cependant, la comparaison des descripteurs est très lente par rapport à la distance de Hamming utilisée par phash. C'est une limitation majeure. Il y a beaucoup de comparaisons à faire, car il y a un descripteur IxJxK maximum qui se compare à la recherche d'une image (I = num images haystack, J = points clés cibles par image de botte de foin, K = points clés cibles par image d'aiguille).

Pour contourner le problème de vitesse, j'ai essayé d'utiliser phash autour de chaque point clé trouvé, en utilisant la taille/le rayon de la fonction pour déterminer le sous-rectangle. L'astuce pour que cela fonctionne bien consiste à agrandir/réduire le rayon pour générer différents niveaux inférieurs (sur l'image de l'aiguille). En règle générale, le premier niveau (non mis à l'échelle) sera identique, mais il en faudra parfois un peu plus. Je ne suis pas sûr à 100% pourquoi cela fonctionne, mais j'imagine que cela permet d'activer des fonctionnalités trop petites pour que phash fonctionne (images d'échelle jusqu'à 32x32).

Un autre problème est que SIFT ne distribuera pas les points-clés de manière optimale. S'il y a une section de l'image avec beaucoup d'arêtes, les points clés se regrouperont là et vous n'en obtiendrez pas dans une autre zone. J'utilise le GridAdaptedFeatureDetector dans OpenCV pour améliorer la distribution. Ne sachant pas quelle taille de grille est la meilleure, j'utilise une petite grille (1x3 ou 3x1 selon l'orientation de l'image).

Vous voudrez probablement redimensionner toutes les images de la botte de foin (et l'aiguille) avant la détection des caractéristiques (j'utilise 210px le long de la dimension maximale). Cela réduira le bruit dans l'image (toujours un problème pour les algorithmes de vision par ordinateur), et focalisera également le détecteur sur des caractéristiques plus importantes.

Pour les images de personnes, essayez la détection de visage et utilisez-la pour déterminer la taille de l'image à redimensionner et la taille de la grille (par exemple, le plus grand visage dont l'échelle a été définie est 100px). Le détecteur de caractéristiques prend en compte plusieurs niveaux d’échelle (à l’aide de pyramides), mais le nombre de niveaux qu’il utilisera est limité (ce qui est bien entendu réglable).

Le détecteur de points-clés fonctionne probablement mieux lorsqu'il renvoie moins que le nombre de fonctionnalités souhaitées. Par exemple, si vous demandez 400 et récupérez 300, c'est bien. Si vous en récupérez 400 à chaque fois, il faudra probablement omettre certaines bonnes fonctionnalités.

L'image de l'aiguille peut avoir moins de points-clés que les images de la botte de foin et toujours obtenir de bons résultats. Ajouter plus ne vous rapporte pas nécessairement d’énormes gains, par exemple avec J = 400 et K = 40, mon taux de réussite est d’environ 92%. Avec J = 400 et K = 400, le taux de réussite ne monte que jusqu'à 96%.

Nous pouvons tirer parti de l’extrême rapidité de la fonction de Hamming pour résoudre les problèmes de mise à l’échelle, de rotation, de mise en miroir, etc. Une technique à passes multiples peut être utilisée. À chaque itération, transformez les sous-rectangles, modifiez le hachage et exécutez à nouveau la fonction de recherche.

34
wally

J'ai une idée qui peut fonctionner et qui sera probablement très rapide. Vous pouvez sous-échantillonner une image pour obtenir une résolution de 80x60 ou une résolution comparable, puis la convertir en niveaux de gris (après le sous-échantillonnage, le temps sera plus rapide). Traitez les deux images que vous souhaitez comparer. Exécutez ensuite la somme normalisée des différences au carré entre deux images (l'image de requête et chacune de la base de données), ou mieux encore, une corrélation croisée normalisée, qui donne une réponse plus proche de 1 si les deux images sont similaires. Ensuite, si les images sont similaires, vous pouvez utiliser des techniques plus sophistiquées pour vérifier qu'il s'agit bien des mêmes images. Il est évident que cet algorithme est linéaire en termes de nombre d'images dans votre base de données, donc même s'il va être très rapide, il peut atteindre 10000 images par seconde avec le matériel moderne. Si vous avez besoin d'invariance par rapport à la rotation, un gradient dominant peut être calculé pour cette petite image, puis l'ensemble du système de coordonnées peut être pivoté selon une orientation canonique, ce qui sera toutefois plus lent. Et non, il n'y a pas d'invariance à l'échelle ici.

Si vous voulez quelque chose de plus général ou utilisant de grandes bases de données (des millions d'images), vous devez vous pencher sur la théorie de la récupération d'images (de nombreux articles sont parus au cours des 5 dernières années). Il y a des indications dans d'autres réponses. Mais c'est peut-être exagéré, et l'approche basée sur l'histogramme fera l'affaire. Bien que je pense que la combinaison de nombreuses approches rapides différentes sera encore meilleure.

6
Denis C

Comme l'a souligné Cartman, vous pouvez utiliser n'importe quel type de valeur de hachage pour trouver les doublons exacts.

Un point de départ pour trouver des images proches pourrait être ici . Il s’agit d’un outil utilisé par les sociétés de CG pour vérifier si les images réaménagées montrent toujours essentiellement la même scène.

6
Tobiesque

Je pense que réduire la taille de l'image à une taille proche de celle de l'icône, disons 48 x 48, puis convertir en niveaux de gris, puis prendre la différence entre les pixels, ou Delta, devrait bien fonctionner. Étant donné que nous comparons le changement de couleur de pixel, plutôt que la couleur de pixel réelle, le fait que l'image soit légèrement plus claire ou plus sombre importe peu. Les changements importants seront importants car les pixels trop clairs/sombres seront perdus. Vous pouvez l'appliquer sur une ligne ou autant que vous le souhaitez pour augmenter la précision. Tout au plus vous auriez 47x47 = 2 209 soustractions à faire afin de former une clé comparable.

5
Tanoshimi

Si vous choisissez 100 points aléatoires, cela signifie que des images similaires (ou parfois même dissemblables) seront marquées comme identiques, ce qui, je suppose, n’est pas ce que vous voulez. Les hachages MD5 ne fonctionneraient pas si les images étaient de formats différents (png, jpeg, etc.), de tailles différentes ou de métadonnées différentes. Réduire toutes les images à une taille plus petite est un bon choix. Effectuer une comparaison pixel par pixel ne devrait pas prendre trop de temps tant que vous utilisez une bonne bibliothèque d'images/langage rapide, et que la taille est suffisamment petite.

Vous pouvez essayer de les rendre minuscules, puis, s’ils sont identiques, effectuez une autre comparaison sur une taille plus grande - cela pourrait être une bonne combinaison de vitesse et de précision ...

3
HarryM

Mon entreprise a environ 24 millions d’images reçues des fabricants tous les mois. Je recherchais une solution rapide pour nous assurer que les images que nous téléchargeons dans notre catalogue sont nouvelles .

Je tiens à dire que j'ai parcouru de très loin Internet pour tenter de trouver la solution idéale. J'ai même développé mon propre algorithme de détection de bord.
J'ai évalué la vitesse et la précision de plusieurs modèles. Mes images, qui ont un fond blanc, fonctionnent extrêmement bien avec le phasage. Comme redcalx a dit, je recommande phash ou ahash. NE PAS utiliser le hachage MD5 ou tout autre hachage cryptographique. À moins que vous ne vouliez que des correspondances d'image EXACT. Tout redimensionnement ou manipulation qui se produit entre les images donnera un hachage différent.

Pour phash/ahash, vérifiez ceci: imagehash

Je voulais prolonger le poste de * redcalx '* en postant mon code et mon exactitude.

Ce que je fais:

from PIL import Image
from PIL import ImageFilter
import imagehash

img1=Image.open(r"C:\yourlocation")
img2=Image.open(r"C:\yourlocation")
if img1.width<img2.width:
    img2=img2.resize((img1.width,img1.height))
else:
    img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
totalaccuracy=phashvalue+ahashvalue

Voici quelques uns de mes résultats:

item1  item2  totalaccuracy
desk1  desk2       3
desk2  phone1     22
chair1 desk1      17
phone1 chair1     34

où l'élément représente le sujet réel de l'image et le nombre représente l'échelle d'orientation.

J'espère que cela t'aides!

2
Tanner Clark

Si vous avez un grand nombre d'images, examinez le filtre de Bloom , qui utilise plusieurs hachages pour un résultat probable mais efficace. Si le nombre d'images n'est pas énorme, un hachage cryptographique tel que md5 devrait suffire.

2
jdigital