2024年5月30日发(作者:)

图像二值化阈值选取常用方法

最近在公司搞车牌识别的项目,车牌定位后,发现对车牌区域二值化的好坏直接影响后面字

符切分的过程,所以就想把常用阈值选取方法做一个总结。

图像二值化阈值选取常用方法:

1.双峰法。

2.P参数法。

3.最大类间方差法(Otsu、大津法)。

4.最大熵阈值法。

5.迭代法(最佳阈值法)。

1.双峰法

在一些简单的图像中,物体的灰度分布比较有规律,背景与目标在图像的直方图各自形成一

个波峰,即区域与波峰一一对应,每两个波峰之间形成一个波谷。那么,选择双峰之间的波

谷所代表的灰度值T作为阈值,即可实现两个区域的分割。如图1所示。

2.P参数法

当目标与背景的直方图分布有一定重叠时,两个波峰之间的波谷很不明显。若采用双峰法,

效果很差。如果预先知道目标占整个图像的比例P,可以采用P参数法。

P参数法具体步骤如下:假设预先知道目标占整个图像的比例为P,且目标偏暗,背景偏亮。

1)、计算图像的直方图分布P(t),t=0,1,.....255。

2)、计算阈值T,使其满足

p(t)

t

0

T

m*n

P

最小。

P参数法一般用于固定分辨率下,目标所占整个图像比例已知的情况。

3.最大类间方差法(Otsu)

最大类间方差法是由Otsu于1979年提出的,是基于整幅图像的统计特性实现阈值的自动

选取的,是全局二值化最杰出的代表。

Otsu算法的基本思想是用某一假定的灰度值t将图像的灰度分成两组,当两组的类间方差

最大时,此灰度值t就是图像二值化的最佳阈值。

设图像有L个灰度值,取值范围在0~L-1,在此范围内选取灰度值T,将图像分成两组G0

和G1,G0包含的像素的灰度值在0~T,G1的灰度值在T+1~L-1,用N表示图像像素总

数,

n

i

表示灰度值为i的像素的个数。

已知:每一个灰度值i出现的概率为

p

i

n

i

/

N

;假设G0和G1两组像素的个数在整体图

像中所占百分比为

0

1

,两组平均灰度值为

0

1

,可得

概率:

0

=

p

i

i

0

T

1

i

T

1

L

1

p

i

1

0

平均灰度值:

0

ip

i

0

T

i

1

i

T

1

L

1

ip

i

图像总的平均灰度值:

0

0

1

1

类间方差:

g(t)

0

0

1

1

0

1

0

1

222

最佳阈值为:T=argmax(g(t))使得间类方差最大时所对应的t值。

算法可这样理解:阈值T将整幅图像分成前景和背景两部分,当两类的类间方差最大时,

此时前景和背景的差别最大,二值化效果最好。因为方差是灰度分布均匀性的一种度量,方

差值越大,说明构成图像的两部分差别越大,当部分目标错分为背景或部分背景错分为目标

都会导致两部分差别变小,因此使类间方差最大的分割阈值意味着错分概率最小。

大律法得到了广泛的应用,但是当物体目标与背景灰度差不明显时,会出现无法忍受的大块

黑色区域,甚至会丢失整幅图像的信息。

以下为一段基于OpenCV的Otsu代码:

BYTEGetPixel(IplImage*img,intx,inty)

{

if(x<0||x>=img->width||y<0||y>=img->height)

return0;

returnimg->imageData[y*img->widthStep+x];

}

voidSetPixel(IplImage*img,intx,inty,BYTEval)

{

if(x<0||x>=img->width||y<0||y>=img->height)

return;

img->imageData[y*img->widthStep+x]=val;

}

#ifndefEPSILON

#defineEPSILON1e-6

#endif

/*

*Function:用Otsu(1979)阈值对图像二值化

*Parameters:

*[IN]img:要二值化的灰度图像

*[OUT]out:二值化的输出图像,要求调用者分配好空间

*Return:(None)

*/

voidOtsu(IplImage*img,IplImage*out)

