OTSU(大津演算法)
阿新 • • 發佈:2018-12-09
在影象處理中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;
}