3장. 디지털 영상과 색공간 변환
실습 5 — inRange 로 빨간 과일 고르기
실습 4에서 읽은 HSV 값으로 범위를 정해, 바나나(노랑)와 사과(빨강)를 마스크로 골라냅니다. 빨강은 H 의 양 끝 두 구간을 따로 고른 뒤 합칩니다. 마지막으로 마스크와 빨강만 남긴 그림을 파일로 저장합니다.
1단계. 노란 바나나 고르기
import cv2
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False
img = cv2.imread("fruit_basket.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 아래 값과 위 값은 [H, S, V] 순서. 바나나 칸은 H=25 근처, S 와 V 가 크다
lower = np.array([20, 100, 100])
upper = np.array([35, 255, 255])
# cv2.inRange(그림, 아래 값, 위 값): 세 숫자가 모두 범위 안이면 255, 아니면 0
yellow = cv2.inRange(hsv, lower, upper)
print("마스크 모양:", yellow.shape, yellow.dtype)
print("들어 있는 값:", [int(v) for v in np.unique(yellow)])
# 흰 칸(255)의 개수와 전체에서의 비율
print(f"노란 칸: {int((yellow > 0).sum()):,}칸, {round(float((yellow > 0).mean()) * 100, 2)}%")마스크 모양: (240, 320) uint8
들어 있는 값: [0, 255]
노란 칸: 1,980칸, 2.58%2단계. 빨강은 두 구간을 합치기
# 빨강 1: H 가 0 근처(주황 쪽으로 기운 빨강)
low_red = cv2.inRange(hsv, np.array([0, 100, 80]), np.array([10, 255, 255]))
# 빨강 2: H 가 179 근처(자홍 쪽으로 기운 빨강)
high_red = cv2.inRange(hsv, np.array([170, 100, 80]), np.array([179, 255, 255]))
# cv2.bitwise_or(마스크1, 마스크2): 둘 중 하나라도 255 인 칸을 255 로
red = cv2.bitwise_or(low_red, high_red)
for name, m in [("빨강 1 (0~10)", low_red), ("빨강 2 (170~179)", high_red), ("합친 빨강", red)]:
print(f"{name:16} {int((m > 0).sum()):5}칸")빨강 1 (0~10) 3667칸
빨강 2 (170~179) 1781칸
합친 빨강 5448칸3단계. 마스크로 빨강만 남겨 보기
# cv2.bitwise_and(그림, 그림, mask=마스크): 마스크가 255 인 칸만 원래 색, 나머지는 0(검정)
only_red = cv2.bitwise_and(img, img, mask=red)
fig, axes = plt.subplots(1, 4, figsize=(12, 2.8))
panels = [("빨강 1 (0~10)", low_red, "gray"), ("빨강 2 (170~179)", high_red, "gray"),
("합친 마스크", red, "gray"), ("빨강만 남김", cv2.cvtColor(only_red, cv2.COLOR_BGR2RGB), None)]
for ax, (title, im, cmap) in zip(axes, panels):
ax.imshow(im, cmap=cmap)
ax.set_title(title)
ax.axis("off")
plt.tight_layout()
plt.show()
4단계. 마스크와 결과 저장하기
# 마스크는 흑백(채널 1) 그림이라 그대로 png 로 저장된다
ok1 = cv2.imwrite("out_ch03_red_mask.png", red)
# 빨강만 남긴 그림은 BGR 그대로 저장한다(RGB 로 바꾼 것을 저장하면 색이 뒤바뀐다)
ok2 = cv2.imwrite("out_ch03_red_only.png", only_red)
print("저장:", ok1, ok2)
# 다시 읽어 마스크의 흰 칸 수가 같은지 확인한다
back = cv2.imread("out_ch03_red_mask.png", cv2.IMREAD_GRAYSCALE)
print("다시 읽은 마스크의 흰 칸:", int((back > 0).sum()))저장: True True
다시 읽은 마스크의 흰 칸: 5448코드 설명
cv2.inRange는 H, S, V 세 숫자가 모두 범위 안일 때만 255 를 적습니다. 결과는 0 과 255 두 값뿐인 uint8 흑백 그림입니다.- 빨강은 H 가 0 근처와 179 근처로 나뉘므로
inRange를 두 번 부르고cv2.bitwise_or로 합칩니다. 한 구간만 쓰면 사과 하나가 통째로 빠집니다. cv2.bitwise_and(img, img, mask=red)는 같은 그림 둘을 AND 하되, 마스크가 255 인 칸만 계산하고 나머지는 0 으로 둡니다. 그래서 빨간 과일만 원래 색으로 남습니다.- 마스크는 채널이 하나인 흑백 그림이라
cv2.imwrite로 그대로 저장되고,IMREAD_GRAYSCALE로 다시 읽으면 흰 칸 수가 같습니다.