这节主要介绍一下Canny
边缘检测算法。
基本思想序言
Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计算理论(Computational theory of edge detection)解释这项技术如何工作。
Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
-
最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
-
最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
-
检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。
算法流程及框架
这里就逐渐开始进入正片了,之前那么多的工作都是铺垫,现在慢慢地就能做一些看起来比较有用的东西了。
算法的实现步骤
Canny
边缘检测主要分成以下几个步骤:
-
使用高斯滤波器,以平滑图像,滤除噪声。
-
计算图像中每个像素点的梯度强度和方向。
-
应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
-
应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
-
通过抑制孤立的弱边缘最终完成边缘检测。
- 第一:对图像进行平滑处理一下,相当于过滤掉噪声。这里做这一步的原因就是我们是在检测边缘,而检测边缘一般是用求梯度的方法计算的,而求梯度的时候当然不希望把噪声作为边缘梯度给求进去,所以需要高斯滤波一下,去掉这些噪声点。
高斯滤波器中间比较大,边缘比较小,对其进行归一化处理后,得到下面这个结果:
H = [ 0.0924 0.1192 0.0924 0.1192 0.1538 0.1192 0.0924 0.1192 0.0924 ] H=\left[\begin{array}{lll} {0.0924} & {0.1192} & {0.0924} \\ {0.1192} & {0.1538} & {0.1192} \\ {0.0924} & {0.1192} & {0.0924} \end{array}\right] H= 0.09240.11920.09240.11920.15380.11920.09240.11920.0924
高斯滤波的计算过程如下所示:
e = H ∗ A = [ h 11 h 12 h 13 h 21 h 22 h 23 h 31 h 32 h 33 ] ∗ [ a b c d e f g h i ] = sum ( [ a × h 11 b × h 12 c × h 13 d × h 21 e × h 22 f × h 23 g × h 31 h × h 32 i × h 33 ] ) e=H * A=\left[\begin{array}{lll} {\mathrm{h}_{11}} & {\mathrm{h}_{12}} & {\mathrm{h}_{13}} \\ {\mathrm{h}_{21}} & {\mathrm{h}_{22}} & {\mathrm{h}_{23}} \\ {\mathrm{h}_{31}} & {\mathrm{h}_{32}} & {\mathrm{h}_{33}} \end{array}\right] *\left[\begin{array}{ccc} {a} & {b} & {c} \\ {d} & {e} & {f} \\ {g} & {h} & {i} \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} {\mathrm{a} \times \mathrm{h}_{11}} & {\mathrm{b} \times \mathrm{h}_{12}} & {\mathrm{c} \times \mathrm{h}_{13}} \\ {\mathrm{d} \times \mathrm{h}_{21}} & {\mathrm{e} \times \mathrm{h}_{22}} & {\mathrm{f} \times \mathrm{h}_{23}} \\ {\mathrm{g} \times \mathrm{h}_{31}} & {\mathrm{h} \times \mathrm{h}_{32}} & {\mathrm{i} \times \mathrm{h}_{33}} \end{array}\right]\right) e=H∗A= h11h21h31h12h22h32h13h23h33 ∗ adgbehcfi =sum a×h11d×h21g×h31b×h12e×h22h×h32c×h13f×h23i×h33
- 第二:就是既要计算梯度的大小还要计算梯度的方向
这里用sobel
算子计算
x
x
x和
y
y
y方向的梯度:
sobel
算子如下所示:
S x = [ − 1 0 1 − 2 0 2 − 1 0 1 ] S y = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] S_{x}=\left[\begin{array}{rrr} {-1} & {0} & {1} \\ {-2} & {0} & {2} \\ {-1} & {0} & {1} \end{array}\right] \quad S_{y}=\left[\begin{array}{ccc} {1} & {2} & {1} \\ {0} & {0} & {0} \\ {-1} & {-2} & {-1} \end{array}\right] Sx= −1−2−1000121 Sy= 10−120−210−1
计算细节:
G x = S x ∗ A = [ − 1 0 1 − 2 0 2 − 1 0 1 ] ∗ [ a b c d e f g h i ] = sum ( [ − a 0 c − 2 d 0 2 f − g 0 i ] ) G y = S y ∗ A = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] ∗ [ a b c d e f g h i ] = sum ( [ a 2 b c 0 0 0 − g − 2 h − i ] ) \begin{array}{l} {G_{x}=S_{x} * A=\left[\begin{array}{ccc} {-1} & {0} & {1} \\ {-2} & {0} & {2} \\ {-1} & {0} & {1} \end{array}\right] *\left[\begin{array}{ccc} {a} & {b} & {c} \\ {d} & {e} & {f} \\ {g} & {h} & {i} \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} {-a} & {0} & {c} \\ {-2 d} & {0} & {2 f} \\ {-g} & {0} & {i} \end{array}\right]\right)} \\ {G_{y}=S_{y} * A=\left[\begin{array}{ccc} {1} & {2} & {1} \\ {0} & {0} & {0} \\ {-1} & {-2} & {-1} \end{array}\right] *\left[\begin{array}{ccc} {a} & {b} & {c} \\ {d} & {e} & {f} \\ {g} & {h} & {i} \end{array}\right]=\operatorname{sum}\left(\left[\begin{array}{ccc} {a} & {2 b} & {c} \\ {0} & {0} & {0} \\ {-g} & {-2 h} & {-i} \end{array}\right]\right)} \end{array} Gx=Sx∗A= −1−2−1000121 ∗ adgbehcfi =sum −a−2d−g000c2fi Gy=Sy∗A= 10−120−210−1 ∗ adgbehcfi =sum a0−g2b0−2hc0−i
方向计算:
G = G x 2 + G y 2 θ = arctan ( G y / G x ) \begin{aligned} &G=\sqrt{G_{x}^{2}+G_{y}^{2}}\\ &\theta=\arctan \left(G_{y} / G_{x}\right) \end{aligned} G=Gx2+Gy2θ=arctan(Gy/Gx)
- 第三:算完梯度之后,我们需要保留梯度最明显的,所以做非极大值抑制。这个思想还是蛮重要的,比如检测一个目标,假设是只猫,那有时候你看半只猫的图片也能看出这是一只猫,但是如果你看全部的话,你的置信度是不一样的,所以取最大的那一个就显得比较重要了。
这里介绍两种非极大值抑制的方法:
- 第四:双阈值。
这个也是我们之后给定函数需要传入进去的两个参数,其大概的思想如下图所示:
- 第五:综合起来得到最终的边缘检测任务。
代码:
import cv2
import numpy as np
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)
res = np.hstack((v1,v2))
cv_show(res,'res')
检测结果:
我的微信公众号名称:小小何先生
公众号介绍:主要研究分享深度学习、机器博弈、强化学习等相关内容!期待您的关注,欢迎一起学习交流进步!