文章目录
前言
Canny 边缘检测(高斯滤波、梯度计算、非极大值抑制、双阈值检测、边缘跟踪)是经典的边缘检测算法之一,本文将详细介绍 Canny 边缘检测的理论基础、实现方法,并提供完整的 C++ 代码示例。
1.理论基础
Canny 边缘检测算法是由 John F. Canny 于 1986 年提出的。Canny 算法提取图像的边缘时表现出了很高的性能。Canny 算法的主要步骤如下:
①使用高斯滤波器,平滑图像,滤除噪声。
②计算图像中每个像素点的梯度。
③非极大值(Non-Maximum Suppression)抑制,去除非边缘的像素点,减少边缘检测中的噪声响应。
④双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
⑤通过连接强边缘像素点和与之相邻的弱边缘像素点,得到完整的边缘图像。
(1)高斯模糊平滑图像(GaussianBlur)
高斯模糊是通过模糊图像来减少噪声和细节,防止这些无关信息在后续的边缘检测中被误检为边缘。
[OpenCV] 数字图像处理 C++ 学习——07图像模糊 附完整代码(小白入门篇)有讲到,有需要可以去看一下
(2)计算图像梯度(Sobel/Scharr)
使用 Sobel 算子计算图像在水平方向(x 方向)和垂直方向(y 方向)的梯度。梯度的大小和方向可以通过以下公式计算:
(3)非极大值抑制 (Non-maximum Suppression)
在图像梯度幅值图上,抑制非边缘的像素,即抑制不是局部最大值的像素。这一步骤是为了减少不必要的边缘响应,只保留真正的边缘像素。
图中将梯度方向划分为了 4 个主要区域,每个区域对应一定的角度范围。这些区域分别是:
- 黄色区域 (0° 和 180°):梯度方向接近水平方向,具体来说,角度在(0° ,22.5° ) 或(157.5° ,180°) 之间。这个方向对应着水平边缘。
- 绿色区域 (45°):梯度方向介于水平和垂直之间,对应的角度范围是(22.5° , 67.5°) 。这个方向通常表示斜向的边缘。
- 蓝色区域 (90°):梯度方向接近垂直方向,角度在(67.5° ,112.5°) 之间。这个方向对应着垂直边缘。
- 红色区域 (135°):梯度方向介于垂直和水平之间,对应的角度范围是 (112.5° , 157.5°)。这个方向也是斜向的,方向与绿色区域的方向相反。
非极大值抑制的处理步骤
- 选择方向:根据每个像素点的梯度方向,将该方向归类到上述的四个主要方向之一。然后沿着这个主要方向进行非极大值抑制。
- 比较相邻像素:对于每个像素点,沿着该点的梯度方向,比较其梯度幅值是否是局部最大值。如果当前像素的梯度幅值大于沿着梯度方向的前一个和后一个像素的梯度幅值,则保留该点为边缘点;否则,将该点的梯度幅值设为零,表示非边缘。
例如:
- 如果梯度方向是接近水平的(黄色区域),则比较当前像素与左侧和右侧像素的梯度幅值。
- 如果梯度方向是接近垂直的(蓝色区域),则比较当前像素与上方和下方像素的梯度幅值。
通过非极大值抑制,梯度幅值图像中的噪声点和不明确的边缘点会被抑制,只保留那些可能真实存在的边缘。
(4)双阈值检测 (Double Threshold)
应用两个阈值对检测到的边缘进行分类:强边缘(强于高阈值)、弱边缘(介于高阈值和低阈值之间)和非边缘(低于低阈值)。强边缘直接被保留,弱边缘如果连接到强边缘则保留,否则舍弃。
- A 点的梯度值值大于 maxVal,因此 A 是强边缘。
- B 和 C 点的梯度值介于 maxVal 和 minVal 之间,因此 B、C 是虚边缘。
- B 点的梯度值介于 maxVal 和 minVal 之间,是虚边缘,但该点与强边缘不相连,故将其抛弃。
- C 点的梯度值介于 maxVal 和 minVal 之间,是虚边缘,但该点与强边缘 A 相连,故将其保留。
- D 点的梯度值小于 minVal,因此 D 被抑制(抛弃)。
(5)边缘跟踪(通过滞后处理)
通过连接强边缘像素点和与之相邻的弱边缘像素点,得到完整的边缘图像。
2.代码实现
cv::Canny()
使用了上述的步骤,自动进行图像平滑、梯度计算、非极大值抑制、双阈值检测和边缘跟踪。
cv::Mat edges;
double lowThreshold = 50;
double highThreshold = 150;
cv::Canny(blurredImage, edges, lowThreshold, highThreshold);
cv::imshow("Canny Edges", edges);
lowThreshold = 50和highThreshold = 150结果
lowThreshold = 50和highThreshold = 100结果
具体的结果需要根据实际图像的特性来调节双阈值,以达到最佳边缘检测效果。
3.完整代码
#include<opencv2/opencv.hpp>
#include<highgui.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
void Canny_edge_detection()
{
cv::Mat image;
image = imread("lena.png", IMREAD_GRAYSCALE);
if (image.empty()) {
printf("could not find the image...\n");
return;
}
namedWindow("input image", cv::WINDOW_AUTOSIZE);
cv::imshow("input image", image);
// 使用高斯滤波器平滑图像
cv::Mat blurredImage;
GaussianBlur(image, blurredImage, Size(5, 5), 1.5);
// 执行 Canny 边缘检测
cv::Mat edges;
double lowThreshold = 50;
double highThreshold = 100;
cv::Canny(blurredImage, edges, lowThreshold, highThreshold);
cv::imshow("Canny Edges", edges);
waitKey(0);
}
int main()
{
Canny_edge_detection();
return 0;
}