본문 바로가기

3학년/OpenCV

[OpenCV] 10장 컬러 영상 처리

10.1 컬러 영상 다루기

 

10.1.1 컬러 영상의 픽셀 값 참조

 

일반적으로는 RGB 색상 순서를 사용하지만 OpenCV의 경우 BGR(파랑, 초록, 빨강) 순서로 저장된 Mat 객체를 사용. 각 색상 성분은 0부터 255 사이의 값을 가질 수 있다. 

 

 

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);

			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행: butterfly.jpg 파일을 3채널 BGR 컬러 영상으로 불러와서 src에 저장
  • 10행: 반전된 영상을 저장할 dst 영상을 생성. dst 영상의 모든 픽셀 값은 이후 for 반복문에서 설정할 것이므로 초깃값은 따로 지정하지 않음
  • 14~15행: src와 dst 영상의 (i, j) 좌표 픽셀 값을 각각 p1과 p2 변수에 참조로 받아옴
  • 17~19행: p1 픽셀의 세 개 색상 성분 값을 모두 반전시켜 p2 픽셀 값으로 설정

 

 

 

10.1.2

 

void cvtColor (InputArray src, OutputArray dst, int code, int dstCn = 0);
  • src: 입력 영상. CV_8U, CV_16U, CV_32F 중 하나의 깊이를 사용해야 함
  • dst: 결과 영상. src와 크기 및 깊이가 같다
  • code: 색 공간 변환 코드. ColorConversionCodes 열거형 상수 중 하나를 지정
  • dstCn : 결과 영상의 채널 수. 0이면 자동으로 결정

 

 

void color_grayscale()
{
	Mat src = imread("butterfly.jpg");

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

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

	imshow("src", src);
	imshow("dst", dst);

	waitKey();
	destroyAllWindows();
}
  • 3행: imread() 함수의 두 번째 인자를 지정하지 않으면 기본적으로 3채널 BRG 컬러 영상 형식으로 불러온다
  • 11행: 3채널 BRG 컬러 영상 src를 그레이스케일 영상으로 변환하여 dst에 저장

 

 

 

10.1.3 색상 채널 나누기

 

void split(const Mat& src, Mat* mvbegin);
void split(InputArray src, OutputArrayOfArray mv);
  • src: 입력 다채널 행렬
  • mvbegin: 분리된 1채널 행렬을 저장할 Mat 배열 주소. 영상 배열 개수는 src 영상 채널 수와 같아야 함
  • mv: 분리된 1채널 행렬을 저장할 벡터

 

 

void merge(const Mat* mv, size_t count, OutputArray dst);
void merge(InputArrayOfArrays mv, OutputArray dst);
  • mv: 1채널 행렬을 저장하고 있는 배열 또는 벡터. 모든 행렬은 크기와 깊이가 같아야 함
  • count: (mv가 Mat 타입의 배열인 경우) Mat 배열의 크기
  • dst: 출력 다채널 행렬

 

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);

	imshow("src", src);
	imshow("B_plane", bgr_planes[0]);
	imshow("G_plane", bgr_planes[1]);
	imshow("R_plane", bgr_planes[2]);

	waitKey();
	destroyAllWindows();
}
  • 3행: condies.png 영상을 3채널 BGR 컬러 영상 형식으로 불러옴
  • 10~11행: src 영상의 채널을 분할하여 bgr_planes 벡터에 저정. bgr_planes[0]에는 파란색 색상 평면, bgr_planes[1]에는 녹색 색상 평면, bgr_planes[2]에는 빨간색 색상 평면이 저장됨. 

 

 

 

 

 

 

10.2 컬러 영상 처리 기법

 

10.2.1 컬러 히스토그램 평활화

 

 

 

 

 

#include "opencv2/opencv.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int main(void)
{
	Mat src = imread("pepper.bmp", IMREAD_COLOR);

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

	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]); // Y channel

	Mat dst_ycrcb;
	merge(ycrcb_planes, dst_ycrcb);

	Mat dst;
	cvtColor(dst_ycrcb, dst, COLOR_YCrCb2BGR);

	imshow("src", src);
	imshow("dst", dst);

	waitKey(0);
	return 0;
}
  • 9행: pepper.bmp 영상을 3채널 BGR 영상으로 불러와서 src에 저장
  • 16~17행: BGR 색 공간의 src 영상을 YCrCb 색 공간으로 변경하여 src_ycrcb에 저장
  • 19~20행: src_ycrcb 영상의 채널을 분리하여 ycrcb_planes에 저장
  • 22행: Y 성분에 해당하는 ycrcb_planes[0] 영상에 대해서만 히스토그램 평활화를 수행
  • 24~25행: ycrcb_planes 벡터에 들어있는 세 영상을 합쳐서 dst_ycrcb 영상을 생성
  •  27~28행: dst_ycrcb 영상의 색 공간을 BRG 색 공간으로 변환하여 dst에 저장

 

 

 

 

 

 

