logo

이진 이미지와 Thresholding

실습 준비

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from yeom import to_pil

이미지 불러오기

newspaper = cv.imread("newspaper.png", cv.IMREAD_GRAYSCALE)
newspaper.shape, newspaper.dtype
실행 결과
((512, 419), dtype('uint8'))

이진화할 신문 원본

이진 이미지

이진 이미지(binary image)는 픽셀값이 두 종류뿐인 이미지. OpenCV에서는 일반적으로 uint8 자료형의 0과 255를 사용.

픽셀값 일반적인 의미
0 배경 또는 선택하지 않은 영역
255 전경 또는 선택한 영역

마스크 역시 이진 이미지. 색상 범위뿐 아니라 그레이스케일 밝기를 기준으로도 생성 가능.

밝기 분포와 문턱값

그레이스케일 히스토그램은 밝기별 픽셀 수를 표현. 배경과 글자의 밝기 분포가 나뉘면 두 집단 사이의 값을 문턱값(threshold)으로 선택 가능.

hist = cv.calcHist([newspaper], [0], None, [256], [0, 256])

plt.plot(hist)
plt.xlim([0, 256])
plt.xlabel("픽셀 값")
plt.ylabel("픽셀 수")
plt.show()

신문 이미지의 그레이스케일 히스토그램

전역 문턱값 처리

전역 문턱값 처리(global thresholding)는 이미지 전체에 하나의 문턱값을 적용. THRESH_BINARY는 문턱값보다 큰 픽셀을 최댓값으로, 나머지를 0으로 변경.

used_threshold, binary = cv.threshold(
    newspaper,
    190,  # 글자와 배경을 나눌 문턱값
    255,  # 문턱값보다 밝은 픽셀에 넣을 값
    cv.THRESH_BINARY,
)
used_threshold  # 실제 사용한 문턱값
실행 결과
190.0
np.unique(binary)  # 결과 이미지의 픽셀값 종류
실행 결과
array([  0, 255], dtype=uint8)

문턱값 190.0 적용 결과는 픽셀값이 0과 255뿐인 이진 이미지.

to_pil(binary)

문턱값 190을 적용한 신문 이진 이미지

cv.threshold()의 반환값은 실제 사용한 문턱값과 결과 이미지. 문턱값을 직접 지정한 경우 첫 번째 반환값은 입력값과 동일.

문턱값 비교

문턱값을 낮추면 흰색 픽셀이 증가하고, 높이면 검정 픽셀이 증가.

threshold_values = [80, 120, 150, 190, 210, 230]  # 낮은 값부터 매우 높은 값까지 비교
binary_images = [
    cv.threshold(newspaper, value, 255, cv.THRESH_BINARY)[1]
    for value in threshold_values
]

comparison = cv.hconcat(binary_images)
to_pil(comparison)

왼쪽부터 문턱값 80, 120, 150, 190, 210, 230의 결과. 값이 지나치게 낮거나 높으면 글자와 배경의 세부 정보가 함께 소실.

문턱값 80, 120, 150, 190, 210, 230을 적용한 결과 비교

문턱값 처리 방식

같은 문턱값도 처리 방식에 따라 다른 결과.

방식 문턱값보다 큰 픽셀 나머지 픽셀
THRESH_BINARY 최댓값 0
THRESH_BINARY_INV 0 최댓값
THRESH_TRUNC 문턱값 원래 값
THRESH_TOZERO 원래 값 0
THRESH_TOZERO_INV 0 원래 값

THRESH_BINARY_INVTHRESH_BINARY의 흑백을 반전. 이 신문 이미지에서는 어두운 글자가 흰색, 밝은 배경이 검정으로 변환.

_, binary_inverse = cv.threshold(
    newspaper,
    190,  # THRESH_BINARY와 같은 문턱값으로 비교
    255,  # 문턱값 이하인 픽셀에 넣을 값
    cv.THRESH_BINARY_INV,
)
to_pil(binary_inverse)

Thresholding 방식별 픽셀 변환

Otsu 문턱값 처리

문턱값을 직접 정하기 어려우면 Otsu 문턱값 처리(Otsu thresholding)로 자동 선택 가능. Otsu 알고리즘은 픽셀을 두 집단으로 나눌 때 집단 내 분산의 가중합을 최소화하는 문턱값을 선택.

