본문 바로가기

영상처리

[OpenCV] YCbCr 컬러 모델을 이용하여 피부 검출하기

YCbCr 컬러 모델을 이용하기 피부 검출하기


YCbCr 은 영상 시스템에서 사용되는 색공간의 일종이다. Y 는 휘도 성분이며 Cb 와 Cr 은 색차 성분이다. YCbCr 은 가끔 YCC 라고 줄여 부르기도 한다. YCbCr 은 절대 색공간 이 아니며 RGB 정보를 인코딩하는 방식의 하나로, 실제로 보여지는 이미지의 색은 신호를 디스플레이 하기 위해 사용된 원본 RGB 정보에 의존한다. 따라서 YCbCr 로 표현된 값은 표준 RGB 색상이 사용된 경우거나, 색상을 변환하기 위해 사용할 ICC 프로파일을 첨부한 경우에만 예측할 수 있다.

(신호를 디지털 형식으로 변경하기 위해 스케일링과 오프셋 조정 단계를 거치기 이전의) YCbCr 신호는 YPbPr 이라고 하며, 이것은 감마 보정된 RGB 원본 영상에서 Kb 와 Kr 이라는 두가지 상수를 이용하여 다음과 같은 공식을 통해 얻을 수 있다:

YPbPr (analog version of YCbCr) from R'G'B'
====================================================
Y' =  Kr * R'        + (1 - Kr - Kb) * G' + Kb * B'
Pb = 0.5 * (B' - Y') / (1 - Kb)
Pr = 0.5 * (R' - Y') / (1 - Kr)
....................................................
R', G', B' in [0; 1]
Y' in [0; 1]
Pb in [-0.5; 0.5]
Pr in [-0.5; 0.5]

- 출처 : 위키백과


 주워들은 바에 의하면 HSV 컬러 모델을 이용한 피부 검출보다 YCbCr 컬러 모델을 이용하는 것이 잡음에 강하다고 합니다. 주워들은 것이기 때문에 정확한 정보는 아닐 수 있습니다..안습

 YCbCr 컬러 모델에서 피부색이 가지는 Cb, Cr의 영역은 

Cb : 77~127, Cr : 133~173 

이다. 위와 같이 피부색 영역의 화소들을 검출내해면 입력 영상에서 피부색을 검출해 낼 수 있습니다.

 

먼저 웹캠을 사용하여서 피부 검출에 이용할 입력 영상을 생성합니다. 

또는 웹캠으로 영상을 연속하여 입력받는 경우가 아니라면 일반 사진을 이용하여도 상관없습니다.

저는 웹캠을 이용하여 영상을 입력 받았습니다.



 다음과 같은 입력 영상을 이용하여 피부 검출을 해보도록 하겠습니다.

일반 웹캠의 경우 RGB 컬러 모델로 영상이 입력되기 때문에 YCbCr 컬러 모델 영역으로 변환해주어야 합니다.

OpenCV의 cvtColor 함수를 이용하면 쉽게 변환할 수 있습니다.

이후 위에서 설명했던 YCbCr 영역에서 피부색이 가지는 Cb와 Cr의 범위를 비교합니다.

이 또한 간단하게 inRange 함수를 이용하면 쉽게 가능합니다.

결과 영상을 보면 다음과 같습니다.



약간의 잡음이 있긴 하지만 손 영역이 뚜렷하게 보이는 것을 확인할 수 있습니다.

이후 모폴로지 연산이나 스무딩 작업을 통해서 결과 영상을 좀 더 깔끔하게 하시면 되겠네요ㅎㅎ

소스 코드는 다음과 같습니다.


#include <opencv2\core\core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;

#define DEFAULT_CAM 0

static char* INPUT_WINDOW = "Input Window";
static char* RESULT_WINDOW = "Result Window";

int main()
{	
	VideoCapture capture(DEFAULT_CAM);
	Mat frame;
	Mat skinMat;

	// 보기 창 생성
	namedWindow(INPUT_WINDOW, CV_WINDOW_AUTOSIZE);
	namedWindow(RESULT_WINDOW, CV_WINDOW_AUTOSIZE);

	while(1) {
		capture.read(frame);

		// 손 영역 검출
		cvtColor(frame, skinMat, CV_BGR2YCrCb);
		inRange(skinMat, Scalar(0, 133, 77), Scalar(255, 173, 127), skinMat);

		// 처리 결과 표시
		imshow(INPUT_WINDOW, frame);
		imshow(RESULT_WINDOW, skinMat);

		if( cvWaitKey(10) > 0 )
			break;
	}
	cvDestroyAllWindows();
	capture.release();
	return 0;
}