OPEN CV - 1채널 영상의 Pixel 데이터 접근 방법

포타토·2020년 1월 1일
0

컴퓨터 비전

목록 보기
1/3

OPEN CV에서 픽셀 접근하기

필자는 틈틈이 컴퓨터 비전을 공부하고 있는데, 아마 대부분의 사람들이 처음 접하게 되는 것이 Matlab 아니면 open CV일 것이다.

필자는 대학에서 두 가지 모두 겉핥기 식으로 배운적이 있지만, 배움이 깊지 않아 오랫동안 다루지 않다보니 다 잊어버리고 말았다.😥

둘 다 다시 깊게 공부하고 싶지만, 좀 더 접근이 쉬운 open CV부터 다시 공부하는 중이다.

각설하고, open CV를 통해 이미지를 다루다 보면 픽셀 데이터에 접근해야 할 경우가 생긴다.
ROI(Region Of Interest)를 설정한다던지, 특정 영역의 픽셀값을 바꾼다던지 등등 많은 경우가 있을 것이다.

그렇다면, 이미지를 읽어들인 후 픽셀접근은 어떻게 하는 것일까? 지금부터 알아보자.

1. Image load

우선, 이미지를 읽어야 한다.

예를 들어, Mat src = imread("lenna.bmp") 와 같은 코드를 이용하여 이미지를 읽어들이지만, 지금은 실습을 위해 다음과 같은 3x3의 작은 1채널 Mat 데이터가 있다고 하자.

코드로는 cv::Mat src = Mat::zeros(3, 3, CV_8UC1); 정도가 될 수 있겠다.

이 데이터를 읽어들인 이미지의 픽셀 정보를 저장한 Mat 데이터라고 하자.
이제 픽셀 데이터에 접근하여 값을 바꿔보도록 하자.

2. 이미지 픽셀에 접근하여 값을 변경한다.

이미지 픽셀에 접근하는 방법은 3가지가 있다. 더 많을 수도 있지만, 필자가 아는 방법은 세가지다. 하나씩 살펴보자.

1) Mat::at 사용

int cnt = 1;
for (int y = 0; y < src1.rows; y++) {
	for (int x = 0; x < src1.cols; x++) {
		src1.at<uchar>(y, x) = cnt;
		cnt++;
	}
}

Mat::at 함수를 쓰면 픽셀 데이터의 y, x 좌표에 접근할 수 있다.
위 코드와 같이 실행하면, 아래와 같이 (y, x) 좌표에 직접 접근하여 값을 변경한다.

2) Mat::ptr 사용

cnt = 1;
for (int y = 0; y < src2.rows; y++) {
	uchar* pt = src2.ptr<uchar>(y);
	for (int x = 0; x < src2.cols; x++) {
		pt[x] = cnt;
		cnt++;
	}
}

Mat::ptr을 사용하면, 픽셀 데이터의 y row에 해당하는 첫 번째 데이터의 주소값을 얻을 수 있다.
따라서, 코드에서도 볼 수 있듯이 x좌표를 따라 순서대로 픽셀 데이터에 접근이 가능하다.

3) Mat::data 사용

cnt = 1;
uchar * pt = src3.data;
for (int y = 0; y < src3.rows; y++) {
	for (int x = 0; x < src3.cols; x++) {
		pt[src3.cols * y + x] = cnt;
		cnt++;
	}
}

Mat::data를 사용하면, 픽셀 데이터의 (0, 0) 위치의 주소값을 얻을 수 있다.
Mat::data를 사용할 경우엔, (y, x)좌표계 개념으로 생각하면 안되고, 일렬로 늘어져 있다고 생각해야 한다.
즉, (0, 0) ~ (2, 2)가 아니라, 0 ~ 8인 것이다.
따라서, 위 코드와 같이 이미지의 width(col의 개수)를 곱해주며 픽셀값을 변경해 주어야 한다.

3. 결론: at? ptr? data?

이미지 픽셀 접근의 3가지 방법을 살펴보았다. 어떤 방법이 제일 좋을까?
직관적으로 쉬운 방법이라면 1. Mat::at이 가장 쉬울 것이다. 그러나, 성능이 안좋다. 느리다는 뜻이다.
다른 두 가지 방법(2. Mat::ptr, 3. Mat::data)의 성능은 비슷하다. 쓰는 취향 차이가 있지 않을까.

필자가 컴퓨터 비전 업무에 종사자를 만날 기회가 있었는데, 그분은 3번째 방법을 쓰고 있었다.
정리해 보자면,

  1. Mat::at - 직관적으로 쉽다. 성능이 낮다.
  2. Mat::ptr - 성능 좋다. 3번 방법보다는 쉽다고 생각한다(필자 기준).
  3. Mat::data - 성능 좋다. 현업에서 자주 쓰이는 것 같이 보인다(필자 기준).

결론은, 맘에 드는 걸로 쓰면 될 것 같다. 필자는 3번째 방법을 쓰려고 한다.

4. 주의사항!!

혹여나 위 코드를 참고하려면, 1채널 이미지(흑백 이미지)에서만 사용해야 한다.
3채널 이미지(컬러 이미지)는 B, G, R 3가지 값이 늘어져 있기 때문에 위와 같은 코드를 썼다가는 그림의 3/1만 변하는 기괴한 이미지를 얻게 될 것이다.
1채널 이미지 픽셀 접근과 크게 다르지 않으므로, 코드를 조금만 더 추가해주면 된다. 이는 다음 기회에 포스팅 해보도록 하겠다.

profile
개발자 성장일기

0개의 댓글