지난 시간에는 영상 분할하는 방법 중 grabcut에 대해 알아봤습니다.
https://blog.naver.com/dorergiverny/223108058366
이번에는 템플릿 매칭에 대해 알아보겠습니다.
템플릿 매칭(template matching)은 원본 영상에서 템플릿 영상(template image)이라고 불리우는 작은 크기의 부분 영상과 동일한 또는 가장 유사한 영역의 위치를 찾아내는 방법으로 물체 인식(검출), 스테레오 영상 등의 대응점 검출 등에 사용될 수 있습니다.
매칭 방법은 상관관계(correlation), SAD(Sum of Absolute Difference) 등의 방법을 사용합니다.
템플릿 매칭의 동작 원리
템플릿 매칭의 동작은 템플릿 영상을 원본 영상 전체에 순회하면서 매칭 방법에 따라 유사도를 계산합니다. 그 이후 유사도가 가장 높은 위치를 찾으면 그 위치가 매칭 결과가 되는 원리입니다.
템플릿 매칭 함수
OpenCV에서 제공하는 템플릿 매칭 함수의 원형은 아래와 같습니다.
src
|
입력 영상
|
tmpl
|
템플릿 영상
|
result
|
유사도 결과 영상, Float32 타입 영상
|
method
|
유사도 계산 방법
|
mask
|
마스크 영상, 기본값: noArray()
|
만약 원본 영상의 크기가 WxH 이고 템플릿 영상의 크기가 wxh일 경우 result 행렬의 크기는 (W-w+1)x(H-h+1)이 됩니다.
유사도 계산 방법은 아래와 같습니다.
1. TM_SQDIFF (제곱차 매칭 방법)
템플릿 영상(T)을 원본 영상(I)에서 이동시켜가며 픽셀 밝기 차이의 제곱의 합을 계산합니다. 매칭되는 위치에서 가장 작은 값을 갖습니다.
2. TM_SQDIFF_NORMED(정규화된 제곱차 매칭 방법)
TM_SQDIFF 를 정규화(normalize) 시킨 방법으로 영상의 전체 밝기가 달라졌을 경우에도 대응이 되는 방법입니다.
3. TM_CCORR(상관관계 매칭 방법)
템플릿 영상(T)를 원본 영상(I)에서 이동시켜가며 곱의 합계를 계산합니다. 매칭되는 위치에서 가장 큰 값을 갖습니다.
4. TM_CCORR_NORMED(정규화된 상관관계 매칭 방법)
TM_CCORR을 위와 동일한 방법으로 정규화를 합니다.
5. TM_CCOEFF(상관계수 매칭 방법)
템플릿 영상(T)은 T의 각 화소값에서 평균을 뺀 영상을 만들고, 원본 영상(I)은 템플릿과 대응되는 위치에서 I의 각 화소값에서 평균을 뺀 영상을 만들어서 Cross Correlation을 계산하는 방법입니다. 즉, 각 평균값으로 보정하여 비교하는 방법으로 매칭되는 위치에서 큰 값을 갖습니다.
6. TM_CCOEFF_NORMED(정규화된 상관계수 매칭 방법)
위와 마찬가지로 TM_CCOEFF 결과를 정규화를 합니다.
일반적으로 정규화된 상관계수 매칭 방법이 가장 성능이 좋은 것으로 알려져 있습니다. 하지만 수식이 복잡한 만큼 연산량이 많을 수 있음을 고려해야 합니다.
템플릿 매칭 (template matching)
위의 방법으로 템플릿 매칭을 해 보도록 하겠습니다.
원본 영상과 템플릿 영상을 읽어 옵니다. 그리고 matchTemplate() 함수를 이용하여 유사도 행렬(result)를 생성합니다. 이번에는 TM_CCOEFF_NORMED 방법을 사용하였습니다.
cv::Mat src = cv::imread("../../images/people.png", cv::IMREAD_GRAYSCALE);
cv::Mat tmpl = cv::imread("../../images/people2.png", cv::IMREAD_GRAYSCALE);
cv::Mat result;
cv::matchTemplate(src, tmpl, result, cv::TM_CCOEFF_NORMED);
그리고 minMaxLoc() 함수를 이용하여 최대값을 갖는 위치를 찾습니다. 그리고 영상에 붉은 사각형을 그려줍니다.
minMaxLoc() 함수는 아래 글에서 설명을 했습니다.
double minVal, maxVal;
cv::Point minLoc, maxLoc;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
cv::Mat src_color;
cv::cvtColor(src, src_color, cv::COLOR_GRAY2BGR);
cv::rectangle(src_color, cv::Rect(maxLoc.x, maxLoc.y, tmpl.cols, tmpl.rows), cv::Scalar(0, 0, 255));
템플릿 영상으로 찾은 결과는 아래와 같습니다.
템플릿 영상 전체 소스는 아래와 같습니다.
#include <iostream>
#include "opencv2/opencv.hpp"
int main()
{
cv::Mat src = cv::imread("../../images/people.png", cv::IMREAD_GRAYSCALE);
cv::Mat tmpl = cv::imread("../../images/people2.png", cv::IMREAD_GRAYSCALE);
cv::Mat result;
cv::matchTemplate(src, tmpl, result, cv::TM_CCOEFF_NORMED);
double minVal, maxVal;
cv::Point minLoc, maxLoc;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
cv::Mat src_color;
cv::cvtColor(src, src_color, cv::COLOR_GRAY2BGR);
cv::rectangle(src_color, cv::Rect(maxLoc.x, maxLoc.y, tmpl.cols, tmpl.rows), cv::Scalar(0, 0, 255));
return 0;
}
다음에는 이 템플릿 매칭을 응용해서 여러 물체가 있을 때 특정 각도의 물체만 찾는 것을 해볼께요.