THRESH_OTSU는 입력한 문턱값 대신 자동 계산값을 사용. 문턱값 인자에는 관례적으로 0을 전달하고, 실제 선택값은 첫 번째 반환값으로 확인.

otsu_threshold, binary_otsu = cv.threshold(
    newspaper,
    0,    # Otsu가 자동으로 대체할 자리표시자
    255,  # 문턱값보다 밝은 픽셀에 넣을 값
    cv.THRESH_BINARY | cv.THRESH_OTSU,
)
otsu_threshold  # Otsu가 선택한 문턱값
실행 결과
169.0

신문 이미지에서 Otsu가 선택한 문턱값은 169.0.

to_pil(binary_otsu)

Otsu 알고리즘이 자동으로 선택한 문턱값의 결과

전역 문턱값의 한계

위치마다 조명이 다르면 같은 물체도 서로 다른 밝기. 이미지 전체에 하나의 문턱값을 적용하면 밝은 영역이나 어두운 영역 한쪽이 소실될 수 있음.

sudoku = cv.imread("sudoku.png", cv.IMREAD_GRAYSCALE)
to_pil(sudoku)

조명이 고르지 않은 스도쿠 원본

global_threshold, global_binary = cv.threshold(
    sudoku,
    0,    # Otsu가 자동으로 대체할 자리표시자
    255,  # 문턱값보다 밝은 픽셀에 넣을 값
    cv.THRESH_BINARY | cv.THRESH_OTSU,
)
global_threshold  # Otsu가 선택한 전역 문턱값
실행 결과
96.0

Otsu가 선택한 문턱값은 96.0. 위치별 조명 차이로 일부 숫자와 격자가 소실.

to_pil(global_binary)

하나의 전역 문턱값을 적용하여 일부가 사라진 결과

불균일한 조명에서 전역 문턱값이 실패하는 영역

적응형 문턱값 처리

적응형 문턱값 처리(adaptive thresholding)는 각 픽셀 주변 영역에서 지역 문턱값을 계산. 조명이 고르지 않은 문서나 사진에 유용.

block_size = 15  # 숫자 획 주변의 조명 변화를 반영할 이웃 크기
c = 5  # 완만하게 어두워진 배경도 흰색으로 유지할 보정값

adaptive_mean = cv.adaptiveThreshold(
    sudoku,
    255,  # 지역 문턱값보다 밝은 픽셀에 넣을 값
    cv.ADAPTIVE_THRESH_MEAN_C,
    cv.THRESH_BINARY,
    block_size,
    c,
)
to_pil(adaptive_mean)
  • block_size: 문턱값 계산에 사용할 이웃 영역 크기. 1보다 큰 홀수
  • c: 이웃 영역에서 계산한 값에서 뺄 상수
  • ADAPTIVE_THRESH_MEAN_C: 이웃 픽셀의 산술 평균

이웃 픽셀의 평균을 이용한 적응형 이진화

  • ADAPTIVE_THRESH_GAUSSIAN_C: 가까운 픽셀에 더 큰 가중치를 둔 평균
adaptive_gaussian = cv.adaptiveThreshold(
    sudoku,
    255,  # 지역 문턱값보다 밝은 픽셀에 넣을 값
    cv.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv.THRESH_BINARY,
    block_size,
    c,
)
to_pil(adaptive_gaussian)

가우시안 가중 평균을 이용한 적응형 이진화

block_size가 너무 작으면 작은 밝기 변화와 잡음에 민감. 너무 크면 지역 조명 차이를 충분히 반영하지 못할 수 있음. 이미지의 글자나 물체 크기에 맞춘 조정 필요.

퀴즈

문제 1 / 6맞음: 0힌트: 0틀림: 0채점중: 0남음: 6

이진 이미지에 관한 설명으로 맞는 것을 모두 고르세요.

  • 일반적으로 픽셀값 0과 255를 사용한다
  • 마스크도 이진 이미지의 한 종류이다
  • 전경과 배경처럼 두 영역을 표현하는 데 사용한다
  • 픽셀값은 반드시 0과 1만 사용해야 한다

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
크로마키 합성