본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] 템플릿 매칭 (template matching) 총정리(1) - matchTemplate minMaxLoc SQDIFF CCORR CCOEFF

지난 시간에는 영상 분할하는 방법 중 grabcut에 대해 알아봤습니다.

https://blog.naver.com/dorergiverny/223108058366

 

[OpenCV][C++] 영상 분할 ( image segmentation ) 총정리(3) - GrabCut graphcut setmousecallback

지난번에는 워터쉐드 (watershed) 알고리즘에 대해 알아봤습니다. https://m.blog.naver.com/dorergiverny/...

blog.naver.com

 

이번에는 템플릿 매칭에 대해 알아보겠습니다.

템플릿 매칭(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() 함수는 아래 글에서 설명을 했습니다.

https://blog.naver.com/dorergiverny/223105068154

 

[OpenCV][C++] 영상에서 히스토그램 (histogram) 구하기 - cv::calcHist graph draw 계산 그리기

이번에는 영상 분석에서 가장 중요한 히스토그램에 대해 알아보도록 하겠습니다. 통계학에서 히스토그램은 ...

blog.naver.com

 

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;
}