OpenCV의 Template Matching을 사용하여 이미지상에서 템플릿 이미지와 유사한 오브젝트를 찾을 수 있다. 쉽고 빠르게 구현이 가능하지만, 템플릿 이미지와 픽셀들을 비교하여 구분하기 때문에 방향이 다르거나 크기가 다른 경우에는 찾을 수 없다는 단점이 있다.
Template Matching in OpenCV
OpenCV에서 제공하는 cv.matchTemplate() 함수는 템플릿 이미지를 입력받은 이미지상 위에서 좌측상단부터 우측으로 이동시키면서 대응하는 픽셀들과 비교한다. 제공되는 비교 방법은 다음과 같다.
- cv.TM_CCOEFF
- cv.TM_CCOEFF_NORMED
- cv.TM_CCORR
- cv.TM_CCORR_NORMED
- cv.TM_SQDIFF
- cv.TM_SQDIFF_NORMED
cv.matchTemplate() 함수의 결과는 Grayscale 이미지를 리턴한다. 각 픽셀은 템플릿 이미지와 어느 정도 일치하는지를 나타낸다. 결과 이미지를 얻었다면 임계치 조건을 통하여 임계치 값보다 큰 모든 Box를 확인하거나 cv.minMaxLoc() 함수를 통해 템플릿과 최댓값 또는 최솟값 하나의 결과만 확인 할 수도 있다. 일치하는 Box의 왼쪽 위 좌표를 돌려주며, Box의 크기는 당연히 비교에 사용된 템플릿 이미지와 일치한다.
Example
다음 그림에서 다른 하나를 찾아보자. 위에서 언급한 두가지 방법으로 찾아볼 수 있다.
1. 임계치를 설정해서 찾는 방법 (threshold)
먼저 임계치를 설정해 찾는 방법이다. 하나의 그림을 템플릿 이미지로 사용해서 Template Matching을 수행하면, 동일한 그림의 경우 박스가 생성될 것이고, 다른 그림의 경우 임계치에 미달하여 박스가 생성되지 않을 것이다.
코드는 다음과 같다.
import cv2
import numpy as np
image = cv2.imread('image/example.jpg')
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = cv2.imread('image/template.jpg', 0)
w, h = template.shape[::-1]
result = cv2.matchTemplate(image_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.95 # 임계치 설정
box_loc = np.where(result >= threshold) # 임계치 이상의 값들만 사용
for box in zip(*box_loc[::-1]):
startX, startY = box
endX, endY = startX + w, startY + h
cv2.rectangle(image, (startX, startY), (endX, endY), (0,0,255), 1)
cv2.imwrite('result.png', image)
다른 그림과의 차이가 크지 않아서, 임계치를 높게 잡고 결과를 출력하였다. 그 결과 다른 하나의 경우 박스가 생성되지 않은 모습을 확인할 수 있다.
2. 최대값 또는 최소값으로 찾는 방법 (minMaxLoc)
다음은 최솟값을 활용하여, 다른 그림 하나를 찾아보겠다. 이번에는 하나의 오브젝트를 찾기 위해서, 다른 그림을 템플릿 이미지로 사용하여 탐색한다.
코드는 다음과 같다.
import cv2
import numpy as np
image = cv2.imread('image/example.jpg')
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = cv2.imread('image/template_answer.jpg', 0)
w, h = template.shape[::-1]
result = cv2.matchTemplate(image_gray, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
startX, startY = max_loc # 만약 cv.TM_SQDIFF 혹은 cv.TM_SQDIFF_NORMED를 사용했을경우 최솟값을 사용해야한다.
endX, endY = startX + w, startY + h
cv2.rectangle(image, (startX, startY), (endX, endY), (0,0,255), 1)
cv2.imwrite('result.png', image)
결과는 하나만 출력되는 것을 확인할 수 있고, 가장 잘 매칭되는 다른 그림 하나가 나온 것을 확인할 수 있다.
추가적으로 사용한 코드에 대한 내용은 깃헙에서도 확인해 볼 수 있다.
Conclusion
OpenCV의 Template Matching에 대해서 간단하게 알아보았다. 이는 사실 딥러닝을 이용한 Object Detection에 비하면 성능이 많이 떨어진다. 그럼에도 다양한 Template을 사용하거나 Confidence를 조절하는 등으로도 성능을 어느 정도 높일 수 있기 때문에 Object Detection을 활용할 정도로 복잡한 Task가 아니라면 간단한 문제에서는 효과적일 것으로 보인다.
Reference