wake-up-neo.com

Algorithmus zum Vergleich von Bildern

Ich versuche, Bilder miteinander zu vergleichen, um herauszufinden, ob sie sich unterscheiden. Zuerst habe ich versucht, eine Pearson-Korrelation der RGB-Werte herzustellen, was auch gut funktioniert, wenn die Bilder nicht ein bisschen verschoben sind. Wenn also ein zu 100% identisches Bild vorliegt, eines aber ein wenig verschoben ist, bekomme ich einen schlechten Korrelationswert.

Irgendwelche Vorschläge für einen besseren Algorithmus? 

BTW, ich spreche von Tausenden von Imgages ...

Edit: Hier ist ein Beispiel meiner Bilder (mikroskopisch):

im1: 

enter image description here

im2: 

enter image description here

im3: 

enter image description here

im1 und im2 sind gleich, aber etwas verschoben/geschnitten, im3 sollte als völlig anders erkannt werden ...

Edit:Problem mit den Vorschlägen von Peter Hansen gelöst! Funktioniert sehr gut! Danke an alle Antworten! Einige Ergebnisse finden Sie hier http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

39
snowflake

Eine ähnliche Frage wurde vor einem Jahr gestellt und hat zahlreiche Antworten, einschließlich einer zur Pixelierung der Bilder, die ich als einen Vorqualifizierungsschritt vorschlagen würde (da dies sehr schnell sehr ungleiche Bilder ausschließen würde) ).

Dort gibt es auch Links zu noch früheren Fragen, die noch mehr Referenzen und gute Antworten enthalten.

Hier ist eine Implementierung, bei der einige der Ideen mit Scipy verwendet werden, wobei Ihre drei obigen Bilder verwendet werden (gespeichert als im1.jpg, im2.jpg, im3.jpg). Die endgültige Ausgabe zeigt im1 im Vergleich zu sich selbst als Basis und jedes Bild im Vergleich zu den anderen.

>>> 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)

Man beachte, dass im1 im Vergleich zu sich selbst eine Punktzahl von 42105 ergibt, im2 im Vergleich zu im1 ist nicht weit davon entfernt, aber im3 im Vergleich zu einem der anderen Werte liegt die Hälfte deutlich unter der Hälfte. Sie müssten mit anderen Bildern experimentieren, um zu sehen, wie gut dies funktioniert und wie Sie es verbessern können.

Die Laufzeit ist lang ... einige Minuten auf meinem Rechner. Ich würde eine Vorfilterung versuchen, um Zeitverschwendung beim Vergleich sehr unterschiedlicher Bilder zu vermeiden, vielleicht mit dem Trick "jpg-Dateigröße vergleichen", der in den Antworten auf die andere Frage erwähnt wurde, oder mit der Pixelisierung. Die Tatsache, dass Sie Bilder in verschiedenen Größen haben, macht die Sache komplizierter, aber Sie haben nicht genug Informationen über das Ausmaß des Schlächtens gegeben, das man erwarten könnte. Daher ist es schwierig, eine bestimmte Antwort zu geben, die dies berücksichtigt.

37
Peter Hansen

Ich habe dies mit einem Bildhistogrammvergleich gemacht. Mein grundlegender Algorithmus war dieser:

  1. Bild in rot, grün und blau aufteilen
  2. Erstellen Sie normalisierte Histogramme für den roten, grünen und blauen Kanal und verketten Sie sie zu einem Vektor (r0...rn, g0...gn, b0...bn). Dabei ist n die Anzahl der "Buckets", 256 sollten ausreichen
  3. subtrahieren Sie dieses Histogramm vom Histogramm eines anderen Bildes und berechnen Sie die Entfernung

hier ist etwas Code mit numpy und 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()

wenn Sie zwei Histogramme haben, können Sie die Entfernung folgendermaßen berechnen:

diff = hist1 - hist2
distance = numpy.sqrt(numpy.dot(diff, diff))

Wenn die beiden Bilder identisch sind, beträgt der Abstand 0, je mehr sie auseinander gehen, desto größer ist der Abstand.

Es funktionierte für mich ziemlich gut, aber bei Grafiken wie Texten und Logos fehlgeschlagen.

13

Wenn es sich bei Ihrem Problem um verschobene Pixel handelt, sollten Sie es mit einer Frequenztransformation vergleichen.

Die FFT sollte in Ordnung sein ( numpy hat eine Implementierung für 2D-Matrizen ), aber ich höre immer, dass Wavelets besser für diese Art von Aufgaben geeignet sind 

Wenn alle Bilder die gleiche Größe haben, wenn ich mich recht gut erinnere, erstellte das FFTW-Paket für jede FFT-Eingangsgröße eine spezielle Funktion, so dass Sie einen Nizza-Leistungsschub erhalten können, der den gleichen Code wiederverwendet ... Ich weiß nicht, ob Numpy auf FFTW basiert, aber wenn nicht, könnten Sie versuchen, dort ein wenig nachzuforschen.

Hier haben Sie einen Prototyp ... Sie können ein wenig damit spielen, um zu sehen, welche Schwelle zu Ihren Bildern passt.

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())

