8장. Threshold, Adaptive Threshold, Otsu
실습 2 — 다섯 가지 임계값 방식 견주기
cv2.threshold 의 다섯 가지 방식을 칸 다섯 개짜리 숫자 줄, 0~255 로 오르는 한 줄, 부품 그림에 차례로 적용해 견줍니다.
1단계. 숫자 다섯 개로 표 만들기
import cv2
import numpy as np
import pandas as pd
row = np.array([[30, 100, 127, 128, 200]], dtype=np.uint8)
# 방식 이름과 상수를 짝지은 사전
kinds = {"BINARY": cv2.THRESH_BINARY, "BINARY_INV": cv2.THRESH_BINARY_INV,
"TRUNC": cv2.THRESH_TRUNC, "TOZERO": cv2.THRESH_TOZERO,
"TOZERO_INV": cv2.THRESH_TOZERO_INV}
table = {"입력": row.ravel().tolist()}
for name, kind in kinds.items():
_, out = cv2.threshold(row, 127, 255, kind)
table[name] = out.ravel().tolist()
# 행과 열을 바꿔(T = transpose, 뒤바꾸기) 방식이 한 줄씩 오게 한다
print(pd.DataFrame(table).T.to_string(header=False))입력 30 100 127 128 200
BINARY 0 0 0 255 255
BINARY_INV 255 255 255 0 0
TRUNC 30 100 127 127 127
TOZERO 0 0 0 128 200
TOZERO_INV 30 100 127 0 02단계. 0~255 로 오르는 한 줄에 적용해 그래프 그리기
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False
# np.arange(256): 0, 1, 2, ..., 255. 세로 1 줄짜리 그림으로 바꾼다
ramp = np.arange(256, dtype=np.uint8).reshape(1, -1)
fig, axes = plt.subplots(1, 5, figsize=(13, 2.8), sharey=True)
for ax, (name, kind) in zip(axes, kinds.items()):
_, out = cv2.threshold(ramp, 127, 255, kind)
# 가로는 들어간 밝기, 세로는 나온 밝기
ax.plot(ramp.ravel(), out.ravel())
ax.axvline(127, color="gray", linestyle="--")
ax.set_title(name)
ax.set_xlabel("들어간 밝기")
axes[0].set_ylabel("나온 밝기")
plt.tight_layout()
plt.show()
3단계. 부품 그림에 적용해 견주기
gray = cv2.imread("ch08_parts.png", cv2.IMREAD_GRAYSCALE)
fig, axes = plt.subplots(2, 3, figsize=(10, 5.6))
panels = [("원본", gray)]
for name, kind in kinds.items():
_, out = cv2.threshold(gray, 127, 255, kind)
panels.append((name, out))
# 결과에 남은 서로 다른 값의 개수: 이진 그림이면 2
print(f"{name:10}: 서로 다른 값 {len(np.unique(out))}개, 평균 밝기 {round(float(out.mean()), 1)}")
for ax, (title, im) in zip(axes.flat, panels):
ax.imshow(im, cmap="gray", vmin=0, vmax=255)
ax.set_title(title)
ax.axis("off")
plt.tight_layout()
plt.show()BINARY : 서로 다른 값 2개, 평균 밝기 210.4
BINARY_INV: 서로 다른 값 2개, 평균 밝기 44.6
TRUNC : 서로 다른 값 58개, 평균 밝기 118.7
TOZERO : 서로 다른 값 79개, 평균 밝기 160.2
TOZERO_INV: 서로 다른 값 59개, 평균 밝기 13.9
코드 설명
- 표의 다섯 줄은 본문의 손계산과 같습니다. 127 은 "127 보다 크지 않으므로" BINARY 에서 0, TOZERO 에서 0, TOZERO_INV 와 TRUNC 에서 그대로 127 입니다.
ramp = np.arange(256).reshape(1, -1)은 0 부터 255 까지 한 칸씩 오르는 세로 1 줄짜리 그림입니다. 들어간 밝기와 나온 밝기를 그래프로 그리면 방식마다 모양이 한눈에 보입니다.- 부품 그림에서 BINARY 와 BINARY_INV 는 서로 다른 값이 2개뿐인 이진 그림입니다. TRUNC 는 58개, TOZERO 는 79개, TOZERO_INV 는 59개의 값이 남아 이진 그림이 아닙니다.
- TRUNC 는 127 보다 밝은 작업대를 127 로 눌러 평균 밝기가 118.7 로 내려갔고, TOZERO_INV 는 밝은 작업대를 0 으로 지워 평균이 13.9 입니다. 부품만 원래 밝기로 남기고 싶을 때 TOZERO_INV 를 씁니다.