8장. Threshold, Adaptive Threshold, Otsu
임계값 방식 다섯 가지
택배 상자를 무게로 나누는 장면을 떠올려 봅시다. "5kg 넘으면 빨간 딱지, 아니면 아무것도 안 붙임"으로 나눌 수도 있고, "5kg 넘는 상자는 5kg 짜리로 쳐서 요금을 깎아 줌"처럼 다르게 처리할 수도 있습니다. cv2.threshold 의 넷째 인자 type 은 커트라인을 넘은 칸과 못 넘은 칸을 어떻게 적을지를 고릅니다.
그래프마다 가로는 들어간 밝기, 세로는 나온 밝기이고, 갈색 점선이 임계값 127 입니다. 왼쪽 위의 회색 선은 아무것도 하지 않은 입력이라 곧게 오릅니다. 초록 BINARY 는 127 에서 0 에서 255 로 뛰어오르고, 다홍 BINARY_INV 는 거꾸로 255 에서 0 으로 떨어집니다. 보라 TRUNC 는 127 위를 평평하게 깎고, 갈색 TOZERO 는 127 아래만 0 으로, 산호 TOZERO_INV 는 127 위만 0 으로 만듭니다. 여기서 빨간 딱지를 붙이는 것이 BINARY, 무거운 상자를 5kg 으로 쳐 주는 것이 TRUNC 입니다.
다섯 방식 견주기
| 방식 | 임계값보다 큰 칸 | 임계값 이하인 칸 | [30, 100, 127, 128, 200] 의 결과 | 쓰는 곳 |
|---|---|---|---|---|
THRESH_BINARY | maxval | 0 | [0, 0, 0, 255, 255] | 밝은 물체를 흰색으로 |
THRESH_BINARY_INV | 0 | maxval | [255, 255, 255, 0, 0] | 어두운 글자·물체를 흰색으로 |
THRESH_TRUNC | 임계값 | 그대로 | [30, 100, 127, 127, 127] | 너무 밝은 곳을 눌러 주기 |
THRESH_TOZERO | 그대로 | 0 | [0, 0, 0, 128, 200] | 어두운 바탕만 지우기 |
THRESH_TOZERO_INV | 0 | 그대로 | [30, 100, 127, 0, 0] | 밝은 곳만 지우기 |
앞의 두 방식만 결과가 0 과 255 두 값인 이진 그림입니다. 나머지 셋은 밝기의 일부를 남기므로 엄밀히는 이진화가 아니지만, 같은 함수로 하므로 함께 배웁니다.
짧은 예시
import cv2
import numpy as np
row = np.array([[30, 100, 127, 128, 200]], dtype=np.uint8)
kinds = ["THRESH_BINARY", "THRESH_BINARY_INV", "THRESH_TRUNC", "THRESH_TOZERO", "THRESH_TOZERO_INV"]
for name in kinds:
# getattr(cv2, 이름): 이름 글자로 cv2 의 상수를 꺼낸다
_, out = cv2.threshold(row, 127, 255, getattr(cv2, name))
print(f"{name:18}", out.ravel().tolist())
# 결과: 위 표의 다섯 줄과 같다getattr 은 get attribute(속성 꺼내기)를 줄인 이름입니다. 상수 다섯 개를 하나씩 쓰지 않고 이름 목록으로 돌려 봅니다.
주의할 점
- 3장의 마스크처럼 "고른 것이 흰색"이 되게 하려면, 어두운 글자나 부품을 고를 때
THRESH_BINARY_INV를 씁니다. - TRUNC 와 TOZERO 에서는 maxval 을 쓰지 않습니다. 그래도 자리를 채우려고 255 를 줍니다.