파이썬 강의/openCV

파이썬 openCV 14. RGB에서 CMYK로(RGB to CMYK)

마리사라 2020. 11. 29. 16:29
반응형

파이썬 openCV 14번째 강의는 RGB에서 CMYK로(RGB to CMYK)입니다.

 

기본적으로 색을 표현할 때 RGB를 많이 씁니다. 하지만 실 생활에서 색을 만들 때는 RGB보다는 CMY가 더 많이 사용됩니다. 심지어 RGB를 사용하고 있다고 생각하는 부분에서도 사실 CMY를 사용하는 것이 더 많습니다.

 

이번 시간에 RGB와 CMY의 차이와 RGB에서 CMY로 바꾸는 방법을 알려드리겠습니다.


0. CMY(K)?

RGB

RGB는 Red(빨강), Green(초록), Blue(파랑)의 약자인것은 많이 들어보셨을 겁니다. 이 3가지 색상을 조합해서 여러 가지 색을 표현할 수 있습니다. 그런데 학교에서 미술시간에 물감을 이용해서 R, G, B의 색을 섞어보신 적이 있으십니까? 원래의 RGB 값이 각각 255(최대)가 되면 흰색이 나와야 합니다. 하지만 실제로는 각각의 색을 섞어도 검은색이 나옵니다.

 

이렇게 이론과 실제가 다른 이유는, RGB는 색의 3원색이 아닌, 빛의 3 원색입니다. 실제로 붉은색 빛과 파란색 빛, 초록색 빛을 동시에 한 곳에 비추면 흰색이 나옵니다. 즉 RGB는 우리가 실제로 사용하는 색깔을 표현하는 방법은 아니라는 뜻입니다.

 

색의 3원색은 CMY(K)로 표현합니다. CMY(K)는 Cyan(시안 : 청록색), Magenta(마젠타 : 자주색), Yellow(노란색)과 blacK 또는 Key(검은색)의 약자입니다. 이때 K는 원래의 색의 조합으로는 들어가지 않지만 색을 표현할 때 사용하여 같이 말하며, Black가 아니라 blacK 또는 Key인 이유는 Blue와 헷갈릴 수 있기 때문입니다.

 

이렇게 RGB는 많이 들어봤는데, CMY(K)는 거의 들어보시지 못하셨을겁니다. 하지만 CMY(K)는 우리 일상생활에 아주 가까이 존재합니다.

Magenta Cyan Yellow BlacK

 위 사진처럼 프린터에서 CMYK를 보실 수 있습니다. 위와 같은 잉크젯 프린터가 아닌 레이저 프린터의 토너도 CMYK의 4가지로 이루어져 있습니다.

 

하지만 CMY(K)는 뚜렷한 단점이 존재합니다. 바로 컴퓨터와 모니터는 RGB를 사용한다는 점입니다. 대부분의 모니터는 색을 출력할 때 RGB 값을 사용하며, 대부분의 프로그램들 역시 RGB로 이루어진 색 조합을 사용합니다. 그렇기에 프린터로 출력하는 컬러 이미지는 색깔이 컴퓨터와 조금씩 달라 보이는 이유입니다.

 

또한 CMY(K)는 RGB와 1:1 대응되지 않습니다. 그 예시로 우리가 빨강이라고 부르는 RGB(255, 0, 0)값은 CMY(K)에서는 존재하지 않는 색입니다. 그래서 프린터 등에서는 이를 만들기 위해 Magenta에 다른 색을 섞어서 그와 가장 비슷한 색을 만들려고 노력하지만, 색깔은 대부분 어두운 빨강이나 칙칙한 빨강 정도로 나오게 됩니다.

 

이러한 단점에도 CMY(K)를 사용하는 이유는, 경제성과 실현 가능성으로 압축 할 수 있습니다. RGB를 통해 모든 색을 표현할 수 있다면 매우 좋겠지만, 그렇게 만들기도 힘들뿐더러 하나의 색을 만들기 위해 엄청난 양의 잉크가 필요할 것입니다.(검은색(255, 255, 255)을 만들려면 RGB잉크가 총 765가 필요합니다) 그렇기에 최대한 많은 색을 만들면서도, 가성비가 가장 높은 방식으로 CMY(K)가 채택된 것입니다.


1. openCV에서의 CMYK

영상의 RGB에서 CMYK의 값들을 만들기 위해서는 가장 먼저 K를 구해야 합니다. 그에 따라 CMY의 값들이 결정되며, 공식은 아래와 같습니다

 

이제 자세한 코드를 알려드리겠습니다.

 

img = cv2.imread('lenna.png')

먼저 이미지를 불러와 줍니다.

bgr = img.astype(np.float) / 255.0

이제 k값을 구하기 위해 RGB값에 255를 나눈 값을 float(실수) 형태로 저장합니다.

 

k = 1 - np.max(bgr, axis=2)
c = (1 - bgr[..., 2] - k) / (1 - k)
m = (1 - bgr[..., 1] - k) / (1 - k)
y = (1 - bgr[..., 0] - k) / (1 - k)

여기서 k값을 구하기 위해 행렬의 최댓값을 찾는 np.max가 사용되었습니다. 그중에서 axis값이 2가 주어졌는데, 이는 각각 0 = 1차원, 1 = 2차원, 2 = 3차원을 뜻하며 2가 주어졌기에 3차원 행렬인 bgr의 3차원, 즉 bgr값이 저장되어 있는 행렬을 뜻합니다.

또한 이전 강의에서도 말씀드렸다시피 openCV에서는 RGB가 아닌 BGR로 읽기 때문에 R = bgr [..., 2] G = bgr [..., 1] B = bgr [..., 0]이 됩니다.

 

cmyk = (np.dstack((c, m, y, k)) * 255).astype(np.uint8)

이제 각각의 값에 255를 곱하고, 하나의 이미지로 합친 후, int8(정수)형으로 형 변환해주어 마무리합니다.

 

이제 각각의 영상의 결과를 보시겠습니다.

원본 영상
C 채널
M 채널
Y 채널
K 채널
CMYK 병합

결과물을 보시면 원본 영상과 많이 다릅니다. 이는 실제 RGB이미지를 CMYK이미지로 변환하려고 한 것이 아닌, 그저 CMYK의 채널로 분리한 후, 병합한 것이기 때문입니다.

출처 : https://www.rgb2cmyk.org/

실제로 외부 사이트를 통해 변환하면 위와 같은 이미지로 변환됩니다. 원본보다 조금 더 어두운 느낌이 나실겁니다.


2. 마치며

이번 강의는 RGB to CMYK이미지 변환이라기보다는 CMYK채널 분리 강의라고 할 수 있습니다. 실제로 색 공간을 변경하기 위해는 openCV보다는 PIL이 더 적합합니다.

 

def rgb2cmy():
    img = cv2.imread('lenna.png')
    bgr = img.astype(np.float) / 255.0
    k = 1 - np.max(bgr, axis=2)
    c = (1 - bgr[..., 2] - k) / (1 - k)
    m = (1 - bgr[..., 1] - k) / (1 - k)
    y = (1 - bgr[..., 0] - k) / (1 - k)
    cmyk = (np.dstack((c, m, y, k)) * 255).astype(np.uint8)
    cv2.imshow('original', img)
    cv2.imshow('c', c)
    cv2.imshow('m', m)
    cv2.imshow('y', y)
    cv2.imshow('k', k)
    cv2.imshow('cmyk', cmyk)
    cv2.waitKey(0)
반응형