算法步驟
1、生成灰度直方圖,並進行歸一化,得到比例直方圖。
2、根據比例直方圖計算整幅圖像的平均灰度$\mu_0$。
3、從灰度0迭代到灰度255,每次迭代計算背景(這里將小於當前迭代灰度的部分視為背景)占整幅圖像的比例$\omega_1$;計算背景的平均灰度$\mu_1$;計算前景和背景的類間方差$\sigma^2 = \frac{\omega_1} { (1 - \omega_1)} · (\mu_1 - \mu_0)^2 $。
4、將最大類間方差對應的灰度設置為閾值,並進行二值化。
算法原理
為了表述的方便,這里先定義一些符號:
像素數占總圖像的比例 | 平均灰度 | 類間方差 | |
背景 | $\omega_1$ | $\mu_1$ | |
前景 | $\omega_2$ | $\mu_2$ | |
圖像 | $\omega_0$ | $\mu_0$ | $\sigma^2 $ |
類間方差指的是前景和背景之間的差異,顯然該差異越大,說明分離度越好。數學上,類間方差的計算方法是:
$\sigma^2 = \omega_1 · (\mu_1 - \mu_0)^2 + \omega_2 · (\mu_2 - \mu_0)^2$(式2-1)
但直接采用該公式進行編程,不免有些繁雜,因此需要先進行一定的化簡。將(式2-1)展開得:
$\sigma^2 = \omega_1 · \mu_1^2 + \omega_2 · \mu_2^2 - 2( \omega_1 · \mu_1 + \omega_2 · \mu_2)· \mu_0 + \mu_0^2 $(式2-2)
根據期望的數學定義式$E(X)=\sum_{k=1 }^{\infty}x_k·p_k$我們可以推知:
$\mu_0 = \omega_1 · \mu_1 + \omega_2 · \mu_2 $(式2-3)
將(式2-3)代入到(式2-2)中,則$\sigma^2 = \omega_1 · \mu_1^2 + \omega_2 · \mu_2^2 - \mu_0^2 $。然后我們希望可以把前景的成分消除掉,因此再次利用(式2-3)和$\omega_2 = 1 - \omega_1$的關系進行替換:
$\sigma^2 = \omega_1 · \mu_1^2 + \frac{\omega_2^2 · \mu_2^2}{1 - \omega_1} - \mu_0^2 = \omega_1 · \mu_1^2 + \frac{(\mu_0 - \omega_1 · \mu_1)^2}{1 - \omega_1} - \mu_0^2 = \frac{\omega_1} { (1 - \omega_1)} · (\mu_1 - \mu_0)^2$(式2-4)
使用(式2-4),我們就只需要統計當前迭代灰度以前的像素即可,這大大提升了程序的效率。此外,類間方差的最大點往往處於直方圖的低谷處(如下圖所示,紅色點代表方差值),因此可以比較好地自動進行前景和背景分離。
算法復現
復現需要注意的一點是背景平均灰度的計算,用公式可以表示為:
$\mu_1 = \frac{ \sum_{i=0}^{T} i · p_i}{ \sum_{i=0}^{T} p_i}$
其中$i$表示迭代灰度,$p_i$表示$i$灰度級的比例,$T$為當前迭代灰度值。
unsigned char Ostu(int size,unsigned char *image) { int i; unsigned char threshold=0; //閾值 float variance=0; //類間方差 float maxvariance=0; //最大方差 float k=0; float w1=0; //背景像素比例 float u1=0; //背景平均灰度 float u0=0; //圖像的平均灰度 float histogram[256]={0}; //直方圖 for (i=0;i<size;i++){ histogram[*(image+i)]++; //像素直方圖 } for (i=0;i<256;i++){ histogram[i]/=size; //比例直方圖 u0+=histogram[i]*i; //計算圖像平均灰度 } for (i=0;i<256;i++){ w1+=histogram[i]; //背景比例 k+=i*histogram[i]; u1=k/w1; //背景平均灰度 variance=w1/(1-w1)*(u1-u0)*(u1-u0); //求方差 if (variance>maxvariance){ maxvariance=variance; threshold=i; //將最大g相應的i值作為圖像的全局閾值 } } return threshold; }