OpenCV 비전 실무2장 · 이미지 입출력과 영상 시각화14 / 21← 이전목차다음 →MetaContents

2장. 이미지 입출력과 영상 시각화

실습 1 — 읽기 모드 세 가지 견주기

투명 바탕의 별 스티커(ch02_sticker.png)를 세 가지 읽기 모드로 읽어 모양을 견주고, 알파 채널의 값을 세어 봅니다. 이 스티커는 OpenCV 로 도형을 그려 만든 연습용 가상 그림입니다.

1단계. 세 모드로 읽고 모양 견주기

Python
import cv2

# 읽기 모드 이름과 값을 사전으로 짝지어 둔다
modes = {
    "COLOR": cv2.IMREAD_COLOR,           # 기본값: 언제나 BGR 세 채널
    "GRAYSCALE": cv2.IMREAD_GRAYSCALE,   # 읽으면서 흑백 한 채널로
    "UNCHANGED": cv2.IMREAD_UNCHANGED,   # 저장된 그대로(투명도 포함)
}
images = {}
for name, flag in modes.items():
    # cv2.imread(파일 이름, 읽기 모드): 둘째 자리에 모드를 준다
    img = cv2.imread("ch02_sticker.png", flag)
    images[name] = img
    # 결과: 모드마다 모양, 차원 수, 메모리 크기가 다르다
    print(f"{name:10} 모양 {str(img.shape):15} 차원 {img.ndim}  {img.nbytes:,}바이트")
출력
COLOR      모양 (160, 160, 3)   차원 3  76,800바이트
GRAYSCALE  모양 (160, 160)      차원 2  25,600바이트
UNCHANGED  모양 (160, 160, 4)   차원 3  102,400바이트

2단계. 알파 채널의 값 세기

Python
import numpy as np

# UNCHANGED 로 읽은 그림의 네 번째 숫자(번호 3)가 알파다
alpha = images["UNCHANGED"][:, :, 3]
total = alpha.size                                   # 전체 칸 수 = 160 × 160
clear = int((alpha == 0).sum())                      # 완전히 투명한 칸
solid = int((alpha == 255).sum())                    # 완전히 불투명한 칸
half = total - clear - solid                         # 그 사이(가장자리)
print("전체 칸:", total)
for name, count in [("투명(0)", clear), ("불투명(255)", solid), ("반투명(1~254)", half)]:
    # 비율 = 칸 수 ÷ 전체 칸 수. 소수 셋째 자리까지
    print(f"{name:12} {count:6,}칸  비율 {round(count / total, 3)}")
출력
전체 칸: 25600
투명(0)        19,491칸  비율 0.761
불투명(255)      5,672칸  비율 0.222
반투명(1~254)      437칸  비율 0.017

3단계. 같은 칸을 세 모드로 읽어 보기

Python
# 왼쪽 위 구석(투명한 곳)과 별 한가운데. 배열은 [행, 열] 순서로 꺼낸다
spots = {"구석 [0, 0]": (0, 0), "별 가운데 [70, 80]": (70, 80)}
for spot, (y, x) in spots.items():
    print(spot)
    for name, img in images.items():
        value = img[y, x]
        # 흑백은 숫자 하나, 컬러는 숫자 여러 개라서 꼴을 맞춰 찍는다
        shown = int(value) if img.ndim == 2 else [int(v) for v in value]
        print(f"  {name:10} {shown}")
출력
구석 [0, 0]
  COLOR      [0, 0, 0]
  GRAYSCALE  0
  UNCHANGED  [0, 0, 0, 0]
별 가운데 [70, 80]
  COLOR      [60, 210, 255]
  GRAYSCALE  206
  UNCHANGED  [60, 210, 255, 255]

4단계. 세 결과 나란히 띄우기

Python
import matplotlib.pyplot as plt

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

fig, axes = plt.subplots(1, 3, figsize=(9, 3.4), layout="constrained")
# 컬러는 BGR→RGB, 흑백은 cmap="gray", 투명 그림은 BGRA→RGBA 로 바꿔 띄운다
axes[0].imshow(cv2.cvtColor(images["COLOR"], cv2.COLOR_BGR2RGB))
axes[1].imshow(images["GRAYSCALE"], cmap="gray", vmin=0, vmax=255)
axes[2].imshow(cv2.cvtColor(images["UNCHANGED"], cv2.COLOR_BGRA2RGBA))
for ax, name in zip(axes, images):
    ax.set_title(f"IMREAD_{name}")
    ax.axis("off")
plt.show()
같은 별 스티커를 COLOR, GRAYSCALE, UNCHANGED 로 읽어 띄운 세 그림

결과를 읽어 봅니다. 같은 파일인데 모드에 따라 메모리 크기가 76,800바이트(160×160×3), 25,600바이트(160×160×1), 102,400바이트(160×160×4)로 달라졌습니다. 알파를 세어 보면 전체 25,600칸 가운데 19,491칸(76.1%)이 완전히 투명하고, 5,672칸(22.2%)이 불투명하며, 별의 가장자리 437칸(1.7%)만 반투명입니다. 투명한 구석의 색 숫자는 [0, 0, 0] 이라, 투명도를 버리는 IMREAD_COLOR 와 IMREAD_GRAYSCALE 그림에서는 바탕이 검게 보입니다. IMREAD_UNCHANGED 그림은 알파가 0 인 곳으로 Matplotlib 의 흰 바탕이 비쳐 보입니다. 별 가운데의 BGR [60, 210, 255] 는 흑백으로 읽으면 206 이 됩니다. 0.299×255 + 0.587×210 + 0.114×60 = 76.2 + 123.3 + 6.8 = 206.3 이기 때문이며, 이 식은 3장에서 자세히 봅니다.

코드 설명

  • cv2.imread(파일, cv2.IMREAD_UNCHANGED) 는 저장된 그대로 읽습니다. 투명 png 는 채널이 4개(BGRA)라 모양이 (160, 160, 4) 입니다.
  • images["UNCHANGED"][:, :, 3] 은 모든 칸의 네 번째 숫자, 곧 알파만 꺼낸 (160, 160) 배열입니다.
  • (alpha == 0).sum() 은 칸마다 "0 인가?"를 True/False 로 따진 뒤 True 의 개수를 셉니다. True 를 1 로 세기 때문입니다.
  • cv2.COLOR_BGRA2RGBA 는 네 채널 그림의 색 순서를 바꾸면서 알파는 그대로 둡니다. Matplotlib 은 알파를 읽어 투명한 곳에 바탕을 비춰 줍니다.
← 이전 절2장 · 실습 지도다음 절 →2장 · 실습 2 — JPG 품질별로 저장하고 크기와 차이 재기