본문 바로가기
코딩/OpenCV

[c++ opencv] 이미지서치 연산속도 향상, Template Matching 사용하기 #2 연산속도 빠르게

by DIYver 2020. 10. 25.

 

본문 목표

이전 포스트에서 이미지서치 Template Matching을 배웠었다.

이번에는 코딩에서 제일 중요한 연산속도 향상에 대해서 다뤄보도록 하겠다.

어떻게 하면 동일한 결과를 얻으면서도 연산속도를 빠르게 할 수 있는지 알아보자.

 

키워드 : 이미지서치, Template Matching, 템플릿매칭

 

 

 

 

 

알아볼 함수 원형

- 테플릿 매칭 ( matchTemplate )

	matchTemplate(src, templ, result, 1);
	normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat() );
	minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );

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 NORMED

 

  ○ mask : templ 이미지에 해당하는 마스크, 필요로 하는 이미지서치에서만 사용

- 0: TM_SQDIFF  , 3: TM CCORR NORMED  두 가지 방법만 mask 이미지 사용 가능

 

 

 

 

 

 

 

코드 설명에 들어가기에 앞서서

먼저 생각해보는 시간을 가져보자.

 

어떻게 하면 연산속도를 향상시킬 수 있을까?

 

이미지 하면 떠오르는 것을 정리해봤다.

1. 크기(해상도)

2. 색상(컬러, 흑백)

 

일단 이미지가 작아야지 연산속도가 빠를 것이고,

색상도 데이터 크기가 더 작은 흑백이미지를 사용한는 것이 연산속도 향상에 기여할 것이다.

 

이제 실전으로 테스트를 해보도록 하자.

 

 

일단 저번 포스트에서 다뤘던 이미지를 그대로 사용하기로하고,

원본 크기(FHD) 에서 연산 시간이 얼마나 걸리는지 확인해보자.

 

사용할 src 이미지

 

 

사용할 templ 이미지

 

 

 

연산 방법에 따른 연산 속도

0: TM_SQDIFF

0.405 초 소요

 


1: TM_SQDIFF NORMED

0.383 초 소요

 


2: TM CCORR

이미지 찾기 실패,

0.272 초 소요

 


3: TM CCORR NORMED

0.4초 소요

 


4: TM COEFF

0.389 초 소요

 

 


5: TM COEFF NORMED

0.438 초 소요

 

 

 

연산 속도로만 보면

방법 중에서 순위는 

1.  TM CCORR (2) : 0.272초

2.  TM_SQDIFF NORMED (1) : 0.383초

3.  TM COEFF (4) : 0.389초

4.  TM CCORR NORMED (3) : 0.400초

5.  TM_SQDIFF (0) : 0.405초

6.  TM COEFF NORMED (5) : 0.438초

 

이렇게 된다.

그런데 1순위 방법은 정확도에서 탈락이다... ㅋㅋ

 

이미지에 따라서 잘 될 수도 있고 안 될 수 있는 부분이라 절대적인 수치는 아닌점 다시 한번 말씀드린다.

 

 

 

이제 이미지 크기를 4분의 1로 줄여본다.

(가로 세로 50% 로 축소)

중요한건 src 와 templ 모두 동일하게 줄여야 한다는 것이다.

 

 

 

이미지 크기 4분의 1로 축소시킨 코드 테스트 

- CODE

#include <opencv2/opencv.hpp>
#include <time.h>

using namespace cv;
using namespace std;

int main(int, char**)
{
	clock_t start, end;

	double minVal, maxVal;
	Point minLoc, maxLoc;
	Point matchLoc;

	Mat src = imread("test_img2.png");
	if (src.empty())
	{
		cout << "no src Image!"<< endl;
		return -1;
	}

	Mat templ = imread("templ.png");
	if (src.empty())
	{
		cout << "no templ Image!" << endl;
		return -1;
	}

	Mat result;



	resize(src, src, Size(src.cols / 2, src.rows / 2));
	resize(templ, templ, Size(templ.cols / 2, templ.rows / 2));

	for (int i = 0; i < 6; i++)
	{
		Mat img_out;
		src.copyTo(img_out);

		int Matching_method = i;
		/*
		0: TM_SQDIFF
		1: TM_SQDIFF NORMED
		2: TM CCORR
		3: TM CCORR NORMED
		4: TM COEFF
		5: TM COEFF NORMED";
		*/

		start = clock();


		matchTemplate(src, templ, result, i);
		normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
		minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
		//minMaxLoc(result, &minVal, NULL, &minLoc, NULL);
		if (Matching_method == 0 || Matching_method == 1)
		{
			matchLoc = minLoc;
		}
		else
			matchLoc = maxLoc;


		end = clock();

		//cout << "Searching time : " << difftime(end, start) / CLOCKS_PER_SEC << endl;
		
		double searching_time = difftime(end, start) / CLOCKS_PER_SEC;
		char mytext[30];
		sprintf_s(mytext, "searching time : %lf", searching_time);

		putText(img_out, mytext, Point(100,100), 1, 2, Scalar(0,0,255),1);

		rectangle(img_out, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 0, 255), 1);

		cvtColor(result, result, COLOR_GRAY2BGR);
		circle(result, matchLoc, 3, Scalar(0, 0, 255), 1);

		imshow("src", img_out);
		//imshow("templ", templ);
		//imshow("result", result);

		waitKey(0);

	}
	return 0;
}

 

