Basics of Color

Primary Colors of Light

Red, Green, Blue 빛의 3원색 조합으로 모든 색을 만들 수 있다.

Secondary Colors of Light

  • Magenta = R + B
  • Yellow = R + G
  • Cyan = G + B

Achromatic Color (무채색)

  • 색이 없는 상태 (Gray Scale)
  • RGB 각 성분 비율이 동일할 때 무채색이 된다.
    • ex) (100,100,100), (255,255,255)

Color Models

RGB

R채널, G채널, B채널로 이루어짐

각 채널마다 [0, 255] 범위

HSI

Hue채널, Saturation채널, Intensity채널로 구성

  • Hue (색상): 파장에 따른 지배적인 색
  • Saturation (채도): 색의 선명함, 값이 클수록 뚜렷한 색
  • Intensity (명도): 밝기, 빛의 세기

HSV 모델과 유사

각 채널 값의 범위

  • 0 < H < 360
  • 0 < S < 1
  • 0 < V < 1

OpenCV에서는

  • H/2 (0 < H < 180)
  • 255*S (0 < S < 255)
  • 255*V (0 < V < 255)

YCbCr (YUV)

  • Y : 밝기
  • Cb : 파란색 성분과 밝기의 차이 (B-Y)
  • Cr : 붉은색 성분과 밝기의 차이 (R-Y)

Grayscale

밝기 값 채널 1개만 존재한다.

Hue, Saturation 값은 0이다.


Color Image Processing

앞서 배운 2_Week_Intensity_Transformation , 3_Week_Histogram Equalization 등 을 컬러 이미지에 적용하려 할때,

RGB 모델을 수정하려 하면 R, G, B 각 채널의 비율 변화로 색이 왜곡될 수 있다.

따라서 HSI, YCbCr로 색공간을 변환 후에 Intensity 채널만 따로 처리해야 한다.

Usage of HSI

분리된 Intensity로 색을 유지하며 영상의 밝기만 조절 가능하다.

Color Slicing

Hue 채널에서 특정 색 영역의 픽셀만 추출하고

다른 색은 Saturation 채널의 값을 0으로 설정하여 원하는 색만 강조 할 수 있다.

Color Conversion

Hue 채널 값에 접근하여 특정 영역의 색상을 다른 색으로 변환할 수 있다.

Pseudo Coloring

인간의 눈은 회색 음영은 30-50 단계만 구분할 수 있지만 색은 100k ~ 10m 가지까지 구분가능하다

회색 영상에 색을 입히면 훨씬 많은 정보를 확인할 수 있다.

Color Scale을 함께 포함하여 색이 어떤 의미인지 이해할 수 있도록 해야 함

White Balancing

컬러 영상의 전반적인 색 intensity를 조정해 흰색인 물체가 실제로 흰색처럼 보이게 만드는 과정

간단한 방법

  • (, , ) : 화이트 밸런싱 이전 원래 값
  • (, , ) : 화이트 밸런싱 이후 값
  • ( , , ) : 흰색이라고 생각되는 기준 픽셀의 값

Color Checker 사용

표준 색상표를 참고해서 조정할 수 있다.

Estimate white color

이미지 속 특정 영역을 흰색이라고 가정하고 보정한다.

Gray World Assumption

색 밸런스가 좋은 사진은 모든 픽셀 색상의 평균값이 중립 회색에 가까울 것이라는 점을 기반으로 전체 색상을 보정


소스 코드 예제

Color Space Conversion

void cvtColor(Mat src, Mat dst, int code, int dstCn =0);
  • code: 변환에 사용할 색상 공간 변환 코드
    • COLOR_BGR2GRAY
    • COLOR_BGR2HSV
    • COLOR_BGR2HLS
    • COLOR_GRAY2BGR
    • COLOR_BGR2RGB
  • dstCn: 출력 이미지의 채널 수를 지정, 기본값은 0으로 code에 따라 자동 결정

example

int main() {
	Mat image, image_YUV;
	
	image = imread("image.png");
 
	return 0;
}

Split / Merge

Split

multi-channel 을 여러개의 single-channel 로 분리

void split(Mat src, Mat* mv);

Merge

여러 개의 single-channel을 하나의 multi-channel 로 병합

void merge(const Mat* mv, size_t count, OutputArray dst);

