Otsu方法是一种全局化的动态二值化方法,又叫大津法,是一种灰度图像二值化的常用算法。该算法的基本思想是:设使用某一个阈值将灰度图像根据灰度大小,分成目标部分和背景部分两类,在这两类的类内方差最小和类间方差最大的时候,得到的阈值是最优的二值化阈值。
我个人对这个算法实践后的结果是:这个算法在光照均匀的时候,可以得到很好的效果,大多数情况下,都可以的到相当不错的效果。而且其本质是很好理解的。说通俗一点的比方,用一个分数线将班上所有学生的成绩分为好学生和差学生两类,要使两类学生的区分看起来最明显,很显然要达到的效果是:好学生和差学生之间要区别最大,同时好学生和好学生之间分数不能拉太大,同时差学生和差学生之间也差距不大。
回到图像的问题上来,对一幅NxM个像素的图像来说。
1.首先计算图像的总体灰度u,计算如下:
统计得到全部图像中灰度为i对应的像素个数n(i),于是
u=1*n(1)/N*M+2*n(2)/N*M+......+i*n(i)/N*M;
2.记t为目标与背景的分割阈值,在该阈值下,得到目标像素(灰度大于t)占图像的比例w1,目标像素的平均灰度为u1,这两个参数计算如下:
遍历所有像素,得到灰度大于t的像素总数W1,于是
w1=W1/N*M;
统计得到目标像素中灰度为i(i>t)对应的像素个数n(i),于是
u1=1*n(1)/W1+2*n(2)/W1+......+i*n(i)/W1;(i>t)
同理,得到背景像素占图像的比例w2,背景像素的平均灰度u2。
3.遍历步骤2中的t,当t使得
G=w1*(u1-u)*(u1-u)+w2*(u2-u)*(u2-u);
G最大时,即得到了最佳阈值,与上式子等价的还有
G=(u1-u)*(u1-u)*(u2-u)*(u2-u);最大
两者的等价关系很容易证明。
以下是我在一个工程中使用该算法的程序片段,实验性代码,不是很规范,仅供参考。
//这是一个车牌的图像,图像范围为PlateTop,PlateBottom,PlateLeft,PlateRight范围内的小片区域。
//获取灰度直方图,八级灰度,存在数组GreyHisto[256]中
memset(GreyHisto,0,256*sizeof(unsigned int));
for (i=PlateTop;i<PlateBottom+1;i++){
for (j=PlateLeft;j<PlateRight+1;j++){
GreyHisto[PreProcessedImage[i*720+j]]++;
}
}
//找出灰度范围,最小灰度为GreyA,最大灰度为GreyB.
for (i=0;i<256;i++){
if (GreyHisto[i]>0){
GreyA=i;break;}
}
for (i=255;i>0;i--){
if (GreyHisto[i]>0){
GreyB=i;break;}
}
//求平均灰度
temp0=0;
for (i=GreyA;i<GreyB+1;i++){
temp0=temp0+i*GreyHisto[i];
}
PlatePixSum=(PlateRight-PlateLeft+1)*(PlateBottom-PlateTop+1);
Ave_u=temp0/PlatePixSum;//总平均灰度u
for (t=GreyA;t<GreyB+1;t++){
temp_u0=0;
temp_uu0=0;
for (i=GreyA;i<t+1;i++)
temp_u0=temp_u0+GreyHisto[i];//目标比例w0 (如果要使用严格意义上的比例,还需要除以总像素,下同)
temp_u1=PlatePixSum-temp_u0;//背景比例w1 (这里为避免除法运算,不做比例,只做整数比)
for (i=GreyA;i<t+1;i++)
temp_uu0=temp_uu0+i*GreyHisto[i];
temp_uu1=temp0-temp_uu0;
Ave_u0=temp_uu0/temp_u0;//目标平均灰度u0
Ave_u1=temp_uu1/temp_u1;//背景平均灰度u1
otsu_G[t]=(Ave_u0-Ave_u)*(Ave_u0-Ave_u)*(Ave_u1-Ave_u)*(Ave_u1-Ave_u);
// otsu_G[t]=temp_u0*(Ave_u0-Ave_u)*(Ave_u0-Ave_u)+temp_u1*(Ave_u1-Ave_u)*(Ave_u1-Ave_u);
}
maxG=0;
for (i=GreyA;i<GreyB+1;i++){
if (otsu_G[i]>maxG){
maxG=otsu_G[i]; Threshold=i;}
}
最后的到的Threshold即为最佳阈值。