【问题标题】:Automatic calculation of low and high thresholds for the Canny operation in opencv自动计算opencv中Canny操作的低阈值和高阈值
【发布时间】:2010-11-27 15:05:38
【问题描述】:

在 OpenCV 中,canny 算子的低阈值和高阈值是强制性的:

cvCanny(input,output,thresh1,thresh2)

在 Matlab 中,有一个选项可以自动计算:

edge(input,'canny')

我已经查看了 Matlab 的边缘代码,但要自动计算这些代码确实不是很直接。

您是否知道 Canny 运算符的任何实现以及 OpenCV 的自动阈值计算?

【问题讨论】:

  • 您能发布执行此操作的 Matlab 代码吗?

标签: image-processing opencv


【解决方案1】:

我在寻找一种自动计算 Canny 阈值的方法时偶然发现了这个答案。

希望这可以帮助任何来寻找确定 Canny 算法自动阈值的好方法的人...


如果您的图像由不同的前景和背景组成,则可以使用以下方法提取前景对象的边缘:

  1. 使用以下方法计算 Otsu 的阈值:

    double otsu_thresh_val = cv::threshold(
        orig_img, _img, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU
    );
    

    我们不需要_img。我们只对otsu_thresh_val 感兴趣,但不幸的是,目前OpenCV 中没有允许您仅计算阈值的方法。

  2. 使用 Otsu 的阈值作为 Canny 算法的较高阈值和较低阈值的一半。

    double high_thresh_val  = otsu_thresh_val,
           lower_thresh_val = otsu_thresh_val * 0.5;
    cv::Canny( orig_img, cannyOP, lower_thresh_val, high_thresh_val );
    

更多与此相关的信息可以在this paper: The Study on An Application of Otsu Method in Canny Operator 中找到。 Otsu 实现的解释可以在here找到。

【讨论】:

  • 这里的简历是什么? @VP。
  • OpenCV @TapanHP
【解决方案2】:

您可以使用输入灰度图像的平均值,并使用标准差定义阈值下限和上限。 你可以在这里有更详细的解释和opencv代码: http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/

