파이썬 강의/openCV

파이썬 openCV 16. RGB에서 YCbCr로(RGB to YCbCr)

마리사라 2020. 12. 2. 14:13
반응형

파이썬 openCV 16번째 강의는 RGB에서 YCbCr로(RGB to YCbCr)입니다. YCbCr는 일반적으로는 들어보신적이 없으실겁니다. 하지만 이 역시 꽤 많이 사용된 색공간으로, 이번 시간에 자세히 알려드리겠습니다.


0. YCbCr?

YCbCr은 일반적으로 실생활에 사용되는 색공간은 아닙니다. YCbCr은 RGB나 cmyk, HSV처럼 무언가의 약자가 아닌 Y(휘도)와 Cb/Cr(색채/크로마)의 값으로 이루어 졌다고 해서 YCbCr입니다. 그래서 YCbCr이라고 하는 경우도 있고, YCrCb 또는 YCC라고 하는 경우도 있습니다.

 

YCbCr 색공간(출처 : 위키백과)

YCbCr의 색공간입니다. RGB와 비슷한 모양인것을 확인하 실 수 있으실겁니다. 그 이유는 YCbCr은 RGB의 인코딩 방식중 하나이기 때문에 RGB와 거의 비슷한 모양의 색공간을 가지게 됩니다. 따라서 RGB를 알면 YCbCr을 1:1 매칭으로 변환시킬 수 있고, YCbCr을 알면 RGB로 다시 역변환 시킬 수 있습니다.


1. openCV에서의 YCbCr

YCbCr역시 RGB에서 변환하는 공식이 따로 있습니다.

 

RGB와 YCbCr 영상간의 변환 공식

이때 delta는 영상에 따라 바뀌게 됩니다. 이번 영상은 0 ~ 255의 RGB값을 갖는 8bit 영상이기에 delta값은 128로 설정하겠습니다.

img = cv2.imread('lenna.png')
height, width, channel = img.shape
b = img[..., 0]
g = img[..., 1]
r = img[..., 2]

이제 기본설정입니다. 이미지를 불러와주고, 크기를 구해주고, rgb로 나누어줍니다. 전에도 말씀드렸다시피 파이썬 openCV는 b, g, r 순서로 불러옵니다.

 

y = np.zeros((height, width), dtype=np.float)
cr = np.zeros((height, width), dtype=np.float)
cb = np.zeros((height, width), dtype=np.float)

이제 y, cb, cr의 값을 구하기 위해 빈 영상을 만들어줍니다.

 

for i in range(height):
    for j in range(width):
        y[i][j] = 0.299 * r[i][j] + 0.587 * g[i][j] + 0.114 * b[i][j]
        cr[i][j] = (r[i][j] - y[i][j]) * 0.713 + 128
        cb[i][j] = (b[i][j] - y[i][j]) * 0.564 + 128

이제 각 화소별 Y, Cb, Cr값을 구하기 위해 중첩 for문을 사용하여 위 공식에 맞게 계산해줍니다.

 

out = (np.dstack((y, cr, cb))).astype(np.uint8)

이제 합쳐진 영상을 만들어 줍니다. 이때, 순서를 YCbCr이 아닌, YCrCb로 맞춰줍니다. 그 이유는 openCV에서는 YCbCr의 순서가 아닌 YCrCb의 순서로 인식하기 때문에 openCV의 기준에 맞게 합쳐주어야 재대로된 결과가 나오기 때문입니다.

 

이제 완성된 영상을 확인해보겠습니다.

원본 영상
YCbCr 영상
Y 영상
Cb 영상
Cr 영상

이번 영상들도 각각의 Y, Cb, Cr은 단일 채널이라 흑백으로 나옵니다. 합쳐진 영상도 원본과는 다르게 보이지만, 역변환공식을 적용하면 다시 원래의 R, G, B를 구하실 수 있습니다.

 

또한, 이번 YCbCr도 HSV와 마찬가지로 openCV 자체에서 변환 함수가 있습니다.

ycbcr = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)

이때 COLOR_BGR2YCrCb와 COLOR_BGR2YCR_CB 두가지가 존재하는데, COLOR_BGR2YCR_CB가 기본적인 포맷입니다. 이렇게 분리한 후

 

y2, cr2, cb2 = cv2.split(ycbcr)

각 채널별로 나눠주시면 똑같이 나오게 됩니다

 

내장 함수 YCbCr
내장 함수 Y
내장 함수 Cb
내장 함수 Cr


2. 마치며

이번 강의에서 내장 함수로 변환한 영상과, 직접 계산한 영상이 똑같은 것을 볼 수 있습니다. 그 이유는 기본적인 RGB에서 YCbCr공식이 아닌, openCV에서 자체적으로 공지하고 있는 YCbCr변환 공식을 사용하였기에 같은 프로세스를 거쳤다고 할 수 있기 때문입니다.

원래 YCbCr 변환 공식(출처 : 위키백과)

원래의 변환 공식으로 계산하실 수 있으나, 표기만 다를 뿐, 사실상 거의 같은 공식입니다.

 

def rgb2ycbcr():
    img = cv2.imread('lenna.png')
    height, width, channel = img.shape
    b = img[..., 0]
    g = img[..., 1]
    r = img[..., 2]
    y = np.zeros((height, width), dtype=np.float)
    cr = np.zeros((height, width), dtype=np.float)
    cb = np.zeros((height, width), dtype=np.float)
    for i in range(height):
        for j in range(width):
            y[i][j] = 0.299 * r[i][j] + 0.587 * g[i][j] + 0.114 * b[i][j]
            cr[i][j] = (r[i][j] - y[i][j]) * 0.713 + 128
            cb[i][j] = (b[i][j] - y[i][j]) * 0.564 + 128
    out = (np.dstack((y, cr, cb))).astype(np.uint8)
    cv2.imshow('original', img)
    cv2.imshow('ycbcr', out)
    cv2.imshow('y', y.astype(np.uint8))
    cv2.imshow('cb', cb.astype(np.uint8))
    cv2.imshow('cr', cr.astype(np.uint8))

    ycbcr = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
    y2, cr2, cb2 = cv2.split(ycbcr)
    cv2.imshow('ycbcr2', ycbcr)
    cv2.imshow('y2', y2)
    cv2.imshow('cb2', cb2)
    cv2.imshow('cr2', cr2)
    cv2.waitKey(0)
반응형