2015-04-16 11 views
5

Potrzebuję program w języku Python, nad którym pracuję, aby móc zrobić mały obraz, określić, czy istnieje on w większym obrazie, a jeśli tak, zgłoś swoją lokalizację. Jeśli nie, zgłoś to. (W moim przypadku duży obrazek będzie zrzutem ekranu, a mały obraz obrazem, który może, ale nie musi być na ekranie, w kanwie HTML5.) Patrząc na linię, dowiedziałem się o dopasowaniu do szablonu w OpenCV, co mają doskonałe wiązania Python. Próbowałem następujących, w oparciu o bardzo podobnym kodzie znalazłem w sieci, za pomocą numpy także:Ustal, czy obraz istnieje w większym obrazie, a jeśli tak, znajdź go, używając Pythona

import cv2 
import numpy as np 
image = cv2.imread("screenshot.png") 
template = cv2.imread("button.png") 
result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED) 
StartButtonLocation = np.unravel_index(result.argmax(),result.shape) 

nie robić to, co muszę to zrobić, ponieważ zawsze zwraca punkt w większy obraz; punkt, w którym mecz jest najbliżej, bez względu na to, jak straszny jest mecz. Chcę czegoś, co znajdzie dokładny, pikselowy piksel dla mniejszego obrazu na większym obrazie, a jeśli go nie ma, podnosi wyjątek lub zwraca False lub coś w tym stylu. I musi być dość szybki. Czy ktoś ma dobry pomysł, jak to zrobić?

+1

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? –

+2

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ą. –

Odpowiedz

14

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 wszablonu 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:

From wikipedia

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:

enter image description here

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:

enter image description here

Suma ciągu dwóch oknach obraz jest to samo, ale ostatni krok funkcji filtruje ten, który nie pasuje dokładnie do szablonu.

+0

To było DOKŁADNIE to, czego potrzebowałem. Rzeczywiście działa idealnie i jest bardzo szybki. Dziękuję Ci bardzo! – clj

+0

@clj Witamy! Cieszę się, że jest to przydatne. –

+0

Ale w tym przypadku dwa obrazy mają to samo źródło. Więc wartości pikseli będą takie same. Co się stanie, jeśli otrzymam obraz w "tpl" z innego źródła? Czy to też zadziała? – Neelesh

1

Ponieważ jesteś zadowolony z OpenCV, proponuję rozpocząć od tego, co już zrobiłeś i uzyskać najlepsze dopasowanie. Po znalezieniu najlepszego dopasowania możesz sprawdzić, czy rzeczywiście pasuje.

Sprawdzenie, czy jest dobrze dopasowane, powinno być tak łatwe, jak wyodrębnienie pasującego obrazu i porównanie go z szablonem. Aby wyodrębnić obraz, konieczne może być użycie cv2.minMaxLoc(result) i przetworzenie danych wyjściowych. Wydaje się, że metoda ekstrakcji zależy od metody stosowanej do porównywania obrazów i jest wykonywana przy użyciu przykładów here.

Po wyodrębnieniu obrazu, powinieneś być w stanie porównać je za pomocą numpy.allclose lub innej metody.

Powiązane problemy