大津算法——自動確定圖像二值化的閾值

在進行圖像二值化的操作中,我們往往要去去定一個閾值,像素值小於閾值的爲背景,像素值大於背景的爲前景。不同的閾值選擇會產生出截然不同的二值化圖像。因此,如何選取一個合適的閾值,是一個關鍵的問題。

大津算法,作爲一個自動確定二值化閾值的算法,是從類內方差和類間方差的比值計算得來,各種方差的定義如下:

類內方差:Sw2=w0 S02+w1 S12{S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2
類間方差:Sb2=w0 (M0Mt)2+w1 (M1Mt)2=w0 w1 (M0M1)2{S_b}^2 = w_0 \ (M_0 - M_t)^2 + w_1\ (M_1 - M_t)^2 = w_0\ w_1\ (M_0 - M_1) ^2
圖像所有像素的方差:St2=Sw2+Sb2=常數{S_t}^2 = {S_w}^2 + {S_b}^2 = \text{常數}

在上式中,我們令小於閾值tt的類記爲00,大於閾值tt的類記爲11
w0w_0w1w_1是閾值tt分開的兩個類中的像素數佔總像素數的比值(滿足w0+w1=1w_0+w_1=1);
S02{S_0}^2S12{S_1}^2是這兩個類中像素值的方差;
M0M_0M1M_1是這兩個類的像素值的平均值;

根據上面的式子,我們定義分離度XX:

X=Sb2Sw2=Sb2St2Sb2 X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2}

在上式中,我們希望最大化分離度XX,即最大化類間方差Sb2{S_b}^2,同時最小化類內方差Sw2{S_w}^2。但由於類間方差與類內方差之和爲常數,因此問題轉化爲最大化類間方差,也就是說:

argmaxt X=argmaxt Sb2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2

因此,如果使Sb2=w0 w1 (M0M1)2{S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2最大,就可以得到最好的二值化閾值tt

代碼實現

import cv2
import numpy as np

#將BGR圖像轉化爲灰度圖(opencv默認顏色通道爲BGR)
def RGB2GRAY(img):
    b = img[:, :, 0]
    g = img[:, :, 1]
    r = img[:, :, 2]

    out = 0.2126 * r + 0.7152 * g + 0.0722 * b
    out = out.astype(np.uint8)

    return out

#大津算法得到二值化圖像
def otsu(img):
    max_sigma = 0
    max_t = 0

    for t in range(1, 255):
        v0 = img[img < t]
        m0 = np.mean(v0) if len(v0) else 0.
        w0 = len(v0) / (H*W)
        v1 = img[img >= t]
        m1 = np.mean(v1) if len(v1) else 0.
        w1 = len(v1) / (H * W)
        sigma = w0 * w1 * ((m0 - m1) ** 2)
        if sigma > max_sigma:
            max_sigma = sigma
            max_t = t

    print('t = ', max_t)
    img[img < max_t] = 0
    img[img >= max_t] = 255
    return img


img = cv2.imread('../imori.jpg').astype(np.float)
gray = RGB2GRAY(img)
H, W = gray.shape

out = otsu(gray)

cv2.imwrite('4.jpg', out)
cv2.imshow('otsu', out)
cv2.waitKey()
cv2.destroyAllWindows()
發佈了38 篇原創文章 · 獲贊 87 · 訪問量 7300
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章