本文长度为1669字,预计阅读5分钟
前言
Canny边缘检测速度很快,OpenCV中经常会用到Canny边缘检测,以前的Demo中使用Canny边缘检测都是自己手动修改高低阈值参数,最近正好要研究点小东西时,就想能不能做个自适应的阈值,在不影响整体效果的基础上不用手动调参,话不多说,直接开始。
实现效果
从上图中可以看出,命令行窗口中min和max就是求出的高低阈值,使用Canny边缘检测时直接就按这两个高低阈值处理的。
要实现自动阈值,方法就是求出图像的灰度直方图,直方图中找出中位数,然后根据中位数值设定一个标准差值,用中位数的值加上标准差来求出高低阈值。
# | 实现思路 |
---|---|
1 | 图像转为灰度图 |
2 | 求出灰度直方图,并找到中位数 |
3 | 根据中位数和设定的sigma值求出高低阈值 |
4 | 使用Canny边缘检测 |
代码实现
微卡智享
核心方法
根据上面的思路,最核心的方法就是求灰度图的中位数,求灰度图的直方图,然后根据直方图中出中位数,求直方图的原理可以看下面的动图。
求中位数代码
代码语言:javascript复制//求Mat的中位数
int CvUtils::GetMatMidVal(Mat& img)
{
//判断如果不是单通道直接返回128
if (img.channels() > 1) return 128;
int rows = img.rows;
int cols = img.cols;
//定义数组
float mathists[256] = { 0 };
//遍历计算0-255的个数
for (int row = 0; row < rows; row) {
for (int col = 0; col < cols; col) {
int val = img.at<uchar>(row, col);
mathists[val] ;
}
}
int calcval = rows * cols / 2;
int tmpsum = 0;
for (int i = 0; i < 255; i) {
tmpsum = mathists[i];
if (tmpsum > calcval) {
return i;
}
}
return 0;
}
求出中位数后,我们再根据设置的sigma值求出高低阈值来。
根据中位数求高低阈值代码
代码语言:javascript复制//求自适应阈值的最小和最大值
void CvUtils::GetMatMinMaxThreshold(Mat& img, int& minval, int& maxval, float sigma)
{
int midval = GetMatMidVal(img);
cout << "midval:" << midval << endl;
// 计算低阈值
minval = saturate_cast<uchar>((1.0 - sigma) * midval);
//计算高阈值
maxval = saturate_cast<uchar>((1.0 sigma) * midval);
}
调用高低阈值Canny检测的代码
代码语言:javascript复制 //转换灰度图
Mat gray, dst;
cvtColor(src, gray, COLOR_BGR2GRAY);
//高斯滤波
GaussianBlur(gray, gray, Size(3, 3), 0.5, 0.5);
//获取自适应阈值
int minthreshold, maxthreshold;
CvUtils::GetMatMinMaxThreshold(gray, minthreshold, maxthreshold);
cout << "min:" << minthreshold << " max:" << maxthreshold << endl;
//Canny边缘提取
Canny(gray, gray, minthreshold, maxthreshold);
这样自适应高低阈值的Canny边缘检测就完成了。
求中位数和取高低阈值的函数我放到了CvUtil的公共类中,等下个Demo完成后会调整一下位置,到时候一起上传上来。
完