- RESULT

 

 

0: TM_SQDIFF

 


1: TM_SQDIFF NORMED

 


2: TM CCORR

 

 


3: TM CCORR NORMED

 

 


4: TM COEFF

 

 

 


5: TM COEFF NORMED

 

 

일단 결과는 동일한 것을 확인할 수 있었고,

소요시간이 확실히 줄어든 것을 확인할 수 있었다.

 

연산 속도로만 보면

방법 중에서 순위는 

1.  TM CCORR (2) : 0.272초 -> 0.064초

2.  TM COEFF (4) : 0.389초 -> 0.089초

3.  TM_SQDIFF NORMED (1) : 0.383초 -> 0.095초

4.  TM_SQDIFF (0) : 0.405초 -> 0.097초

5.  TM CCORR NORMED (3) : 0.400초 -> 0.098초

6.  TM COEFF NORMED (5) : 0.438초 -> 0.107초

 

순위도 살짝 바뀌었지만 그렇게 유의미한 차이는 아닌것 같고

 

연산 속도 차이를 보면 기존 해상도보다 약 4배정도 빨라진 것을 볼 수 있다.

이미지의 해상도를 25%로 축소시켰기 때문에 그만큼 빨라진 것이라 할 수 있겠다.

 

결과에는 차이가 없으면서도 속도 향상이 되었으므로 이 방법은 옳은 방법이라 할 수 있겠다.

 

 

 

 

 

 

이미지를 흑백으로 전환해서 테스트

 

이번에는 이미지를 흑백으로 전환한 다음에 결과를 살펴보자.

일단 예상하건데 

컬러이미지는 흑백보다 데이터 크기가 3배이지만,

이미 템플릿매칭을 할 때 내부적으로 흑백처리를해서 연산을 할 것 같다는 생각이다.

그래서 굳이 흑백 전환을 할 필요가 있나 싶은 생각이다.

 

일단 현재 코드에서는 소요시간이 향상되어야 정상이다. 

왜냐하면 src 와 templ 이미지를 둘 다 grayscale 로 변환해주는 시간은 측정이 안 되기 때문이다.

 

그래도 얼마나 빨라지는지 알아볼 필요가 있겠다.

뭐, 결과를 살펴보면 알겠지.

 

- CODE

#include <opencv2/opencv.hpp>
#include <time.h>


using namespace cv;
using namespace std;

int main(int, char**)
{
	clock_t start, end;

	double minVal, maxVal;
	Point minLoc, maxLoc;
	Point matchLoc;

	Mat src = imread("test_img2.png");
	if (src.empty())
	{
		cout << "no src Image!"<< endl;
		return -1;
	}

	Mat templ = imread("templ.png");
	if (src.empty())
	{
		cout << "no templ Image!" << endl;
		return -1;
	}

	Mat result;



	//resize(src, src, Size(src.cols / 2, src.rows / 2));
	//resize(templ, templ, Size(templ.cols / 2, templ.rows / 2));
	cvtColor(src, src, COLOR_BGR2GRAY);
	cvtColor(templ, templ, COLOR_BGR2GRAY);


	for (int i = 0; i < 6; i++)
	{
		Mat img_out;
		src.copyTo(img_out);

		int Matching_method = i;
		/*
		0: TM_SQDIFF
		1: TM_SQDIFF NORMED
		2: TM CCORR
		3: TM CCORR NORMED
		4: TM COEFF
		5: TM COEFF NORMED";
		*/

		start = clock();


		matchTemplate(src, templ, result, i);
		normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
		minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
		//minMaxLoc(result, &minVal, NULL, &minLoc, NULL);
		if (Matching_method == 0 || Matching_method == 1)
		{
			matchLoc = minLoc;
		}
		else
			matchLoc = maxLoc;


		end = clock();

		//cout << "Searching time : " << difftime(end, start) / CLOCKS_PER_SEC << endl;
		
		double searching_time = difftime(end, start) / CLOCKS_PER_SEC;
		char mytext[30];
		sprintf_s(mytext, "searching time : %lf", searching_time);

		putText(img_out, mytext, Point(100,100), 1, 2, Scalar(0,0,255),1);

		rectangle(img_out, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 0, 255), 1);

		cvtColor(result, result, COLOR_GRAY2BGR);
		circle(result, matchLoc, 3, Scalar(0, 0, 255), 1);

		imshow("src", img_out);
		//imshow("templ", templ);
		//imshow("result", result);

		waitKey(0);

	}
	return 0;
}

 

 

 

- RESULT

 

0: TM_SQDIFF


1: TM_SQDIFF NORMED


2: TM CCORR

 


3: TM CCORR NORMED

 


4: TM COEFF

 

 

 


5: TM COEFF NORMED

 

 

일단 결과는 놀라웠다. 

순위를 나열해보면

 

1.  TM CCORR (2) : 0.272초 -> 0.057초

2.  TM_SQDIFF (0) : 0.405초 -> 0.067초

3.  TM CCORR NORMED (3) : 0.400초 -> 0.074초

4.  TM COEFF (4) : 0.389초 -> 0.092초

5.  TM_SQDIFF NORMED (1) : 0.383초 -> 0.109초

6.  TM COEFF NORMED (5) : 0.438초 -> 0.129 초

 

순위가 또 많이 바뀌었다.

 

시간 단축이 거의 3~6배 정도 차이가 나는 것을 볼 수 있다.

 

일단 확실한 것은 흑백으로 미리 전환해주면 전환해준만큼 연산속도 향상에 도움이 된다는 것이다.

이미지를 줄이는 것보다 흑백이 더 빠른 것을 보면 이미지 처리에서 grayscale로 연산을 고집하는 이유를 더 체감할 수 있는 것 같다.

 

 

 

 

 

 

그렇다면

이미지 크기도 줄이면서 흑백이미지를 사용하면 어떻게 될까?

완전 끝장나는 연산속도를 보여주지 않을까 싶다.

 

당장 테스트해보자.

 

 

이미지 크기 4분의 1로 축소시키고 흑백으로 테스트

 

코드는 굳이 첨부 안 하겠다.

 

- RESULT

 

0: TM_SQDIFF

 


1: TM_SQDIFF NORMED

 


2: TM CCORR

 


3: TM CCORR NORMED

 


4: TM COEFF

 


5: TM COEFF NORMED

 

연산속도를 기준으로 순위를 메기고,

기존 컬러대비 연산속도 비교를 해보면

 

1.  TM CCORR (2) : 0.272초 -> 0.013초

2.  TM_SQDIFF (0) : 0.405초 -> 0.015초

3.  TM CCORR NORMED (3) : 0.400초 -> 0.019초

4.  TM COEFF (4) : 0.389초 -> 0.023초

5.  TM_SQDIFF NORMED (1) : 0.383초 -> 0.026초

6.  TM COEFF NORMED (5) : 0.438초 -> 0.036초

 

와 이건 뭐 미친속도라고 할 수 있겠다.

제일 늦은  TM COEFF NORMED 의 방법이라도

1초 동안 약 28장의 이미지를 처리할 수 있는 속도가 되었다.

원래는 1초에 2장 처리할 수 있었던 것에 비하면 엄청나게 빨라졌다는 것을 알 수 있다.

제일 빠른 방법의 경우 약 77프레임을 처리할 수 있는 속도이다.

보통 카메라가 30프레임에서 60프레임을 1초에 찍는다고 했을때, 모른 촬영 이미지를 다 처리할 수 있다는 뜻이다.

 

 

 

 

이번 포스트에서는 고해상도의 이미지를 사용했으면서도 어떻게 하면 연산속도를 더 빠르게 할 수 있는지 알아봤다.

당연한 이야기라서 안 다뤘지만 연산속도를 빠르게 하기위해서는 ROI(region of interest)를 설정해주어야 한다는 것이다.

 

ROI 까지 사용한다면 연산속도는 뭐 고민할 필요도없이 빨라질 것이 분명하다.

 

 

구글에서 이미지서치가 위와같은 원리로 이미지 검색을 하는 것이다.

마찬가지로 이미지 크기가 다를경우에는 검색이 힘들다.

예로들면 원본은 FHD 인데

그걸 핸드폰 화면에서 캡쳐한 사진으로 이미지 검색하면 검색이 잘 안 된다는 것이다.(해상도가 바뀌었기 때문에)

요새는 사용안해봐서 모르겠는데, 아무튼 옛날에는 그랬었다.

 

 

 

 

 

결론.

1. 이미지 크기를 줄이면 Template Matching 속도가 향상된다. 

 - 이미지 크기를 줄인 정도 만큼 줄어듦

2. 이미지를 흑백으로 이용하면 Template Matching 속도가 향상된다. 

 - 최소3배 에서 최대 6배까지 빨라짐

3. RoI 를 설정하고 그 영역내에서만 이미지 서치를 하면 Template Matching 속도가 향상된다. 

 - 1번 결론과 비슷한 속도 향상이 나올 것으로 기대됨

 

 

 

 

 

 

 

<연관 글 보러가기>

 

[c++ opencv] 이미지서치, Template Matching 사용하기 #1

 

[c++ opencv] 이미지서치, Template Matching 사용하기 #1 기본사용

본문 목표 OpenCV를 다루다보면 이미지서치를 사용할 때가 온다. 자율주행 시스템에서도 표지판인식, 번호판 인식에 사용하기도 하고, 컴퓨터 화면에서도 이용하기도 한다. 또한 생산 공장등에서

diyver.tistory.com

 

 

 

 

 

도움이 되었거나, 문제가 있는 경우 댓글로 알려주세요~!

감사의 댓글은 작성자에게 큰 힘이 됩니다 ^^

댓글