Python에 의한 화상 처리 100개 노크 #4 오쓰의 2치화(판별 분석법)

소개



아무래도, 라무입니다.
2치화에 이용하는 역치를 자동으로 결정하는 수법인 오쓰의 2치화(판별 분석법)를 실장합니다.

4번째:오쓰의 2치화



이진화는 이미지를 흑백 2색만 흑백 이미지로 변환하는 과정입니다. 또한, 임계값을 정해두고, 임계값 미만의 화소값은 흰색, 임계값 이상의 화소값을 가지는 화소는 검정으로 치환합니다. 여기까지는, 전회의 2치화로 설명했습니다.
이번에는 이 임계값을 자동으로 결정하는 기법을 취급합니다.

오쓰의 2치화에서는 임계치에 의해 클래스를 2개로 분할합니다.
이 2개의 클래스에 있어서 분리도가 최대가 될 때의 임계치를 2진화시의 임계치로 합니다.
분리도를 계산하는 데 필요한 파라미터는 다음 방정식으로 계산할 수 있습니다.

분리도: $X =\dfrac{\sigma _{b}^{2}}{\sigma _{w}^{2}}$

클래스 내 분산: $\sigma _{b}^{2} =\dfrac{\omega _{0}\omega _{1}}{(\omega _{0}+\omega _{1}) ^2 } (M _{0}+M _{1}) ^2$

클래스 간 분산: $\sigma _{b}^{2} =\omega _{0}\sigma _{0}^{2}+\omega _{1}\sigma _{1}^{2}$

클래스 0,1에 속하는 픽셀 수: $\omega _0,\omega _1$

클래스 0,1에 속하는 픽셀 값의 분산: $\sigma _0,\sigma _1$

클래스 0,1에 속하는 픽셀 값의 평균: $M_0,M_1$

전체 이미지의 픽셀 값 평균: $M$

클래스 0,1에 속하는 픽셀 값의 합계: $P_0,P_1$

정리하면 임계값이 0에서 255인 경우 분리도를 256회 계산하여 분리도가 최대가 되는 임계값을 구하면 됩니다.

otsuBinarization.py
import numpy as np
import cv2
import matplotlib.pyplot as plt
# from statistics import variance
import statistics as st
plt.gray()


def otsuBinarization(img):
  # 画像コピー
  dst = img.copy()
  # グレースケール化
  gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)

  w,h = gray.shape
  Max = 0

  # 画像全体の画素値平均
  M = np.mean(gray)

  # 閾値256値全てに適用
  for th in range(256):
    # クラス分け
    g0,g1 = gray[gray<th],gray[gray>=th]

    # 画素数
    w0,w1 = len(g0),len(g1)
    # 画素値分散
    s0_2,s1_2 = g0.var(),g1.var()
    # 画素値平均
    m0,m1 = g0.mean(),g1.mean()
    # 画素値合計
    p0,p1 = g0.sum(),g1.sum()

    # クラス内分散
    sw_2 = w0*s0_2 + w1*s1_2
    # クラス間分散
    sb_2 = ((w0*w1) / ((w0+w1)*(w0+w1))) * ((m0-m1)*(m0-m1))
    # 分離度
    if (sb_2 != 0):
      X = sb_2 / sw_2
    else:
      X = 0

    if (Max < X):
      Max = X
      t = th

  # 二値化
  idx = np.where(gray < t)
  gray[idx] = 0
  idx = np.where(gray >= t)
  gray[idx] = 255

  return gray


# 画像読込
img = cv2.imread('image.jpg')

# 大津の二値化
mono = otsuBinarization(img)

# 画像保存
cv2.imwrite('result.jpg', mono)
# 画像表示
plt.imshow(mono)
plt.show()



이미지 왼쪽은 입력 이미지, 이미지 중간은 수동으로 임계값을 128로 설정했을 때의 출력 이미지, 이미지 오른쪽은 이번 출력 이미지입니다.
자동으로 역치를 결정해 2치화해도 그다지 위화감이 없는 화상이 출력되고 있군요.
여담입니다만, 내 실장에서는 화상 전체의 화소치 평균 M은 사용하지 않네요.

결론



만약, 질문이 있는 분이 계시면 부담없이 부디.
imori_imori씨의 Github 에 공식의 해답이 실려 있으므로 꼭 그쪽도 확인해 보세요.
그런 다음 파이썬은 초보자이므로 잘못된 곳이 있더라도 부드럽게 지켜보고 댓글을 달아주세요.

좋은 웹페이지 즐겨찾기