{

inthist[256],i,j,total,maxk;

floatut,uk;/*ut表示整个图像的均值*/

floatp[256],sigma,wk,maxs;

/*得到灰度直方图*/

memset(hist,0,sizeof(int)*256);

total=img->width*img->height;

for(i=0;iheight;i++)

{

for(j=0;jwidth;j++)

{

hist[GetPixel(img,j,i)]++;

}

}

/*计算大津阈值*/

ut=0;

for(i=0;i<256;i++)

{

p[i]=(float)hist[i]/total;

ut+=i*hist[i];

}

ut/=total;

wk=.0;

uk=.0;

maxs=.0;

for(i=0;i<256;i++)

{

uk+=i*p[i];

wk+=p[i];

if(wk<=EPSILON||wk>=1.0f-EPSILON)

{

continue;

}

sigma=(ut*wk-uk);

sigma=sigma*sigma/(wk*(1.0f-wk));

if(sigma>maxs)

{

maxs=sigma;

maxk=i;

}

}

/*得到了阈值,对原图像分割*/

for(i=0;iheight;i++)

{

for(j=0;jwidth;j++)

{

if(GetPixel(img,j,i)<=maxk)

{

SetPixel(out,j,i,0);

}

else

{

SetPixel(out,j,i,255);

}

}

}

}

4、最大熵阈值法

将信息论中的shannon熵概念用于图像分割,其依据是使得图像中目标与背景分布的信息量

最大,即通过测量图像灰度直方图的熵,找出最佳阈值。

根据shannon熵的概念,对于灰度范围为0,1,2,…,L-1的图像,其直方图的熵定义为(仅仅

是定义):

H

ip

i

,p

i

为像素值为i的像素占整个图像的概率。

i

0

设阈值t将图像划分为目标O和背景B两类,他们的概率分布分别为:

L

1

O区:

p

i

,i

0,1,...,t

,其中

p

t

p

t

p

i

,i

t

1,...,L

1

1

p

t

t

p

i

i

0

B区:

则目标O和背景B的熵函数分别为:

H

O

t



i

0

L

1

t

p

i

pH

p

i

lnp

i

ln

i

lnP

t

t

,其中

H

t



P

t

P

t

P

t

i

0

t

L

1

p

i

p

i

H

H

t

,其中

H



p

i

lnp

i

H

B

t



ln

ln

1

P

t

1

P1

P1

P

i

0

i

t

1

ttt

图像的总熵为:

H

t

H

O

t

H

B

t

ln

P

t

1

P

t

H

t

H

H

t

P

t

1

P

t

最佳阈值T为使得图像的总熵取得最大值:T=argmax(H(t))

此方法不需要先验知识,而且对于非理想双峰直方图的图像也可以进行较好的分割。缺点是

运算速度较慢不适合实时处理。仅仅考虑了像素点的灰度信息,没有考虑到像素点的空间信

息,所以当图像的信噪比较低时分割效果不理想。

以下为一段基于Opencv最大熵阈值算法例子:

#include"stdafx.h"

#include"cv.h"

#include"highgui.h"

#pragmacomment(lib,"")

#pragmacomment(lib,"")

#pragmacomment(lib,"")

#pragmacomment(lib,"")

intHistogramBins=256;

floatHistogramRange1[2]={0,255};

float*HistogramRange[1]={&HistogramRange1[0]};

typedefenum{back,object}entropy_state;

doublecaculateCurrentEntropy(CvHistogram*Histogram1,

intcur_threshold,entropy_statestate)

{

intstart,end;

if(state==back)

{

start=0;end=cur_threshold;

}

else

{

start=cur_threshold;end=256;

}

inttotal=0;

for(inti=start;i

{

total+=(int)cvQueryHistValue_1D(Histogram1,i);

}

doublecur_entropy=0.0;

for(inti=start;i

{

if((int)cvQueryHistValue_1D(Histogram1,i)==0)

{

continue;

}

doublepercentage=cvQueryHistValue_1D(Histogram1,i)/total;

cur_entropy+=-percentage*logf(percentage);

}

returncur_entropy;

}

voidMaxEntropy(IplImage*src,IplImage*dst)

{

assert(src!=NULL);

assert(src->depth==8&&dst->depth==8);

assert(src->nChannels==1);

CvHistogram*hist

cvCreateHist(1,&HistogramBins,CV_HIST_ARRAY,HistogramRange);

cvCalcHist(&src,hist);

doublemaxentropy=-1.0;

intmax_index=-1;

for(inti=0;i

{

doublecur_entropy=caculateCurrentEntropy(hist,i,object)+

caculateCurrentEntropy(hist,i,back);

if(cur_entropy>maxentropy)

{

maxentropy=cur_entropy;

max_index=i;

}

}

cvThreshold(src,dst,(double)max_index,255,CV_THRESH_BINARY);

=

cvReleaseHist(&hist);

}

intmain(intargc,_TCHAR*argv[])

{

IplImage*src;//声明IplImage指针

//载入图像

if(argc==2&&

(src=cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE))!=0)

{

cvNamedWindow("Image",1);//创建窗口

IplImage*dst=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);

MaxEntropy(src,dst);

cvShowImage("Image",dst);//显示图像

cvWaitKey(0);//等待按键

cvDestroyWindow("Image");//销毁窗口

cvReleaseImage(&src);//释放图像

cvReleaseImage(&dst);//释放图像

return0;

}

return-1;

}

5、迭代法(最佳阈值法)

迭代法是基于逼近的思想,迭代阈值的获取步骤可以归纳如下:

1)选择一个初始阈值T(j),通常可以选择整体图像的平均灰度值作为初始阈值。j为迭代次

