zoukankan      html  css  js  c++  java
  • 实现Canny算法 python

    创作很累,如果您觉得对您有帮助,请点赞支持,感谢!


    一. 总的算法流程:

    ① 使用高斯滤波器滤波

    ② 使用 Sobel 滤波器滤波获得在 x 和 y 方向上的输出,在此基础上求出边缘的强度和边缘的角度


    edge 为边缘强度,tan 为边缘角度 ↑
     

    ③ 对边缘角度进行量化处理


    对边缘角度进行量化处理算法 ↑
     

    ④ 根据边缘角度对边缘强度进行非极大值抑制(Non-maximum suppression),使图像边缘变得更细


    非极大值抑制算法:0°时取(x,y)、(x+1,y)、(x-1,y) 中的最大值,其它角度类似 ↑
     

    ⑤ 使用滞后阈值对图像进行二值化处理,优化图像显示效果


    算法如上所示 ↑
     

    ⑥ 输出图像边缘提取效果


    二. 使用python手动实现 Canny 算法,完成图像边缘提取

      1 # writer:wojianxinygcl@163.com
      2 # date  :2020.3.20
      3 import cv2
      4 import numpy as np
      5 import matplotlib.pyplot as plt
      6 
      7 def Canny(img):
      8 
      9     # Gray scale
     10     def BGR2GRAY(img):
     11         b = img[:, :, 0].copy()
     12         g = img[:, :, 1].copy()
     13         r = img[:, :, 2].copy()
     14 
     15         # Gray scale
     16         out = 0.2126 * r + 0.7152 * g + 0.0722 * b
     17         out = out.astype(np.uint8)
     18 
     19         return out
     20 
     21 
     22     # Gaussian filter for grayscale
     23     def gaussian_filter(img, K_size=3, sigma=1.4):
     24 
     25         if len(img.shape) == 3:
     26             H, W, C = img.shape
     27             gray = False
     28         else:
     29             img = np.expand_dims(img, axis=-1)
     30             H, W, C = img.shape
     31             gray = True
     32 
     33         ## Zero padding
     34         pad = K_size // 2
     35         out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float)
     36         out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
     37 
     38         ## prepare Kernel
     39         K = np.zeros((K_size, K_size), dtype=np.float)
     40         for x in range(-pad, -pad + K_size):
     41             for y in range(-pad, -pad + K_size):
     42                 K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma * sigma))
     43         #K /= (sigma * np.sqrt(2 * np.pi))
     44         K /= (2 * np.pi * sigma * sigma)
     45         K /= K.sum()
     46 
     47         tmp = out.copy()
     48 
     49         # filtering
     50         for y in range(H):
     51             for x in range(W):
     52                 for c in range(C):
     53                     out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x + K_size, c])
     54 
     55         out = np.clip(out, 0, 255)
     56         out = out[pad : pad + H, pad : pad + W]
     57         out = out.astype(np.uint8)
     58 
     59         if gray:
     60             out = out[..., 0]
     61 
     62         return out
     63 
     64 
     65     # sobel filter
     66     def sobel_filter(img, K_size=3):
     67         if len(img.shape) == 3:
     68             H, W, C = img.shape
     69         else:
     70             H, W = img.shape
     71 
     72         # Zero padding
     73         pad = K_size // 2
     74         out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
     75         out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
     76         tmp = out.copy()
     77 
     78         out_v = out.copy()
     79         out_h = out.copy()
     80 
     81         ## Sobel vertical
     82         Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]
     83         ## Sobel horizontal
     84         Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]
     85 
     86         # filtering
     87         for y in range(H):
     88             for x in range(W):
     89                 out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x + K_size]))
     90                 out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x + K_size]))
     91 
     92         out_v = np.clip(out_v, 0, 255)
     93         out_h = np.clip(out_h, 0, 255)
     94 
     95         out_v = out_v[pad : pad + H, pad : pad + W]
     96         out_v = out_v.astype(np.uint8)
     97         out_h = out_h[pad : pad + H, pad : pad + W]
     98         out_h = out_h.astype(np.uint8)
     99 
    100         return out_v, out_h
    101 
    102 
    103     # get edge strength and edge angle
    104     def get_edge_angle(fx, fy):
    105         # get edge strength
    106         edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2))
    107         edge = np.clip(edge, 0, 255)
    108 
    109         # make sure the denominator is not 0
    110         fx = np.maximum(fx, 1e-10)
    111         #fx[np.abs(fx) <= 1e-5] = 1e-5
    112 
    113         # get edge angle
    114         angle = np.arctan(fy / fx)
    115 
    116         return edge, angle
    117 
    118     
    119     # 将角度量化为0°、45°、90°、135°
    120     def angle_quantization(angle):
    121         angle = angle / np.pi * 180
    122         angle[angle < -22.5] = 180 + angle[angle < -22.5]
    123         _angle = np.zeros_like(angle, dtype=np.uint8)
    124         _angle[np.where(angle <= 22.5)] = 0
    125         _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45
    126         _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90
    127         _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135
    128 
    129         return _angle
    130 
    131 
    132     def non_maximum_suppression(angle, edge):
    133         H, W = angle.shape
    134         _edge = edge.copy()
    135         
    136         for y in range(H):
    137             for x in range(W):
    138                     if angle[y, x] == 0:
    139                             dx1, dy1, dx2, dy2 = -1, 0, 1, 0
    140                     elif angle[y, x] == 45:
    141                             dx1, dy1, dx2, dy2 = -1, 1, 1, -1
    142                     elif angle[y, x] == 90:
    143                             dx1, dy1, dx2, dy2 = 0, -1, 0, 1
    144                     elif angle[y, x] == 135:
    145                             dx1, dy1, dx2, dy2 = -1, -1, 1, 1
    146                     # 边界处理
    147                     if x == 0:
    148                             dx1 = max(dx1, 0)
    149                             dx2 = max(dx2, 0)
    150                     if x == W-1:
    151                             dx1 = min(dx1, 0)
    152                             dx2 = min(dx2, 0)
    153                     if y == 0:
    154                             dy1 = max(dy1, 0)
    155                             dy2 = max(dy2, 0)
    156                     if y == H-1:
    157                             dy1 = min(dy1, 0)
    158                             dy2 = min(dy2, 0)
    159                     # 如果不是最大值,则将这个位置像素值置为0
    160                     if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) != edge[y, x]:
    161                             _edge[y, x] = 0
    162 
    163         return _edge
    164 
    165 
    166     # 滞后阈值处理二值化图像
    167     # > HT 的设为255,< LT 的设置0,介于它们两个中间的值,使用8邻域判断法
    168     def hysterisis(edge, HT=100, LT=30):
    169         H, W = edge.shape
    170 
    171         # Histeresis threshold
    172         edge[edge >= HT] = 255
    173         edge[edge <= LT] = 0
    174 
    175         _edge = np.zeros((H + 2, W + 2), dtype=np.float32)
    176         _edge[1 : H + 1, 1 : W + 1] = edge
    177 
    178         ## 8 - Nearest neighbor
    179         nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32)
    180 
    181         for y in range(1, H+2):
    182                 for x in range(1, W+2):
    183                         if _edge[y, x] < LT or _edge[y, x] > HT:
    184                                 continue
    185                         if np.max(_edge[y-1:y+2, x-1:x+2] * nn) >= HT:
    186                                 _edge[y, x] = 255
    187                         else:
    188                                 _edge[y, x] = 0
    189 
    190         edge = _edge[1:H+1, 1:W+1]
    191                                 
    192         return edge
    193 
    194     # grayscale
    195     gray = BGR2GRAY(img)
    196 
    197     # gaussian filtering
    198     gaussian = gaussian_filter(gray, K_size=5, sigma=1.4)
    199 
    200     # sobel filtering
    201     fy, fx = sobel_filter(gaussian, K_size=3)
    202 
    203     # get edge strength, angle
    204     edge, angle = get_edge_angle(fx, fy)
    205 
    206     # angle quantization
    207     angle = angle_quantization(angle)
    208 
    209     # non maximum suppression
    210     edge = non_maximum_suppression(angle, edge)
    211 
    212     # hysterisis threshold
    213     out = hysterisis(edge, 80, 20)
    214 
    215     return out
    216 
    217 
    218 if __name__ == '__main__':
    219     # Read image
    220     img = cv2.imread("../paojie.jpg").astype(np.float32)
    221 
    222     # Canny
    223     edge = Canny(img)
    224 
    225     out = edge.astype(np.uint8)
    226 
    227     # Save result
    228     cv2.imwrite("out.jpg", out)
    229     cv2.imshow("result", out)
    230     cv2.waitKey(0)
    231     cv2.destroyAllWindows()

    三. 实验结果:


    原图 ↑
     

    Canny 算法 提取图像边缘结果 ↑
     

      可以看到,我的代码如愿以偿地提取了图像边缘,而且效果很好!


    四. 参考内容:

       https://www.jianshu.com/p/ff4c1a6a68d8


    五. 版权声明:

      未经作者允许,请勿随意转载抄袭,抄袭情节严重者,作者将考虑追究其法律责任,创作不易,感谢您的理解和配合!

  • 相关阅读:
    python
    【转载】【CPU】关于x86、x86_64/x64、amd64和arm64/aarch64
    【百思不得其解1】诡异的速度差异
    LeakCanary 与 鹅场Matrix ResourceCanary对比分析
    android 插件化框架VitualAPK
    android高级篇收录
    滴滴Booster移动APP质量优化框架 学习之旅 三
    滴滴Booster移动APP质量优化框架 学习之旅 二
    滴滴Booster移动APP质量优化框架 学习之旅
    不一样视角的Glide剖析
  • 原文地址:https://www.cnblogs.com/wojianxin/p/12533526.html
Copyright © 2011-2022 走看看