8장. Threshold, Adaptive Threshold, Otsu
blockSize 와 C 의 효과
반마다 커트라인을 정할 때 "반"을 얼마나 크게 잡을지 정해야 합니다. 짝꿍 둘만 보고 정하면 둘 다 잘해도 하나는 떨어지는 일이 생기고, 전교생을 한 반으로 보면 다시 커트라인 하나가 됩니다. 그리고 "반 평균보다 조금만 높으면 합격"이라고 하면 아슬아슬한 학생이 너무 많이 합격합니다. 그래서 평균에서 조금 빼 주는 여유를 둡니다.
그림의 세 상자가 이 절의 세 가지입니다. blockSize 가 작으면 좁은 동네만 보므로 굵은 글자의 속이 빕니다. 글자 한가운데 칸은 주변도 모두 글자라 평균이 낮아지고, 그래서 글자 칸도 기준을 넘어 흰색이 되기 때문입니다. 바탕에 얼룩이나 노이즈가 있으면 그 작은 얼룩까지 따로 골라내 잡티가 됩니다. blockSize 가 크면 넓은 동네를 보므로 글자 둘레의 종이까지 기준이 낮아지지 못해 글자가 굵게 번지고, 그늘이 급히 바뀌는 곳이나 그림 가장자리에서 기준이 따라가지 못합니다. C 는 기준을 낮춰 주는 여유로, 크게 하면 바탕의 잡티가 줄지만 너무 크면 옅은 글자도 함께 사라집니다. 여기서 반의 크기가 blockSize, 여유가 C 입니다.
두 값을 바꾸면
| 값 | 작게 하면 | 크게 하면 | 이 장의 안내문에서 무난한 값 |
|---|---|---|---|
| blockSize | 굵은 글자 속이 비고, 얼룩이 잡티로 남는다 | 글자가 굵게 번지고, 그늘 경계·가장자리에서 실패한다 | 5~15 |
| C | 바탕에 점이 많이 생긴다 | 옅고 가는 글자가 끊긴다 | 10 |
blockSize 는 글자 굵기의 몇 배쯤, 그늘이 바뀌는 너비보다는 작게 고르는 것이 출발점입니다. C 는 보통 2~15 사이에서 결과를 보며 고릅니다.
짧은 예시
import cv2
import numpy as np
gray = cv2.imread("ch08_shadow_doc.png", cv2.IMREAD_GRAYSCALE)
for block in [3, 15]:
out = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, block, 10)
# 검정(글자로 본 칸)의 비율
print(block, round(float((out == 0).mean()), 3))
# blockSize 가 짝수면 OpenCV 가 오류를 낸다
try:
cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 4, 10)
except cv2.error:
print("blockSize 4 는 쓸 수 없다")교안 PC 에서는 3 이 0.055, 15 가 0.071 입니다. 작은 블록에서는 굵은 글자의 속이 비어 검은 칸이 오히려 줄어듭니다. cv2.error 는 OpenCV 가 내는 오류의 이름입니다. 오류 글에는 blockSize % 2 == 1 && blockSize > 1 이 찍혀 "2 로 나눈 나머지가 1 이고 1 보다 커야 한다"는 약속을 알려 줍니다.
주의할 점
- blockSize 는 3, 5, 7, … 처럼 1 보다 큰 홀수여야 합니다. 가운데 칸을 두고 양옆에 같은 수의 칸을 두기 때문입니다.
- 좋은 값은 그림마다 다릅니다. 실습 4처럼 몇 개를 격자로 놓고 견줘서 고릅니다.