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

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

실습 5 — inRange 로 빨간 과일 고르기

실습 4에서 읽은 HSV 값으로 범위를 정해, 바나나(노랑)와 사과(빨강)를 마스크로 골라냅니다. 빨강은 H 의 양 끝 두 구간을 따로 고른 뒤 합칩니다. 마지막으로 마스크와 빨강만 남긴 그림을 파일로 저장합니다.

1단계. 노란 바나나 고르기

Python
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단계. 빨강은 두 구간을 합치기

Python
# 빨강 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단계. 마스크로 빨강만 남겨 보기

Python
# 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()
빨강 1 마스크, 빨강 2 마스크, 합친 마스크, 빨강만 남긴 그림을 나란히 놓은 결과

4단계. 마스크와 결과 저장하기

Python
# 마스크는 흑백(채널 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 로 다시 읽으면 흰 칸 수가 같습니다.
← 이전 절3장 · 실습 4 — HSV 로 바꿔 세 숫자 보기다음 절 →3장 · 프로젝트 1 — 색으로 과일 고르고 넓이 비율 재기