C++ OpenCV自适应阈值Canny边缘检测

2021-07-07 19:11:03 浏览数 (1)

本文长度为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完成后会调整一下位置,到时候一起上传上来。

0 人点赞