1、主要参考
(1)官网:https://docs.opencv.org/
(2)很不错:https://blog.csdn.net/WZZ18191171661/article/details/89762062
(3)非常不错:https://zhuanlan.zhihu.com/p/651610886
2、原理
2.1函数定义
(1)python
cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] ) -> edges
(2)官网
(3)参数,参考官网
- 第1个参数是(8-bit input image)灰度输入图像;
- 第2个参数,输出边缘(8位图,和输入尺寸相同)
- 第3个参数,threshold1 first threshold for the hysteresis procedure(哈哈).
- 第4个参数,second threshold for the hysteresis procedure(哈哈).
- 第5个参数用来计算图像梯度的 Sobel 卷积核的大小,默认值为 3;
- 第6个参数是 L2gradient,用来设定求梯度大小的方程。如果设为 True,就会使用我们上面提到过的方程,否则使用方程:代替,默认值为 False。
2.2补充第3个参数和第4个参数说明
这两个参数:一个低阈值TL和一个高阈值TH,比例在1:2到1:3内
双阈值检测是确定那些边界才是真正的边界。需要设置两个阈值:minVal 和 maxVal
这里需要判断图像的灰度梯度与 maxVal 和 minVal 的大小:
- 梯度值 > maxVal : 视为边界
- minVal < 梯度值 < maxVal : 是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃
- 梯度值 < minVal :舍弃
例如下图举例说明:
A 高于阈值 maxVal 所以是真正的边界点,C 虽然低于 maxVal 但高于 minVal 并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因为他不仅低于 maxVal 而且不与真正的边界点相连。所以选择合适的 maxVal 和 minVal 对于能否得到好的结果非常重要。 在这一步一些小的噪声点也会被除去,因为已经假设边界都是一些长的线段。
PS:第3和第4个参数的如何设定很重要,下面第4章再说
2.2补充第6个参数说明
第6个参数,如果是True,计算公式是L2距离(这个更OK,PS)
第6个参数,如果是False,计算公式是L1距离
官网解释如下:
3详细描述
主要参考了https://blog.csdn.net/WZZ18191171661/article/details/89762062
3.1什么是Canny边缘检测算法?
Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计算理论(Computational theory of edge detection)解释这项技术如何工作。
通常情况下边缘检测的目的是在保留原有图像属性的情况下,显著减少图像的数据规模。目前有多种算法可以进行边缘检测,虽然Canny算法年代久远,但可以说它是边缘检测的一种标准算法,而且仍在研究中广泛使用。其效果如下图所示:
3.2最优边缘准则是什么
- 最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
- 最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
- 检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。为了满足这些要求 Canny 使用了变分法(calculus of variations),这是一种寻找优化特定功能的函数的方法。最优检测使用四个指数函数项表示,但是它非常近似于高斯函数的一阶导数
3.3Canny算法实现步骤
- (1)应用高斯滤波来平滑图像,目的是去除噪声;
- (2)计算图像的强度梯度(intensity gradients);
- (3)应用非最大抑制(non-maximum suppression)技术来消除边误检(本来不是但检测出来是);
- (4)应用双阈值的方法来决定可能的(潜在的)边界;
- (5)利用滞后技术来跟踪边界。
4、双阈值参数自动选择方法1(还行)
Canny的两个参数如何定合适,下面的例子给出了说明
import numpy as np
import cv2
# import argparse
# import glob
# import os
# 定义auto_canny函数
def auto_canny(image, sigma=0.33):
# 计算单通道像素强度的中位数
v = np.median(image)
# 选择合适的lower和upper值,然后应用它们
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
# edged = cv2.Canny(image, lower, upper)
# edged = cv2.Canny(image, lower, upper)
edged = cv2.Canny(image, lower, upper,L2gradient=True)
return edged
if __name__ == '__main__':
# 读取图片
image = cv2.imread("D:/chen_visionprojectdevelop/0pic/dstyaowang.bmp")
# image = cv2.imread("D:/chen_visionprojectdevelop/0pic/Dst1.bmp")
# image = cv2.imread("D:/chen_visionprojectdevelop/0pic/Src2.jpg")
# 灰度化处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 进行高斯模糊去噪
# blurred = cv2.GaussianBlur(gray, (3, 3), 0)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
# blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# blurred = cv2.GaussianBlur(gray, (5, 5), 10)
# 分别使用宽阈值、窄阈值和自动确定的阈值进行测试
wide = cv2.Canny(blurred, 10, 200,L2gradient=True)
# tight = cv2.Canny(blurred, 225, 250)
# tight = cv2.Canny(blurred, 225, 250)
tight = cv2.Canny(blurred, 280, 360)
auto = auto_canny(blurred,0.1)
# auto = auto_canny(blurred,2)
result = np.hstack([wide, tight, auto])
# 显示并保存结果
cv2.namedWindow("Original", cv2.WINDOW_NORMAL)
cv2.namedWindow("Edges", cv2.WINDOW_NORMAL)
cv2.imshow("Original", image)
cv2.imshow("Edges", result)
cv2.waitKey(0)
具体可以参考https://blog.csdn.net/WZZ18191171661/article/details/89762062
注意:自动缘分阈值的sigma需要考虑如何合适,大部分图0.33是合适的
5、双阈值参数自动选择方法2(感觉更)
参考了:https://blog.csdn.net/weixin_43271137/article/details/130035772?share_token=11191b94-559f-48d4-9186-6a30a1a68644
(1)优化选择的代码
def auto_canny2(image):
# 计算单通道像素强度的中位数
v = np.median(image)
# 选择合适的lower和upper值,然后应用它们
# 可以将高阈值设置为平均灰度值的1.5倍或2倍。
upper = int(2*v)
# 可以将低阈值设置为高阈值的1/3或1/4。
lower = int(upper/3)
# edged = cv2.Canny(image, lower, upper)
# edged = cv2.Canny(image, lower, upper)
edged = cv2.Canny(image, lower, upper,L2gradient=True)
return edged
(2)完整测试,修改字数受限,简化
...................
...................
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
...................
...................
auto2 = auto_canny2(blurred)
...................
6、总结
(1)高斯核选择(5,5)
(2)参数选择很重要,要么手动调不鲁棒,要么自动算法。感觉方法2的自动算还可以
(3)高斯中使用sobel算子的评价,感觉L2比较好。也就是,L2gradient=True