Canny边缘检测是否不应该检测到纯粹、非旋转的水平线?以下是输入图像:
左:100%水平(非旋转)非模糊线
右:相同图像旋转1°。
我必须承认,我不熟悉Canny或者计算机视觉的理论。我刚刚开始接触它。尽管我可以想象这可能是由Sobel函数使用的Gy矩阵的零行引起的(在Canny函数文章中提到过)。
如果这确实是Canny的限制,通常处理这种情况的方法是什么(简要说明)?
我将以实证的方式来处理这个问题:我创建了一个白到黑的渐变图像,并在其上方放置了一种黑色的格栅图案。这个格栅图案代表着原始测试图像在不同背景下的线条。
我们可以检查生成的边缘。
使用这个一行代码,所有图像都被生成为一个马赛克(精灵),以便更容易地进行检查:
这是结果: 正如我们所看到的,随着较低阈值的增加,Canny在最小值一侧(顶部)无法检测到越来越多的边缘。我预料到了这一点。 虽然在最大值一侧,无论给出什么阈值,Canny都从未能够检测到边缘。
左:100%水平(非旋转)非模糊线
右:相同图像旋转1°。
相应的输出“边缘”图像:
(为更好的判断,这里将图像缩放了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的限制,通常处理这种情况的方法是什么(简要说明)?
我将以实证的方式来处理这个问题:我创建了一个白到黑的渐变图像,并在其上方放置了一种黑色的格栅图案。这个格栅图案代表着原始测试图像在不同背景下的线条。
这是我们的输入图像:
(裁剪和调整大小以获得更美观的帖子,下载原始文件。)
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
这是结果: 正如我们所看到的,随着较低阈值的增加,Canny在最小值一侧(顶部)无法检测到越来越多的边缘。我预料到了这一点。 虽然在最大值一侧,无论给出什么阈值,Canny都从未能够检测到边缘。
low=100
和high=200
获得了正确的结果。检测线条的替代方法是使用HoughLinesP
或构建水平核并使用形态学。 - nathancy