【从零学习OpenCV 4】Canny算法
小白学视觉”,选择“星标”公众号
重磅干货,第一时间送达
经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。 |
本节中最后介绍的边缘检测算法是Canny算法,该算法不容易受到噪声的影响,能够识别图像中的弱边缘和强边缘,并结合强弱边缘的位置关系,综和给出图像整体的边缘信息。Canny边缘检测算法是目前最优越的边缘检测算法之一,该方法的检测过程分为以下5个步骤:
Step1:使用高斯滤波平滑图像,减少图像中噪声。一般情况下使用式(5.23)所示的5×5的高斯滤波器。
Step2:计算图像中每个像素的梯度方向和幅值。首先通过Sobel算子分别检测图像X方向的边缘和Y方向的边缘,之后利用式(5.24)计算梯度的方向和幅值。
为了简便,梯度方向常取值0°、45°、90°和135°这个四个角度之一。
Step3:应用非极大值抑制算法消除边缘检测带来的杂散响应。首先将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较, 如果当前像素的梯度强度与另外两个像素梯度强度相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
Step4:应用双阈值法划分强边缘和弱边缘。将边缘处的梯度值与两个阈值进行比较,如果某像素的梯度幅值小于较小的阈值,则会被去除掉;如果某像素的梯度幅值大于较小阈值但小于较大阈值,则将该像素标记为弱边缘;如果某像素的梯度幅值大于较大阈值,则将该像素标记为强边缘。
Step5:消除孤立的弱边缘。在弱边缘的8邻域范围寻找强边缘,如果8邻域内存在强边缘,则保留该弱边缘,否则将删除弱边缘,最终输出边缘检测结果。
Canny算法具有复杂的流程,然而在OpenCV 4中提供了Canny()函数用于实现Canny算法检测图像中的边缘,极大的简化了使用Canny算法提取边缘信息的过程。Canny()函数的函数原型在代码清单5-32中给出。
代码清单5-32 Canny()函数原型
1. void cv::Canny(InputArray image,
2. OutputArray edges,
3. double threshold1,
4. double threshold2,
5. int apertureSize = 3,
6. bool L2gradient = false
7. )
image:输入图像,必须是CV_8U的单通道或者三通道图像。
edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U。
threshold1:第一个滞后阈值。
threshold2:第二个滞后阈值。
apertureSize:Sobel算子的直径。
L2gradient:计算图像梯度幅值方法的标志,幅值的两种计算方式如式(5.25)所示。
该函数利用Canny算法提取图像中的边缘信息。第一个参数是需要提取边缘的输入图像,目前只支持数据类型为CV_8U的图像,输入图像可以是灰度图像或者彩色图像。第二个参数是边缘检测结果的输出图像,图像是数据类型为为CV_8U的单通道灰度图像。函数第三个和第四个参数是Canny算法中用于区分强边缘和弱边缘的两个阈值,两个参数不区分较大阈值和较小阈值,函数会自动比较区分两个阈值的大小,不过一般情况下,较大阈值与较小阈值的比值在2:1到3:1之间。函数最后一个参数是计算梯度幅值方法的选择标志,无特殊需求的情况下,使用默认值即可。
为了更好的理解Canny()函数的使用方法,在代码清单5-33中给出了利用Canny()函数检测图像边缘的示例程序。程序中通过设置不同的阈值来比较阈值的大小对图像边缘检测效果的影响,程序的输出结果在图5-35给出。通过结果可以发现,较高的阈值会降低噪声信息对图像提取边缘结果的影响,但是同时也会减少结果中的边缘信息。同时程序中先对图像进行高斯模糊后再进行边缘检测,结果表明高斯模糊在边缘纹理较多的区域能减少边缘检测的结果,但是对纹理较少的区域影响较小。
代码清单5-33 myCanny.cpp利用Canny算法提取图像边缘
1. #include <opencv2\opencv.hpp>
2. #include <iostream>
3.
4. using namespace cv;
5. using namespace std;
6.
7. int main()
8. {
9. //读取图像,黑白图像边缘检测结果较为明显
10. Mat img = imread("equalLena.png", IMREAD_ANYDEPTH);
11. if (img.empty())
12. {
13. cout << "请确认图像文件名称是否正确" << endl;
14. return -1;
15. }
16. Mat resultHigh, resultLow, resultG;
17.
18. //大阈值检测图像边缘
19. Canny(img, resultHigh, 100, 200, 3);
20.
21. //小阈值检测图像边缘
22. Canny(img, resultLow, 20, 40, 3);
23.
24. //高斯模糊后检测图像边缘
25. GaussianBlur(img, resultG, Size(3, 3), 5);
26. Canny(resultG, resultG, 100, 200, 3);
27.
28. //显示图像
29. imshow("resultHigh", resultHigh);
30. imshow("resultLow", resultLow);
31. imshow("resultG", resultG);
32. waitKey(0);
33. return 0;
34. }
图5-35 myCanny.cpp程序中图像提取边缘结果