大津算法(OTSU)
在圖像處理領(lǐng)域,我們會遇到如下需求:把圖像中的目標物體和背景分開。比如背景用白色表示,目標物體用黑色表示。此時我們知道目標物體的灰度值相互接近,背景灰度值相互接近,那么用大津算法能很好把目標從背景當中區(qū)分開來。
算法思想
目標
比如對于下面這張灰度圖片

目標物體是中間黑色的幾何物體,我們想讓這些物體和背景區(qū)分更明顯一些,比如讓物體為純黑,背景全白。那么我們就需要找到一個合適的閾值,使圖片上灰度值大于這個閾值的像素點為255(白色),灰度值小于閾值的像素點為0(黑色)。也就是變成下面這幅圖:

那么大津算法需要處理的就是找到最佳的閾值,讓目標和物體盡可能分離開。
灰度直方圖
為了找到合適的閾值,我們首先觀察原圖的灰度直方圖??:

這是用 Matlab 對原圖形成的灰度直方圖,灰度直方圖的含義是統(tǒng)計圖像中不同灰度值的分布情況。由圖我們可以看出兩個尖峰,在灰度值為0~20的地方存在一個尖峰,代表原圖中有大量像素點灰度值為0~20,經(jīng)觀察我們可以認為這部分對應(yīng)于目標物體。同理我們可以看出背景的灰度值大多集中于100~140之間,為了讓目標和背景區(qū)分更加明顯,我們想讓目標物體的灰度值全為0,背景的灰度值全為255,這種處理手法也稱為二值化法。
那么閾值取多少合適呢?從圖上看似乎取50~100中的任意一點都可以,但是實際情況并不想?yún)⒖紙D那樣明顯,有些圖片背景和目標物體較為接近,我們需要一個算法來找到最優(yōu)閾值才行。
聚類
首先我們思考什么樣的東西才能成為一類,而我們又是怎么分類的。對于參考圖來說,我們可以認為灰度值接近的為一類,灰度值不接近的不是同一類。那我們又是如何衡量灰度值接近的程度呢?這里面就需要用到方差的概念。
方差相比大家都了解,同一類的物體方差小,不同類的物體方差大。所以對于此圖我們希望分類的結(jié)果是對于灰度值相近的同一類物體,它的方差最小,稱為類內(nèi)方差最小?;叶戎挡唤咏牟煌愇矬w,它的方差大,稱為類間方差最大。
步驟
所以步驟總結(jié)如下:
首先我們要形成參考圖的灰度直方圖,這樣方便我們找到最佳閾值。
接下來我們通過窮舉每一個灰度值,計算以此為閾值的類內(nèi)和類間方差。
找到能形成類內(nèi)方差最小的或類間方差最大的閾值,這個就是我們要找的最佳閾值。
算法
下面以兩類分割講解下具體的算法,實際上大津算法可以分割多類出來。
因為簡書不支持顯示 MathJax 語法的公式,所以對這部分感興趣的直接移步到《大津算法(OTSU)》查看吧。
代碼實現(xiàn)
C語言實現(xiàn)
/* OTSU 算法
* *src 存儲灰度圖像,width 圖像寬,height 圖像長
* 返回最佳閾值
*/
int otsu(const int *src, int width, int height)
{
int histogram[256]; //存儲灰度直方圖,這里為256色灰度
int t,thred;
float wf,wb,ub,uf,curVal,maxVal;
int sumb=0,sumf=0,sumW=0,sumPixel=width*height;
wb=wf=maxVal=0.0f;
//求灰度直方圖
memset(histogram,0,sizeof(histogram));
for(i=0;i<width*height;i++)
{
histogram[src[i]]++;
}
for (i=0;i<256;i++)
sumW+=i*histogram[I];
//枚舉每個灰度
for(t=0;t<256;t++)
{
//求兩類類概率密度
wb+=histogram[t];
wf=sumPixel-wb;
if (wb==0||wf==0)
continue;
//求類均值
sumb+=i*histogram[t];
sumf=sumW-sumb;
ub=sumb/wb;
uf=sumf/wf;
//求當前類間方差
curVal=wb*wf*(ub-uf)*(ub-uf);
if(curVal>maxVal)
{
thred=t;
maxVal=curVal;
}
}
return thred;
}