10.2.2 색상 범위 지정에 의한 영역 분할

 

 

void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
  • src: 입력 영상
  • lowerb: 하한 값. 주로 Mat 또는 Scalar 객체를 지정
  • upperb: 상한 값. 주로 Mat 또는 Scalar 객체를 지정
  • dst: 출력 마스크 영상. 입력 영상과 크기가 같고, 타입은 CV_8UC1

 

 

 

 

#include "opencv2/opencv.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int lower_hue = 40, upper_hue = 80;
Mat src, src_hsv, mask;

void on_hue_changed(int, void*);

int main(int argc, char* argv[])
{
	src = imread("candies.png", IMREAD_COLOR);

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

	cvtColor(src, src_hsv, COLOR_BGR2HSV);

	imshow("src", src);

	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(0);
	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);
}
  • 7행: 두 개의 트랙바 위치를 저장할 정수형 변수 lower_hue, upper_hue를 전역 변수로 선언
  •  8행: main() 함수와 트랙바 콜백 함수 on_hue_changed() 함수에서 함께 사용할 Mat 객체를 전역 변수로 선언
  • 14행: candies.png 파일을 불러와서 src 변수에 저장
  • 21행: src 영상을 HSV 색 공간으로 변환하여 src_hsv에 저장
  • 26~27행: 색상의 하한 값과 상한 값을 조절할 수 있는 두 개의 트랙바를 생성. 색상의 최댓값을 179로 설정. 두 트랙바의 콜백 함수를 모두 on_hue_change() 함수로 설정
  • 28행: 프로그램이 처음 실행될 때 영상이 정상적으로 출력되도록 트랙바 콜백 함수를 강제로 호출
  • 36~37행: 사용자가 지정한 색상의 하한 값과 상한 값을 이용하여 lowerb, upperb 객체를 생성. 채도의 범위느 ㄴ임이의 100부터 255로 설정. 명도의 영항은 무시하도록 범위를 0부터 255로 설정
  • 38행: src_hsv 영상에서 HSV 색 성분 범위가 lowerb부터 upperb 사이인 위치의 픽셀만 흰색으로 설정한 mask 영상을 생성

 

 

 

 

 

 

10.2.3 히스토그램 역투영

 

void calcBackProject(cont Mat* images, int nimages, const int* channels, 
		InputArray hist, OutputArray backProeject, const float** ranges, 
        double scale = 1, bool uniform = true);
  • images: 입력 영상의 배열 또는 입력 영상의 주소. 영상의 배열인 경우, 모든 영상의 크기와 깊이는 같아야 한다
  • nimages: 입력 영상 개수
  • channels: 역투영 계산 시 사용할 채널 번호 배열
  • hist: 입력 히스토그램
  • backProject: 출력 히스토그램 역투영 영상. 입력 영상과 같은 크기, 같은 깊이를 갖는 1채널 행렬
  • ranges: 각 차원의 히스토그램 빈 범위를 나타내는 배열의 배열
  • scales: 히스토그램 역투영 값에 추가적으로 곱할 값
  • uniform: 히스토그램 빈의 간격이 균등한지를 나타내는 플래그

 

 

 

 

#include "opencv2/opencv.hpp"
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	// Calculate CrCb histogram from a reference image

	Mat ref, ref_ycrcb, mask;
	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);

	// Apply histogram backprojection to an input image

	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(0);

	return 0;
}
  • 12행: 피부색 히스토그램 정보를 추출할 기준 영상 ref.png 파일을 불러옴
  • 13행: 기준 영상에서 피부색이 있는 위치를 흰색으로 표시한 마스크 영상 mask.bmp 파일을 불러옴
  • 14행: 기준 영상을 YCrCb 색 공간으로 변환
  • 16~24행: 기준 영상에서 피부색 영역의 CrCb 2차원 히스토그램을 계산하여 hist에 저장
  • 28~30행: 입력 영상 kids.png 파일을 불러와 YCrCb 색 공간으로 변환
  • 32~33행: 앞서 구한 히스토그램 hist 를 이용하여 입력 영상에서 히스토그램 역투영을 수행. 역투영 결과는 backproj에 저장