일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 카카오
- OpenCV
- @Component
- git
- 컴포넌트스캔
- C언어
- 해시
- nestJS
- 시스템호출
- 파이썬
- 카카오 코테
- AWS
- nestjs typeorm
- python
- TypeORM
- C++
- Spring
- 프로그래머스
- 스프링
- nestjs auth
- spring boot
- 구조체배열
- Nodejs
- 가상면접사례로배우는대규모시스템설계기초
- 카카오 알고리즘
- 코테
- @Autowired
- 알고리즘
- thymeleaf
- 코딩테스트
- Today
- Total
공부 기록장 💻
[OpenCV/C++] 컬러 영상 처리 2 - 컬러 영상 평활화와 히토그램 역투영 (Color Histogram Equalization and Backprojection) 본문
[OpenCV/C++] 컬러 영상 처리 2 - 컬러 영상 평활화와 히토그램 역투영 (Color Histogram Equalization and Backprojection)
dream_for 2022. 12. 7. 00:29OpenCV 4로 배우는 영상 처리와 컴퓨터 비전 CH10. 컬러 영상 처리 정리
영상의 색상 정보를 이용해 영상을 처리하는 기법에 대해 알아보자.
영상의 명암비를 높이는 컬러 히스토그램 평활화 기법, 그리고 컬러 영상에서 원하는 색상 영역을 검출하는 방법에 대해 알아보자.
컬러 히스토그램 평활화
그레이스케일 영상에서 활용한 equalizeHist() 함수를 통해 히스토그램 평활화를 수행했다. 3채널 컬러 영상에 대해 히스토그램 평활화를 수행하기 위해서는, RGB 3개의 채널에 대해 각각 평활화를 수행하는 것이 아니라, YCrCb 색 공간으로 변환하고, Y 성분에 대해서만 히스토그램 평활화를 수행해야 한다.
만약 다음과 같이 RGB 각 채널에 히스토그램 평활화를 수행하면 어떻게 될까?
이 방식으로 각 채널마다 서로 다른 형태의 명암비 변환 함수를 사용하면, 원본 영상과 다른 색상의 결과 영상이 만들어진다. 아래 오른쪽 그림과 같이 녹색 성분이 많이 포함되어 있는 영상에 히스토그램 평활화 수행을 한 후 다시 채널을 합쳐 만들게 되면 원본 영상의 색감과 완전히 다른 결과 영상이 만들어진다.
따라서 컬러 영상의 색감은 변경하지 않고, 명암비를 높이고자 하는 경우 영상의 밝기 정보만을 사용해야 한다.
따라서 보통 히스토그램 평활화 수행 시, 입력 영상을 밝기 정보와 색상 정보로 분리한 후, 밝기 정보에 대해서만 히스토그램 평활화를 수행한다. 예를 들어 YCrCb 색공간을 사용하는 경우, Y 성분에 대해서만 히스토그램 평활화를 수행하고, 변경된 Y 채널과 나머지 채널을 다시 합쳐 결과를 도출한다.
결국 색상 정보는 전혀 변경하지 않아 입력 영상의 색감이 그대로 유지되고, 오직 밝기 성분에 대해서만 명암비가 증가학 ㅔ된다.
다음 예제를 살펴 보자.
// 컬러 영상의 히스토그램 평활화
void color_histogram() {
Mat src = imread("pepper.bmp", IMREAD_COLOR);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
Mat src_ycrcb;
cvtColor(src, src_ycrcb, COLOR_BGR2YCrCb); // YCrCb 색 공간으로 변경
vector<Mat> ycrcb_planes;
split(src_ycrcb, ycrcb_planes); // 채널 분리
// Y 성분(명암비)에 해당하는 영상만 히스토그램 평활화 수행
equalizeHist(ycrcb_planes[0], ycrcb_planes[0]);
// 세 채널에 대한 영상 다시 합치기
Mat dst_ycrcb;
merge(ycrcb_planes, dst_ycrcb);
Mat dst;
cvtColor(dst_ycrcb, dst, COLOR_YCrCb2BGR); // BGR 색 공간으로 다시 변환
imshow("src", src);
imshow("dst", dst);
waitKey(0);
return;
}
우선 BGR 색 공간의 평면 pepper src 영상에 대하여, YCrCB 색 공간으로 변환을 수행한다.
이후 채널을 분리한 영상의 0번 값인, Y 성분(brightness, 밝기 성분)에 해당하는 영상에 대해서만 equalizeHist() 함수를 통해 히스토그램 평활화를 수행하고, 세 채널 영상을 다시 합쳐 BGR 색 공간으로 변환하고 결과를 출력하게 된다.
결과는 다음과 같다. 색감은 그대로 유지한채, 밝기에 대한 히스토그램 평활화가 이루어져 명암비가 높아지며 조금 더 뚜렷한 영상이 나타난 것을 확인할 수 있다. 히스토그램 평활화를 통해 어두운 부분은 더욱 어두워졌으며, 밝은 부분은 더욱 밝아진 것이 확실하게 나타난다.
색상 범위 지정에 의한 영역 분할
컬러 영상을 다룰 때 특정 색상 영역을 추출하는 작업이 많이 요구되기도 한다. 예를 들어 입력 영상에서 빨간색 픽셀을 모두 찾아내면 빨간색 객체의 위치와 크기를 알아낼 수 있다. 컬러 영상에서 대표적인 색상 영역을 구분하고자 할 때는 색상 정보가 따로 설정되어 있는 HSV 공간을 사용하는 것이 유리하다.
inRange() 함수
OpenCV 에서 행렬의 원소값이 특정 범위 안에 있는지 확인하기 위해 inRange() 함수를 사용한다.
inRange() 는 입력 영상 src의 픽셀 값이 지정한 밝기 또는 색상 범위에 포함되어 있으면 흰색, 그렇지 않으면 검은색으로 채워진 마스크 영상 dst를 반환한다.
만약 그레이스케일 영상을 입력으로 사용하는 경우, 특정 밝기 값 범위에 있는 픽셀 영역을 추출할 수도 있다. 1채널 영상에 대해 inRange() 함수의 동작을 수식으로 표현하면 다음과 같다.
입력 영상의 채널이 2개 이상이라면, 각 채널 값이 모두 지정된 범위를 만족할 때 dst 영상의 픽셀 값이 255로 설정된다.
여기서 lowerp와 upperb 인자에는 Mat 객체 또는 Scalar 객체를 지정할 수도 있다. 만약 Mat 객체를 지정하는 경우, src 의 모든 픽셀에 각기 다른 하한 값과 상한 값을 지정할 수 있다. Scalar 또는 int, double 과 같은 기본 자료형을 지정하는 경우, src 모든 픽셀에 동일한 하한, 상한 값이 적용된다.
다음 예제를 살펴보자.
예제는 영상 출력 창에 두 개의 트랙바를 붙여서 사용자가 HSV 색 공간에서 색상의 하한값과 상한 값을 조절하여, 사용자가 설정한 색상 값 범위에 해당하는 영역을 흰색, 그 외의 영역은 검은색으로 표현된 마스크 영상을 화면에 출력하게 된다.
inRange() 함수의 lowerb, upperb 인자에는 Scalar 값을 지정하였는데, 이를 이용하여 색상의 하한과 상한 값, 채도와 명도의 범위르 지정하였다.
// 특정 색상 영역 분할
int lower_hue = 40, upper_hue = 80; // upperb, lowerb의 default 값 설정
Mat src, src_hsv, mask;
void on_hue_changed(int, void*) {
// 채도의 범위를 100부터 255로 설정, 명도는 0부터 255까지 설정
Scalar lowerb(lower_hue, 100, 0);
Scalar upperb(upper_hue, 255, 255);
// 색 성분 범위가 lowerb와 upperb 사이인 위치의 픽셀만 흰색으로 설정한 mask 영상 생성
inRange(src_hsv, lowerb, upperb, mask);
imshow("mask", mask);
}
void in_range() {
src = imread("candies.png", IMREAD_COLOR);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return;
}
// HSV 색 공간으로 변환
cvtColor(src, src_hsv, COLOR_BGR2HSV);
imshow("src", src);
// 상한, 하한 값을 조정하는 두 트랙바 생성, 색상의 최댓값을 179로 지정
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;
}
결과는 +다음과 같이 나타난다.
다음은 색상H의 범위가 42~82 (Green), 채도 S 의 범위가 100~255, 명도 V의 범위가 9~255 사이인 픽셀 위치를 찾은 결과이다.
H 값이 60 근방이면 녹색을 나타낸다. 채도 값이 100보다 큰 픽셀만 찾은 것은 충분히 선명한 녹색만 찾기 위함이다. 명도 범위를 0~255 사이로 지정한 것은 명도의 값은 상관하지 않겠다는 의미이다.
두번째와 같이 100~140 사이의 H 색상을 지정한 경우 120 근방의 값을 갖는 파란색 성분만 흰색으로 표현이 된다.
히스토그램 역투영
위에서 설명한 inRange() 함수를 사용하여 특정 색상 영역을 검출하는 경우, 빨강, 노랑, 파랑 등 원색에 가까운 색상을 찾기에는 효과적이다. 그러나 사람의 피부색(살구색)과 같은 경우 미세한 변화가 있거나 색상 값을 수치적으로 지정하기 어려운 경우에는 적합하지 않은 방법이다.
히스토그램 역투영 (Historgram Backprojection) 이란, 입력 영상에서 찾고자 하는 객체의 기준 영상을 미리 가지고 있는 경우 컬러 히스토그램 정보를 이용하여 주어진 입력 영상에서 해당 히스토그램 모델과 일치하는 픽셀을 찾아내는 기법이다.
calcBackProject() 함수
히스토그램 역투영은 calcBackProject() 함수를 이용하여 수행할 수 있다.
위 함수는 입력 영상 images에서 히스토그램 hist 를 따르는 픽셀을 찾고, 그 정보를 backProject 영상으로 반환한다.
이 때 사용하는 히스토그램 행렬은 calcHist() 함수로 구하게 된다.
이를 이용해 입력 영상에서 피부색 영역을 추출하는 예제를 살펴보자.
// 히스토그램 역투영을 이용한 피부색 영역 검출 예제
void histogram_project() {
Mat ref, ref_ycrcb, mask;
ref = imread("ref.png", IMREAD_COLOR); // 기존의 사람 영상
mask = imread("mask.bmp", IMREAD_GRAYSCALE); // 피부에 해당하는 곳만 흰색으로 표시한 마스크
// ycrcb 공간으로 변환
cvtColor(ref, ref_ycrcb, COLOR_BGR2YCrCb);
Mat hist;
int channels[] = { 1,2 };
int cr_bins = 128; int cb_bins = 128;
int histSize[] = { cr_bins, cr_bins };
float cr_range[] = { 0,256 };
float cb_range[] = { 0,256 };
const float* ranges[] = { cr_range, cb_range };
// 피부색 영역의 CrCb 2차원 히스토그램 계산하여 출력 영상을 hist에 저장
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); // Ycrcb 공간으로 변환
// 히스토그램 역투영
Mat backproj;
// 입력 영상 hist 히스토그램을 이용하여 입력 영상에 대한 역투영 수행하여 backProject 출력 영상에 저장
calcBackProject(&src_ycrcb, 1, channels, hist, backproj, ranges, 1, true);
imshow("reference", ref); // 원본 아이 영상 (히스토그램 도출을 위한 기준 영상)
imshow("mask", mask); // 피부색에 해당하는 곳만 흰색 영역으로 나타낸 마스크 영상
imshow("src", src); // 두 아이 영상
imshow("backproj", backproj); // 히스토그램 역투영 결과 영상
waitKey();
return;
}
위 예제는 피부색이 포함된 아이의 영상을, 피부색이 나타난 영역만 흰색으로 표시한 마스크 영상을 이용해 기준 영상에서 피부색 영역의 CrCb 히스토그램을 구하고, 그 결과를 콘솔에 출력하였다. 이는 CrCb 평면에서 피부색을 표현하는 영역이 가운데에서 좌측 하단 부분에 나타난 것 을 확인할 수 있다.
이후 두 아이가 담긴 src 영상에 대하여, hist 히스토그램을 역투영을 수행하였다. backproj 영상에서 밝은 회색, 흰색으로 표시된 영역은 입력 영상의 픽셀 값이 지정한 히스토그램 hist에서 높은 빈도수로 표현된 것이다. 검은색으로 나타난 부분은 해당 픽셀 값이 히스토그램에서 빈도수가 낮거나 0임을 의미한다.