OpenCV - day05 Canny_EdgeDetection

612 阅读3分钟

Canny边缘检测

具体流程如下:

  1. 使用高斯滤波器,用以平滑图像,滤除噪声 (噪声对边缘像素点会有影响)
  2. 计算图像中的每个像素点的梯度强度和方向
  3. 应用非极大值抑制(Non-Maximum Supression),来消除边缘检测带来的杂散响应
    • 极大值抑制:取不是最大的Class框,是极大值就被抑制而不见啦
    • 非极大值抑制:取最大的Class框,不是极大值就被抑制而不见啦
  4. 应用双阈值(Double-Threshold)检测来确定真实和潜在的边缘
  5. 通过抑制孤立的筛选边缘最终完成边缘检测

一、 高斯滤波器

下面还是进行了归一化处理(平均) H=[0.09240.11920.09240.11920.15380.11920.09240.11920.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]

下面是阵列乘法的计算过程: e=HA=[h11 h12 h13 h21 h22 h23 h31 h32 h33][abcdefghi]=sum([a×h11 b×h12c×h13 d×h21e×h22f×h23 g×h31 h×h32i×h33])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}{lll} \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),最终的这个 sum 值就是中心点值。

二、梯度和方向

G=Gx2+Gy2θ=arctan(Gy/Gx)这里的θ取值范围为[pi/2,pi/2]\begin{array}{l} G=\sqrt{G_{x}^{2}+G_{y}^{2}} \\ \theta=\arctan \left(G_{y} / G_{x}\right) \end{array}这里的\theta取值范围为[-pi / 2, pi / 2]。

下面是利用Sobel算子得出的横纵两个方向的梯度值:

Sx=[101202101]Sy=[121000121]\quad 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]

Gx=SxA=[101202101][abcdefghi]=sum([a0c2d02fg0i])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)

Gy=SyA=[121000121][abcdefghi]=sum([a2bc000g2hi])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)

三、非极大值抑制

1. 方法一

非极大值抑制1.PNG

  • 利用已知点的梯度来计算未知点的梯度
  • 得出点 c 的梯度后,若比 dump1 和 dump2 都大则保留(不是极大值就被抑制)

2. 方法二

非极大值抑制2.PNG

  • 谁近和谁比
  • 最大就留下

四、双阈值检测

双阈值检测.PNG

  • 也就是设上下限,只有当阈值大于maxVal或者 minVal < 阈值 < maxVal且该点和边界相连则将其保留

  • 连有边界怎么判断呢D8邻域是否有梯度超过MaxVal的像素点即为和边界相连

  • 双阈值检测的流程:

    1. 双阈值检测&边缘连接算法比较简单:先设置高、低两个阈值(一般高阈值是低阈值的2~3倍)
    2. 遍历整个灰度矩阵,若某点的梯度高于高阈值,则在结果中置1,若该点的梯度值低于低阈值,则在结果中置0
    3. 若该点的梯度值介于高低阈值之间,则需要进行如下判断:检查该点(将其视为中心点)的8邻域点,看是否存在梯度值高于高阈值的点,若存在,则说明该中心点和确定的边缘点相连接,故在结果中置1,否则置0。
import cv2
import matplotlib
import matplotlib.pyplot as plt #取别名(用于绘图展示)
import numpy as np #取别名,下面是notepad专用,立即显示图像
%matplotlib inline 
def cv_show(name, img): #定义函数用于显示图片,此处name为窗口名,img为cv2调用imread方法的返回值
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

五、上下限设置效果对比

img = cv2.imread('tgcf.png', cv2.IMREAD_GRAYSCALE)

cv_show("gray", img)
v1 = cv2.Canny(img, 80, 150) #上下限,过滤掉噪音点,但边缘信息较少
v2 = cv2.Canny(img, 20, 50) #边缘信息比较多,但更易手噪音点侵扰

res = np.hstack([v1, v2])
cv_show('Compare', res)

双阈值效果对比.PNG

p = cv2.imread('Park.jpg',cv2.IMREAD_GRAYSCALE)

cv_show('Park', p)
v1 = cv2.Canny(p, 100, 200)  #Canny直接把上述的所有5个步骤全部集成到一块了
v2 = cv2.Canny(p, 25, 50)

res = np.hstack([p, v1, v2])
cv_show('Compare', res)