it-swarm.com.de

Vergleichen Sie die Ähnlichkeit von Bildern mit OpenCV mit Python

Ich versuche, ein Bild mit einer Liste anderer Bilder zu vergleichen und eine Auswahl von Bildern (wie Google-Suchbildern) dieser Liste mit bis zu 70% Ähnlichkeit zurückzugeben.

Ich bekomme diesen Code in diesem Beitrag und ändere meinen Kontext

# Load the images
img =cv2.imread(MEDIA_ROOT + "/uploads/imagerecognize/armchair.jpg")

# Convert them to grayscale
imgg =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# SURF extraction
surf = cv2.FeatureDetector_create("SURF")
surfDescriptorExtractor = cv2.DescriptorExtractor_create("SURF")
kp = surf.detect(imgg)
kp, descritors = surfDescriptorExtractor.compute(imgg,kp)

# Setting up samples and responses for kNN
samples = np.array(descritors)
responses = np.arange(len(kp),dtype = np.float32)

# kNN training
knn = cv2.KNearest()
knn.train(samples,responses)

modelImages = [MEDIA_ROOT + "/uploads/imagerecognize/1.jpg", MEDIA_ROOT + "/uploads/imagerecognize/2.jpg", MEDIA_ROOT + "/uploads/imagerecognize/3.jpg"]

for modelImage in modelImages:

    # Now loading a template image and searching for similar keypoints
    template = cv2.imread(modelImage)
    templateg= cv2.cvtColor(template,cv2.COLOR_BGR2GRAY)
    keys = surf.detect(templateg)

    keys,desc = surfDescriptorExtractor.compute(templateg, keys)

    for h,des in enumerate(desc):
        des = np.array(des,np.float32).reshape((1,128))

        retval, results, neigh_resp, dists = knn.find_nearest(des,1)
        res,dist =  int(results[0][0]),dists[0][0]


        if dist<0.1: # draw matched keypoints in red color
            color = (0,0,255)

        else:  # draw unmatched in blue color
            #print dist
            color = (255,0,0)

        #Draw matched key points on original image
        x,y = kp[res].pt
        center = (int(x),int(y))
        cv2.circle(img,center,2,color,-1)

        #Draw matched key points on template image
        x,y = keys[h].pt
        center = (int(x),int(y))
        cv2.circle(template,center,2,color,-1)



    cv2.imshow('img',img)
    cv2.imshow('tm',template)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Meine Frage ist, wie kann ich das Bild mit der Liste der Bilder vergleichen und nur ähnliche Bilder erhalten? Gibt es dafür eine Methode?

29
leeeandroo

Ich schlage vor, dass Sie einen Blick auf die Entfernung der Erdbewegungsvorrichtung (EMD) zwischen den Bildern werfen. Diese Metrik gibt an, wie schwer es ist, ein normalisiertes Graustufenbild in ein anderes zu transformieren, kann aber für Farbbilder verallgemeinert werden. Eine sehr gute Analyse dieser Methode finden Sie in folgendem Artikel:

robotics.stanford.edu/~rubner/papers/rubnerIjcv00.pdf

Dies kann sowohl für das gesamte Bild als auch für das Histogramm erfolgen (was wirklich schneller ist als die gesamte Bildmethode). Ich bin nicht sicher, welche Methode einen vollständigen Bildvergleich zulässt, aber für den Vergleich von Histogrammen können Sie die Funktion cv.CalcEMD2 verwenden.

Das einzige Problem ist, dass diese Methode keinen Prozentsatz der Ähnlichkeit definiert, sondern eine Entfernung, nach der Sie filtern können.

Ich weiß, dass dies kein voll funktionsfähiger Algorithmus ist, aber immer noch eine Basis dafür ist, also hoffe ich, dass es hilft.

BEARBEITEN:

Hier ist eine Parodie, wie das EMD im Prinzip funktioniert. Die Grundidee besteht darin, zwei normalisierte Matrizen (zwei Graustufenbilder, geteilt durch ihre Summe) und die Definition einer Flussmatrix, die beschreibt, wie Sie das Grau vom ersten Bild zum anderen verschieben, um das zweite zu erhalten (es kann sogar definiert werden) für nicht normalisierte, ist aber schwieriger).

