返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV如何实现拉普拉斯算子的离散模拟
下一篇 :OpenCV系列文章目录(持续更新中......)
目标
在本教程中,您将学习如何:
- 使用 OpenCV 函数 cv::Canny 实现 Canny 边缘检测器。
理论
Canny Edge探测器[48]由John F. Canny于1986年开发。Canny 算法也被许多人称为最佳检测器,旨在满足三个主要标准:
- 低错误率:这意味着仅对现有边缘的良好检测。
- 良好的本地化:必须最小化检测到的边缘像素与实际边缘像素之间的距离。
- 最小响应:每个边沿只有一个检测器响应。
步骤
- 过滤掉任何噪音。高斯滤波器用于此目的。可能使用的(size = 5)高斯核示例如下所示:
- 找到图像的强度渐变。为此,我们遵循类似于 Sobel 的过程:
a).应用一对卷积掩码在 x 和y 方向上:
编辑
b).通过以下方式找到梯度强度和方向::
编辑
- 方向四舍五入为四个可能的角度之一(即 0、45、90 或 135)
- 应用非最大抑制。这将删除不被视为边的一部分的像素。因此,将只保留细线(候选边)。
-
滞后:最后一步。Canny 确实使用两个阈值(上限和下限):
- 如果像素渐变高于上限阈值,则该像素被接受为边缘
- 如果像素渐变值低于下限阈值,则将拒绝该值。
- 如果像素渐变介于两个阈值之间,则仅当它连接到高于上限阈值的像素时,才会被接受。
Canny 建议在 2:1 和 3:1 之间使用上下比例。
- 有关更多详细信息,您可以随时查阅您最喜欢的计算机视觉书籍。
1、C++代码演示:
- 教程代码如下所示。您也可以从这里下载
#include "opencv2/imgproc.hpp" #include "opencv2/highgui.hpp" #include <iostream> using namespace cv; Mat src, src_gray; Mat dst, detected_edges; int lowThreshold = 0; const int max_lowThreshold = 100; const int ratio = 3; const int kernel_size = 3; const char* window_name = "Edge Map"; static void CannyThreshold(int, void*) { blur( src_gray, detected_edges, Size(3,3) ); Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size ); dst = Scalar::all(0); src.copyTo( dst, detected_edges); imshow( window_name, dst ); } int main( int argc, char** argv ) { CommandLineParser parser( argc, argv, "{@input | fruits.jpg | input image}" ); src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); // Load an image if( src.empty() ) { std::cout << "Could not open or find the image!\n" << std::endl; std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl; return -1; } dst.create( src.size(), src.type() ); cvtColor( src, src_gray, COLOR_BGR2GRAY ); namedWindow( window_name, WINDOW_AUTOSIZE ); createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold ); CannyThreshold(0, 0); waitKey(0); return 0; }
- 这个程序是做什么的?
- 要求用户输入一个数值来设置我们的 Canny Edge Detector 的下限阈值(通过跟踪栏)。
- 应用 Canny Detector 并生成蒙版(亮线表示黑色背景上的边缘)。
- 应用在原始图像上获取的蒙版并将其显示在窗口中。
创建一些需要的变量:
2、说明(C++ 代码)
Mat src, src_gray;
Mat dst, detected_edges;
int lowThreshold = 0;
const int max_lowThreshold = 100;
const int ratio = 3;
const int kernel_size = 3;
const char* window_name = "Edge Map";
-
请注意以下事项:
- 我们建立了 3:1 的下限:上限阈值(具有可变比率)。
- 我们将内核大小设置为 (用于由 Canny 函数在内部执行的 Sobel 操作)。3
- 我们为 的下限阈值设置了最大值。100
- 加载源图像:
CommandLineParser parser( argc, argv, "{@input | fruits.jpg | input image}" );
src = i mread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); // Load an image
if( src.empty() )
{
std::cout << "Could not open or find the image!\n" << std::endl;
std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
return -1;
}
- 创建一个与 src 类型和大小相同的矩阵(待 dst):
dst.create( src.size(), src.type() );
- 将图像转换为灰度(使用函数 cv::cvtColor ):
cvtColor( src, src_gray, COLOR_BGR2GRAY );
- 创建一个窗口来显示结果:
namedWindow( window_name, WINDOW_AUTOSIZE );
- 为用户创建一个跟踪栏,以输入我们的 Canny 检测器的下限:
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
- 请注意以下事项:
- 要由 Trackbar 控制的变量是 lowThreshold,限制为 max_lowThreshold(我们之前将其设置为 100)
- 每次 Trackbar 注册操作时,都会调用回调函数 CannyThreshold。
- 让我们一步一步地检查 CannyThreshold 函数:
a、首先,我们用内核大小为 3 的过滤器对图像进行模糊处理
blur( src_gray, detected_edges, Size(3,3) );
b、其次,我们应用 OpenCV 函数 cv::Canny
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
- 其中参数为:
- detected_edges:源图像、灰度
- detected_edges:检测器输出(可与输入相同)
- lowThreshold:用户移动跟踪栏时输入的值
- highThreshold:在程序中设置为下限的三倍(遵循 Canny 的建议)
- kernel_size:我们将其定义为 3(内部使用的 Sobel 内核的大小)
7、我们用零填充目标图像(表示图像完全是黑色的)。
dst = Scalar::all(0);
8、最后,我们将使用函数 cv::Mat::copyTo 仅映射图像中标识为边缘的区域(在黑色背景上)。cv::Mat::copy将 src 映像复制到 dst 上。但是,它只会复制像素具有非零值的位置。由于 Canny 检测器的输出是黑色背景上的边缘轮廓,因此生成的 dst 在除检测到的边缘之外的所有区域都将是黑色的。
src.copyTo( dst, detected_edges);
9、我们显示我们的结果
结果
- 编译上面的代码后,我们可以运行它,将图像的路径作为参数。例如,使用以下图像作为输入:
- 移动滑块,尝试不同的阈值,我们得到以下结果:
请注意图像如何叠加到边缘区域的黑色背景上。
参考文献:
1、《Canny Edge Detector》---Ana Huamán