#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 14
10장 컬러 영상 처리
10.1 컬러 영상 다루기
OpenCV의 컬러영상 : 기본적으로 BGR 색상 순서로 픽셀 값을 표현
또한, 각 색상 성분 값은 uchar 자료형을 사용하여 표현 = Vec3b 자료형
// 컬러 영상의 픽셀 값 반전
void color_inverse()
{
Mat src = imread("butterfly.jpg", IMREAD_COLOR);
if(src.empty())
{
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);
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();
}
10.1.2 색 공간 변환
컬러 영상 처리 : 색상 구분이 용이한 HSV, HSL 색 공간을 사용하거나 휘도 성분이 구분되어 있는 YCrCb, YUV 등 색 공간을 사용하는 것이 유리
BGR2GRAY : BGR 컬러 영상을 그레이스케일 영상으로 변환(연산 속도와 메모리 사용량을 줄이기 위함)할 때 사용 = CV_8UC1 타입으로 설정됨
Y = 0.299R + 0.587G + 0.114B
GRAY2BGR : 그레이스케일 영상을 BGR 컬러 영상으로 변환할 때 사용 = CV_8UC3 타입으로 설정됨
R = G = B = Y
HSV : 색상(hue), 채도(saturation), 명도(value)로 색을 표현하는 방식
BGR2HSV : 8비트 BGR 영상을 HSV 영상으로 변환할 경우, H 값은 0부터 179 사이의 정수로 표현되고, S 와 V는 0부터 255 사이의 정수로 표현됨
색상 값을 보통 0도부터 360도 사이의 각도로 표현하지만, uchar 자료형으로는 256이상의 정수를 표현할 수 없기 때문에 각도를 2로 나눈 값을 H 성분으로 저장함
만약, cvtColor()의 입력 BGR 영상이 0에서 1 사이 값으로 정규화된 CV_32FC3 타입의 행렬이라면 H 값은 0에서 360 사시의 실수로 표현되고, S와 V는 0에서 1 사이의 실수 값으로 표현됨
YCrCb : Y 성분은 밝기 또는 휘도 정보를 나타내고, Cr과 Cb 성분은 색상 또는 색차 정보를 나타냄 = 그레이스케일 정보와 색상 정보를 분리하여 처리할 때 사용
BGR2YCrCb : 8비트 BGR 영상을 YCrCb 영상으로 변환할 경우, Y, Cr, Cb 각각의 성분 값은 0부터 255 사이의 값으로 표현됨
만약, cvtColor()의 입력 BGR 영상이 0에서 1 사이 값으로 정규화된 CV_32FC3 타입의 행렬이라면 Y, Cr, Cb 각각의 성분 값도 0에서 1사이의 실수 값으로 표현됨
// 컬러 영상을 그레이스케일 영상으로 변환
void color_grayscale()
{
Mat src = imread("butterfly.jpg", IMREAD_COLOR);
if (src.empty())
{
return;
}
Mat dst;
cvtColor(src, dst, COLOR_BGR2GRAY);
imshow("src", src);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
10.1.3 색상 채널 나누기
컬러 영상을 다루다 보면 빨간 색 성분만을 이용하거나 HSV 색 공간으로 변환한 후 H 성분만을 이용하는 경우가 발생, 이 경우에는 3채널 Mat 객체를 1채널 Mat 객체 세 개로 분리해서 다루는 것이 효율적
// BGR 컬러 영상의 채널 나누기
void color_split()
{
Mat src = imread("candies.png");
if (src.empty())
{
return;
}
vector<Mat> bgr_planes;
split(src, bgr_planes);
imshow("src", src);
imshow("B_plane", bgr_planes[0]);
imshow("G_plane", bgr_planes[1]);
imshow("R_plane", bgr_planes[2]);
waitKey();
destroyAllWindows();
}
10.2 컬러 영상 처리 기법
10.2.1 컬러 히스토그램 평활화
컬러 영상의 색감은 변경하지 않고 명암비를 높이려면 영상의 밝기 정보만을 사용
입력 영상을 밝기 정보와 색상 정보로 분리한 후, 밝기 정보에 대해서만 히스토그램 평활화를 수행
// 컬러 영상의 히스토그램 평활화
void color_eq()
{
Mat src = imread("pepper.bmp", IMREAD_COLOR);
if (src.empty())
{
return;
}
Mat src_ycrcb;
cvtColor(src, src_ycrcb, COLOR_BGR2YCrCb);
vector<Mat> ycrcb_planes;
split(src_ycrcb, ycrcb_planes);
equalizeHist(ycrcb_planes[0], ycrcb_planes[0]);
Mat dst_ycrcb;
merge(ycrcb_planes, dst_ycrcb);
Mat dst;
cvtColor(dst_ycrcb, dst, COLOR_YCrCb2BGR);
imshow("src", src);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
10.2.2 색상 범위 지정에 의한 영역 분할
컬러 영상을 다루는 응용에서 자주 요구되는 기법은 특정 색상 영역을 추출하는 작업
inRange() 함수는 입력 영상의 픽셀 값이 지정한 밝기 또는 색상 범위에 포함되어 있으면 흰색, 그렇지않으면 검은색으로 채워진 마스크 영상을 반환
// inRange() 함수를 이용한 특정 색상 영역 분할
int lower_hue = 40, upper_hue = 80;
Mat src1, src_hsv, mask;
int main()
{
src1 = imread("candies.png", IMREAD_COLOR);
if (src1.empty())
{
return -1;
}
cvtColor(src1, src_hsv, COLOR_BGR2HSV);
imshow("src1", src1);
namedWindow("mask");
createTrackbar("Lower Hue", "mask", &lower_hue, 179, on_hue_changed);
createTrackbar("Upper Hue", "mask", &upper_hue, 179, on_hue_changed);
on_hue_changed(0, 0);
waitKey();
return 0;
}
void on_hue_changed(int, void*)
{
Scalar lowerb(lower_hue, 100, 0);
Scalar upperb(upper_hue, 255, 255);
inRange(src_hsv, lowerb, upperb, mask);
imshow("mask", mask);
}
10.2.3 히스토그램 역투영
HSV 색 공간에서 H 값을 이용하면 간단하게 특정 색상을 골라낼 수 있어서 원색에 가까운 색상을 찾기에는 효과적이지만 사람의 피부색처럼 미세한 변화가 있거나 색상 값을 수치적으로 지정하기 어려운 경우에는 적합하지 않음
만약, 입력 영상에서 찾고자 하는 객체의 기준 영상을 미리 가지고 있다면 컬러 히스토그램 정보를 이용하여 비슷한 색상 영역을 찾을 수 있습니다. 즉, 기준 영상으로부터 찾고자 하는 객체의 컬러 히스토그램을 미리 구하고, 주어진 입력 영상에서 해당 히스토그램에 부합하는 영역을 찾아내는 방식입니다.
히스토그램 역투영 : 히스토그램 모델과 일치하는 픽셀을 찾아내는 기법
// 히스토그램 역투영을 이용한 피부색 영역 검출
int main()
{
Mat ref, ref_ycrcb, mask1;
ref = imread("ref.png", IMREAD_COLOR);
mask = imread("mask.bmp", IMREAD_GRAYSCALE);
cvtColor(ref, ref_ycrcb, COLOR_BGR2YCrCb);
Mat hist;
int channels[] = { 1, 2 };
int cr_bins = 128;
int cb_bins = 128;
int histSize[] = { cr_bins, cb_bins };
float cr_range[] = { 0, 256 };
float cb_range[] = { 0, 256 };
const float* ranges[] = { cr_range, cb_range };
calcHist(&ref_ycrcb, 1, channels, mask, hist, 2, histSize, ranges);
Mat src, src_ycrcb;
src = imread("kids.png", IMREAD_COLOR);
cvtColor(src, src_ycrcb, COLOR_BGR2YCrCb);
Mat backproj;
calcBackProject(&src_ycrcb, 1, channels, hist, backproj, ranges, 1, true);
imshow("src", src);
imshow("backproj", backproj);
waitKey();
return 0;
}