opencv template matching

Viele von euch kennen wahrscheinlich die “Where’s Waldo”, bzw. auf deutsch, “Wo ist Walter” Bilder. Bei den Bildern geht es einzig darum Walter unter den Unmengen anderer Personen zu finden. Teils gar nicht so einfach. Ich habe hier ein Beispiel, womit wir Walter anhand opencv template matching finden und ihn kenntlich machen. Dazu habe ich 2 Bilder vorbereitet. Das erste ist das Gesicht vom Walter.

Wo ist Walter Gesicht

Nach diesem kleinen Schnipsel wird opencv nachher das komplette Bild absuchen.

Wo ist Walter

Wer natürlich Lust hat, kann Walter vorher suchen. Diese Arbeit musste ich vorab schon machen, um das Gesicht als Bild zu haben, die Arbeit habt ihr euch also schon gespart :).

Voraussetzungen

An sich gibt es nicht viele Voraussetzungen. Lediglich opencv, matplotlib und numpy müssen installiert sein.

pip install opencv-python
pip3 install matplotlib
pip3 install numpy

template matching

Für das template matching gibt es in opencv die Funktion cv.matchTemplate(). Diese erwartet das komplette Bild, den Schnipsel und eine Vergleichsmethode als Parameter übergeben. Vergleichsmethoden können TM_SQDIFF, TM_SQDIFF_NORMED, TM_CCORR, TM_CCORR_NORMED, TM_CCOEFF und TM_CCOEFF_NORMED sein. Jede Methode verwendet einen unterschiedlichen Berechnungsweg, um an das Ziel zu gelangen. Das Ergebnis ist ein weiteres numpy.ndarray der Größe (W-w +1) x (H -h +1). W/H ist die Größe des vollständigen Bilds und w/h die des Schnipsels. Hier einmal als Vergleich.

vollständige Bild(H x W): 
print(img_rgb.shape)
-> (1760, 2800, 3)

Schnipsel(h x w): 
print(face.shape)
-> (22, 22)

Ergebnis (H - h + 1) x (W - w + 1): 
print(res.shape)
-> (1739, 2779)

Die Vergleichsmethode schiebt das Schnipsel über das große Bild und vergleicht jeweils die gerade übereinanderliegenden Stellen von der Größe w x h und schreibt den Wert in das res numpy.ndarray. Bei allen Vergleichsmethoden außer TM_SQDIFF oder TM_SQDIFF_NORMED gilt, je höher der Wert desto mehr sieht sich die gerade überlappende Stelle ähnlich. Für diesen Beitrag habe ich TM_CCOEFF_NORMED verwendet, spielt letzten Endes aber keine große Rolle.

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

face = cv.imread('waldoface.jpg',0)
img_rgb = cv.imread('whereiswaldo.jpg')
img_gray = cv.imread('whereiswaldo.jpg',0)
w,h = face.shape[::-1]

res = cv.matchTemplate(face,img_gray,cv.TM_CCOEFF_NORMED)

Nachdem wir nun das Ergebnis der Vergleichsmethode haben, können wir uns den besten Treffer mit der Funktion cv2.minMaxLoc() aus dem ndarray extrahieren.

min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
print(min_val, max_val, min_loc, max_loc)
-> -0.5772402882575989 0.9950668215751648 (912, 212) (1169, 298)

cv2.minMaxLoc() sucht sich den kleinsten und größten Wert mit jeweils dem Index raus. Da unser ndarray aus mehr als 1000 Zeilen und Spalten besteht, verdeutliche ich die funktionsweise von cv2.minMaxLoc() kurz anhand eines kleineren Arrays.

array = [
    [1,4,6,8,9], #0 Dimension
    [2,5,4,6,5], #1
    [6,4,3,6,5]  #2
    #0,1,2,3,4
]

min_val,max_val,min_indx,max_indx=cv2.minMaxLoc(array)
print(min_val,max_val,min_indx,max_indx)
-> 1.0, 9.0, (0, 0), (4, 0)

Anhand dieses Beispiels können wir sehen, dass der kleinste Wert (=1.0) das 0 Element im der 0 Dimension ist. Ein Array startet üblicherweise immer mit 0. Der größte Wert(=9.0) ist das 4 Element in der 0 Dimension.
Genau das gleiche machen wir mit unserem res ndarray. Unser Waldo müsste dementsprechend beim Index (1169, 298) liegen. Wir gehen also zu diesem Punkt im Bild und zeichnen von dort an ein Rechteck der Größe 22 x 22 (w x h).

top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
print(top_left, bottom_right)
-> (1169, 298) (1191, 320)

cv.rectangle(img_rgb,top_left,bottom_right, (0, 255, 0), 2)
cv.putText(img_rgb,'Waldo', bottom_right, cv.FONT_HERSHEY_TRIPLEX,3, (0,255,0),3)
plt.imshow(img_rgb)
plt.suptitle('Ergebnis')
plt.show()

Das Ergebnis zeigen wir uns dann mittels matplotlib an. Wer das Bild noch abspeichern möchte kann dies mittels opencv machen.

cv.imwrite('Waldogefunden.jpg', img_rgb)

Damit haben wir Waldo mittels template matching im Bild gefunden und kenntlich gemacht.

template Matching Waldo gefunden

Den vollständigen Code könnt ihr auf meinem Github finden.

2 Gedanken zu „opencv template matching

  1. Hi Stevie!

    Dieser Beitrag hat mir echt gut geholfen zu verstehen wie das Template matching funktioniert. Wie man mehrere matches findet, wäre noch sehr interessant. Vielleicht kommt dazu auch noch ein Tutorial.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert