Pixel Access
Pixel Access는 이미지에서 특정 위치의 픽셀 값을 가져오는 방법이다.
다음 코드를 살펴보자
그레이스케일 이미지와 컬러 이미지에서 특정 좌표의 픽셀 값을 어떻게 가져오는지를 보여주는 코드다.
image = imread("lena.png");
image_gray = imread("lena.png", 0);
이미지를 다음과 같이 선언했다고 해보자.
value = image_gray.at<uchar>(50, 100);
이 코드에서는 그레이스케일 이미지의 (50, 100) 좌표에 있는 픽셀 값을 가져오고 있다.
여기서 이 좌표값은 (세로, 가로)이다.
이 값은 uchar 타입으로, 픽셀의 밝기 값(0-255)을 나타낸다.
value_B = image.at<Vec3b>(50, 100)[0];
value_G = image.at<Vec3b>(50, 100)[1];
value_R = image.at<Vec3b>(50, 100)[2];
여기서는 컬러 이미지에서 (50, 100) 좌표의 픽셀 값을 Vec3b 타입으로 가져온다.
Vec3b는 BGR (Blue, Green, Red) 순서로 세 가지 값을 가지고 있다.
그럼 이번에는 데이터 멤버 접근 방식을 사용해 픽셀에 접근하는 방법에 대해서 알아보자
이 방법은 at 연산자를 사용하는 것보다 빠르지만, 그만큼 직접 메모리 주소를 다뤄야 하기 때문에 조금 더 복잡하다.
DATA_TYPE* data = (DATA_TYPE*)image.data;
이 부분에서는 image.data를 사용해 이미지의 데이터 버퍼에 직접 접근하고 있다.
image.data는 이미지의 첫 번째 픽셀을 가리키는 포인터다.
이 포인터를 DATA_TYPE*로 캐스팅해서 data 포인터로 사용하고 있다.
여기서 DATA_TYPE은 픽셀 데이터의 타입이다.
예를 들어 unsigned char 또는 Vec3b 같은 걸 쓸 수 있다.
data[WANT_ROW * image.cols + WANT_COL]
이 표현식은 이미지의 특정 좌표에 있는 픽셀 값에 접근하는 방법이다.
WANT_ROW * image.cols는 원하는 행에 있는 첫 번째 열의 픽셀로 이동하는 데 필요한 오프셋을 계산하고
그 후에 WANT_COL을 더해서 특정 열의 픽셀에 접근하게 되는 거다.
다음은 데이터 멤버 함수를 이용해서 이미지에서 픽셀 값을 직접 접근하는 예제 코드이다.
이미지를 다음과 같이 불러온다고 치자
image = imread("lena.png");
channels = image.channels();
image.channels()이라는 함수는 이미지의 채널 수를 반환한다.
컬러 이미지니까 channels는 3이 될 거다.
uchar* data = (uchar*)image.data;
image.data는 아까 말했던 이미지의 첫 번째 픽셀을 가리키는 포인터를 반환한다.
uchar* data = (uchar*)image.data는 이 포인터를 uchar*로 캐스팅해서 data라는 포인터에 저장한다.
여기서 uchar는 부호 없는 8비트 정수(unsigned char)를 의미한다.
즉 픽셀 하나의 채널 값을 저장할 수 있는 타입이다.
value_B = data[(50 * image.cols + 100) * channels + 0];
value_G = data[(50 * image.cols + 100) * channels + 1];
value_R = data[(50 * image.cols + 100) * channels + 2];
이 부분이 제일 중요하다.
여기서 (50 * image.cols + 100)는 (50, 100) 위치의 픽셀을 가리키는 오프셋을 계산하는 거다.
image.cols는 이미지의 가로 픽셀 수를 나타낸다.
* channels를 해주는 이유는 각 픽셀이 B, G, R 세 개의 값을 가지고 있기 때문이다.
그래서 (50 * image.cols + 100) 위치의 픽셀은 3개의 연속된 값(blue, green, red)으로 저장돼있다.
+0은 Blue 채널, +1은 Green 채널, +2는 Red 채널 값을 가져오는거다.