Mathematisch gesehen handelt es sich bei der Fließmatrix eigentlich um einen viereckigen Tensor, der den Fluss vom Punkt (i, j) des alten Bilds zum Punkt (k, l) des neuen Bilds liefert. Wenn Sie jedoch Ihre Bilder abflachen, können Sie es transformieren zu einer normalen Matrix, nur etwas schwieriger zu lesen.

Diese Flussmatrix hat drei Einschränkungen: Jeder Ausdruck sollte positiv sein, die Summe jeder Zeile sollte den gleichen Wert des Bezeichnungspixels und die Summe jeder Spalte den Wert des Startpixels zurückgeben.

In Anbetracht dessen müssen Sie die Kosten der Transformation minimieren, die sich aus der Summe der Produkte jedes Flusses von (i, j) bis (k, l) für den Abstand zwischen (i, j) und (k, l) ergibt.

Es sieht in Wörtern etwas kompliziert aus, also hier der Testcode. Die Logik ist korrekt, ich bin mir nicht sicher, warum der Scipy-Solver sich darüber beschwert (Sie sollten vielleicht nach openOpt oder etwas Ähnlichem suchen):

#original data, two 2x2 images, normalized
x = Rand(2,2)
x/=sum(x)
y = Rand(2,2)
y/=sum(y)

#initial guess of the flux matrix
# just the product of the image x as row for the image y as column
#This is a working flux, but is not an optimal one
F = (y.flatten()*x.flatten().reshape((y.size,-1))).flatten()

#distance matrix, based on euclidean distance
row_x,col_x = meshgrid(range(x.shape[0]),range(x.shape[1]))
row_y,col_y = meshgrid(range(y.shape[0]),range(y.shape[1]))
rows = ((row_x.flatten().reshape((row_x.size,-1)) - row_y.flatten().reshape((-1,row_x.size)))**2)
cols = ((col_x.flatten().reshape((row_x.size,-1)) - col_y.flatten().reshape((-1,row_x.size)))**2)
D = np.sqrt(rows+cols)

D = D.flatten()
x = x.flatten()
y = y.flatten()
#COST=sum(F*D)

#cost function
fun = lambda F: sum(F*D)
jac = lambda F: D
#array of constraint
#the constraint of sum one is implicit given the later constraints
cons  = []
#each row and columns should sum to the value of the start and destination array
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[i,:])-x[i]}     for i in range(x.size) ]
cons += [ {'type': 'eq', 'fun': lambda F:  sum(F.reshape((x.size,y.size))[:,i])-y[i]} for i in range(y.size) ]
#the values of F should be positive
bnds = (0, None)*F.size

from scipy.optimize import minimize
res = minimize(fun=fun, x0=F, method='SLSQP', jac=jac, bounds=bnds, constraints=cons)

die Variable res enthält das Ergebnis der Minimierung ... aber wie gesagt, ich weiß nicht, warum sie sich über eine singuläre Matrix beschwert.

Das einzige Problem bei diesem Algorithmus ist, dass er nicht sehr schnell ist. Daher ist es nicht möglich, ihn auf Anforderung auszuführen, aber Sie müssen ihn bei der Erstellung des Datensatzes mit Geduld ausführen und die Ergebnisse irgendwo speichern

23
EnricoGiampieri

Sie haben ein massives Problem, das als "content based image retrieval" oder CBIR bezeichnet wird. Es ist ein riesiges und aktives Feld. Es gibt noch keine fertigen Algorithmen oder Standardansätze, obwohl es viele Techniken mit unterschiedlichem Erfolg gibt.

Selbst die Google-Bildersuche führt dies (noch) nicht durch - sie führt eine textbasierte Bildersuche durch - beispielsweise sucht sie nach Text auf einer Seite, die dem gesuchten Text entspricht. (Und ich bin sicher, dass sie daran arbeiten, CBIR einzusetzen; es ist der heilige Gral für viele Bildverarbeitungsforscher.)

Wenn Sie eine knappe Frist haben oder dies erledigen müssen und bald arbeiten müssen ...

Hier ist eine Tonne Papiere zum Thema:

http://scholar.google.com/scholar?q=content+based+image+retrieval

