Zaproponuję odpowiedź, która działa szybko i idealnie, jeśli zależy Ci na wielkości i wartościach obrazu.
Chodzi o to, aby obliczyć szukanie brute force z poszukiwanych h x w
szablonu w większej H x W
obrazu. Metoda bruteforce polegałaby na sprawdzeniu wszystkich możliwych okien h x w
na obrazie i sprawdzeniu korespondencji piksel po pikselu wewnątrz szablonu. Jest to jednak bardzo kosztowne obliczeniowo, ale można je przyspieszyć.
im = np.atleast_3d(im)
H, W, D = im.shape[:3]
h, w = tpl.shape[:2]
Dzięki zastosowaniu inteligentnego integral images można obliczyć bardzo szybko suma wewnątrz okna h x w
wyjściowego na każdym pikselu. Integralną obraz jest sumowane tabela obszar (narastająco podsumował array), która może być obliczona z numpy naprawdę szybko, jak:
sat = im.cumsum(1).cumsum(0)
i ma naprawdę ładne właściwości, takie jak obliczenia sumy wszystkich wartości w okno z zaledwie 4 operacji arytmetycznych:
Zatem obliczając sumę szablonu i dopasowanie go z sumą h x w
okien ponad integralnego obrazu, nie jest łatwo znaleźć listę „możliwe Windows ", gdzie suma wartości wewnętrznych jest taka sama jak suma wartości wt szablon (szybkie przybliżenie).
iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:]
lookup = iD - iB - iC + iA
Powyższe jest numpy wektoryzacja funkcjonowania przedstawionej na obrazie dla wszystkich możliwych h x w
prostokątów nad obrazem (tym samym, naprawdę szybki).
Spowoduje to znaczną redukcję liczby możliwych okien (do 2 w jednym z moich testów). Ostatnim krokiem byłoby, aby sprawdzić dokładnie meczów z szablonu:
posible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)]))
for y, x in zip(*posible_match):
if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl):
return (y+1, x+1)
Zauważ, że tutaj y
i x
współrzędne odpowiadają punktu w obrazie, który jest poprzedni rząd i kolumna z szablonem.
Umieszczenie wszystko razem:
def find_image(im, tpl):
im = np.atleast_3d(im)
tpl = np.atleast_3d(tpl)
H, W, D = im.shape[:3]
h, w = tpl.shape[:2]
# Integral image and template sum per channel
sat = im.cumsum(1).cumsum(0)
tplsum = np.array([tpl[:, :, i].sum() for i in range(D)])
# Calculate lookup table for all the possible windows
iA, iB, iC, iD = sat[:-h, :-w], sat[:-h, w:], sat[h:, :-w], sat[h:, w:]
lookup = iD - iB - iC + iA
# Possible matches
possible_match = np.where(np.logical_and.reduce([lookup[..., i] == tplsum[i] for i in range(D)]))
# Find exact match
for y, x in zip(*possible_match):
if np.all(im[y+1:y+h+1, x+1:x+w+1] == tpl):
return (y+1, x+1)
raise Exception("Image not found")
Działa zarówno w skali szarości i kolorowych obrazów oraz wyciągów w 7ms
dla obrazu 303x384
kolor z 50x50
szablonu.
Praktyczny przykład:
>>> from skimage import data
>>> im = gray2rgb(data.coins())
>>> tpl = im[170:220, 75:130].copy()
>>> y, x = find_image(im, tpl)
>>> y, x
(170, 75)
I ilustrate wynik:
Lewy obraz oryginalny, prawda szablon. I tu dokładne dopasowanie:
>>> fig, ax = plt.subplots()
>>> imshow(im)
>>> rect = Rectangle((x, y), tpl.shape[1], tpl.shape[0], edgecolor='r', facecolor='none')
>>> ax.add_patch(rect)
i wreszcie, po prostu przykładem possible_matches
dla testu:
Suma ciągu dwóch oknach obraz jest to samo, ale ostatni krok funkcji filtruje ten, który nie pasuje dokładnie do szablonu.
Szybkie pytanie: czy można założyć, że "mały" obraz pojawi się na obrazie "dużym", zawsze w oryginalnym rozmiarze i dokładnie z jego oryginalnymi wartościami? Lub musisz poradzić sobie ze zmiennymi rozmiarami "małych" obrazów, które mogą być "interpolowane" i obsługiwać wariacje "iluminacji"? Chodzi mi o to, że wspominasz o "ścisłym dopasowaniu", czy to naprawdę dokładne? –
Czy gwarantujesz ZAWSZE korzystanie z formatu "PNG"? Pytam, ponieważ 'JPEG' jest poddawany kwantyzacji i stratnej kompresji, a rzeczy, które są podobno identyczne, mogą różnić się wewnętrzną reprezentacją. –