반응형

'Pillow 이미지 서치(Image search) 1' 은 target과 source의 모든 픽셀이 정확히 일치하는 경우만 True로 판단 하기 때문에 PNG나 BMP같은 무손실 압축 그래픽 파일에만 적용 가능하다. JPEG같은 손실 압축 그래픽 파일은 target과 source의 오차를 감안해야 한다.

 

2018/11/30 - [Software/Python] - Pillow 이미지 서치(Image Search) 1

2019/07/08 - [Software/OpenCV] - Template Matching(Image Searching) - 부분 이미지 검색

2019/07/10 - [Software/OpenCV] - Template Matching(Image Searching) for multiple objects - 반복되는 이미지 모두 찾기

2019/07/12 - [Software/OpenCV] - Template Matching(Image Searching) with a mask for multiple objects - 마스크를 이용해 (배경이 다른) 반복되는 이미지 모두 찾기

 

Target: 93 X 47

 

Source: 600 X 600

 

강아지의 앞발에 위치한 타겟 위치를 찾아 보자.

 

Tolerance: 30

Step: 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from PIL import Image
from PIL import ImageDraw
from PIL import ImageChops
from PIL import ImageStat
import sys
import time
 
source = Image.open("source.jpg")
sx, sy = source.size
target = Image.open("target.jpg")
tx, ty = target.size
tolerance = 30 # 오차 범위는 30 정도면 적당한거 같다.
step = 2 # 모든 픽셀을 검사하면 너무 오랜 시간이 걸린다. 한 개 건너 한 개 픽셀만 검사.
 
print("Source size: ", source.size)
print("Target size: ", target.size)
 
trial = 0 # Image search 시도 횟수.
 
def Search(cx, cy, tolerance):
    compare = source.crop((cx, cy, cx + tx, cy + ty)) # 소스에서 타겟으로 판단되는 위치의 이미지를 타겟 사이즈 만큼 잘라낸다.
    # Returns a rectangular region from this image. The box is a 4-tuple defining the left, upper, right, and lower pixel coordinate.
    print("Compare size: ", compare.size)
 
    diff = ImageChops.difference(compare, target) # 타겟과 타겟으로 판단되는 부분의 픽셀값 비교.
    stat = ImageStat.Stat(diff)
    global trial
    if max(max(stat.extrema[0]), max(stat.extrema[1]), max(stat.extrema[2])) <= tolerance:
        print("Target found(Min, max): ", stat.extrema)
        return True
    else:
        trial += 1
        return False
 
draw = ImageDraw.Draw(source)    # Creates an object that can be used to draw in the given image.
start = time.time()
 
for y in range(sy - ty):                # 소스의 처음부터 타겟 사이즈를 뺀 위치 까지 전체 검색을 시작 한다.
    for x in range(0, sx - tx, step):    # 처음 (10 X 10)개 픽셀의 값이 비슷 하다면 Search()로 타겟 사이즈 전체를 다시 확인한다.
        compare = source.crop((x, y, x + 10, y + 10))
        partial_target = target.crop((001010))
        diff = ImageChops.difference(compare, partial_target) # 각 픽셀값 차의 절대값이 반환 된다.
        # Returns the absolute value of the pixel-by-pixel difference between the two images.
        stat = ImageStat.Stat(diff)
 
        if max(max(stat.extrema[0]), max(stat.extrema[1]), max(stat.extrema[2])) < tolerance:
            if Search(x, y, tolerance) == True:
                print("Top left point: (%d, %d)" %(x, y))
                print("Center of targe point: (%d, %d)" %(x + target.width / 2, y + target.height / 2))
                print("Number of total wrong detection: ", trial)
                draw.rectangle((x, y, x + target.width, y + target.height), outline = (25500))
                # Draws a rectangle. 소스 이미지의 타겟 부분에 빨간 사각형을 그린다.
                end = time.time()
                print("Seraching time: ", end - start)
                source.show()
                sys.exit()
            else:
                print("At (%d, %d): Target not found" %(x, y))
                print("Wrong detection count: ", trial)
 
end = time.time()
print("Image search failed.")
print("Seraching time: ", end - start)
 
 

 

소스를 입력하고 빌드한다.

 

 

결과

 

123번 잘못된 지점을 검색했고 (232, 497)위치의 Target을 찾는데 총 15.66초가 걸렸다.

JPEG파일의 손실 압축 때문에 Target과 Source의 픽셀이 최대 RGB(16, 9, 15)만큼 차이가 발생 했다.

Target을 찾지 못한다면 Step과 Tolerance 값을 적당히 수정해야 한다.

 

반응형
Posted by J-sean
: