본문 바로가기

Computer Vision by OpenCV

OpenCV 함수 at( )과 포인터 사용간의 시간 차이

반응형
SMALL


OpenCV 성능향상을 위한 포인터(Pointer) 사용


함수 at( )의 문제점


이미지의 각 픽셀값을 바꿀 때

함수 at( )을 사용했다.

기능상으로는 아무 문제가 없지만, 속도가 느린 단점이 있다.


예를 들어, 다음과 같은 컬러이미지를 반전하는 경우를

생각해보자.


원본 컬러영상


반전한 컬러이미지


함수 at( )을 이용한 이미지 반전코드

	for (int r = 0; r < image_rows; r++)
	{
		for (int c = 0; c < image_cols; c++)
		{
			for (int ch = 0; ch < 3; ch++)
			{
				outputImg.at<Vec3b>(r, c)[ch] = 255 - outputImg.at<Vec3b>(r, c)[ch];
			}
		}
	}

각 픽셀에 대해,

픽셀별로 R, G, B 채널에 대해서 반전시키면 된다.

그런데, 사실 이 방법은 

시간이 오래 걸린다.


시간측정방법

코드가 수행하는데 걸리는 시간을 측정하기 위해서는

함수 getTickCount( )를 이용하면 된다.

	const int64 start = getTickCount();
        .....
	int64 elapsed = (getTickCount() - start);


함수 at( )을 이용하여, 반전이미지 만드는데 걸리는 시간

함수 getTickCount( )를 이용해서

측정한 시간은 평균적으로 210,000 tick 정도 된다.


포인터를 이용한 속도 개선

함수 at ( )을 사용하지 않고,

이미지 데이터에 대해 포인터를 이용하여 직접 픽셀값을

바꾸면 속도를 엄청나게 개선시킬 수 있다.

for (int r = 0; r < image_rows; r++) { uchar* value = inputImg.ptr<uchar>(r); uchar* result = outputImg.ptr<uchar>(r); for (int c = 0; c < image_cols; c++) { *result++ = *value++ ^ 0xff; *result++ = *value++ ^ 0xff; *result++ = *value++ ^ 0xff; } }

위의 코드는 반전이미지를 동일한 방식으로 만들되,

포인터를 이용하여 각 픽셀값을 직접 접근하는 방식이다.

이렇게 했을 때, 측정시간은

평균적으로 4,300 tick이다.


비교 그래프

아래는 함수 at( )과 포인터를 이용했을 때의

각각의 소요시간이다.

포인터를 이용했을 때, 약 50배 정도 속도가 빨라지는 것을 알 수 있다.

따라서, 성능이 중요할 경우에는 포인터 사용을 적극 고려해야 한다.



소스코드

위의 실험수행을 위한 프로그램이다.

중간에 매크로 USE_AT을 #define하면 함수 at( )을 이용하는 방식으로 동작하고,

comment-out하면 포인터를 이용한 방식으로 동작한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{

	if (argc != 2)
	{
		cout << " Provide image name to read" << endl;
		return -1;
	}

	Mat inputImg;
	Mat outputImg;

	inputImg = imread(argv[1], CV_LOAD_IMAGE_COLOR);
	outputImg = inputImg.clone();

	const int64 start = getTickCount();

	int image_rows = inputImg.rows;
	int image_cols = inputImg.cols;

//#define USE_AT
#ifdef USE_AT
	for (int r = 0; r < image_rows; r++)
	{
		for (int c = 0; c < image_cols; c++)
		{
			for (int ch = 0; ch < 3; ch++)
			{
				outputImg.at<Vec3b>(r, c)[ch] = 255 - outputImg.at<Vec3b>(r, c)[ch];
			}
		}
	}
#else if
	for (int r = 0; r < image_rows; r++)
	{
		uchar* value = inputImg.ptr<uchar>(r);
		uchar* result = outputImg.ptr<uchar>(r);
		for (int c = 0; c < image_cols; c++)
		{
			*result++ = *value++ ^ 0xff;
			*result++ = *value++ ^ 0xff;
			*result++ = *value++ ^ 0xff;
		}
	}
#endif
	int64 elapsed = (getTickCount() - start);

	cout << "Elapsed time " << elapsed << endl;

	namedWindow("input", CV_WINDOW_AUTOSIZE);
	namedWindow("output", CV_WINDOW_AUTOSIZE);
	moveWindow("input", 100, 100);
	moveWindow("output", 120, 120);
	imshow("input", inputImg);
	imshow("output", outputImg);

	waitKey(0);
	return 0;
}


반응형
LIST