opencv函数系列--自适应阈值

来源:互联网 发布:潍坊行知学校贴吧 编辑:程序博客网 时间:2024/05/17 17:44

在图像处理中较为常用的二值化方法有:1)全局固定阈值;2)局部自适应阈值;3)OTSU等。

全局固定阈值很容易理解,就是对整幅图像都是用一个统一的阈值来进行二值化。

局部自适应阈值则是根据像素的邻域块的像素值分布来确定该像素位置上的二值化阈值。这样做的好处在于每个像素位置处的二值化阈值不是固定不变的,而是由其周围邻域像素的分布来决定的。亮度较高的图像区域的二值化阈值通常会较高,而亮度较低的图像区域的二值化阈值则会相适应地变小。不同亮度、对比度、纹理的局部图像区域将会拥有相对应的局部二值化阈值。常用的局部自适应阈值有:1)局部邻域块的均值;2)局部邻域块的高斯加权和。

而在opencv中实现了一下以上几种二值化方法:

一、全局阈值函数

threshold ( const void* src, void* dst, double thresh, double maxval, int type )

src源数组,dst为目标数组,thresh为阈值,maxval(M)为欲设最大值,type为阈值处理的类型,有如下几种:

CV_THRESH_BINARY,表示dst=(src>T)?M:0。

CV_THRESH_BINARY_INV,表示dst=(src>T)?0:M。

CV_THRESH_TRUNC,表示dst=(src>T)?M:src。

CV_THRESH_TOZERO_INV,表示dst=(src>T)?0:src。

CV_THRESH_TOZERO,表示dst=(src>T)?src:0。

二、自适应阈值函数

adaptiveThreshold(

const CvArr* src,    输入图像. 

CvArr* dst,     输出图像.

double max_value,

int adaptive_method=CV_ADAPTIVE_THRESH_MEAN_C,

int threshold_type=CV_THRESH_BINARY,

int block_size=3,

double param=5 )

   max_value:使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值(见上文).

   adaptive_method:自适应阈值算法使用:CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C .

   threshold_type:取阈值类型:必须是CV_THRESH_BINARY或者CV_THRESH_BINARY_INV

   block_size:用来计算阈值的象素邻域大小: 3, 5, 7, ...

   param:与方法有关的参数。对方法 CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数(见讨论), 尽管它可以是负数。

对于max_value中的两个方式中的T是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均然后减去一个常数param,从而得到该点的阈值。b由block_size指定,常数由param1指定

对方法 CV_ADAPTIVE_THRESH_MEAN_C,先求出块中的均值,再减掉param。

对方法 CV_ADAPTIVE_THRESH_GAUSSIAN_C ,那么区域中(x,y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算, 再减掉param。

三、使用方法

threshold(gray, t, 50, 255,CV_THRESH_BINARY);  

adaptiveThreshold(gray, Iat, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 3, 5);

但是网上有人看到有帖子说,adaptiveThreshold的作用不是二值化而是提取对象边缘的观点,关键在于里面的block_size参数,该参数是决定局部阈值的block的大小,当block很小时,如block_size=3 or 5 or 7时,“自适应”的程度很高,即容易出现block里面的像素值都差不多,这样便无法二值化,而只能在边缘等梯度大的地方实现二值化,结果显得它是边缘提取函数。当把block_size设为比较大的值时,如block_size=21 or 31 or 41时,adaptiveThreshold便是二值化函数。

四、OSTU算法

本来没打算研究这个的,但是看到这里了就顺便百度了下,也算小小的学习了一下。

     otsu法(最大类间方差法,有时也称之为大津算法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别 来划分。  所以 可以在二值化的时候 采用otsu算法来自动选取阈值进行二值化。otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

     用数学的表示就是:设分割前景点和背景点的阈值为T,则W0为前景点在图像中所占的像素比例,U0为所有前景点的均值,W1为背景点在图像中所占的像素比例,U1为所有背景点的均值,则整个图像的均值为U=W0*U0+W1*U1,目标函数g(t)=W0*(U0-U)^2+W1*(U1-U)^2为阈值T时的类间方差表达式。OTSU就是使得g(t)取得全局最大值,当g(t)为最大时所对应的t称为最佳阈值(遍历所有的灰度级然后找到使其最大的灰度)。由于otsu算法是对图像的灰度级进行聚类,在执行otsu算法之前,需要计算该图像的灰度直方图。

五、函数实现:

int main(){Mat a = imread("D:\\workspace_vs\\lpr\\chepai\\chepai1.jpg", 1);imshow("source",a);Mat gray, global,local,local2;cvtColor(a, gray,CV_RGB2GRAY);int thres =100; int maxval = 255;threshold(gray,global,thres,maxval,CV_THRESH_BINARY);imshow("threshold", global);int block_size = 21;double param = 5;adaptiveThreshold(gray, local, maxval,CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,block_size,param);imshow("adaptiveThreshold", local);int block_size = 3;adaptiveThreshold(gray, local, maxval,CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,block_size,param);imshow("adaptiveThreshold", local2); waitKey(0);return 0;}
int otsu(const Mat& src){if(src.channels() != 1){printf("otsu need 1 depth image");exit(0);}int width = src.cols;int height = src.rows;int pixelNum[256];float pixelPro[256];int pixelSum = width*height;//初始化for(int i=0; i<256; i++){pixelNum[i] = 0;pixelPro[i] = 0.0;}//计算每个像素的个数const uchar* p=NULL;for(int i=0; i<height; i++){p = src.ptr<uchar>(i);for(int j=0; j<width; j++){pixelNum[p[j]]++;}}float w0,u0,w1,u1,u,gmax,u1temp,u0temp,gtemp;int thresh = 0;for(int i=0;i<256;i++){pixelPro[i]=(float)(pixelNum[i])/(float)(pixelSum);}        //计算最大的分割阈值gmax=0.0;for(int i=0;i<256;i++){w0=u0=w1=u1=u1temp=u0temp=gtemp=0.0;for(int j=0; j<256; j++){       if( j <= i ){    //  小于阈值的为背景点w1 += pixelPro[j];u1temp += j*pixelPro[j];                                                                                                                                                                                                                                                                                                                                                  }else{w0 += pixelPro[j];u0temp += j*pixelPro[j];}}      //这里关于均值的计算转换了一个角度,{j*number}之和除以j个number之和为其均值,同时除以总像素,
                   、// 就为{j*每个对应像素所占比例}之和除以每个对应像素所占                       
                    //比例之和u1 = u1temp / w1;u0 = u0temp / w0;u = u0temp + u1temp;gtemp = w0*(u0-u)*(u0-u)+w1*(u1-u)*(u1-u);if(gtemp > gmax){gmax = gtemp;thresh = i;}}return thresh;}

下面是ostu:












0 0