OpenCV 비전 실무3장 · 디지털 영상과 색공간 변환17 / 21← 이전목차다음 →MetaContents

3장. 디지털 영상과 색공간 변환

실습 4 — HSV 로 바꿔 세 숫자 보기

대표 색과 바구니 그림을 HSV 로 바꿔, H·S·V 가 어떤 숫자로 나오는지 확인합니다. 과일 가운데의 HSV 값을 표로 모으고, H·S·V 를 각각 흑백 그림으로 띄워 봅니다. 이 실습에서 읽은 값으로 실습 5의 색 범위를 정합니다.

1단계. 대표 색의 HSV 보기

Python
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False

# 대표 색 여덟 개를 BGR 로 적어 1×8 칸짜리 그림을 만든다
colors = {"빨강": (0, 0, 255), "노랑": (0, 255, 255), "초록": (0, 255, 0), "파랑": (255, 0, 0),
          "어두운 빨강": (0, 0, 128), "옅은 빨강": (128, 128, 255), "회색": (128, 128, 128), "흰색": (255, 255, 255)}
px = np.array([list(colors.values())], dtype=np.uint8)
# cv2.COLOR_BGR2HSV: BGR 을 H(색상), S(채도), V(명도)로 바꾼다
px_hsv = cv2.cvtColor(px, cv2.COLOR_BGR2HSV)
for name, (h, s, v) in zip(colors, px_hsv[0]):
    print(f"{name:6} H={int(h):3}  S={int(s):3}  V={int(v):3}")
출력
빨강     H=  0  S=255  V=255
노랑     H= 30  S=255  V=255
초록     H= 60  S=255  V=255
파랑     H=120  S=255  V=255
어두운 빨강 H=  0  S=255  V=128
옅은 빨강  H=  0  S=127  V=255
회색     H=  0  S=  0  V=128
흰색     H=  0  S=  0  V=255

2단계. 바구니 그림을 HSV 로 바꾸기

Python
img = cv2.imread("fruit_basket.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
print("HSV 그림의 모양:", hsv.shape, hsv.dtype)
# 채널마다 가장 작은 값과 가장 큰 값. H 는 179 를 넘지 않는다
for k, name in enumerate(["H", "S", "V"]):
    print(f"{name}: {int(hsv[..., k].min())} ~ {int(hsv[..., k].max())}")
출력
HSV 그림의 모양: (240, 320, 3) uint8
H: 0 ~ 179
S: 31 ~ 223
V: 60 ~ 255

3단계. 과일 가운데의 HSV 값을 표로 모으기

Python
# 과일과 배경의 한 칸씩: 이름 → (x, y)
spots = {"사과(왼쪽)": (140, 135), "사과(오른쪽)": (190, 145), "사과(위)": (158, 102),
         "바나나": (80, 92), "라임": (62, 128), "포도": (232, 127), "바구니": (160, 200), "벽": (20, 20)}
rows = []
for name, (x, y) in spots.items():
    b, g, r = (int(v) for v in img[y, x])
    h, s, v = (int(v) for v in hsv[y, x])
    rows.append({"자리": name, "B": b, "G": g, "R": r, "H": h, "S": s, "V": v})
print(pd.DataFrame(rows).to_string(index=False))
출력
     자리   B   G   R   H   S   V
 사과(왼쪽)  34  37 206   1 213 206
사과(오른쪽)  61  26 208 174 223 208
  사과(위)  27  45 217   3 223 217
    바나나  41 212 243  25 212 243
     라임  42 186  92  50 197 186
     포도 120  49 100 142 151 120
    바구니  47  80 119  14 154 119
      벽 204 228 243  18  41 243

4단계. H, S, V 를 각각 흑백으로 보기

Python
fig, axes = plt.subplots(1, 4, figsize=(12, 2.8))
axes[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0].set_title("원본")
# H 는 0~179, S 와 V 는 0~255 를 회색으로 그대로 대응시킨다
for ax, k, title, top in [(axes[1], 0, "H 색상 (0~179)", 179), (axes[2], 1, "S 채도", 255), (axes[3], 2, "V 명도", 255)]:
    ax.imshow(hsv[..., k], cmap="gray", vmin=0, vmax=top)
    ax.set_title(title)
for ax in axes:
    ax.axis("off")
plt.tight_layout()
plt.show()
원본과, HSV 의 H, S, V 를 각각 흑백으로 띄운 네 그림을 나란히 놓은 결과

코드 설명

  • 대표 색에서 어두운 빨강과 옅은 빨강은 H 가 빨강과 같은 0 이고, V 또는 S 만 줄었습니다. 회색과 흰색은 S 가 0 이라 H 가 뜻이 없습니다.
  • cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 의 결과도 uint8 의 (240, 320, 3) 배열이고, 세 칸이 B, G, R 대신 H, S, V 입니다. H 의 가장 큰 값은 179 를 넘지 않습니다.
  • 과일 표에서 사과 둘은 H 가 1, 3 이고, 오른쪽 사과는 174 입니다. 같은 빨강이 H 띠의 양 끝으로 나뉜 것입니다. 그래서 H 그림에서 앞의 둘은 검게, 오른쪽 사과는 하얗게 보이고, 실습 5에서 범위를 둘 잡습니다.
  • hsv[..., k] 처럼 채널을 인덱싱으로 꺼내 cmap="gray" 로 띄우면 숫자가 큰 칸이 밝게 보입니다. 채널을 나누고 합치는 전용 함수는 4장에서 배웁니다.
← 이전 절3장 · 실습 3 — BGR, RGB, Gray 바꾸기다음 절 →3장 · 실습 5 — inRange 로 빨간 과일 고르기