관리 메뉴

공부 기록장 💻

[OpenCV/C++] 컬러 영상 처리 1 - 픽셀 값 참조와 색 공간 변환 본문

# Tech Studies/Computer Vision • OpenCV

[OpenCV/C++] 컬러 영상 처리 1 - 픽셀 값 참조와 색 공간 변환

dream_for 2022. 12. 3. 22:24

OpenCV 4로 배우는 영상 처리와 컴퓨터 비전 CH10. 컬러 영상 처리 정리

 

컬러 영상의 픽셀 값 참조

 

OpenCV에서 영상 파일을 불러와 Mat 객체를 생성할 때 imread() 함수를 사용하는데, 이때 두번째 인자를 IMREAD_COLOR로 설정하면 영상을 3채널 컬러 영상 형식으로 불러올 수 있다.

일반적으로 컬러 영상은 흔히 RGB라 불르는 빨간색(R), 초록색(G), 파란색(B) 색상 성분의 조합으로 픽셀 값을 표현한다. 그러나 OpenCV는 RGB가 아닌, BGR 색상 순서로 픽셀 값을 표현한다. 즉, imread() 함수로 영상을 3채널 컬러 영상 형식으로 불러오면 각 픽셀의 색상 값이 B, G, R 순서로 저장된 Mat 객체가 생성된다.

 

컬러 영상에서 각각의 R, G, B 색상 성분은 0부터 255 사이의 값을 가지며, 0이면 색상 성분이 전혀 없는 상태(검정)이고, 255면 해당 색상 성분이 가득 차 있음(흰색)을 의미한다. 각 생상 성분 값은 uchar 자료형을 사용해 표현하며, 컬러 영상에서 하나의 픽셀은 3개의 색상 성분을 가지고 있으므로, Vec3b 자료형을 통해 표현한다. Vec3b 클래스는 크기가 3인 uchar 자료형 배열을 멤버 변수로 갖는 클래스로, 바이트 크기는 정확하게 3바이트며, 이는 실제 3채널 컬러 영상의 한 픽셀이 차지하는 바이트 수와 같다.

 

컬러 영상에서 픽셀 값을 참조할 때에도 Mat::at() 함수를 사용한다. 이는 템플릿으로 정의된 함수이므로, 3채널 컬러 영상에 대해서는 Vec3b자료형을 명시해야 한다.

 

아래 예제는 Vec3b 참조형으로 선언된 변수 pixel을 통해 img 영상의 (0,0) 좌표에서 BGR 색상 정보를 가져오고,

포인터 ptr에 컬러 영상의 특정 행 시작 주소를 얻어와 픽셀 값에 접근하여 BGR 색상 성분을 얻어오는 예제이다.

 

Mat src = imread("butterfly.jpg", IMREAD_COLOR);

// 한 픽셀 참조
Vec3b& pixel = img.at<Vec3b>(0,0); // (0,0) 좌표에서 BGR 색상 정보 참조

uchar b1 = pixel[0]; // 파란색 성분 값
uchar g1 = pixel[1]; // 초록색 성분 값
uchar r1 = pixel[2]; // 빨간색 성분 값

// 포인터를 이용하기
Vec3b* ptr = img.ptr<Vec3b>(0);

uchar b2 = ptr[0][0];
uchar g2 = ptr[0][1];
uchar g2 = ptr[0][2];

 

 

컬러 영상의 픽셀 값 참조 방법을 이용해 컬러 영상을 반전하는 예제는 다음과 같다.

 

// 컬러 영상의 픽셀 값 반전
void color_inverse() {
	Mat src = imread("butterfly.jpg", IMREAD_COLOR);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat dst(src.rows, src.cols, src.type());

	for (int j = 0;j < src.rows; j++) {
		for (int i = 0;i < src.cols; i++) {
			Vec3b& p1 = src.at<Vec3b>(j, i);
			Vec3b& p2 = dst.at<Vec3b>(j, i);

			// BGR 값 각각 반전 수행
			p2[0] = 255 - p1[0]; // B
			p2[1] = 255 - p1[1]; // G
			p2[2] = 255 - p1[2]; // R
		}
	}
	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();
}

 

3채널 컬러 영상의 각 모든 픽셀의 성분 값을 255에서 빼는 연산을 수행하여 나타나는 결과 영상은 다음과 같다.

src와 dst 영상의 (j, i) 위치 픽셀 값을 각각 Vec3b 자료형 변수 p1와 p2로 참조하여 반전한 방법인데,

다음과 같이 B, G, R 색상 성분의 반전을 Vec3b 클래스에서 지원하는 - 연산자 재정의를 이용해 3채널의 반전 연산을 한꺼번에 수행 가능하다.

 

	for (int j = 0;j < src.rows;j++) {
		for (int i = 0;i < src.cols;i++) {
			dst.at<Vec3b>(j, i) = Vec3b(255, 255, 255) - src.at<Vec3b>(j, i);
		}
	}

 

