파이썬 강의/openCV

파이썬 openCV 15. RGB에서 HSV(HSI)로(RGB to HSV)

마리사라 2020. 12. 1. 19:29
반응형

파이썬 openCV 15번째 강의는 RGB에서 HSV(HSI)로(RGB to HSV)입니다. 이번시간에도 RGB에서 HSV로 바꾸는 공식과, 그 공식을 적용한 결과를 위주로 강의하겠습니다.


0. HSV?

HSV는 Hue(색상), Saturation(채도), Value(명도)의 약자입니다. 때때로 V가 아닌 Intensity로 사용한 HSI나 Brighteness를 사용한 HSB, Lightness를 사용한 HSL이라고도 표기합니다.

 

HSV 원뿔 (출처 : 위키백과)

HSV는 RGB의 3차원 정육면체인것과는 매우 다른 3차원 원뿔 모양의 색 구조를 가지고 있습니다. 이때 원의 각도를 통해색상을, 원의 중심에서 얼마나 멀어져 있느냐로 채도를, 높이가 어느정도인가로 명도를 나타냅니다. 

 

이러한 모양이 낮설게 느껴지실 수 있으시지만, 대부분의 사람들은 HSV를 한번씩 보셨을겁니다.

그림판 색 편집

바로 그림판에서 사용자 지정 색을 고르는 부분의 오른쪽이 그 주인공입니다. 오른쪽 막대가 명도를, 마우스로 색상과 채도를 선택 할 수 있습니다. 바로 밑에 직접 색상, 채도, 명도를 골라 줄 수도 있습니다.


1. openCV에서의 HSV

HSV도 CMYK와 같이 변환 공식이 있습니다. 

RGB to HSV 공식

 

이제 이를 직접 파이썬에 적용해 보겠습니다.

img = cv2.imread('lenna.png')
height, width, channel = img.shape

이번에도 이미지를 불러와주고, 이미지의 크기를 구해줍니다.

 

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

이제 위의 공식을 적용하기위해 정수형이던 값들을 불러와서, 실수형으로 만들어줍니다. 이때 위 공식을 적용하기 위해서는 R, G, B값이 각각 0 ~ 1의 값을 가져야 합니다. 그렇기에 모든 값들을 255로 나누어 줍니다.

 

b, g, r = cv2.split(bgr)

이제 각각의 r, g, b값으로 나누어 줍니다.

 

h = np.zeros((height, width), dtype=np.float)
s = np.zeros((height, width), dtype=np.float)

H와 S값은 조건에 따라 값이 달라지므로, 빈 영상으로 만들어줍니다.

 

v = np.max(bgr, axis=2)

V값은 각각의 화소의 R, G, B값의 최댓값을 넣어줍니다.

 

for i in range(height):
    for j in range(width):
        if v[i][j] == 0:
            h[i][j] = 0
            s[i][j] = 0
        else:
            min_rgb = min(bgr[i][j])
            
            s[i][j] = 1 - (min_rgb / v[i][j])
            
            if v[i][j] == r[i][j]:
                h[i][j] = 60 * (g[i][j] - b[i][j]) / (v[i][j] - min_rgb)
            elif v[i][j] == g[i][j]:
                h[i][j] = 120 + (60 * (b[i][j] - r[i][j])) / (v[i][j] - min_rgb)
            elif v[i][j] == b[i][j]:
                h[i][j] = 240 + (60 * (r[i][j] - g[i][j])) / (v[i][j] - min_rgb)
            if h[i][j] < 0:
                h[i][j] += 360
            h[i][j] /= 360

이제 각각의 H와 S값을 구해줍니다.

 

우선 V의 값이 0, 즉 최댓값이 0인경우는 모든 값이 0인 경우이므로, H와 S값도 0이 됩니다.

 

그 외에는 위의 공식을 적용하게 됩니다. S = (V - min_rgb) / V이므로, V를 앞으로 빼서 1 - min_rgb / V로 간단히 해 주었습니다.

 

H값은 위 공식에 맞게 구해주게 됩니다. 이때 H값이 0 미만일 경우 360을 더해주게 됩니다. 그 이유는 H는 원모양이므로, 0에서 뒤로 가게되면 다시 360에서 시작하는것과 같기 때문입니다.

 

이후 H값에 360을 나누어 0 ~ 1의 값으로 만들어 줍니다.

 

hsv_img = (np.dstack((h, s, v)) * 255).astype(np.uint8)

 

이제 각각의 값들에 255를 곱하여 0에서 255사이의 값으로 만들어 준 후, 정수화 시켜서 하나로 합칩니다. 이렇게 하면 RGB에서 HSV로 변경된 최종 영상이 나오게 됩니다.

 

이제 결과물을 확인해 보겠습니다.

 

원본 영상
HSV
H
S

 

V

각각의 H, S, V는 단일채널이므로, 흑백으로만 나오게 됩니다.

 

그리고 openCV는 자체적으로 HSV로 변환하는 함수를 내장하고 있습니다.

 

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h2, s2, v2 = cv2.split(hsv)

이렇게 BGR2HSV로 영상을 HSV로 변환해 준 후, H, S, V로 각각 나누어 영상을 출력해보겠습니다.

 

내장함수 HSV
내장함수 H
내장함수 S
내장함수 V

두 방식은 거의 비슷하지만, H(색상)의 차이로 변환된 HSV이미지에 차이가 생깁니다. 이는 변환 공식과 컴퓨터 자체의 변환의 차이로 생기는 결과입니다.


2. 마치며

HSV는 생각보다 잘 모르는 사람들이 많았습니다. 하지만 이번 강의를 통해 HSV가 생각보다 본적이 많다는 점과, RGB에서 HSV로 변환하는 공식을 알아가셨으면 합니다. 감사합니다.

 

def rgb2hsv():
    img = cv2.imread('lenna.png')
    height, width, channel = img.shape
    bgr = img.astype(np.float) / 255.0

    b, g, r = cv2.split(bgr)

    h = np.zeros((height, width), dtype=np.float)
    s = np.zeros((height, width), dtype=np.float)
    v = np.max(bgr, axis=2)

    for i in range(height):
        for j in range(width):
            if v[i][j] == 0:
                h[i][j] = 0
                s[i][j] = 0
            else:
                min_rgb = min(bgr[i][j])

                s[i][j] = 1 - (min_rgb / v[i][j])

                if v[i][j] == r[i][j]:
                    h[i][j] = 60 * (g[i][j] - b[i][j]) / (v[i][j] - min_rgb)
                elif v[i][j] == g[i][j]:
                    h[i][j] = 120 + (60 * (b[i][j] - r[i][j])) / (v[i][j] - min_rgb)
                elif v[i][j] == b[i][j]:
                    h[i][j] = 240 + (60 * (r[i][j] - g[i][j])) / (v[i][j] - min_rgb)
                if h[i][j] < 0:
                    h[i][j] += 360
                h[i][j] /= 360

    hsv_img = (np.dstack((h, s, v)) * 255).astype(np.uint8)
    cv2.imshow('original', img)
    cv2.imshow('hsv', hsv_img)
    cv2.imshow('h', h)
    cv2.imshow('s', s)
    cv2.imshow('v', v)

    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h2, s2, v2 = cv2.split(hsv)
    cv2.imshow('hsv2', hsv)
    cv2.imshow('h2', h2)
    cv2.imshow('s2', s2)
    cv2.imshow('v2', v2)

    cv2.waitKey(0)
반응형