또는

void merge(InputArrayOfArrays mv, OutputArray dst);

Example

void testSplitAndMerge() {
  Mat image, image_YUV, dst;
  Mat yuv_channels[3];
 
  image = imread("lena.png");
 
  cvtColor(image, image_YUV, COLOR_BGR2YUV);
 
  split(image_YUV, yuv_channels);
 
  merge(yuv_channels, 3, dst);
 
  imshow("input", image);
  imshow("Y", yuv_channels[0]);
  imshow("U", yuv_channels[1]);
  imshow("V", yuv_channels[2]);
  imshow("YUV image", dst);
 
  waitKey(0);
}

YUV 채널들을 merge() 한 결과가 이상한데, 이는 imshow() 함수가 BGR 로 이미지를 출력하기 때문

Color Processing

Usage of HSI

Example

void testHSI() {
  Mat image = imread("colorful.png");
 
  Mat HSV, intensity_change, mask_out, change_color;
 
  vector<Mat> ic(3);
  vector<Mat> mo(3);
  vector<Mat> cc(3);
 
  int rows = image.rows;
  int cols = image.cols;
 
  uchar* h;
  uchar* s;
  uchar* v;
 
  cvtColor(image, HSV, COLOR_BGR2HSV);
  split(HSV, ic);
  split(HSV, mo);
  split(HSV, cc);
 
  // histogram equalize
  equalizeHist(ic[2], ic[2]);
 
  // masking out except orange
  for (int r = 0; r < rows; r++) {
    h = mo[0].ptr<uchar>(r);
    s = mo[1].ptr<uchar>(r);
 
    for (int c = 0; c < cols; c++) {
      if (9 < h[c] && h[c] < 23) {
        s[c] = s[c];
      } else {
        s[c] = 0;
      }
    }
  }
 
  // changing all colors
  for (int r = 0; r < rows; ++r) {
    h = cc[0].ptr<uchar>(r);
    s = cc[1].ptr<uchar>(r);
 
    for (int c = 0; c < cols; ++c) {
      if (179 < (h[c] + 50)) {
        h[c] = h[c] + 50 - 179;
      } else {
        h[c] += 50;
      }
    }
  }
 
  merge(ic, intensity_change);
  merge(mo, mask_out);
  merge(cc, change_color);
 
  cvtColor(intensity_change, intensity_change, COLOR_HSV2BGR);
  cvtColor(mask_out, mask_out, COLOR_HSV2BGR);
  cvtColor(change_color, change_color, COLOR_HSV2BGR);
 
  imshow("image", image);
  imshow("intensity change", intensity_change);
  imshow("mask out", mask_out);
  imshow("change color", change_color);
 
  waitKey(0);
}

Pseudo Coloring

void testPseudoColoring() {
  Mat gray = imread("xray.png", 0);
 
  Mat color;
 
  applyColorMap(gray, color, COLORMAP_JET);
 
  imshow("gray", gray);
  imshow("color", color);
 
  waitKey(0);
}

White Balancing

void white_balancing(Mat img) {
  Mat bgr_channels[3];
  split(img, bgr_channels);
 
  double avg;
  int sum, temp;
 
  for (int channel = 0; channel < img.channels(); ++channel) {
    sum = 0;
    avg = 0;
 
    for (int r = 0; r < img.rows; ++r) {
      for (int c = 0; c < img.cols; ++c) {
        sum += bgr_channels[channel].at<uchar>(r, c);
      }
    }
 
    avg = sum / (img.rows * img.cols);
 
    for (int r = 0; r < img.rows; ++r) {
      for (int c = 0; c < img.cols; ++c) {
        temp = (128 / avg) * bgr_channels[channel].at<uchar>(r, c);
 
        if (255 < temp)
          bgr_channels[channel].at<uchar>(r, c) = 255;
        else
          bgr_channels[channel].at<uchar>(r, c) = temp;
      }
    }
  }
 
  merge(bgr_channels, 3, img);
}
 
void testWhiteBalancing() {
  Mat image = imread("balancing.png");
  Mat balancing_result;
 
  balancing_result = image.clone();
 
  white_balancing(balancing_result);
 
  imshow("image", image);
  imshow("balancing", balancing_result);
 
  waitKey(0);
}