数,初始时j=0。

2)用T(j)分割图像,将图像分为2个区域

C

1

C

2

3)计算两区域的平均灰度值,其中

N

1

N

2

为第j次迭代时区域C1和C2的像素点个数,

f(x,y)表示图像中(x,y)点的灰度值。

j



j

j



j

1

j

1

j

N

1

j

f

x,y

C

1



f

x,y

j

2

1

j

N

2

j

f

x,y

C

2

f

x,y

4)再计算新的门限值,即

T

j

1

j

1

j

2

2

5)令j=j+1,重复2~4),直到T(j+1)与T(j)的差小于规定值或j达到最大的迭代次数。

以下为基于OpenCV的代码:

/*======================================================================*/

/*迭代法*/

/*======================================================================*/

//nMaxIter:最大迭代次数;nDiffRec:使用给定阀值确定的亮区与暗区平均灰度差

异值

intDetectThreshold(IplImage*img,intnMaxIter,int&iDiffRec)//阀值分割:

迭代法

{

//图像信息

intheight=img->height;

intwidth=img->width;

intstep=img->widthStep/sizeof(uchar);

uchar*data=(uchar*)img->imageData;

iDiffRec=0;

intF[256]={0};//直方图数组

intiTotalGray=0;//灰度值和

intiTotalPixel=0;//像素数和

bytebt;//某点的像素值

uchariThrehold,iNewThrehold;//阀值、新阀值

uchariMaxGrayValue=0,iMinGrayValue=255;//原图像中的最大灰度值和最小灰度

uchariMeanGrayValue1,iMeanGrayValue2;

//获取(i,j)的值,存于直方图数组F

for(inti=0;i

{

for(intj=0;j

{

bt=data[i*step+j];

if(bt

{

iMinGrayValue=bt;

}

if(bt>iMaxGrayValue)

{

iMaxGrayValue=bt;

}

F[bt]++;

}

}

iThrehold=0;//

iNewThrehold=(iMinGrayValue+iMaxGrayValue)/2;//初始阀值

iDiffRec=iMaxGrayValue-iMinGrayValue;

for(inta=0;(abs(iThrehold-iNewThrehold)>0.5)&&a

条件

{

iThrehold=iNewThrehold;

//小于当前阀值部分的平均灰度值

for(inti=iMinGrayValue;i

{

iTotalGray+=F[i]*i;//F[]存储图像信息

iTotalPixel+=F[i];

}

iMeanGrayValue1=(uchar)(iTotalGray/iTotalPixel);

//大于当前阀值部分的平均灰度值

iTotalPixel=0;

iTotalGray=0;

for(intj=iThrehold+1;j

{

iTotalGray+=F[j]*j;//F[]存储图像信息

iTotalPixel+=F[j];

}

iMeanGrayValue2=(uchar)(iTotalGray/iTotalPixel);

iNewThrehold=(iMeanGrayValue2+iMeanGrayValue1)/2;

iDiffRec=abs(iMeanGrayValue2-iMeanGrayValue1);

//新阀值

}

//cout<<"TheThresholdofthisImageinimgIterationis:"<

returniThrehold;

}

另外还有两种方法:

一、均匀性度量算法

该算法步骤如下:

1)给定一个初始阈值Th=Th0(例如:可以默认为1,或者是128等),则将原图分为C1

和C2两类。

2)分别计算两类的类内方差:

3)分别计算两类像素在图像中的分布概率:

计算分布概率的目的是:统计该类像素对图像的影响程度。

4)选择最佳阈值Th=Th*,使得下式成立:

二、聚类的方法

1)给定一个初始阈值Th=Th0(例如:可以默认为1,或者是128等),则将原图分为C1

和C2两类;

2)分别计算两类的类内均值与方差:

3)进行重新分类处理。如果

则f(x,y)属于C1,否则f(x,y)属于C2。

4)对上一步重新分类后得到的C1和C2中的所有像素,分别重新计算其各自的均值与方差。

5)如果下式成立:

则输出计算得到的阈值Th(t),否则重复4),5)。

本文博客地址:/xgmiao