1. 程式人生 > >OTSU(大津演算法)

OTSU(大津演算法)

在影象處理中Otsu方法,是以 Nobuyuki otsu 的名字命名的(日本人,大津展之),常用於基於影象分割的聚類。該演算法的理論依據是:假定影象包含兩類畫素(前景畫素和背景畫素),直方圖為雙峰直方圖,然後計算使得兩類畫素能分開的最佳閾值(類內方差),或等價的間類間方差最大。

Otsu演算法原理:

對於影象 I(x,y),前景(即目標)和背景的分割閾值記作 T,屬於前景的畫素點數佔整幅影象的比例記為 ω0,平均灰度為 μ0;背景畫素點數佔整幅影象的比例為 ω1,平均灰度為 μ1;整幅影象的平均灰度記為μ,類間方差記為g。 假設影象大小為M×N,影象中畫素的灰度值小於閾值 T 的畫素個數為 N0,畫素灰度大於閾值T的畫素個數為 N1,那麼:

      ω0=N0/ M×N (1)       ω1=N1/ M×N (2)       N0+N1=M×N (3)       ω0+ω1=1    (4)       μ=ω0*μ0+ω1*μ1 (5)       g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)       g=ω0ω1(μ0-μ1)^2    (7)

採用遍歷的方法使得類間方差g最大的閾值T,即為所求。Ostu方法可以形象地理解為:求取直方圖有兩個峰值的影象中那兩個峰值之間的低谷值 T 。

Otsu演算法實現:

    getThreshVal_Otsu_8u( const Mat& _src )  
    {  
        Size size = _src.size();  
        if( _src.isContinuous() )  
        {  
            size.width *= size.height;  
            size.height = 1;  
        }  
        const int N = 256;  
        int i, j, h[N] = {0};  
        for
( i = 0; i < size.height; i++ ) { const uchar* src = _src.data + _src.step*i; j = 0; #if CV_ENABLE_UNROLLED for( ; j <= size.width - 4; j += 4 ) { int v0 = src[j], v1 = src[j+1]; h[v0]++; h[v1]++; v0 = src[j+2]; v1 = src[j+3]; h[v0]++; h[v1]++; } #endif for( ; 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; double max_sigma = 0, max_val = 0; for( i = 0; i < N; i++ ) { double p_i, q2, mu2, sigma; p_i = h[i]*scale; mu1 *= q1; q1 += p_i; q2 = 1. - q1; if( std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. - FLT_EPSILON ) continue; 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; }