8장. Threshold, Adaptive Threshold, Otsu
전역 임계값과 cv2.threshold
학교가 전교생에게 커트라인 하나를 똑같이 적용하는 장면을 떠올려 봅시다. 어느 반 학생이든 60점이면 같은 결과를 받습니다. 그림 전체에 숫자 하나를 똑같이 대는 이진화를 전역 임계값이라고 하고, OpenCV 에서는 cv2.threshold 가 이 일을 합니다.
그림의 네 상자가 cv2.threshold 에 주는 네 인자입니다. 흑백 그림(src), 커트라인 숫자(thresh), 넘은 칸에 넣을 값(maxval), 나누는 방식(type)을 차례로 줍니다. 여기서 전교생의 점수가 src, 커트라인이 thresh, "합격" 도장이 maxval, 합격·불합격을 어떻게 적을지가 type 입니다.
네 인자와 돌려주는 두 값
| 순서 | 이름 | 보통 값 | 뜻 |
|---|---|---|---|
| 1 | src(source) | gray | 이진화할 흑백 그림(uint8) |
| 2 | thresh | 127 | 임계값. 이 값보다 커야 넘은 것으로 친다 |
| 3 | maxval | 255 | 넘은 칸에 넣을 값 |
| 4 | type | cv2.THRESH_BINARY | 나누는 방식. 다음 절에서 다섯 가지를 본다 |
| 돌려줌 1 | retval(return value) | 127.0 | 실제로 쓴 임계값(소수). Otsu 를 쓰면 자동으로 고른 값이 여기 온다 |
| 돌려줌 2 | dst(destination) | 결과 그림 | src 와 같은 크기의 그림 |
손으로 계산해 보기
칸 다섯 개 30, 100, 127, 128, 200 에 임계값 127, maxval 255, THRESH_BINARY 를 줍니다.
| 칸의 밝기 | 127 보다 큰가 | 결과 |
|---|---|---|
| 30 | 아니다 | 0 |
| 100 | 아니다 | 0 |
| 127 | 아니다(같다) | 0 |
| 128 | 크다 | 255 |
| 200 | 크다 | 255 |
짧은 예시
import cv2
import numpy as np
# 세로 1, 가로 5 짜리 아주 작은 흑백 그림
row = np.array([[30, 100, 127, 128, 200]], dtype=np.uint8)
# 돌려주는 값은 두 개: (쓴 임계값, 결과 그림)
ret, out = cv2.threshold(row, # src: 흑백 그림
127, # thresh: 임계값
255, # maxval: 넘은 칸에 넣을 값
cv2.THRESH_BINARY) # type: 크면 maxval, 아니면 0
print(ret) # 결과: 127.0
print(out.tolist()) # 결과: [[0, 0, 0, 255, 255]]ret 은 return(돌려주다)을 줄여 흔히 쓰는 이름입니다. 직접 127 을 주었으니 127.0 이 그대로 돌아옵니다. 이 값이 쓸모 있는 것은 Otsu 를 쓸 때입니다.
주의할 점
- 돌려주는 값이 두 개라서
out = cv2.threshold(...)로 받으면out이 튜플이 됩니다.ret, out = ...이나out = cv2.threshold(...)[1]로 받습니다. - 인터넷 예제에는
_, out = cv2.threshold(...)처럼 첫 값을_로 버리는 꼴이 많습니다. "이 값은 쓰지 않는다"는 표시입니다.