📔 오늘 공부한 내용

이전에 다루었던 내용들이 Grayscale 이미지를 기반으로 했다면, 오늘부터는 Color 이미지를 기반으로 영상 처리 학습을 진행했다.

컬러 영상

OpenCV에서 컬러 영상은 RGB 3개의 성분을 사용한다. 각각 256단계로 표현하며, 16M개의 색상 표현이 가능하다. 타입으로는 CV_8UC3이며, OpenCV에서는 BGR 순서로 데이터를 저장하고 처리한다.

// 컬러 영상으로 Mat 객체 생성
Mat img1 = imread("lenna.bmp", IMREAD_COLOR);
Mat img2(rows, cols, CV_8UC3);

// Grayscale image to Color Image(R, G, B 성분이 모두 같음)
Mat img3 = imread("lenna.bmp", IMREAD_GRAYSCALE);
Mat img4 = cvtColor(img3, img4, COLOR_GRAY2BGR);

이전에 사용했던 이미지 처리 기법과 동일한 방법들을 사용할 수 있다. Scalar를 사용시 각각의 성분을 지정해주면 되며, uchar를 사용한 방법들은 Vec3b 타입을 사용하면 된다. Vec3b는 ucahr 타입 3개로 구성된 타입니다. 다만, Vec3b 타입을 사용시에는 참조로 접근해야 변경이 가능하다.

for(int y = 0; y < src.rows; y++){
	for(int x = 0; x < src.cols; x++){
    	Vec3b& p1 = src.at<Vec3b>(y, x);
        Vec3b& p2 = dst.at<Vec3b>(y, x);
        
        p2 = Vec3b(255, 255, 255) - p1;
    }
}

Color Image → Grayscale Image

RGB 색상을 그레이 스케일로 변환할 때에는 보통 3:6:1의 비율로 변환을 해준다. 이러한 변환을 해주는 이유는 데이터를 저장하는 용량을 줄이거나 연산 처리 속도를 향상시키기 위해 사용한다.

Y=0.299R+0.587G+0.114BY = 0.299R + 0.587G + 0.114B

직접 변환을 해주지 않아도 OpenCV에서 cvtColor() 함수를 지원하기에, 간편하게 변환할 수 있다.

void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0);
--- code
  	COLOR_BGR2GRAY 		| 	COLOR_GRAY2BGR
  	COLOR_BGR2RGB 		| 	COLOR_RGB2BGR
  	COLOR_BGR2HSV 		| 	COLOR_HSB2BGR
  	COLOR_BGR2YCrCb 	| 	COLOR_YCrCb2BGR

색공간

색(Color)는 사람의 눈에 있는 원추 세포가 빛의 특정 파장에 따라 다르게 반응해 인지하는 것이다. 색상으로 인식하는 가시광선은 약 380 ~ 750nm의 파장을 볼 수 있다.

색공간 변환

색은 데이터 처리를 위해 여러 요소들을 조합해 사용한다. 영상처리에서는 RGB 색공간을 사용하지만, RGB 뿐만 아니라 HSV, YCrCb, Lab 등 다양한 색공간이 존재하며, 이러한 기반으로 만들어진 이미지들은 변환하는 과정들이 필요하다. 위에서의 cvtColor() 함수를 사용해 간편하게 변환할 수 있다.

  • RGB(3색): Red(255), Grean(255), Blur(255)
  • HSV(색상, 채도, 명도): Hue(179), Saturation(255), Value(255)
  • YCrCb(휘도, 색차): Y(255), Cr(255), Cb(255)

이러한 데이터들을 변환하는 과정에서는 채널 분리를 한 뒤, 각 값들을 변환하는 공식에 따라 계산하고 결합하는 과정을 수행하게 된다.

// 채널 분리
void split(const Mat& src, Mat* mvbegin);
void split(InputArray src, OutputArrayOfArrays mv);

// 채널 결합
void merge(const Mat* mv, size_t count, OutputArray dst);
void merge(InputArrayOfArrays mv, OutputArray dst);

컬러 영상 처리

컬러 영상의 히스토그램 평활화

컬러영상에서는 R, G, B 각각에 대해 히스토그램 평활화를 수행한 뒤, 다시 병합하는 방법으로 평활화를 수행할 수 있다. 다만, 이 경우 주어진 이미지의 색상 변경이 많이 생긴다. 따라서, 컬러 영상에서는 밝기 성분에 대해서만 히스토그램 평활화를 수행한다.

이미지 채널을 YCrCb로 변환한 뒤, Y(휘도) 채널에 대해서만 히스토그램 평활화를 수행하는 방식으로 코드를 구성한다.

Mat src = imread("peppers.bmp", IMREAD_COLOR);

// BGR 이미지를 YCrCB로 변환
Mat src_ycrcb;
cvtColor(src, src_ycrcb, COLOR_BGR2YCrCb);

// 채널 분리(Y, Cr, Cb)
vector<Mat> planes;
split(src_ycrcb, planes);

// Y 채널에 대한 히스토그램 평활화
equalizeHist(planes[0], planes[0]);

