如何使用PIL库找到子图像?

时间:2013-07-10 09:14:45

标签: python python-imaging-library

我想使用PIL库从大图像中找到子图像。我也想知道找到它的坐标?

3 个答案:

答案 0 :(得分: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)

这对我来说效果很好而且效率很高。

答案 1 :(得分:5)

我设法只使用PIL。

一些警告:

  1. 这是一个像素完美搜索。它只是寻找匹配的RGB像素。
  2. 为简单起见,我删除了alpha /透明度通道。我只是在寻找RGB像素。
  3. 此代码将整个子图像素像素阵列加载到内存中,同时将大图像保留在内存中。在我的系统上,Python通过一个1920x1200的屏幕截图,为一个小的40x30子图像维护了大约26 MiB的内存占用。
  4. 这个简单的例子不是很有效,但提高效率会增加复杂性。在这里,我将事情直截了当,易于理解。
  5. 此示例适用于Windows和OSX。没有在Linux上测试过。它仅截取主显示屏(适用于多显示器设置)。
  6. 以下是代码:

    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)
    

    速度:

    $ 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
    

    在运行上述命令时,我在timeit运行时对角移动了包含子图像的窗口。

答案 2 :(得分:0)

听起来你想要执行对象检测,可能是通过模板匹配。这不是一个微不足道的问题,除非你正在寻找一个精确的逐像素匹配,而PIL并不意味着这样做。

Jan是对的,你应该尝试OpenCV。它是一个强大的计算机视觉库,具有良好的Python绑定。

这是Python中一个很好的简短示例,它在匹配区域周围绘制一个矩形: https://github.com/jungilhan/Tutorial/blob/master/OpenCV/templateMatching.py