养生 装修 购物 美食 感冒 便秘 营销 加盟 小吃 火锅 管理 创业 搭配 减肥 培训 旅游

图像分割:Otsu大津算法阈值选择

时间:2024-10-09 16:27:37

绪:大津法(OTSU)是一种确定图像分割阈值的算法,由日本学者大津于1979年提出;原理上来讲,该方法又称作最大类间方差法,有时也称之为大津算法;其按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大;其被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用。

图像分割:Otsu大津算法阈值选择

方法/步骤

1、Otsu算法原理:Otsu算法使用的是聚类的思想:把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小;通过方差的计算来寻找一个合适的灰度级别来划分;特点:它是按图像的灰度特性,将图像分成背景和前景两部分;因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小;因此,使类间方差最大的分割意味着错分概率最小。

图像分割:Otsu大津算法阈值选择

2、对图像I(x,y),前景和背景的分割阈值记作T,前景像素点数占整幅图像的比例为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度μ1;图像的总平均灰度记为μ=ω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)将式(5)代入式(6),得到等价公式:      g=ω0*ω1*(μ0-μ1)^2; (7) 这就是类间方差采用遍历的方法得到使类间方差g最大的阈值T,即为所求。【注】:由于Otsu算法是对图像的灰度级进行聚类,因此在执行Otsu算法之前,需要计算该图像的灰度直方图;

图像分割:Otsu大津算法阈值选择

3、matlab算法实现:matlab中函数graythresh()即使用大津法求得分割阈值T;用法如下:      T=graythresh(img);%求阈值      BW=im2bw(img,T);%二值化大津法的形象理解:对于直方图有两个峰值的图像,大津法求得的T近似等于两个峰值之间的低谷;      imhist(img);.........................T=graythresh(img);下图为图像的直方图,使大津法求得的T=0.5294,转换在[0,255]之间为134.9970,正好是两个峰值之间低谷的位置;

图像分割:Otsu大津算法阈值选择

4、OpenCV算法实现:根据大津法思路编写程序:代码一:#incl掼鸿乡羰ude<opencv2\opencv.hpp>#include惺绅寨瞀<opencv2\highgui\highgui.hpp>#include<opencv2\features2d\features2d.hpp>#include<opencv2\core\core.hpp>usingnamespacestd;usingnamespacecv;intOtsuThreshold(IplImage*frame);voidmain(){IplImage*raw_img=cvLoadImage("4-1.jpg",0);cvShowImage("0",raw_img);doublet=(double)getTickCount();intm_threshold=OtsuThreshold(raw_img);//返回该处代码执行所耗的时间,单位为秒t=((double)getTickCount()-t)/getTickFrequency();cout<<t*1000<<"ms"<<endl;for(intj=0;j<raw_img->width;j++)//lie{for(inti=0;i<raw_img->height;i++){unsignedchar*p_data=(unsignedchar*)raw_img->imageData+i*raw_img->widthStep;if(p_data[j]<m_threshold){p_data[j]=0;}elsep_data[j]=255;}}cvShowImage("1",raw_img);cvWaitKey(0);}intOtsuThreshold(IplImage*frame){intwidth=frame->width;//lieintheight=frame->height;//hangconstintGrayScale=256;intpixelCount[GrayScale];floatpixelPro[GrayScale];inti;for(i=0;i<GrayScale;i++)//数组初始化{pixelCount[i]=0;pixelPro[i]=0;}intj,pixelSum=width*height,threshold=0;unsignedchar*data=(unsignedchar*)frame->imageData;//指向像素数据的指针for(i=0;i<height;i++)//统计灰度级中每个像素在整幅图像中的个数{for(j=0;j<width;j++){pixelCount[(int)data[i*width+j]]++;//将像素值作为计数数组的下标}}//计算每个像素在整幅图像中的比例floatmaxPro=0.0;intkk=0;for(i=0;i<GrayScale;i++){pixelPro[i]=(float)pixelCount[i]/pixelSum;if(pixelPro[i]>maxPro){maxPro=pixelPro[i];//最大比例kk=i;}}//遍历灰度级[0,255]floatw0,w1,u0tmp,u1tmp,u0,u1,u,deltaTmp,deltaMax=0;for(i=0;i<GrayScale;i++)//遍历灰度等级,i为阈值{w0=w1=u0tmp=u1tmp=u0=u1=u=deltaTmp=0;for(j=0;j<GrayScale;j++){if(j<=i)//背景部分{w0+=pixelPro[j];//背景像素点占整个图像的比例u0tmp+=j*pixelPro[j];}else//前景部分{w1+=pixelPro[j];//前景像素点占整个图像的比例u1tmp+=j*pixelPro[j];}}u0=u0tmp/w0;//背景平均灰度μ0u1=u1tmp/w1;//前景平均灰度μ1u=u0tmp+u1tmp;deltaTmp=w0*pow((u0-u),2)+w1*pow((u1-u),2);//g=ω0(μ0-μ)^2+ω1(μ1-μ)^2;类间方差if(deltaTmp>deltaMax){deltaMax=deltaTmp;threshold=i;}}returnthreshold;}