// 채널 결합
Mat dst_ycrcb;
merge(planes, dst_ycrcb);

// YCrCb 이미지를 BGR로 변환
Mat dst;
cvtColor(dst_ycrcb, dst, COLOR_YCrCb2BGR);

컬러 영상의 색감 바꾸기

이미지의 색감을 바꾸는 것은 많이 사용하는 기능 중 하나이다. 위와 유사한 방법으로 컬러 영상을 변경할 때에는 BGR 값을 변경해 처리할 수 있다.

특정 색상 영역 추출

특정 색상 영역 추출

이미지 처리에서 연산을 위해 특성 색상만 추출하는 과정이 필요할 때가 존재한다. OpenCV에서는 inRange() 함수를 통해 색상을 추출할 수 있다.

void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
--- lowerb: 하한 값(Mat 또는 Scalar)
--- upperb: 상한 값(Mat 또는 Scalar)
--- dst: 출력으로, 범위안에 들어가는 픽셀만 255로 설정됨

입력 영상에서 특정 색상을 유지하고, 나머지 영역을 그레이스케일로 바꾸는 방법은 다음과 같이 구성할 수 있다.

// 색상 필터를 위한 mask 생성
Scalar lowerb(50, 150, 0);
Scalar upperb(150, 255, 255);
inRange(src, lowerb, upperb, mask);

// BGR → GRAY → BGR: 남은 데이터를 그레이스케일로 유지하기 위해 변환
cvtColor(src, dst, COLOR_BGR2GRAY);
cvtColor(dst, dst, COLOR_GRAY2BGR);

// mask(색상 필터)에 대한 값들만 원래 값으로 복원
src.copyTo(dst, mask);

위 값을 통해 초록색만 남기게 이미지를 변환할 수 있다.

히스토그램 역투영(Histogram backprojection)

주어진 히스토그램 모델에 영상의 픽셀들이 얼마나 일치하는지를 검사하는 방법이다. 임의의 색상을 검출할 때 효과적인 방법이다. HSV 색 공간에서 HS 성분 또는 YCrCB에서 CrCb 성분만을 사용한다.

void calcBackProject(
	const Mat* images, int nimages, 
	const int* channels,
    InputArray hist, OutputArray backProject,
    const float** ranges,
    duble scale = 1,
    bool uniform = true
);
--- images: 입력 영상 주소
--- nimages: 입력 영상 개수
--- channels: 역투영 계산에 사용할 채널 목록
--- hist: 입력 히스토그램
--- backProject: 히스토그램 역투영 결과 행렬
--- ranges: 히스토그램 빈 경계값 배열

부분을 선택해 히스토그램 역투영을 수행하는 코드를 다음과 같이 구성할 수 있다.

Mat src = imread("cropland.png", IMREAD_COLOR);

// YCrCb로 변환
Mat src_ycrcb;
cvtColor(src, src_ycrcb, COLOR_RGB2CrCb);

// 특정 영역만 선택
Rect rc = selectROI(src);
Mat crop = src_ycrcb(rc):

// 부분 영상에 대한 히스토그램 계산
Mat hist;
int channel[] = {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(&crop, 1, channels, Mat(), hist, 2, histSize, ranges);

// 전체 영상에 대해 히스토그램 역투영
Mat backproj;
calcBackProject(&src_ycrcb, 1, channels, hsit, backproj, ranges);

선택하는 부분에 대해 히스토그램을 계산한 뒤 전체 이미지에 대해 색상 필터를 적용하는 것을 볼 수 있다.

히스토그램 역투영을 통해 사람 얼굴의 해당하는 영역의 히스토그램을 구하고, 얼굴만 분리해주는 영상도 구성할 수 있다.

📝 TIL을 정리하며

🤕 어려웠던 점

크게 어려웠던 점은 없었다.

🤔 궁금한 점

아직은 궁금한 점이 없었다.

😁 느낀 점

오늘 내용은 어렵지 않아서, 쉬어가는 느낌으로 구상할 수 있었다. 근데, 오늘 내운 지식들이 다음에 꼭 필요한 사전지식이 될 거라는 것을 직감할 수 있었다.


📌 프로그래머스 데브코스 6기 자율주행 인지과정(Perception) 수강 내용을 바탕으로 정리한 TIL 입니다.
📅 Today: 2023.10.24.

profile
그냥 끄적여보는 블로그

1개의 댓글

comment-user-thumbnail
2024년 4월 22일

안녕하세요. 현재 자율주행 로봇의 SLAM 알고리즘을 개발하는 업무를 하고 있는 개발자입니다.
image 내 그리는 방법을 상세하게 정리해주셔서 감사합니다~!

블로그의 다른 글들을 살펴보니 computer vision에 관심이 많으신 것 같아 공부하신 경험에 대한 간단한 대화(30~40분)를 나누고 싶어 이메일을 남깁니다.

이메일: irobou0915@gmail.com

LinkedIn profile: https://www.linkedin.com/in/samwoo-seong-037438170/

오늘도 좋은 하루 보내세요!

답글 달기