Eine andere Möglichkeit besteht darin, die Bilder unscharf zu machen und dann die Pixelwerte von den beiden Bildern abzuziehen. Wenn die Differenz nicht null ist, können Sie eines der Bilder um 1 px in jede Richtung verschieben und erneut vergleichen. Wenn die Differenz geringer ist als im vorherigen Schritt, können Sie die Verschiebung in Richtung des Farbverlaufs wiederholen und bis zur Differenz subtrahieren ist niedriger als eine bestimmte Schwelle oder steigt wieder an. Das sollte funktionieren, wenn der Radius des Unschärfekerns größer ist als die Verschiebung der Bilder.

Sie können auch einige der Werkzeuge verwenden, die häufig im Fotografie-Workflow zum Mischen mehrerer Expositionen oder zum Erstellen von Panoramen verwendet werden, z. B. die Pano-Tools .

6
fortran

Man muss die Frage wirklich genauer spezifizieren, aber wenn man diese 5 Bilder betrachtet, scheinen die Organismen alle gleich auszurichten. Wenn dies immer der Fall ist, können Sie versuchen, eine normalisierte Kreuzkorrelation zwischen den beiden Bildern auszuführen und den Spitzenwert als Ähnlichkeitsgrad zu nehmen. Ich kenne keine normalisierte Kreuzkorrelationsfunktion in Python, aber es gibt eine ähnliche fftconvolve () - Funktion, und Sie können die zirkuläre Kreuzkorrelation selbst durchführen:

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)

Dies funktioniert nicht wie geschrieben, da die Bilder unterschiedliche Größen haben und die Ausgabe überhaupt nicht gewichtet oder normalisiert wird.

Die Position des Spitzenwerts der Ausgabe gibt den Versatz zwischen den beiden Bildern an, und die Größe des Spitzenwerts zeigt die Ähnlichkeit an. Es sollte eine Möglichkeit geben, sie zu gewichten/zu normalisieren, damit Sie den Unterschied zwischen einem guten und einem schlechten Spiel erkennen können.

Das ist keine so gute Antwort, wie ich will, da ich noch nicht herausgefunden habe, wie ich es normalisieren könnte, aber ich werde es aktualisieren, wenn ich es herausfinde, und es wird Ihnen eine Idee geben, die ich untersuchen sollte.

6
endolith

Ich habe schon vor einiger Zeit einige Bildverarbeitungskurse durchgeführt, und ich erinnere mich daran, dass ich beim Abgleich normalerweise mit dem Erstellen der Graustufen des Bildes begonnen habe und dann die Bildränder schärfer gemacht habe, so dass Sie nur Kanten sehen. Sie (die Software) können dann die Bilder verschieben und subtrahieren, bis der Unterschied minimal ist. 

Wenn diese Differenz größer ist als der von Ihnen festgelegte Schwellenwert, sind die Bilder nicht gleich und Sie können mit dem nächsten fortfahren. Bilder mit einem kleineren Schwellenwert können als nächstes analysiert werden.

Ich denke, dass Sie im besten Fall mögliche Übereinstimmungen radikal ausdünnen können, aber mögliche Übereinstimmungen persönlich vergleichen müssen, um festzustellen, dass sie wirklich gleichwertig sind.

Ich kann Code nicht wirklich zeigen, da es vor langer Zeit war, und ich habe Khoros/Cantata für diesen Kurs verwendet.

2
extraneon

Zunächst einmal ist Korrelation ein sehr rechenintensives, eher ungenaues Maß für Ähnlichkeit. Warum nicht einfach die Summe der Quadrate wählen, wenn Unterschiede zwischen einzelnen Pixeln bestehen?

Eine einfache Lösung, wenn die maximale Verschiebung begrenzt ist: Generieren Sie alle möglichen verschobenen Bilder und finden Sie das Bild, das am besten passt. Stellen Sie sicher, dass Sie Ihre Übereinstimmungsvariable (d. H. Korrelation) nur für die Teilmenge der Pixel berechnen, die in allen verschobenen Bildern abgeglichen werden können. Außerdem sollte Ihre maximale Verschiebung deutlich geringer sein als die Größe Ihrer Bilder.

Wenn Sie weitere Bildverarbeitungsverfahren verwenden möchten, sollten Sie SIFT betrachten. Dies ist eine sehr leistungsfähige Methode, die (theoretisch sowieso) die Elemente in Bildern unabhängig von der Translation, Rotation und Skalierung richtig zuordnen kann.

1
jilles de wit

Damit die Importe unter Ubuntu 16.04 (Stand April 2017) korrekt funktionieren, habe ich Python 2.7 und folgende installiert:

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

Dann habe ich Snowflakes Importe in diese geändert:

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

Wie fantastisch, dass Snowflakes Drehbücher 8 Jahre später für mich funktionierten!

0
Doug Null

Ich schlage eine Lösung vor, die auf dem Jaccard-Ähnlichkeitsindex der Bildhistogramme basiert. Siehe: https://en.wikipedia.org/wiki/Jaccard_index#Weighted_Jaccard_similarity_and_distance

