【Python/OpenCV】大津の手法で二値化処理

この記事では、Python版OpenCV(cv2.threshold)もしくはNumPyで大津の手法(判別分析法)を実装し、画像を二値化する方法をソースコード付きで解説します。

大津の手法

大津の手法(判別分析法)は、自動的に閾値を決定して二値化処理を行う手法の1つです。
この手法では、分離度が最も大きくなるときの閾値を求めます。(詳細は下記ページ参照)
【参考】大津の二値化処理の原理

今回は、Python言語で以下の2通りの方法で処理を実装してみました。

方法①・・・NumPyで大津のアルゴリズムを書いて実装
方法②・・・cv2.thresholdで簡単に実装(1行で書けちゃうので楽チン)

書式(cv2.threshold)

ret, dst = cv2.threshold(src, threshold, max_value, threshold_type)
パラメータ名 説明
src 入力画像(グレースケール)
threshold 閾値(大津の手法を使う場合は自動で設定してくれるので0)
max_value 二値化したときの最大値(真っ白にするなら255)
threshold_type 使用する二値化手法(大津ならcv2.THRESH_OTSUを指定)
dst 出力画像

ソースコード(Python3+OpenCV3)

サンプルプログラムのソースコードです。

方法①

#-*- coding:utf-8 -*-
import cv2
import numpy as np

# 大津の手法
def threshold_otsu(gray, min_value=0, max_value=255):

    # ヒストグラムの算出
    hist = [np.sum(gray == i) for i in range(256)]

    s_max = (0,-10)

    for th in range(256):
        
        # クラス1とクラス2の画素数を計算
        n1 = sum(hist[:th])
        n2 = sum(hist[th:])
        
        # クラス1とクラス2の画素値の平均を計算
        if n1 == 0 : mu1 = 0
        else : mu1 = sum([i * hist[i] for i in range(0,th)]) / n1   
        if n2 == 0 : mu2 = 0
        else : mu2 = sum([i * hist[i] for i in range(th, 256)]) / n2

        # クラス間分散の分子を計算
        s = n1 * n2 * (mu1 - mu2) ** 2

        # クラス間分散の分子が最大のとき、クラス間分散の分子と閾値を記録
        if s > s_max[1]:
            s_max = (th, s)
    
    # クラス間分散が最大のときの閾値を取得
    t = s_max[0]

    # 算出した閾値で二値化処理
    gray[gray=t] = max_value

    return gray



def main():
    # 入力画像の読み込み
    img = cv2.imread("input.jpg")

    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    # 方法1(NumPyで実装)
    th1 = threshold_otsu(gray)

    # 結果を出力
    cv2.imwrite("th1.jpg", th1)


if __name__ == "__main__":
    main()

方法①

#-*- coding:utf-8 -*-
import cv2
import numpy as np

def main():
    # 入力画像の読み込み
    img = cv2.imread("input.jpg")

    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    
    # 方法2 (OpenCVで実装)      
    ret, th2 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)    

    # 結果を出力
    cv2.imwrite("th2.jpg", th2)


if __name__ == "__main__":
    main()

※動作には、OpenCVライブラリのインストールが必要です。

【ポイント】
クラス間分散の分子が最大ならば、分離度も最大になります。
よって、計算高速化のためにクラス間分散の分子のみを計算して比較しています。

実行結果

サンプルプログラムの実行結果です。

■入力画像(左)と出力画像(右)

【おすすめ記事】
PythonでOpenCV入門 サンプル集
【Python】画像処理プログラミング入門
【画像処理入門】アルゴリズム&プログラミング

シェア&フォローお願いします!