2013-07-10 9 views
6

Chcę znaleźć obraz podrzędny z dużego obrazu przy użyciu biblioteki PIL. Chciałbym również znać współrzędne, w których się znajduje?Jak znaleźć subimage przy użyciu biblioteki PIL?

+0

Czy możesz być bardziej konkretny? W każdym razie - jeśli chcesz coś takiego jak wykrywanie twarzy i tak dalej - zapomnij o PIL (nie jest to zaprojektowane do tego rodzaju pracy) i poszukaj OpenCV. –

+0

Czy możesz być trochę bardziej zrozumiały? podaj kilka linii kodu z tego, co już zrobiłeś, jaki format danych ma twój obraz itp. – usethedeathstar

+0

Jeszcze nie zacząłem kodowania. Chcę subimage z dużego obrazu. na przykład mamy zrzut ekranu dowolnego gracza. mamy obraz paska wyszukiwania. teraz chcę znaleźć lokalizację seekbar przy użyciu PIL. – Sagar

Odpowiedz

5
import cv2 
import numpy as np 
image = cv2.imread("Large.png") 
template = cv2.imread("small.png") 
result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED) 
print np.unravel_index(result.argmax(),result.shape) 

To działa prawidłowo i w sposób efektywny dla mnie.

0

Wygląda na to, że chcesz wykonać wykrywanie obiektów , prawdopodobnie za pomocą szablonu pasującego do. To nie jest banalny problem, chyba że szukasz dokładnego piksela po pikselu, a PIL nie jest przeznaczony do tego typu rzeczy.

Jan ma rację, że powinieneś wypróbować OpenCV. Jest to solidna biblioteka wizji komputerowej z dobrymi powiązaniami Pythona.

Oto miły krótki przykład w Pythonie, który rysuje prostokąt wokół dopasowane regionu: https://github.com/jungilhan/Tutorial/blob/master/OpenCV/templateMatching.py

2

Udało mi się to zrobić tylko za pomocą PIL.

Niektóre Ostrzeżenia:

  1. To piksel doskonały wyszukiwania. Po prostu szuka pasujących pikseli RGB.
  2. Dla uproszczenia usuwam kanał alfa/przezroczystość. Szukam tylko pikseli RGB.
  3. Ten kod ładuje całą macierz pikseli subimage do pamięci, zachowując duży rozmiar wolnego miejsca w pamięci. W moim systemie Python utrzymywał pamięć o wielkości ~ 26 MiB dla małego podobrazia 40x30, przeszukując zrzuty ekranu 1920x1200.
  4. Ten prosty przykład nie jest zbyt wydajny, ale zwiększenie wydajności zwiększy złożoność. Tutaj utrzymuję rzeczy proste i łatwe do zrozumienia.
  5. Ten przykład działa w systemach Windows i OSX. Nie testowane na Linuksie. Wykonuje zrzut ekranu głównego ekranu (w przypadku konfiguracji z wieloma monitorami).

Oto kod:

import os 
from itertools import izip 

from PIL import Image, ImageGrab 


def iter_rows(pil_image): 
    """Yield tuple of pixels for each row in the image. 

    From: 
    http://stackoverflow.com/a/1625023/1198943 

    :param PIL.Image.Image pil_image: Image to read from. 

    :return: Yields rows. 
    :rtype: tuple 
    """ 
    iterator = izip(*(iter(pil_image.getdata()),) * pil_image.width) 
    for row in iterator: 
     yield row 


def find_subimage(large_image, subimg_path): 
    """Find subimg coords in large_image. Strip transparency for simplicity. 

    :param PIL.Image.Image large_image: Screen shot to search through. 
    :param str subimg_path: Path to subimage file. 

    :return: X and Y coordinates of top-left corner of subimage. 
    :rtype: tuple 
    """ 
    # Load subimage into memory. 
    with Image.open(subimg_path) as rgba, rgba.convert(mode='RGB') as subimg: 
     si_pixels = list(subimg.getdata()) 
     si_width = subimg.width 
     si_height = subimg.height 
    si_first_row = tuple(si_pixels[:si_width]) 
    si_first_row_set = set(si_first_row) # To speed up the search. 
    si_first_pixel = si_first_row[0] 

    # Look for first row in large_image, then crop and compare pixel arrays. 
    for y_pos, row in enumerate(iter_rows(large_image)): 
     if si_first_row_set - set(row): 
      continue # Some pixels not found. 
     for x_pos in range(large_image.width - si_width + 1): 
      if row[x_pos] != si_first_pixel: 
       continue # Pixel does not match. 
      if row[x_pos:x_pos + si_width] != si_first_row: 
       continue # First row does not match. 
      box = x_pos, y_pos, x_pos + si_width, y_pos + si_height 
      with large_image.crop(box) as cropped: 
       if list(cropped.getdata()) == si_pixels: 
        # We found our match! 
        return x_pos, y_pos 


def find(subimg_path): 
    """Take a screenshot and find the subimage within it. 

    :param str subimg_path: Path to subimage file. 
    """ 
    assert os.path.isfile(subimg_path) 

    # Take screenshot. 
    with ImageGrab.grab() as rgba, rgba.convert(mode='RGB') as screenshot: 
     print find_subimage(screenshot, subimg_path) 

Speed:

$ python -m timeit -n1 -s "from tests.screenshot import find" "find('subimg.png')" 
(429, 361) 
(465, 388) 
(536, 426) 
1 loops, best of 3: 316 msec per loop 

Podczas pracy powyższego polecenia przeniosłem okno zawierające subimage przekątnej jako timeit został uruchomiony.