【讨论】:

    【解决方案3】:

    此外,还有一些代码可以自动执行此操作,方法是将其放入 OpenCV 构建中。我在 OpenCV 用户邮件列表中找到了它,所以不能保证。 :)

    讨论:http://opencv-users.1802565.n2.nabble.com/Automatic-thresholding-in-cvCanny-td5871024.html GitHub(代码):https://gist.github.com/756833

    【讨论】:

      【解决方案4】:

      查看此链接:http://www.pyimagesearch.com/2015/04/06/zero-parameter-automatic-canny-edge-detection-with-python-and-opencv/

      他们使用基本统计数据实现了类似的解决方案,以确定 Canny 边缘检测的低阈值和高阈值。

      def auto_canny(image, sigma=0.33):
           # compute the median of the single channel pixel intensities
           v = np.median(image)
      
          # apply automatic Canny edge detection using the computed median
          lower = int(max(0, (1.0 - sigma) * v))
          upper = int(min(255, (1.0 + sigma) * v))
          edged = cv2.Canny(image, lower, upper)
      
          # return the edged image
          return edged
      

      【讨论】:

      • 此解决方案是否有可用的 JavaScript 等效项?
      【解决方案5】:

      我查看了 Matlab Canny 边缘检测的源代码,并设法使用 OpenCV 3 用 Ja​​va 编写了它。

      private static Mat getpartialedge(Mat image){
          double nonEdgeRate = 0.6;
          double thresholdRate = 0.6;
          double w = image.cols();
          double h = image.rows();
          int bins = 256;
          Mat sobel = new Mat();
          Mat sobelx = new Mat();
          Mat sobely = new Mat();
          Mat sobelxabs = new Mat();
          Mat sobelyabs = new Mat(); 
          Size gsz = new Size(5, 5);
          if(false) {
              Imgproc.Canny(image, sobel, 41, 71);
          }else {
      
              //Imgproc.GaussianBlur(graycopy,graycopy, gsz, 2);
              //Imgproc.dilate(image, image, kernel8);
              Imgproc.GaussianBlur(image, image, gsz, 2);
      
      
              int apertureSize = 3;
              Imgproc.Sobel(image, sobelx, CvType.CV_16S, 1, 0, apertureSize, 1, 0);
              Core.convertScaleAbs(sobelx, sobelxabs);
              Imgproc.Sobel(image, sobely, CvType.CV_16S, 0, 1, apertureSize, 1, 0);
              Core.convertScaleAbs(sobely, sobelyabs);
              Core.addWeighted(sobelxabs, 1, sobelyabs, 1, 0, sobel);
              sobel.convertTo(sobel, CvType.CV_8U);
      
      
              Mat equalized = new Mat();
              Imgproc.equalizeHist(sobel, equalized);
              Imgcodecs.imwrite(filePath + "aftersobel(eq).png", equalized);
              Imgcodecs.imwrite(filePath + "aftersobel.png", sobel);
      
      
              Mat hist = new Mat();
              List<Mat> matList = new ArrayList<Mat>();
              matList.add(sobel);
              Imgproc.calcHist(matList, new MatOfInt(0), new Mat(), hist, new MatOfInt(bins), new MatOfFloat(0f, 256f));
              float accu = 0;
              float t = (float) (nonEdgeRate * w * h);
              float bon = 0;
              float[] accutemp = new float[bins];
              for (int i = 0; i < bins; i++) {
                  float tf[] = new float[1];
                  hist.get(i, 0, tf);
                  accu = accu + tf[0];
                  accutemp[i] = accu;
                  if (accu > t) {
                      bon = (float) i;
                      break;
                  }
              }
              Imgproc.threshold(sobel, sobel, bon, 255, Imgproc.THRESH_BINARY);
              double ut = bon;
              double lt = thresholdRate * bon;
      
      
              Imgproc.Canny(image, sobel, lt, ut);
              //Imgproc.dilate(sobel, sobel, kernel2);
          }
          return sobel;
      }
      

      文件路径是保存输出图像的地方。并且输入图像应该是U8数据类型的灰度图像。 基本原理是通过亮度将nonEdgeRate(60%)像素排除为非边缘像素。直方图用于对亮度进行排序,并将设置上限阈值,使其下方有 60% 的像素。下阈值是通过将上阈值乘以 thresholdRate(0.6) 来设置的。

      请注意,在我的特定用例中,我自己调整了 double nonEdgeRate = 0.6 和 double thresholdRate = 0.6。在matlab中th原始值分别为0.7和0.4。

      【讨论】:

        【解决方案6】:

        我有另一种方法来解决同样的问题。该解决方案还涉及为边缘检测选择最佳阈值。

        • 首先计算灰度图像的中值
        • 根据中位数选择两个值(阈值下限和上限) 灰度图像的值。

        以下伪代码向您展示了它是如何完成的:

        v = np.median(gray_img)
        sigma = 0.33
        
        #---- apply optimal Canny edge detection using the computed median----
        lower_thresh = int(max(0, (1.0 - sigma) * v))
        upper_thresh = int(min(255, (1.0 + sigma) * v))
        

        将这些阈值固定为 canny 边缘检测函数中的参数。

        说明:如果您观察统计中的高斯曲线,则分布中考虑曲线两侧 0.33 之间的值。这些点之外的任何值都被假定为异常值。由于图像被认为是数据,因此这里也假设了这个概念。

        【讨论】:

        • 你见过this,还是你的理论?
        • @yode 我从来没有遇到过!感谢您提及!
        【解决方案7】:

        正如 Luca Del Tongo 所建议的,您可以根据灰度图像计算阈值,例如在 Java 中使用 OpenCV...

        MatOfDouble mu = new MatOfDouble();
        MatOfDouble stddev = new MatOfDouble();
        Core.meanStdDev(greyMat, mu, stddev);
        threshold1 = mu.get(0, 0)[0];
        threshold2 = stddev.get(0, 0)[0];
        Imgproc.Canny(greyMat, outputMat, threshold1, threshold2);
        

        【讨论】:

        • 你是不是错过了一些数学?平均值通常大于偏差。所以我想它应该类似于threshold1 = mu - stddevthreshold2 = mu + stddev
        • 对于明亮桌子上的白色物体,我得到 180 和 67 的平均值和偏差。 Canny 不会检测到具有这些值的任何边缘。 14 和 30 非常适合该图像。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-07
        相关资源
        最近更新 更多