1. 程式人生 > >影象處理演算法2——Otsu最佳閾值分割法

影象處理演算法2——Otsu最佳閾值分割法

    Otsu法是1979年由日本大津提出的。該方法在類間方差最大的情況下是最佳的,即統計鑑別分析中所用的度量。Otsu方法有一個重要的特性,就是它完全以在一幅影象的直方圖上執行計算為基礎,而直方圖是很容易得到的一維陣列。

    具體的公式推理及公式細節就不說了,詳見 Conzalez 那本書,我是第三版的,在P.479——P.482 上面。

給出具體步驟如下:

1、計算輸入影象的直方圖,並歸一化。

2、計算累積均值mu,以及全域性灰度均值。

3、計算被分到類1的概率q1,和被分到類2的概率q2。

4、用公式計算類間方差,sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2)

5、迴圈尋找類間方差最大值,並記下此時的閾值,即為最佳閾值。

6、利用最佳閾值進行影象閾值化。

關於otsu部分的程式程式碼如下:

[cpp] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. double getThreshVal_Otsu_8u( const Mat& _src )  
  2. {  
  3.     Size size = _src.size();  
  4.     constint N = 256;  
  5.     int i, j, h[N] = {0};  
  6.     unsigned char* src;  
  7.     //直方圖統計
  8.     for( i = 0; i < size.height; i++ )  
  9.     {  
  10.         src = _src.data + _src.step*i;  
  11.         j = 0;  
  12.         for(j = 0; j < size.width; j++ )  
  13.             h[src[j]]++;  
  14.     }  
  15.     //畫素平均值
  16.     double mu = 0, scale = 1./(size.width*size.height);  
  17.     for( i = 0; i < N; i++ )  
  18.     {  
  19.         mu += i*(double)h[i];//累加均值
  20.     }  
  21.     mu *= scale;//平均
  22.     double mu1 = 0, q1 = 0;//q1 ,q2 為類1和類2的概率累積和,mu1=mg*q1
  23.     double p_i, q2, mu2, sigma;  
  24.     double max_sigma = 0, max_val = 0;  
  25.     //迴圈求取最大閾值
  26.     for( i = 0; i < N; i++ )  
  27.     {  
  28.         p_i = h[i]*scale;//直方圖歸一化
  29.         mu1 *= q1;  
  30.         q1 += p_i;  
  31.         q2 = 1. - q1;  
  32.         mu1 = (mu1 + i*p_i)/q1;  
  33.         mu2 = (mu - q1*mu1)/q2;  
  34.         sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);//類間方差
  35.         if( sigma > max_sigma )  
  36.         {  
  37.             max_sigma = sigma;  
  38.             max_val = i;//記下使類間方差最大的閾值
  39.         }  
  40.     }  
  41.     return max_val;//返回閾值
  42. }  
save_snippets.png
double getThreshVal_Otsu_8u( const Mat& _src )
{
	Size size = _src.size();
	const int N = 256;
	int i, j, h[N] = {0};
	unsigned char* src;
	//直方圖統計
	for( i = 0; i < size.height; i++ )
	{
		src = _src.data + _src.step*i;
		j = 0;
		for(j = 0; j < size.width; j++ )
			h[src[j]]++;
	}
	//畫素平均值
	double mu = 0, scale = 1./(size.width*size.height);
	for( i = 0; i < N; i++ )
	{
		mu += i*(double)h[i];//累加均值
	}
	mu *= scale;//平均

	double mu1 = 0, q1 = 0;//q1 ,q2 為類1和類2的概率累積和,mu1=mg*q1
	double p_i, q2, mu2, sigma;
	double max_sigma = 0, max_val = 0;
	//迴圈求取最大閾值
	for( i = 0; i < N; i++ )
	{
		p_i = h[i]*scale;//直方圖歸一化
		mu1 *= q1;
		q1 += p_i;
		q2 = 1. - q1;
		mu1 = (mu1 + i*p_i)/q1;
		mu2 = (mu - q1*mu1)/q2;
		sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);//類間方差
		if( sigma > max_sigma )
		{
			max_sigma = sigma;
			max_val = i;//記下使類間方差最大的閾值
		}
	}
	return max_val;//返回閾值
}

注意,上面這部分傳遞的影象是 Mat 型別的,和 Iplimage 型別在影象引數部分寫法上有一點區別,注意區分開來

顯示結果如下: