파이썬 openCV 15번째 강의는 RGB에서 HSV(HSI)로(RGB to HSV)입니다. 이번시간에도 RGB에서 HSV로 바꾸는 공식과, 그 공식을 적용한 결과를 위주로 강의하겠습니다.
0. HSV?
HSV는 Hue(색상), Saturation(채도), Value(명도)의 약자입니다. 때때로 V가 아닌 Intensity로 사용한 HSI나 Brighteness를 사용한 HSB, Lightness를 사용한 HSL이라고도 표기합니다.
HSV는 RGB의 3차원 정육면체인것과는 매우 다른 3차원 원뿔 모양의 색 구조를 가지고 있습니다. 이때 원의 각도를 통해색상을, 원의 중심에서 얼마나 멀어져 있느냐로 채도를, 높이가 어느정도인가로 명도를 나타냅니다.
이러한 모양이 낮설게 느껴지실 수 있으시지만, 대부분의 사람들은 HSV를 한번씩 보셨을겁니다.
바로 그림판에서 사용자 지정 색을 고르는 부분의 오른쪽이 그 주인공입니다. 오른쪽 막대가 명도를, 마우스로 색상과 채도를 선택 할 수 있습니다. 바로 밑에 직접 색상, 채도, 명도를 골라 줄 수도 있습니다.
1. openCV에서의 HSV
HSV도 CMYK와 같이 변환 공식이 있습니다.
이제 이를 직접 파이썬에 적용해 보겠습니다.
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로 변경된 최종 영상이 나오게 됩니다.
이제 결과물을 확인해 보겠습니다.
각각의 H, S, V는 단일채널이므로, 흑백으로만 나오게 됩니다.
그리고 openCV는 자체적으로 HSV로 변환하는 함수를 내장하고 있습니다.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) h2, s2, v2 = cv2.split(hsv) |
이렇게 BGR2HSV로 영상을 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)
'파이썬 강의 > openCV' 카테고리의 다른 글
파이썬 openCV 17. 에지검출 : 유사 연산자(Homogeneity)와 차 연산자(Difference)기법 (1) | 2020.12.06 |
---|---|
파이썬 openCV 16. RGB에서 YCbCr로(RGB to YCbCr) (4) | 2020.12.02 |
파이썬 openCV 14. RGB에서 CMYK로(RGB to CMYK) (0) | 2020.11.29 |
파이썬 openCV 13. 블러링(blurring)/스무딩(smoothing)/샤프닝(sharpening) (0) | 2020.11.28 |
파이썬 openCV 12. 엠보싱 기법(embossing) (0) | 2020.11.25 |