索引地址:系列索引
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。但是这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。
自适应阈值
当同一幅图像上的不同部分的具有不同亮度时,这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
函数:
1 2 3 4 5 6 7
| void cv::adaptiveThreshold (InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
|
参数说明:
- InputArray类型的src,输入图像,填单通道,单8位浮点类型Mat即可。
- OutputArray:函数运算后的结果存放在这。即为输出图像(与输入图像同样的尺寸和类型)。
- maxValue:预设满足条件的最大值。
- adaptiveMethod:指定自适应阈值算法。可选择ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C两种。(具体见下面的解释)。
- thresholdType:指定阈值类型。可选择THRESH_BINARY或者THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)。
- blockSize:表示邻域块大小,用来计算区域阈值,一般选择为3、5、7…等。
- 参数C表示与算法有关的参数,它是一个从均值或加权均值提取的常数,可以是负数。(具体见下面的解释)。
对参数adaptiveMethod与参数C内容的解释:
自适应阈值化计算大概过程是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B∗B区域内的像素加权平均,然后减去一个常数C,从而得到该点的阈值。B由参数6指定,常数C由参数7指定。
- ADAPTIVE_THRESH_MEAN_C,为局部邻域块的平均值,该算法是先求出块中的均值,再减去常数C。
- ADAPTIVE_THRESH_GAUSSIAN_C,为局部邻域块的高斯加权和。该算法是在区域中(x, y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算,再减去常数C。
举个例子:如果使用平均值方法,平均值mean为190,差值delta(即常数C)为30。那么灰度小于160的像素为0,大于等于160的像素为255。如下图:
如果是反向二值化,如下图:
delta(常数C)选择负值也是可以的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream> using namespace std; using namespace cv;
int main() { Mat srcImage = imread("1.jpg"); if (!srcImage.data) { cout << "读取图片错误,请重新输入正确路径!\n"; return -1; } imshow("Original", srcImage); Mat srcGray; cvtColor(srcImage, srcGray, COLOR_BGR2GRAY); imshow("Gray", srcGray); Mat dstImage; const int maxVal = 255; int blockSize = 3; int constValue = 10; int adaptiveMethod = 0; int thresholdType = 1;
adaptiveThreshold(srcGray, dstImage, maxVal, adaptiveMethod, thresholdType, blockSize, constValue); imshow("Adaptive", dstImage); waitKey(0); return 0; }
|
效果如图:
OTSU二值化
如果是一副双峰图像(简单来说双峰图像是指图像直方图中存在两个峰)呢?我们岂不是应该在两个峰之间的峰谷选一个值作为阈值?这就是OTSU二值化要做的。简单来说就是对一副双峰图像自动根据其直方图计算出一个阈值。(对于非双峰图像,这种方法得到的结果可能会不理想)。
函数还是threshold(),但是需要多传入一个参数(flag): THRESH_OTSU。这时要把阈值设为 0。然后算法会找到最优阈值,这个最优阈值就是返回值。如果不使用OTSU二值化,返回的值与设定的阈值相等。
算法分类的原理是让背景和目标之间的类间方差最大,因为背景和目标之间的类间方差越大,说明构成图像的两部分的差别越大,错分的可能性越小。
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <opencv2/opencv.hpp>
using namespace std; using namespace cv;
int main(){ Mat srcImage=imread("1.jpg",IMREAD_GRAYSCALE); if(srcImage.empty()){ cout<<"Read image error"<<endl; return -1; }
imshow("original",srcImage);
Mat resultImage; threshold(srcImage,resultImage,0,255,THRESH_BINARY+THRESH_OTSU);
imshow("result",resultImage);
waitKey();
return 0; }
|
测试结果:
三角法二值化
三角法求阈值最早见于Zack的论文《Automatic measurement of sister chromatid exchange frequency》主要是用于染色体的研究,该方法是使用直方图数据,基于纯几何方法来寻找最佳阈值,它的成立条件是假设直方图最大波峰在靠近最亮的一侧,然后通过三角形求得最大直线距离,根据最大直线距离对应的直方图灰度等级即为分割阈值,图示如下:
对上图的详细解释:
在直方图上从最高峰处bmx到最暗对应直方图bmin(p=0)%构造一条直线,从bmin处开始计算每个对应的直方图b到直线的垂直距离,知道bmax为止,其中最大距离对应的直方图位置即为图像二值化对应的阈值T。
扩展情况:
有时候最大波峰对应位置不在直方图最亮一侧,而在暗的一侧,这样就需要翻转直方图,翻转之后求得值,用255减去即得到为阈值T。扩展情况的直方图表示如下:
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <opencv2/opencv.hpp>
using namespace std; using namespace cv;
int main(){ Mat srcImage=imread("1.jpg",IMREAD_GRAYSCALE); if(srcImage.empty()){ cout<<"Read image error"<<endl; return -1; }
imshow("original",srcImage);
Mat resultImage; threshold(srcImage,resultImage,0,255,THRESH_BINARY+THRESH_TRIANGLE);
imshow("result",resultImage);
waitKey();
return 0; }
|
测试结果:
双阈值法
双阈值是使用两个阈值进行分别进行二值化操作,然后将操作结果合并起来.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" int main( ) { cv::Mat srcImage = cv::imread("..\\images\\hand1.jpg"); if( !srcImage.data ) return 1; cv::Mat srcGray; cv::cvtColor(srcImage, srcGray, cv::COLOR_RGB2GRAY); cv::imshow("srcGray", srcGray); const int maxVal = 255; int low_threshold = 150; int high_threshold = 210; cv::Mat dstTempImage1, dstTempImage2, dstImage; cv::threshold( srcGray, dstTempImage1, low_threshold, maxVal, cv::THRESH_BINARY ); cv::threshold( srcGray, dstTempImage2, high_threshold, maxVal,cv::THRESH_BINARY_INV ); cv::bitwise_and( dstTempImage1, dstTempImage2, dstImage ); cv::imshow("dstImage", dstImage); cv::waitKey(0); return 0; }
|
效果为:
半阈值法
半阈值法是先阈值处理,然后将操作结果与原图进行与操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace std; using namespace cv; int main( ) { cv::Mat srcImage = cv::imread("..\\images\\hand1.jpg"); if( !srcImage.data ) return 1; cv::Mat srcGray; cv::cvtColor(srcImage, srcGray, cv::COLOR_RGB2GRAY); cv::imshow("srcGray", srcGray); const int maxVal = 255; int thresholdVal = 150; cv::Mat dstTempImage, dstImage; cv::threshold( srcGray, dstTempImage, thresholdVal, 255, cv::THRESH_BINARY ); cv::bitwise_and( srcGray, dstTempImage, dstImage ); cv::imshow("dstImage", dstImage); cv::waitKey(0); return 0; }
|
效果为:
参考资料