템플릿 매칭 목차
템플릿 매칭
템플릿 매칭은 주어진 템플릿 이미지와 일치하는 영역을 입력 이미지에서 찾는 방법
OpenCV에서는 템플릿 매칭을 위해 matchTemplate ()
함수를
탬플릿을 비교하기 위한 6가지 방법
2차원 컨볼루션처럼 템플릿 이미지를 입력 이미지 위에서 이동시키면서 대응하는 픽셀들을 비교
cv2.TM_SQDIFF
유사하면 0(검은색), 비유사하면 255를 반환
템플릿 영상에서 입력 영상의 부분 영상(같은 위치에 있는)의 픽셀를 뺀 것을 제곱하여 다 더하겠다는 의미
(R의 결과)
- 0: 템플릿 영상과 구분 영상이 완전히 동일
- 다르면 값이 커짐
cv2.TM_SQDIFF_NORMED
cv2.TM_SQDIFF를 [0, 1]로 정규화한 것
cv2.TM_CCORR
유사하면 255, 비유사하면 0을 반환
템플릿 영상의 픽셀값과 부분 영상의 픽셀 값을 같은 위치의 픽셀값을 곱하고 다 더하는 방법
원본 영상에서의 흰색 부분은 결과값이 크게 나오는 경향이 있음
- 밝기 보정, 정규화 필요
cv2.TM_CCORR_NORMED
cv2.TM_CCORR를 정규화
cv2.TM_CCOEFF
밝기를 보정하고 cv2.TM_CCORR방법을 이용
유사하면 255, 비유사하면 0을 반환
- T’ : T(템플릿 영상)의 평균 값으로 뺀 값
- I’ : I(부분 영상)의 평균값으로 뺀 값
영상기준으로 평균 크기만큼 빼서 보상을 하여 보정을 하는 방법
밝기 보정이 들어가서 결과가 좀 더 좋음
cv2.TM_CCOEFF_NORMED
가장 성능이 좋음
but) 수식 복잡, 연산량 많음
완전히 일치하면 1, 역일치하면 -1, 상호 연관성이 없으면 0을 반환
특징 정리
최댓값 사용
cv.TM_CCOEFF, cv.TM_CCOEFF_NORMED, cv.TM_CCORR, cv.TM_ CCORR_NORMED
최솟값 사용
cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED
함수 원형
matchTemplate( img, templ, result, match_method, mask)
- img : 이미지 원본
- templ : 찾고자 하는 이미지
- result : 연산 결과를 저장하기 위한 이미지
- match_method : 이미지서치 방법
- **0: TM_SQDIFF
- 1: TM_SQDIFF NORMED
- 2: TM CCORR
- 3: TM CCORR NORMED
- 4: TM COEFF
- 5: TM COEFF NORME
- mask : templ 이미지에 해당하는 마스크, 필요로 하는 이미지서치에서만 사용
- 두 가지 방법만 mask 이미지 사용 가능
- 0: TM_SQDIFF
- 3: TM CCORR NORMED
(template& target)
(result)
코드
[C++]
# include < opencv2/opencv.hpp>
using namespace cv;
int main()
{
// 템플릿 이미지를 검출할 이미지를 불러옵니다.
Mat img_rgb = imread("test.jpg");
Mat img_gray;
cvtColor(img_rgb, img_gray, COLOR_BGR2GRAY);
// 템플릿 이미지를 불러옵니다.
Mat img_template = imread("template.jpg", IMREAD_GRAYSCALE);
int w = img_template.cols;
int h = img_template.rows;
// 템플릿 매칭을 합니다.
Mat result;
matchTemplate(img_gray, img_template, result, TM_CCOEFF_NORMED);
double min_val, max_val;
Point min_loc, max_loc;
minMaxLoc(result, & min_val, & max_val, & min_loc, & max_loc);
// 검출된 템플릿 이미지와 유사한 영역에 사각형을 그려줍니다.
Point top_left = max_loc;
Point bottom_right = Point(top_left.x+w, top_left.y+h);
rectangle(img_rgb, top_left, bottom_right, (0, 0, 255), 2);
imshow("result", img_rgb);
waitKey(0);
return 0;
}
[Python]
import cv2 as cv
import numpy as np
# 템플릿 이미지를 검출할 이미지를 불러옵니다.
img_rgb = cv.imread('test.jpg')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
# 템플릿 이미지를 불러옵니다.
img_template = cv.imread('template.jpg', cv.IMREAD_GRAYSCALE)
w, h = img_template.shape[:2]
# 템플릿 매칭을 합니다.
res = cv.matchTemplate(img_gray, img_template, cv.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
# 검출된 템플릿 이미지와 유사한 영역에 사각형을 그려줍니다.
top_left = max_loc
bottom_right = (top_left[0]+w, top_left[1]+h)
cv.rectangle(img_rgb, top_left, bottom_right, (0, 0, 255), 2)
cv.imshow('result', img_rgb)
cv.waitKey(0)
다수의 오브젝트 찾기
minMaxLoc () 함수는 값이 최대 또는 최소인 하나의 위치만 알려줌
때문에 하나 이상의 오브젝트를 템플릿 매칭해야 하는 경우에는 사용할 수 없음.
이 경우 임계값을 사용하면 됨.
[C++]
# include < opencv2/opencv.hpp>
# include < cmath>
using namespace cv;
using namespace std;
vector<Point> detectedObjects;
// 새로운 좌표와 근사한 좌표가 기존 리스트에 있는지 체크합니다.
bool notInList(Point newObject)
{
for (int i = 0; i < detectedObjects.size(); i++)
{
float a = detectedObjects[i].x - newObject.x;
float b = detectedObjects[i].y - newObject.y;
if (sqrt(a*a+b*b) < 5.0) return false;
}
return true;
}
int main()
{
Mat img_rgb = imread("test.jpg");
Mat img_gray;
cvtColor(img_rgb, img_gray, COLOR_BGR2GRAY);
Mat img_template = imread("template.jpg", IMREAD_GRAYSCALE);
int w = img_template.cols;
int h = img_template.rows;
Mat result;
matchTemplate(img_gray, img_template, result, TM_CCOEFF_NORMED);
// 임계값 0.9보다 크고 // 기존에 템플릿 검출 리스트에 포함된 좌표와 거리가 5 이상이어야
// 새로운 좌표로 리스트에 추가합니다.
int count = 0;
for (int x=0; x<result.cols; x++)
for (int y=0; y<result.rows; y++)
{
if (result.at<float>(y, x) > 0.9&& notInList(Point(x, y)))
{
detectedObjects.push_back(Point(x, y));
rectangle(img_rgb, Point(x,y), Point(x+w, y+h),
Scalar(0, 0, 255), 1);
count++;
}
}
cout << count << endl;
imshow("result", img_rgb);
waitKey(0);
return 0;
}
[Python]
import cv2 as cv
import numpy as np
# 새로운 좌표와 근사한 좌표가 기존 리스트에 있는지 체크합니다.
def notInList(newObject):
for detectedObject in detectedObjects:
a = newObject[0]-detectedObject[0]
b = newObject[1]-detectedObject[1]
if np.sqrt(a*a+b*b) < 5:
return False
return True
detectedObjects=[]
img_rgb = cv.imread('test.jpg')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
img_template = cv.imread('template.jpg', cv.IMREAD_GRAYSCALE)
w, h = img_template.shape[:2]
res = cv.matchTemplate(img_gray, img_template, cv.TM_CCOEFF_NORMED)
# 임계값 0.9보다 크고 # 기존에 템플릿 검출 리스트에 포함된 좌표와 거리가 5 이상이어야
# 새로운 좌표로 리스트에 추가합니다.
count = 0
for × in range(res.shape[1]):
for y in range(res.shape[0]):
if res[y, x] > 0.9 and notInList((x, y)):
detectedObjects.append((x, y))
cv.rectangle(img_rgb, (x, y), (x+w, y+h), [ 0, 0, 255], 1)
count = count + 1
print(count)
cv.imshow('result', img_rgb)
cv.waitKey(0)