【OpenCV C++20 学习笔记】Canny边缘检测

Canny边缘检测

原理

Canny边缘检测也称为"最优检测"(optimal detector),它的开发主要有以下3个目标:

  • 低错误率:只检测真实存在的边缘
  • 良好的定位:检测出来的边缘与真实的边缘之间的距离要达到最小
  • 最小的反馈:每个边缘只有一个检测结果

步骤

  1. 过滤噪音:使用滤波对噪音进行过滤,可以使用归一化滤波、高斯滤波、中值滤波等。
  2. 使用类似于Sobel的方法,计算图片中颜色值的斜率:
  • 在 x x x和 y y y方向应用以下卷积掩码:
    G x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ I G_x= \begin{bmatrix} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{bmatrix} * I Gx= −1−2−1000+1+2+1 ∗I
    G y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] ∗ I G_y= \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{bmatrix} * I Gy= −10+1−20+2−10+1 ∗I
  • 计算近似斜率及其方向:
    G = G x 2 + G y 2 G = \sqrt{G_x^2+G_y^2} G=Gx2+Gy2
    θ = arctan ⁡ ( G y G x ) \theta = \arctan(\frac{G_y}{G_x}) θ=arctan(GxGy)
    θ \theta θ只能取4个值,0, 45, 90以及135,所以要取近似值。
  1. 非最大值抑制(non-maximum suppression):将不属于边缘部分的像素去除,只保留边缘的细线条
  2. 迟滞(hysteresis):使用2个阈值(上阈值,下阈值)
  • 如果像素的斜率高于上阈值,该像素将被识别为在边缘上
  • 如果像素的斜率低于下阈值,该像素将不被识别
  • 如果像素的斜率在两个阈值中间,则只有当该像素与高于上阈值的像素相邻时才被识别为在边缘上

API

在OpenCV中使用Canny()函数进行Canny边缘检测,其函数原型如下:

cpp 复制代码
void cv::Canny(	InputArray	image,				//8位的输入图
				OutputArray	edges,				//输出图;单通道8位类型,与输入图具有相同的尺寸
				double		threshold1,			//迟滞操作中的下阈值
				double		threshold2,			//迟滞操作中的上阈值
				int			apertureSize = 3,	//Sobel操作中的卷积核的尺寸
				bool		L2gradient = false)	//是否使用更精确的L2方法计算斜率
  • 如果L2gradient = true,则使用 L 2 n o r m = ( d I / d x ) 2 + ( d d I / d y ) 2 L_2 norm = \sqrt{(dI/dx)^2+(ddI/dy)^2} L2norm=(dI/dx)2+(ddI/dy)2 计算斜率
  • 如果L2gradient = false,则使用 L 1 n o r m = ∣ d I / d x ∣ + ∣ d I / d y ∣ L_1 norm = |dI/dx| + |dI/dy| L1norm=∣dI/dx∣+∣dI/dy∣计算斜率,这也是默认的方法

实例

本例中首先将原图灰度化,然后使用归一化滤波去除图中的噪音。

接着使用50-90的阈值范围,以及 3 × 3 3 \times 3 3×3的卷积核进行边缘检测。

最后将检测结果与原图结合,从而使得边缘线条具有与原图相同的颜色。

完整代码如下:

cpp 复制代码
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;

int main() {
	Mat src{ imread("fruits.jpg") };

	//灰度化
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	//归一化滤波
	Mat blured;
	blur(gray, blured, Size(3, 3));

	//canny边缘检测
	Mat canny;
	Canny(blured, canny, 50, 90, 3);

	//将检测的边缘与原图结合
	Mat dst{ src.size(), src.type() };
	dst = Scalar::all(0);
	src.copyTo(dst, canny);	//canny相当与掩码,这样只有canny中检测到的边缘才能显示颜色,其余都是黑色

	imshow("原图", src);
	imshow("canny", dst);
	waitKey(0);
}

运行结果:

可以看到,边缘检测的效果非常细节。

相关推荐
IVEN_7 天前
Python OpenCV: RGB三色识别的最佳工程实践
python·opencv
西岸行者12 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意12 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码12 天前
嵌入式学习路线
学习
毛小茛12 天前
计算机系统概论——校验码
学习
babe小鑫12 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms13 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下13 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。13 天前
2026.2.25监控学习
学习
im_AMBER13 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode