otsu C++閾值分割
簡單的說,這種演算法假設一副影象由前景色和背景色組成,通過統計學的方法來選取一個閾值,使得這個閾值可以將前景色和背景色儘可能的分開。 或者更準確的說是在某種判據下最優。與數理統計領域的 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 計算出的效果會更好一些。當然這個 α 值就要全憑經驗來定了