해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.
색의 3속성으로 색상, 명도, 채도가 있다. YIQ, YUV, YCbCr은 명도만 따로 분리한 것이다. 색 공간의 축을 색의 3속성으로 구성하는 게 사람이 색을 인지하는 체계에 더 가깝고 '밝은 노랑'이나 '진한 노랑'을 표현하고자 할 때 색의 3속성을 이용하면 더욱 직관적으로 색을 선택할 수 있다. 기본적으로 색의 3속성을 축으로 하여 색 공간을 표현하는 것으로 HSV, HSL, HSI 가 있다.
※ HSV
다음 그림은 HSV 색 공간을 육각뿔 형태로 표현한 것이다.
육각뿔의 높이 V(Value)는 밝기값, 육각뿔 축으로부터의 거리 S(Saturation)는 색의 진한 정도인 채도, 육각뿔에서 각도 H는 빨주노초파남보와 같은 색상을 표현한다. S 값이 0이면 색채가 없는 회색이 되고 V 값이 0이면 검은색이 된다. S값이 증가할수록 색이 진해져 원색에 가까워지며, V값이 증가할수록 밝은 색상이 된다. RGB 각 채널이 0에서 1 사이의 실수 값을 가지는 색을 HSV 색 공간으로 변환하면 S와 V값의 범위는 0에서 1사이가 된다. 그래서 HSV에서 RGB, RGB에서 HSV로 변환하는 함수를 구현할 때 사용하는 수식이 RGB 채널값이 0에서 1사이를 기준으로 한 것이기 때문에 입력 영상의 각 채널 값을 255로 나누는 작업을 해야 한다. H는 각도가 0이면 빨간색, 60이면 노란색, 120이면 초록색, 180이면 하늘색, 240이면 파란색, 300이면 자홍색에 해당한다. 육각뿔의 각 꼭짓점마다 RGB와 CMY 색 공간의 기본색이 등장한다.
※ HSL
HSL 색 공간은 다음의 그림과 같이 이중의 뿔 구조로 나타낼 수 있다.
H 값과 S 값은 HSV 색 공간과 거의 같지만 밝기를 나타내는 축, L 값이 조금 다르다. L이 0이면 검은색, 1이면 흰색을 나타낸다. 0.5일 경우 RGB와 CMY 색 공간의 기본색이 원색으로 나타난다.
- RGB 색 공간에서 HSV 새 공간으로 변환하는 방법
M = max(R, G, B)
m = min(R, G, B)
D = M - m
H의 결과로 음수가 나올 시 360도를 더해준다.
- RGB 색 공간에서 HSL색 공간으로 변환하는 방법
H 값은 HSV 색 공간으로 변환하는 방법에서 나온 식과 같다.
- HSV 색 공간에서 RGB 색 공간으로 변환하는 식
- RGB를 HSV 색 공간으로 변환하는 함수
template <typename T>
CDoubleImage RGB2HSV(const CMyImage<T>& src) { ASSERT(src.GetChannel() == 3); int nWidth = src.GetWidth(); int nHeight = src.GetHeight(); CDoubleImage dst(nWidth, nHeight, 3); double vR, vG, vB; double vMin, vMax, delta, H; for (int r=0 ; r<nHeight ; r++) { T* pSrc = src.GetPtr(r); double* pDst = dst.GetPtr(r); int pos = 0; for (int c=0 ; c<nWidth ; c++) { vB = pSrc[pos ] / 255.0; vG = pSrc[pos+1] / 255.0; vR = pSrc[pos+2] / 255.0; vMax = MAX(MAX(vR, vG), vB); vMin = MIN(MIN(vR, vG), vB); delta = vMax - vMin; pDst[pos] = vMax; // V if (delta==0) // Gray { pDst[pos+1] = 0; pDst[pos+2] = 0; } else { pDst[pos+1] = delta / vMax; // S if (vR==vMax) H = (vG-vB)/delta; // 노란색과 자홍색 사이 else if (vG==vMax) H = 2 + (vB-vR)/delta; // 하늘색과 노란색 사이 else H = 4 + (vR-vG)/delta; // 자홍색과 하늘색 사이 H *= 60.0; if (H<0) H += 360.0; pDst[pos+2] = H; // H } pos += 3; } } return dst; } #define MIN(a,b) ((a) > (b) ? (b) : (a)) #define MAX(a,b) ((a) < (b) ? (b) : (a))
- HSV를 RGB 색 공간으로 변환하는 함수
template <typename T> CDoubleImage HSV2RGB(const CMyImage<T>& src) { ASSERT(src.GetChannel() == 3); int nWidth = src.GetWidth(); int nHeight = src.GetHeight(); CDoubleImage dst(nWidth, nHeight, 3); double vS, vH, vV; double f, p, t, n; for (int r=0 ; r<nHeight ; r++) { T* pSrc = src.GetPtr(r); double* pDst = dst.GetPtr(r); int pos = 0; for (int c=0 ; c<nWidth ; c++) { vV = pSrc[pos ]; vS = pSrc[pos+1]; vH = pSrc[pos+2]; if (vS==0) { pDst[pos ] = vV*255; pDst[pos+1] = vV*255; pDst[pos+2] = vV*255; } else { while (vH >= 360) vH -= 360; while (vH < 0) vH += 360; vH /= 60.0; int k = (int)vH; f = vH - k; t = vV * (1 - vS); n = vV * (1 - vS*f); p = vV * (1 - vS*(1-f)); switch (k) // 6개의 구간에 따라 { case 1: pDst[pos+2] = n*255; pDst[pos+1] = vV*255; pDst[pos ] = t*255; break; case 2: pDst[pos+2] = t*255; pDst[pos+1] = vV*255; pDst[pos ] = p*255; break; case 3: pDst[pos+2] = t*255; pDst[pos+1] = n*255; pDst[pos ] = vV*255; break; case 4: pDst[pos+2] = p*255; pDst[pos+1] = t*255; pDst[pos ] = vV*255; break; case 5: pDst[pos+2] = vV*255; pDst[pos+1] = t*255; pDst[pos ] = n*255; break; default: // case 0 pDst[pos+2] = vV*255; pDst[pos+1] = p*255; pDst[pos ] = t*255; break; } } pos += 3; } } return dst; }
'영상처리 프로그래밍' 카테고리의 다른 글
HSV 색 공간을 이용한 히스토그램 평활화 (0) | 2017.06.13 |
---|---|
HSV 색 공간을 이용한 색감 조절 (0) | 2017.06.13 |
색 공간(2) - YIQ, YUV, YCbCr (0) | 2017.06.13 |
색 공간(1) - RGB, CMY (0) | 2017.06.13 |
영상의 채널 단위 접근과 변환 (0) | 2017.06.12 |