본문 바로가기
SoftWare/OpenCV

opencv image 접근 방법 및 성능 그리고 병렬처리

by 학수씨 2015. 3. 16.

안녕하세요.. 강좌라고 하긴 좀 그렇고... 가끔 성능 때문에 질문들이 올라오는걸 볼수 있는데

image에 어떻게 접근하느냐에 따라 성능 분석을 좀 해봤습니다.

for문과 parallel_for 의 성능 차이와 (data접근)
2차 for문과 2차 parallel_for 의 성능 차에 (at접근)

어떻게 이미지 처리를 해야 성능이 최대치로 나올수 있는지에 대해 분석한 결과를 써드릴게요~

1. data배열 1차 for문 (data접근)
2. rows, cols 2차 for문 (at접근)

3. data배열 1차 parallel_for (data접근)
4. rows, cols 2차 parallel_for (at접근)

------------------------------------------------------------------------------------------------

일단 Mat이건 IplImage건 opencv에서 접근하는 방법은 크게 3가지가 있습니다.

1. at 접근 (유효성 검사 진행을 함)
장점 : 안정하고 정확
단점 : 메모리복사가 이루어지기 때문에 느림

2. ptr 접근(포인터형으로 변환하여 접근) (유효성 검사를 안함)
장점 : at접근 방식보다 빠름
단점 : image data를 포인터형으로 변환하는 과정에서 메모리누수 또는 충돌문제,

3. data 접근(가장 빠름) (유효성 검사를 안함)
장점 : 가장 빠름 (월등히)
단점 : 잘못 접근했다간 ;;;;;;; -> 이부분 설명하기 좀 애매 하네요 ㅋㅋ

일단.. 저는 3번이나 2번을 가장 많이 사용을 합니다..
사용법은 아래~~~

1. at접근
Mat image(ROWS, COLS, CV_TYPE);
cv::Vec3b v = image.at<cv::Vec3b>(y, x);   //Get image data
image.at<cv::Vec3b>(y, x) = v;    //Set image data

2. ptr 접근
Mat image(ROWS, COLS, CV_TYPE);
for (int y = 0; y < image.rows; y++) 
{
uchar *scanLine = image.ptr<uchar>(y);
for (int x = 0; x < image.cols; x++) 
{
// scanLine[x*CHANNEL + CHANNEL]
}
}

3. data접근
Mat image(ROWS, COLS, CV_TYPE);
uchar* data = (uchar*)image.data;   //Get image data's
data[y * x + CHANNEL] = ?;    //Set image data

위처럼 사용합니다.. (Mat 기준입니다.)

성능은 1번은 완전...;;; 그냥 테스트 수준이라 할수 있는 정도로 성능이 않나오고
2번 3번은 비슷한 성능이 나옵니다. 하지만 복잡한 수식이 들어가면 3번이 좀더 빠릅니다.

------------------------------------------------------------------------------------------------

좀 성능 분석 결과자료를 올리려고 했으나 좀 무의미해진것 같네요..
맨 위에 분석해본개수 4가지의 샘플 code와 각각의 성능을 써드리겠습니다.~~
예제는 brightness contrast 효과를 준 code이며 성능분석을 위해 Debug 모드로 진행하였습니다.

CPU : i5 Quad Core
Image Width : 720px
Image Height : 406px
Image Channel : 3


1. data배열 1차 for문    // 평균 30ms 
uchar* data = (uchar*)rawCopyFrame.data;
int length = rawCopyFrame.rows  * rawCopyFrame.cols * rawCopyFrame.channels();

for(int i=0;i<length;i++)
{
data[i] = cv::saturate_cast<uchar>( alpha * data[i]  + beta );
}

2. rows, cols 2차 for문    //평균 125ms
for( int y = 0; y < rawCopyFrame.rows; y++ )
for( int x = 0; x < rawCopyFrame.cols; x++ )
{
cv::Vec3b v1 = rawCopyFrame.at<cv::Vec3b>(y,x);

v1[0] = cv::saturate_cast<uchar>( alpha* v1[0] + beta );
v1[1] = cv::saturate_cast<uchar>( alpha* v1[1] + beta );
v1[2] = cv::saturate_cast<uchar>( alpha* v1[2] + beta );

rawCopyFrame.at<cv::Vec3b>(y,x) = v1;
}
}

3. data배열 1차 parallel_for      //평균 4ms
uchar* data = (uchar*)rawCopyFrame.data;
int length = rawCopyFrame.rows  * rawCopyFrame.cols * rawCopyFrame.channels();

tbb::parallel_for(0, length, [&]( int i) 
{
data[i] = cv::saturate_cast<uchar>( alpha * data[i]  + beta );
});


4. rows, cols 2차 parallel_for    //평균 50ms
tbb::parallel_for(0, rawCopyFrame.rows, [&]( int y ) 
{
tbb::parallel_for(0, rawCopyFrame.cols, [&]( int x ) 
{
cv::Vec3b v1 = rawCopyFrame.at<cv::Vec3b>(y,x);
v1[0] = cv::saturate_cast<uchar>( alpha* v1[0] + beta );
v1[1] = cv::saturate_cast<uchar>( alpha* v1[1] + beta );
v1[2] = cv::saturate_cast<uchar>( alpha* v1[2] + beta );
rawCopyFrame.at<cv::Vec3b>(y,x) = v1;
});
});

------------------------------------------------------------------------------------------------

성능분석시 ptr접근 방법은 사용하지 안았습니다.. (잘 사용 안함;;; data쓰는게 차라리 ㅋ)
너무 글을 두서없이 쓴것 같네요...

image data에 접근할시 제가 쓴글이 유용했으면 하는 바램입니다.~

-------------- 추가------------------------------------------------------------
댓글에 Release 질문이 있으셔서.. 귀찮지만 ㅋ 릴리즈로 성능분석을 해봤습니다.

1. data배열 1차 for문             // 평균 0.03ms
2. rows, cols 2차 for문           // 평균 0.04ms
3. data배열 1차 parallel_for      // 평균 0.008ms 
4. rows, cols 2차 parallel_for    // 평균 0.02ms

릴리즈모드에서도 역시나 3번인 1차 paraller_for(data접근) 방법이 월등히 빠릅니다....

물론 1번, 2번, 4번 방법도 릴리즈모드에서는 그렇게 느린편은 아니지만... 3번 방법이 월등히 빠르기 때문에
성능이 중요하신 분들께서는 3번을 사용하시는게 가장 성능을 극대화 시킬수 있는 방법 입니다.


-------------- 추가------------------------------------------------------------
랩자드님의 의견을 토대로 data접근과 ptr접근의 속도를 비교해봤습니다.
병렬처리가 아닌 일반 for문으로 분석해봤습니다.. 
병렬처리는 위에 설명드린것과 마찬가지로 훨씬 좋은 성능을 내줍니다.

아래 성능은 brightness contrast 효과에 대한 성능입니다. (※이미지 사이즈는 위에 참고)

1. data 접근
0.03000ms
0.03059ms
0.02998ms
0.03001ms
0.03009ms
0.03027ms
0.03983ms
0.03053ms

2. ptr 접근
0.04937ms
0.05000ms
0.04994ms
0.04162ms
0.04000ms
0.03998ms
0.04052ms
0.04035ms


비교차트 입니다. ptr접근 방법과 data접근 방법의 경우 속도차가 너무 미미하게 납니다.
누가 더 좋다 안좋다 할수 없을 수준 입니다..

그래서...!! 좀더 복잡한 Auto White Balance 를 돌려봤습니다..
수치는 또 적기 귀차나 차트만... 첨부합니다.



역시나. 좀더 복잡한 알고리즘이 돌아가도 누가 더 좋다 라고 할수 있는 수준은 아닙니다..
거의 비슷합니다.

결론은 Ptr 접근 방법과 Data 접근 방법은 접근 방법이 다르기 때문에
구현하고자하는 수식 or 알고리즘에 따라 사용하시면 될것입니다..

uchar 형식의 Mat을 사용하길 경우에는 Data를 써도 무방하지만
uchar 형식이 아닌 Mat을 사용하실 경우에는 랩자드님의 의견처럼 Ptr을 쓰는게 좋습니다..

-----------------------------------------------------------------------------------------

가장 중요한 사항을 안적어 놨군요...
병렬처리시 2중 병렬처리는 추천드리지 않습니다..메모리 접근 문제가 있기  때문에
병렬처리할때에는 꼭 1차 fot문만 돌려야 합니다.


'SoftWare > OpenCV' 카테고리의 다른 글

yolov3 학습된 파일을 C# 에서 사용하기  (1) 2021.09.11
Google Colab 에서 Yolov3 학습하기  (0) 2021.09.10
Android JNI OpenCV 성능  (0) 2015.02.11
opencv 1.x VS opencv 2.x 성능차이  (0) 2015.02.09
OpenCV Vibrance&Saturation  (0) 2015.01.13

댓글