결과는 다음과 같다.

 

위에서는 컬러 영상의 픽셀 값 참조 방법을 설명하기 위해 각 픽셀 위치에 접근하여 반전 연산을 수행하도록 구현했지만, 실전에서는 다음과 같이 Mat 클래스에 정의된 - 연산자 재정의 함수를 이용하면 매우 쉽게 구현이 가능하다.

 

Mat dst = Scalar(255, 255, 255) - src;

 


색 공간 변환

 

컴퓨터에서는 빛의 삼원색인 빨강(R), 초록(G), 파랑(B) 성분의 조합으로 색을 표현하는데, OpenCV 에서 컬러 영상을 Mat 객체에 저장할 때에는 파란, 녹색, 빨강의 순서로 색 정보를 저장하여 BGR 을 표현하게 된다.

이렇게 3가지 색 성분의 조합으로 색을 표현하는 방식을 RGB 색 모델, RGB 색 공간 (color sapce) 표현이라고 한다. 

 

그러나 컬러 영상 처리 관점에서는, RGB 색 공간보다, 색상 구분이 용이한 HSV, HSL 색 공간을 사용하거나, 휘도 성분이 구분되어 있는 YCrCb, YUV 등의 색 공간을 사용하는 것이 유리하다. 따라서 OpenCV는 BGR 순서로 색상이 저장된 컬러 영상의 색 공간을 HSV, YCrCb 등 다른 색 공간으로 변환하는 인터페이스를 제공한다.

 

 

cvtColor() 함수

 

우선 OpenCV는 색 공간을 다른 색 공간으로 변환하는 cvtColor() 함수를 제공한다.

색 공간을 변환하는&nbsp; cvtColor() 함수

위 함수는 입력 영상 src의 색 공간을 변환하여 결과 영상 dst를 생성하여 반환한다. code 인자에 CololrConversionCodes 열거형 상수를 지정하여 어떻게 색 공간을 변환할지 결정하게 된다.

 

 

그 중 사용성이 높은 몇 가지 색 공간 변환에 대해 알아보자.

 

BGR2GRAY, GRAY2BGR

 

BGR2GRAY 색 공간 변환 코드는 BGR 컬러 영상을 그레이스케일 영상으로 변환할 때 사용한다. 그레이스케일 영상으로 변환하는 주된 이유는 연삭 속도와 메모리 사용량을 줄이기 위함이다. 기본적으로 컬러 영상은 3채널 영상으로, 3배 많은 메모리를 필요로 하고 더 많은 연산 시간을 필요로 한다. 따라서 색상 정보의 활용도가 그리 높지 않은 경우 입력 영상을 그레이 스케일 영상으로 변환하여 처리하는 것이 효율적이다.

 

BGR 3채널 컬러 영상을 그레이스케일 영상으로 변환할 때는 다음 공식이 사용된다.

반대로 GRAY2BGR 색 공간 변환 코드는 그레이 스케일 영상을 BGR 컬러 영상으로 변환할 때 사용한다. 이 때 결과 영상은 CV_8UC3 타입으로 결정되고, BGR 색상 성분 값은 다음과 같이 결정된다.

주로 그레이스케일 영상 위에 색깔이 있는 선, 글씨를 나타내는 경우 미리 그레이스케일 영상을 BGR 컬러 영상으로 변환이 필요하다.

 

 

BGR2HSV, HSV2BGR

 

HSV 모델은 색상(hue), 채도(saturation), 명도(value)로 색을 표현하는 방식이다.

색상은 빨강, 노랑, 녹색과 같은 색의 종류를, 채도는 색의 순도를 나타낸다. 즉 채도가 높은 경우 맑은 색을, 낮은 경우 탁한 색을 띄게 된다.

명도는 빛의 세기로, 명도가 높으면 밝고, 낮으면 어둡게 느껴진다.

 

HSV 색 공간은 다음과 같이 원뿔 모양으로 표현이 가능하다.

HSV 색 공간 모형에서 색상은 원뿔을 가로로 잘랐을 대 나타나는 원형에서 각도로 정의된다. 각도가 낮을 수록 빨간색을, 각도가 증가할 수록 노란, 녹색, 하늘, 파랑, 보라색을 거쳐 각도가 360도에 가까워지면 다시 빨간색으로 표현된다.

채도는 원뿔을 가로로 잘랐을 때 나타나는 원 모양의 중심에서 최솟값을, 원의 중심에서 방사형으로 멀어지는 방향으로 값이 증가한다.

명도는 원뿔 아래쪽 꼭짓점에서 최솟값을, 원뿔의 축을 따라 올라가서 증가한다.

