1. 程式人生 > >otsu C++閾值分割

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 計算出的效果會更好一些。當然這個 α 值就要全憑經驗來定了