图像分割:Otsu大津算法阈值选择

5、处理一张图片花费时间多,非常低效,但是是一种非常直接的,显而易见的实现方法;代码二:intotsu(IplImage*src){intWidth=src->width;intHeight=src->height;CvScalarcs=cvSum(src);//图像灰度值累加和doubleU_t=1.0*cs.val[0]/(Width*Height);//图像的总平均灰度值intthreshold=0;doubledelta=0;for(intk=0;k<256;k++)//灰度级{doublesumOfGray=0;intnum=0;for(inti=0;i<Width;i++)//lie{for(intj=0;j<Height;j++)//hang{intt=cvGet2D(src,j,i).val[0];if(t<k){num++;//统计灰度小于k的像素点的个数sumOfGray+=t;//灰度值小于k的像素点的灰度和}}}doublew0=1.0*num/(Width*Height);//灰度值小于阈值k的像素的概率doublew1=1.0-w0;//灰度值大于k像素的概率doubleu0=1.0*sumOfGray/num;//灰度值小于阈值k的像素的平均灰度值doubleu1=1.0*(cs.val[0]-sumOfGray)/(Width*Height-num);//灰度值大于阈值k的像素的平均灰度值doubledelta_tmp=w0*pow(u0-U_t,2)+w1*pow(u1-U_t,2);//类间方差if(delta_tmp>delta){delta=delta_tmp;threshold=k;}}cout<<cs.val[0]<<endl;cout<<U_t<<endl;returnthreshold;}

图像分割:Otsu大津算法阈值选择

6、代码三:借用opencv中的直方图函数,大大减少循环数;【注】:预先宏定凶及淄靥义:#definecvQueryHistValue_1D(hist,i颊俄岿髭dx0)((float)cvGetReal1D((hist)->bins,(idx0)))intotsu_1(IplImage*src){inthist_size=256;//直方图尺寸inthist_height=256;floatrange[]={0,255};//灰度级的范围float*ranges[]={range};//创建一维直方图,统计图像在[0255]像素的均匀分布CvHistogram*gray_hist=cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);//计算灰度图像的一维直方图cvCalcHist(&src,gray_hist,0,0);//归一化直方图cvNormalizeHist(gray_hist,1.0);intWidth=src->width;intHeight=src->height;intthreshold=0;doubledelta=0;CvScalarsumOfGray=cvSum(src);//图像灰度值累加和doubleU_t=sumOfGray.val[0]/(Width*Height);for(intk=0;k<256;k++)//灰度级数,阈值{doubleu0=0,u1=0,w0=0,w1=0,delta_tmp=0,u00=0,u11=0;for(inti=0;i<k;i++){u00+=cvQueryHistValue_1D(gray_hist,i)*i;//灰度小于阈值k的像素的平均灰度值w0+=cvQueryHistValue_1D(gray_hist,i);//灰度小于阈值k的像素的概率}u0=u00/w0;for(intj=k;j<256;j++){u11+=cvQueryHistValue_1D(gray_hist,j)*j;//灰度大于阈值k的像素的平均灰度值w1+=cvQueryHistValue_1D(gray_hist,j);//灰度大于阈值k的像素的概率}u1=u11/w1;delta_tmp=w0*pow(u0-U_t,2)+w1*pow(u1-U_t,2);//类间方差if(delta_tmp>delta){delta=delta_tmp;threshold=k;}}cvReleaseHist(&gray_hist);returnthreshold;}

图像分割:Otsu大津算法阈值选择

7、代码四:一次循环实现,其中的公式需要自己用笔推导一下,非常精妙!【注】:悴匙吭佰预先宏定义:#definecvQueryHistValue_1D烫喇霰嘴(hist,idx0)((float)cvGetReal1D((hist)->bins,(idx0)))intotsu_2(IplImage*src){inthist_size=256;//直方图尺寸inthist_height=256;floatrange[]={0,255};//灰度级的范围float*ranges[]={range};//创建一维直方图,统计图像在[0255]像素的均匀分布CvHistogram*gray_hist=cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);//计算灰度图像的一维直方图cvCalcHist(&src,gray_hist,0,0);//归一化直方图cvNormalizeHist(gray_hist,1.0);intWidth=src->width;intHeight=src->height;intthreshold=0;doubledelta=0;doubleU_t=0;for(intm=0;m<256;m++){U_t+=cvQueryHistValue_1D(gray_hist,m)*m;//总平均灰度}doubleu=0,w=0;for(intk=0;k<256;k++)//阈值{u+=cvQueryHistValue_1D(gray_hist,k)*k;//w+=cvQueryHistValue_1D(gray_hist,k);//灰度大于阈值k的像素的概率doublet=U_t*w-u;doubledelta_tmp=t*t/(w*(1-w));if(delta_tmp>delta){delta=delta_tmp;threshold=k;}}cvReleaseHist(&gray_hist);returnthreshold;}

图像分割:Otsu大津算法阈值选择

© 一点知识