BGR2HSV 색 공간 변환 코드 사용 시, 8비트 BGR 영상을 HSV 영상으로 변환하게 되는데, H 값은 0~179 사이의 정수, S와 V는 0~255 사이의 정수로 표현된다. OpenCV 에서 각도는 2로 나눈 값을 H 성분으로 저장한다. (360도 각도로 표현하지만 uchar 자료형으로는 256 이상의 정수를 표현할 수 없기 때문)

 

 

BGR2YCrCb, YCrCb2BGR

 

YCrCb 색 공간에서 Y 성분을 밝기 또는 휘도(lumincance) 정보를, Cr과 Cb 성분은 색상 또는 색차 (chrominacne) 정보를 나타낸다. RGB 색상 성분으로부터 Y 성분을 계산하는 공식은 그레이스케일 계산 공식과 똑같고, Cr과 Cb 성분은 밝기가 아닌 오직 색상에 대한 정보만을 갖는다. 따라서 YCrCb 색 공간은 영상을 그레이스케일 정보와 색상 정보로 분리하여 처리할 때 매우 유용하다.

Y, Cr, Cb 각 성분은 0~255 사이의 값으로 표현되고, cvtColor() 함수 입력 영상이 0에서 1 사이 값으로 정규화된 CV_32FC3 타입의 행렬이라면, 각각 0과 1 사이의 실수 값으로 표현된다.

 

Y 성분을 128로 고정한 상태에서 Cr과 Cb 값에 따른 색상 표현을 그림오르 나타내면 다음과 같다.

HSV 색 공간에서는 H 값만을 이용해 색 종류를 구분할 수 있었지만, YCrCb 색 공간에서는 Cr과 Cb를 함께 조합하여 색을 구분할 수 있다.

 

 

 

다음 코드로 간단히 그레이스케일 영상으로 변환해보자.

컬러 영상을 불러올 때 두번째 인자를 지정하지 않으면 기본적으로 3채널 BGR 컬러 영상 형식으로 불러오게 된다.

이후 cvtColor() 함수를 이용해 컬러 영상을 그레이스케일 영상으로 변환하여 dst 영상을 저장하게 된다.

 

Mat src = imread("butterfly.jpg");

/*

*/

Mat dst;
cvtColor(src, dst, COLOR_BGR2GRAY);

 


 

색상 채널 나누기

 

imread() 함수로 생성된 컬러 영상은 하나의 픽셀이 B,G,R 3개의 색상 정보를 가지고 있으며, 보통 uchar 자료형을 사용하여 3개의 채널을 갖는 Mat 객체로 표현한다. 컬러 영상을 다루다 보면, 빨간색 성분만을 이용하거나 HSV 색 공간으로 변환 후 H 성분만을 이용하게 되는 경우가 종종 발생하는데, 이러한 경우 3채널 Mat 객체를 1채널 Mat 객체 3개로 분리해 다루는 것이 효율적이다.

 

 

split(), merge() 함수

 

다채널 행렬을 1채널 행렬 여러 개로 변환할 때는 split() 함수를, 다시 1채널 행렬을 여러 개로 합쳐 다채널 행렬 하나를 생성할 때는 merge() 함수를 사용한다.

 

분할된 결과는 Mat 자료형의 배열(포인터), 또는 vector<Mat> 형식의 변수로 받을 수 있다. 배열을 사용할 경우 배열의 크기가 입력 영상의 채널 수보다 같거나 커야 한다.

 

 

 

다음 예제를 통해 컬러 영상을 각 B,G,R 채널로 분리해보자

 

// BGR 컬러 영상의 채널을 각각 나누기
void color_split() {
	Mat src = imread("candies.png");

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	vector<Mat> bgr_planes;
	split(src, bgr_planes); // src 입력 영상을 bgr_planes 벡터에 각 채널을 분리

	imshow("src", src);
	// 각 성분에 대한 값이 큰 경우 밝은 흰색으로 표현
	imshow("B_plane", bgr_planes[0]); // B 색상
	imshow("G_plane", bgr_planes[1]); // G 색상
	imshow("R_plane", bgr_planes[2]); // R 색상

	waitKey();
	destroyAllWindows();
}

 

candies src 영상을 vector 자료형인 bgr_planes에 저장하였고, 각 채널 영상을 결과로 나타내면 다음과 같다. 

각 색상 성분이 많이 포함되어 있는 경우 255에 가까운 밝은 흰색 성분으로 나타나는 것을 확인할 수 있다.

 

  

간단히 RGB 색 공간에서 색상 채널을 나누는 예제를 살펴보았다.

하지만 컬러 영상을 다루는 경우 RGB 색 공간보다는 HSV, YCrCb 등 색 공간에서 채널을 나누어 색 정보를 다루는 경우가 많다. 다음 글을 통해 HSV, YCrCb 색 공간에서 색상 평면을 나누거나 합치는 방법을 알아보도록 하자.

728x90
반응형
Comments