理论 ¶
Canny边缘检测是由John F. Canny在1986年开发的。许多人也将其称为最佳检测器,Canny算法旨在满足三个主要标准。
- 好的检测:算法能够尽可能多地标识出图像中的实际边缘。
- 好的定位:标识出的边缘要与实际图像中的实际边缘尽可能接近。
- 最小响应:图像中的边缘只能标识一次,并且可能存在的图像雜訊不应标识为边缘。
步骤 ¶
- 降噪:使用高斯滤波来达到,下面是一个大小为 的高斯核的例子:
找到图像的亮度梯度:为此,我们遵循一个类似于
Sobel
的程序:- 应用一对卷积
masks
(在 和 方向上):
- 寻找梯度强度和方向:
方向被四舍五入为四个可能的角度之一(即 、 、 或 )。
- 应用一对卷积
过滤非最大值:在高斯滤波过程中,边缘有可能被放大了。这个步骤使用一个规则来过滤不是边缘的点,使边缘的宽度尽可能为1个像素点:如果一个像素点属于边缘,那么这个像素点在梯度方向上的梯度值是最大的。否则不是边缘,将灰度值设为 。
- 滞后:这是最后一步,Canny确实使用了两个阈值(上限和下限)。
- 如果一个像素的梯度值高于上限阈值,则该像素被接受为边缘。
- 如果一个像素的梯度值低于下限阈值,那么它将被拒绝。
- 如果像素梯度在两个阈值之间,那么只有当它与高于上层阈值的像素相连时,才会被接受。 Canny建议上下限的比值在 和 之间。
Code ¶
Explanation ¶
变量定义
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";
- 我们建立了一个 的下限:上限阈值的比值(用可变比值)。
- 我们设定内核大小为 (用于
Canny
函数内部进行的Sobel
运算) - 我们将下限阈值的最大值设定为 。
加载图像
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;
}
创建一个与src相同类型和大小的矩阵(dst)。
dst.create( src.size(), src.type() );
灰度化
cvtColor( src, src_gray, COLOR_BGR2GRAY );
创建一个窗口来显示结果
namedWindow( window_name, WINDOW_AUTOSIZE );
创建一个Trackbar,让用户为我们的Canny检测器输入低阈值
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
让我们一步一步地观察CannyThreshold函数
首先,我们用内核大小为 的滤波器对图像进行模糊处理。
blur( src_gray, detected_edges, Size(3,3) );
接着,我们应用OpenCV函数
cv::Canny
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
detected_edges
:源图像(灰度图)detected_edges
:输出图像(可与输入相同)lowThreshold
:用户移动Trackbar
时输入的值highThreshold
:在程序中设置为lowThreshold
的 倍(按照Canny的建议)kernel_size
:我们将其定义为 (内部使用的Sobel
内核的大小)。
我们用零来填充dst图像(意味着图像是完全黑色的)
dst = Scalar::all(0);
显示
最后,我们将使用函数cv::Mat::copyTo
只映射图像中被识别为边缘的区域(在黑色背景上)。cv::Mat::copyTo
将src
图像复制到dst
上。注意,它只会复制像素的非零值的位置。因为Canny检测器的输出是黑色背景上的边缘轮廓,所以除了检测到的边缘,生成的dst
将是黑色的。
若没有这一步,那么我们得到的图像是边缘的灰度图
结果 ¶
References ¶
https://docs.opencv.org/4.5.5/da/d5c/tutorial_canny_detector.html