Im Allgemeinen müssen Sie einige Dinge tun:

  1. Extrahieren von Features (entweder an lokalen Interessenpunkten oder global oder irgendwie, SIFT, SURF, Histogramme usw.)
  2. Cluster/Erstellen eines Modells von Image-Distributionen

Dies kann Feature-Deskriptoren , Image Gists , Mehrfach-Instanz-Lernen beinhalten. usw.

10
Pete

Ich habe vor 2 Jahren ein Programm geschrieben, um etwas sehr ähnliches mit Python/Cython zu machen. Später habe ich es auf Go umgeschrieben, um eine bessere Leistung zu erzielen. Die Grundidee stammt von findimagedupes IIRC.

Grundsätzlich wird für jedes Bild ein "Fingerabdruck" berechnet und diese Fingerabdrücke dann verglichen, um sie mit ähnlichen Bildern abzugleichen.

Der Fingerabdruck wird erzeugt, indem die Größe des Bilds auf 160 x 160 geändert wird, es in Graustufen konvertiert wird, Unschärfe hinzugefügt wird, es normalisiert wird und dann die Größe auf 16 x 16 Schwarzweiß geändert wird. Am Ende haben Sie 256 Bit Ausgabe: das ist Ihr Fingerabdruck. Dies ist sehr einfach mit convert zu tun:

convert path[0] -sample 160x160! -modulate 100,0 -blur 3x99 \
    -normalize -equalize -sample 16x16 -threshold 50% -monochrome mono:-

(Der [0] in path[0] wird verwendet, um nur den ersten Frame animierter GIFs zu extrahieren. Wenn Sie nicht an solchen Bildern interessiert sind, können Sie ihn einfach entfernen.)

Nachdem Sie dies auf 2 Bilder angewendet haben, haben Sie 2 (256-Bit) Fingerabdrücke, fp1 und fp2.

Die Ähnlichkeitsbewertung dieser 2 Bilder wird dann durch XOR-Verknüpfung dieser 2 Werte und Zählen der auf 1 gesetzten Bits berechnet. Um diese Bitzählung durchzuführen, können Sie die Funktion bitsoncount() von diese Antwort :

# fp1 and fp2 are stored as lists of 8 (32-bit) integers
score = 0
for n in range(8):
    score += bitsoncount(fp1[n] ^ fp2[n])

score ist eine Zahl zwischen 0 und 256, die angibt, wie ähnlich Ihre Bilder sind. In meiner Anwendung dividiere ich es durch 2,56 (normalisiere auf 0-100) und habe festgestellt, dass Bilder mit einer normalisierten Punktzahl von 20 oder weniger oft identisch sind.

Wenn Sie diese Methode implementieren und zum Vergleichen vieler Bilder verwenden möchten, empfehle ich nachdrücklich , Cython (oder einfach nur C) so oft wie möglich zu verwenden : XOR- und Bitzählen ist mit reinen Python -Zahlen sehr langsam.

Es tut mir wirklich leid, aber ich kann meinen Python Code nicht mehr finden. Im Moment habe ich nur eine Go-Version, aber ich fürchte, ich kann sie hier nicht veröffentlichen (fest in einen anderen Code integriert und wahrscheinlich ein wenig hässlich, da es mein erstes ernstes Programm in Go war ...).

Es gibt auch eine sehr gute Funktion zum Suchen nach Ähnlichkeit in GQView/Geeqie. seine Quelle ist hier .

10
Schnouki

Für eine einfachere Implementierung von Earth Mover's Distance (auch bekannt als Wasserstein Distance) in Python können Sie Scipy verwenden:

from scipy.stats import wasserstein_distance
from scipy.ndimage import imread
import numpy as np

def get_histogram(img):
  '''
  Get the histogram of an image. For an 8-bit, grayscale image, the
  histogram will be a 256 unit vector in which the nth value indicates
  the percent of the pixels in the image with the given darkness level.
  The histogram's values sum to 1.
  '''
  h, w = img.shape
  hist = [0.0] * 256
  for i in range(h):
    for j in range(w):
      hist[img[i, j]] += 1
  return np.array(hist) / (h * w)

a = imread('a.jpg')
b = imread('b.jpg')
a_hist = get_histogram(a)
b_hist = get_histogram(b)
dist = wasserstein_distance(a_hist, b_hist)
print(dist)
1
duhaime