OpenCV 비전 실무8장 · Threshold, Adaptive Threshold, Otsu11 / 21← 이전목차다음 →MetaContents

8장. Threshold, Adaptive Threshold, Otsu

Otsu 가 잘 안 맞는 경우와 블러 먼저 하기

흐린 날 물안경 없이 수영장 바닥을 보면 타일의 경계가 흐릿합니다. 그림에 자잘한 점(노이즈)이 많이 끼면 히스토그램의 두 봉우리가 넓게 퍼져 서로 섞이고, 골짜기가 메워집니다. 그러면 Otsu 가 고른 값이 어긋나지 않더라도, 바탕의 점들이 커트라인을 넘나들어 결과에 소금 뿌린 듯한 잡티가 생깁니다.

노이즈 낀 그림, 가우시안 블러, Otsu 의 세 단계 흐름도

그림처럼 이진화 전에 가우시안 블러로 칸마다 주변과 섞어 점을 줄이면, 봉우리가 다시 좁아지고 결과가 깨끗해집니다. 여기서 흐린 물속 시야가 노이즈, 물안경이 블러입니다. 블러는 6장에서 배운 필터로, 여기서는 cv2.GaussianBlur 한 줄만 씁니다.

노이즈 낀 부품 그림과 바로 Otsu 한 결과, 블러 뒤 Otsu 한 결과, 그리고 세 히스토그램

부품 그림에 씨앗 8 로 노이즈(표준편차 35)를 더했습니다. 아래 줄 가운데의 다홍 히스토그램은 두 봉우리가 퍼져 골짜기가 거의 없습니다. 바로 Otsu 하면(위 가운데) 고른 값은 144 로 원본(136)과 크게 다르지 않지만 작업대 위에 검은 점이 가득합니다. 7×7 가우시안 블러 뒤에 Otsu 하면(위 오른쪽) 봉우리가 다시 좁아지고 결과도 깨끗합니다.

Otsu 가 힘든 경우

경우히스토그램하는 일
노이즈가 많다봉우리가 퍼져 골짜기가 메워진다블러 먼저
조명이 고르지 않다바탕이 넓게 퍼진다Adaptive Threshold
물체가 아주 작다물체 봉우리가 거의 안 보인다관심 영역만 잘라서(13장) 하거나 직접 정하기
무리가 셋 이상봉우리가 셋 이상이다직접 정하거나 두 번 나누기

짧은 예시

Python
import cv2
import numpy as np

gray = cv2.imread("ch08_parts.png", cv2.IMREAD_GRAYSCALE)
# 씨앗을 고정한 노이즈: 평균 0, 표준편차 35 의 정규분포 숫자를 칸마다 더한다
rng = np.random.default_rng(8)
noisy = np.clip(gray + rng.normal(0, 35, gray.shape), 0, 255).astype(np.uint8)

# cv2.GaussianBlur(그림, (가로, 세로 크기), 0): 6차시에서 배운 블러. 0 은 퍼짐을 크기에 맞춰 정하라는 뜻
blur = cv2.GaussianBlur(noisy, (7, 7), 0)
for name, im in [("바로", noisy), ("블러 뒤", blur)]:
    ret, _ = cv2.threshold(im, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    print(name, "Otsu 가 고른 값:", int(ret))
# 결과: 바로 144, 블러 뒤 138

np.random.default_rng(8) 은 씨앗 8 로 난수 생성기(random number generator)를 만듭니다. 씨앗이 같으면 몇 번을 돌려도 같은 노이즈가 나옵니다.

주의할 점

  • 블러를 너무 세게 하면(크기 21 이상 등) 가는 글자나 작은 물체의 테두리까지 뭉개집니다. 3, 5, 7 처럼 작은 크기부터 해 봅니다.
  • 블러는 Adaptive Threshold 앞에도 쓸 수 있습니다. 블러 뒤의 이진화에도 남은 잡티는 9장의 모폴로지로 지웁니다.
← 이전 절8장 · Otsu 알고리즘다음 절 →8장 · 세 방법 견주기와 고르는 기준