簡單的說,這種算法假設一副圖像由前景色和背景色組成,通過統計學的方法來選取一個門檻值,使得這個門檻值可以将前景色和背景色盡可能的分開。
或者更準确的說是在某種判據下最優。與數理統計領域的 fisher 線性判别算法其實是等價的。
otsu算法中這個判據就是最大類間方差 (intra-class variance or the variance within the class)。下面就來詳細說說什麼是 intra-class variance。
我們知道一副灰階圖像,可以計算它的顔色平均值,或者更進一步。可以計算出灰階直方圖。
這裡給出一個示例圖檔:
這個圖檔拍攝的是一個條形碼。在這個圖中,前景色就是黑色的條形碼,背景色是其餘部分的灰色。那麼我們可以計算出這個圖像的灰階直方圖。
圖中那個大的峰是背景色的部分,小的峰是前景色。灰階值的均值是 122. 我們稱這個均值為 M。
現在任意選取一個灰階值 t,則可以将這個直方圖分成前後兩部分。我們稱這兩部分分别為 A 和 B。對應的就是前景色和背景色。這兩部分各自的平均值成為 MA 和 MB。
A 部分裡的像素數占總像素數的比例記作 PA,B部分裡的像素數占總像素數的比例記作 PB。
Nobuyuki Otsu 給出的類間方差定義為:
那麼這個最佳的門檻值t 就是使得 ICV 最大的那個值。
對于上面的測試圖像,我們可以周遊 t 的各種取值,計算 ICV。之後可以畫出這樣的ICV 曲線(綠色線條):
可以看出,ICV 取最值的點确實将前景色和背景色分開了。
以上理論部分參考了這篇部落格http://blog.csdn.net/liyuanbhu/article/details/49387483
下面是C++結合OpenCV2.x實作的代碼:
int otsu(Mat image)
{
int width = image.cols;
int height = image.rows;
int x = 0, y = 0;
int pixelCount[256];
float pixelPro[256];
int i, j, pixelSum = width * height, threshold = 0;
uchar* data = (uchar*)image.data;
//初始化
for (i = 0; i < 256; i++)
{
pixelCount[i] = 0;
pixelPro[i] = 0;
}
//統計灰階級中每個像素在整幅圖像中的個數
for (i = y; i < height; i++)
{
for (j = x; j<width;i++)
{
pixelCount[data[i * image.step+ j]]++;
}
}
//計算每個像素在整幅圖像中的比例
for (i = 0; i < 256; i++)
{
pixelPro[i] = (float)(pixelCount[i]) / (float)(pixelSum);
}
//經典ostu算法,得到前景和背景的分割
//周遊灰階級[0,255],計算出方差最大的灰階值,為最佳門檻值
float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
for (i = 0; i < 256; i++)
{
w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;
for (j = 0; j < 256; j++)
{
if (j <= i) //背景部分
{
//以i為門檻值分類,第一類總的機率
w0 += pixelPro[j];
u0tmp += j * pixelPro[j];
}
else //前景部分
{
//以i為門檻值分類,第二類總的機率
w1 += pixelPro[j];
u1tmp += j * pixelPro[j];
}
}
u0 = u0tmp / w0; //第一類的平均灰階
u1 = u1tmp / w1; //第二類的平均灰階
u = u0tmp + u1tmp; //整幅圖像的平均灰階
//計算類間方差
deltaTmp = w0 * (u0 - u)*(u0 - u) + w1 * (u1 - u)*(u1 - u);
//找出最大類間方差以及對應的門檻值
if (deltaTmp > deltaMax)
{
deltaMax = deltaTmp;
threshold = i;
}
}
//傳回最佳門檻值;
return threshold;
}
利用這個方法計算出的門檻值做了二值化後得到圖像如下:
可以看到效果很好。
Otsu 方法也不是萬能的。當目标與背景的大小比例懸殊時,類間方差準則函數可能呈現雙峰或多峰,此時效果不好。這時就要考慮其他的辦法了。
其實,我們可以仔細觀察 ICV 的計算公式。
這裡面 PA 和 PB 相當于是個前景色和背景色部分做個權重。目前景色或背景色有一個區域很小時。比如 PA 非常的小。那麼這時計算出來的 t 就會和 B 區域很接近,這時的分割效果就會比較差。我們可以對ICV的公式進行一點小小的改造。
這裡的 α 可以取一個 0-1之間的值。比如上面的例子圖檔,如果我們取 α=0.8 計算出的效果會更好一些。當然這個 α 值就要全憑經驗來定了