自适应的阈值化

来源:互联网 发布:oracle认证 java 编辑:程序博客网 时间:2024/05/17 09:42

对图像应用阈值创建二值图像,是提取有意义元素的好方法。但有的时候单一阈值达不到目标提取的效果。
下面比较一下几种方法的效果。例如,给定一幅图如下,将“富贵白头”四个字提取出来。
这里写图片描述
通过OpenCV阈值化函数cv::threshold(),纯手工选择参数,进行固定阈值二值化,发现当阈值设为80时效果最好。

//采用固定阈值cv::threshold(image, binaryFixed, 80, 255, cv::THRESH_BINARY);

固定阈值(取为80)的效果,其实还行了,只不过阈值还是得手工微调:
这里写图片描述

采用最大类间方差法怎么样?自动选择最优阈值(其实还是单一阈值)。

//大津法std::cout<<cv::threshold(image,binaryOTSU,70,255,cv::THRESH_OTSU);//经测试cout输出为143

可见大津法算出的阈值为143。但是阈值化效果是很差的:
这里写图片描述
下面采用OpenCV的自适应阈值化方法:

cv::adaptiveThreshold(image,binaryAdaptive,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,7,-4);

效果如下,还不错,同时花树的轮廓也描绘出了:
这里写图片描述

自适应阈值是一种局部方法。它的原理是根据每个像素的邻域计算阈值,包括上面语句采用的将每个像素的值与邻域的平均值进行比较。如果某像素的值与它的局部平均值差别很大,就会被当做异常值在阈值化过程中被分离。
代码实现:
1、使用积分图像实现自适应阈值化

//自己实现自定义阈值    cv::Mat iimage;    cv::integral(image,iimage,CV_32S);    cv::Mat binary;    image.copyTo(binary);    int blockSize = 7;    int threshold = -4;//像素将与(mean-threshold)比较    int halfSize = blockSize / 2;    for (int j = halfSize; j < image.rows-halfSize-1;j++)    {        //j行首地址        uchar* data = binary.ptr<uchar>(j);        int *data1 = iimage.ptr<int>(j-halfSize);        int *data2 = iimage.ptr<int>(j+halfSize+1);        for (int i = halfSize; i < image.cols - halfSize - 1;i++)        {            int sum = (data2[i + halfSize + 1] -                 data2[i-halfSize] -                 data1[i + halfSize + 1] +                 data1[i-halfSize])/                (blockSize*blockSize);            if (data[i] > (sum - threshold))                data[i] = 255;            else                data[i] = 0;        }    }    cv::imshow("thresholdAdaptive",binary);

为了简化计算,计算像素周围方形邻域像素的和,采用了积分图像。假设邻域块的大小为blockSize*blockSize,不采用积分图像,每次需要计算blockSize*blockSize次加法运算;而采用积分图像,运算复杂度并不随邻域大小而改变,每次只需计算2次加法和2次减法。
2、使用盒子滤波实现自适应阈值化

cv::Mat filtered;cv::Mat binaryFiltered;cv::boxFilter(image,filtered,CV_8U,cv::Size(7,7));//这个盒子滤波相当于平均的效果filtered = filtered + 4;binaryFiltered = image >= filtered;imshow("thresholdBox",binaryFiltered);
0 0
原创粉丝点击