Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find subimage using the PIL library?

I want to find the sub-image from large image using PIL library. I also want to know the coordinates where it is found ?

like image 230
Sagar Avatar asked Jul 10 '13 09:07

Sagar


People also ask

How do I view an image using PIL?

Python – Display Image using PIL To show or display an image in Python Pillow, you can use show() method on an image object. The show() method writes the image to a temporary file and then triggers the default program to display that image. Once the program execution is completed, the temporary file will be deleted.

What is PIL library used for?

Python Imaging Library is a free and open-source additional library for the Python programming language that adds support for opening, manipulating, and saving many different image file formats. It is available for Windows, Mac OS X and Linux. The latest version of PIL is 1.1.

How do I use the pillow library in Python?

To load the image, we simply import the image module from the pillow and call the Image. open(), passing the image filename. Instead of calling the Pillow module, we will call the PIL module as to make it backward compatible with an older module called Python Imaging Library (PIL).

How do you get pixels of PIL images?

With PIL you can easily access and change the data stored in the pixels of an image. To get the pixel map, call load() on an image. The pixel data can then be retrieved by indexing the pixel map as an array.


2 Answers

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)

This works fine and in efficient way for me.

like image 181
Sagar Avatar answered Oct 16 '22 04:10

Sagar


I managed to do this only using PIL.

Some caveats:

  1. This is a pixel perfect search. It simply looks for matching RGB pixels.
  2. For simplicity I remove the alpha/transparency channel. I'm only looking for RGB pixels.
  3. This code loads the entire subimage pixel array into memory, while keeping the large image out of memory. On my system Python maintained a ~26 MiB memory footprint for a tiny 40x30 subimage searching through a 1920x1200 screenshot.
  4. This simple example isn't very efficient, but increasing efficiency will add complexity. Here I'm keeping things straight forward and easy to understand.
  5. This example works on Windows and OSX. Not tested on Linux. It takes a screenshot of the primary display only (for multi monitor setups).

Here's the code:

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

While running the above command I moved the window containing the subimage diagonally as timeit was running.

like image 42
Robpol86 Avatar answered Oct 16 '22 03:10

Robpol86