Canny边缘检测无法检测到100%水平/未旋转的线。

3
Canny边缘检测是否不应该检测到纯粹、非旋转的水平线?以下是输入图像:
左:100%水平(非旋转)非模糊线
右:相同图像旋转1°。

Pure horizontal lines, input image Horizontal lines 1° rotated, input image

相应的输出“边缘”图像:

Pure horizontal lines, output image Horizontal lines 1° rotated, output image

(为更好的判断,这里将图像缩放了2倍且没有平滑处理。)

图像处理时使用的阈值为low=250x0.66和high=250x1.33,核大小为k=3。

虽然梯形图像生成了令人满意的结果,但前面的图像却没有。

目前,这些图像并未进行进一步的预处理,您所看到的就是输入Canny算法的内容。

我创建了第一个图像作为测试数据。它可能是一个构造出来的情况,尽管在真实场景中也可能出现这样的线条。

MCVE:

public class q58884116 {
    public static void main(String[] args) throws Exception {
        System.load(new File(Paths.get("target", "classes", "libopencv_java412.so").toString()).getAbsolutePath());

        File inFile = new File(args[0]);
        File outFile = new File(args[1]);

        Mat imageColor = Imgcodecs.imread(inFile.getAbsolutePath(), Imgcodecs.IMREAD_COLOR);
        Mat patternProbeImage = new Mat();
        Imgproc.cvtColor(imageColor, patternProbeImage, Imgproc.COLOR_BGR2GRAY);

        Mat edges = new Mat();
        // Value determined from histogram in Gimp.
        Imgproc.Canny(patternProbeImage, edges, 250 * 0.66, 250 * 1.33, 3, true);

        Imgcodecs.imwrite(outFile.getAbsolutePath(), edges);
    }
}

输入图像(未缩放):input.png

  • 从Git编译
  • OpenCV 4.1.2
  • cmake 3.7.2
  • gcc 6.3.0
  • openjdk 1.8.0_232
  • 使用Java库。

构建步骤:

$ git clone https://github.com/opencv/opencv.git
$ cd opencv/
$ git checkout 4.1.2
$ cd cmake/
$ cmake -DCMAKE_INSTALL_PREFIX="/opt/opencv" ..
$ make -j4
$ make install

我必须承认,我不熟悉Canny或者计算机视觉的理论。我刚刚开始接触它。尽管我可以想象这可能是由Sobel函数使用的Gy矩阵的零行引起的(在Canny函数文章中提到过)。
如果这确实是Canny的限制,通常处理这种情况的方法是什么(简要说明)?
我将以实证的方式来处理这个问题:我创建了一个白到黑的渐变图像,并在其上方放置了一种黑色的格栅图案。这个格栅图案代表着原始测试图像在不同背景下的线条。

这是我们的输入图像:

Gradient with grating (cropped preview)

(裁剪和调整大小以获得更美观的帖子,下载原始文件。)

MCVE被扩展以生成各种阈值范围的边缘(并不是所有可能的组合,但相当多):

for (int i = 0; i < 255 - 50; i++) {
    Mat edges = new Mat();
    Imgproc.Canny(patternProbeImage, edges, i, i + 50, 3, true);

    Imgcodecs.imwrite(getOutputFile(outFile, "lb-0-" + String.format("%03d", i)).getAbsolutePath(), edges);
}

我们可以检查生成的边缘。
使用这个一行代码,所有图像都被生成为一个马赛克(精灵),以便更容易地进行检查:
$ convert gradient-grating.out.lb-0-*.png +append  gradient-grating.out.lb.png

这是结果:

Edge image mosaic of the above input image

正如我们所看到的,随着较低阈值的增加,Canny在最小值一侧(顶部)无法检测到越来越多的边缘。我预料到了这一点。 虽然在最大值一侧,无论给出什么阈值,Canny都从未能够检测到边缘。

1
Canny主要用于通过确定图像亮度发生剧烈变化的位置来进行边缘检测。它用于提取图像中物体的结构。在计算机视觉中,边缘基本上是突然的像素变化。这可能是由于您的阈值所致。我使用low=100high=200获得了正确的结果。检测线条的替代方法是使用HoughLinesP或构建水平核并使用形态学。 - nathancy
1
我刚在Python中尝试了这个,它可以工作。以前,我在Java中使用OpenCV工作过,我发现Canny滤波器在OpenCV/Python中的行为与在OpenCV/Java中的不同。 - Stephen Meschke
非常有价值的评论,Stephen。nathancy,我之前尝试过其他阈值,现在用了你的值 - 仍然没有成功。 - try-catch-finally
1
使用我的Java Opencv(版本3.3),我得到了类似的输出结果,尽管它们不如第二张图片的输出结果好,但肯定比第一张好。使用我的c++ Opencv(版本4.0),我的结果与您的完全相同。我想知道@StephenMeschke测试的是哪个版本。稍后我会进一步调查。 - unlut
我目前正在研究水平线的类似问题,并发现通过查看 HLS 转换,线条上有一些伪影导致我的期望边缘消失。这可能是其他人遇到此问题的原因。 - foureyedraven
显示剩余3条评论
1个回答

0
尝试保存图像,然后直接打开图像文件。这可能仅仅是绘图程序渲染图像的问题。
我自己也遇到了同样的问题,但解决了。我通过jupyter notebook使用matplotlib查看我的边缘图像时,100%的水平边缘在笔记本内部显示时无法呈现。但当我将其保存为png文件并直接打开图像文件时,水平边缘实际上存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接