Sie können den Unterschied in der Verteilung der Pixelfarben berechnen. Dies ist in der Tat ziemlich unveränderlich für Übersetzungen.

from PIL.Image import Image
from typing import List

def jaccard_similarity(im1: Image, im2: Image) -> float:
    """Compute the similarity between two images.
    First, for each image an histogram of the pixels distribution is extracted.
    Then, the similarity between the histograms is compared using the weighted Jaccard index of similarity, defined as:
    Jsimilarity = sum(min(b1_i, b2_i)) / sum(max(b1_i, b2_i)
    where b1_i, and b2_i are the ith histogram bin of images 1 and 2, respectively.

    The two images must have same resolution and number of channels (depth).

    See: https://en.wikipedia.org/wiki/Jaccard_index
    Where it is also called Ruzicka similarity."""

    if im1.size != im2.size:
        raise Exception("Images must have the same size. Found {} and {}".format(im1.size, im2.size))

    n_channels_1 = len(im1.getbands())
    n_channels_2 = len(im2.getbands())
    if n_channels_1 != n_channels_2:
        raise Exception("Images must have the same number of channels. Found {} and {}".format(n_channels_1, n_channels_2))

    assert n_channels_1 == n_channels_2

    sum_mins = 0
    sum_maxs = 0

    hi1 = im1.histogram()  # type: List[int]
    hi2 = im2.histogram()  # type: List[int]

    # Since the two images have the same amount of channels, they must have the same amount of bins in the histogram.
    assert len(hi1) == len(hi2)

    for b1, b2 in Zip(hi1, hi2):
        min_b = min(b1, b2)
        sum_mins += min_b
        max_b = max(b1, b2)
        sum_maxs += max_b

    jaccard_index = sum_mins / sum_maxs

    return jaccard_index

In Bezug auf den mittleren quadratischen Fehler liegt der Jaccard-Index immer im Bereich [0,1], wodurch Vergleiche zwischen verschiedenen Bildgrößen möglich sind.

Dann können Sie die beiden Bilder vergleichen, aber nach dem erneuten Skalieren auf die gleiche Größe! Andernfalls müssen die Pixelzahlen irgendwie normalisiert werden. Ich habe das benutzt:

import sys

from skincare.common.utils import jaccard_similarity

import PIL.Image
from PIL.Image import Image

file1 = sys.argv[1]
file2 = sys.argv[2]

im1 = PIL.Image.open(file1)  # type: Image
im2 = PIL.Image.open(file2)  # type: Image

print("Image 1: mode={}, size={}".format(im1.mode, im1.size))
print("Image 2: mode={}, size={}".format(im2.mode, im2.size))

if im1.size != im2.size:
    print("Resizing image 2 to {}".format(im1.size))
    im2 = im2.resize(im1.size, resample=PIL.Image.BILINEAR)

j = jaccard_similarity(im1, im2)
print("Jaccard similarity index = {}".format(j))

Testen Ihrer Bilder:

$ python CompareTwoImages.py im1.jpg im2.jpg
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(373, 109)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.7238955686269157
$ python CompareTwoImages.py im1.jpg im3.jpg 
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.22785529941822316
$ python CompareTwoImages.py im2.jpg im3.jpg 
Image 1: mode=RGB, size=(373, 109)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (373, 109)
Jaccard similarity index = 0.29066426814105445

Sie könnten auch überlegen, mit verschiedenen Resampling-Filtern (wie NEAREST oder LANCZOS) zu experimentieren, da diese natürlich die Farbverteilung bei der Größenänderung verändern.

Bedenken Sie außerdem, dass das Austauschen von Bildern die Ergebnisse verändert, da das zweite Bild möglicherweise nicht hoch-, sondern heruntergerechnet wird.

0
fnunnari

Ich denke, du könntest so etwas tun:

  • schätzung der vertikalen/horizontalen Verschiebung des Referenzbildes gegenüber dem Vergleichsbild. Eine einfache SAD (Summe der absoluten Differenz) mit Bewegungsvektoren würde dazu beitragen. 

  • verschieben Sie das Vergleichsbild entsprechend 

  • berechnen Sie die Pearson-Korrelation, die Sie versuchen wollten

Die Schichtmessung ist nicht schwierig. 

  • Nehmen Sie eine Region (etwa 32x32) als Vergleichsbild. 
  • Verschieben Sie es um x Pixel in horizontaler und y Pixel in vertikaler Richtung. 
  • Berechnen Sie die SAD (Summe der absoluten Differenz) w.r.t. Original Bild
  • Tun Sie dies für mehrere Werte von x und y in einem kleinen Bereich (-10, +10)
  • Finden Sie den Ort, an dem der Unterschied minimal ist
  • Wählen Sie diesen Wert als Verschiebungsbewegungsvektor 

Hinweis:

Wenn die SAD für alle Werte von x und y sehr hoch ist, können Sie trotzdem davon ausgehen, dass die Bilder sehr unterschiedlich sind und die Schichtmessung nicht erforderlich ist.

0
Shailesh Kumar