canny 算子python實現


1. Canny介紹

Canny算子與Marr(LoG)邊緣檢測方法類似,也屬於是先平滑后求導數的方法。John Canny研究了最優邊緣檢測方法所需的特性,給出了評價邊緣檢測性能優劣的三個指標:

  • 1  好的信噪比,即將非邊緣點判定為邊緣點的概率要低,將邊緣點判為非邊緣點的概率要低;
  • 2  高的定位性能,即檢測出的邊緣點要盡可能在實際邊緣的中心;
  • 3  對單一邊緣僅有唯一響應,即單個邊緣產生多個響應的概率要低,並且虛假響應邊緣應該得到最大抑制。

 

2. Canny檢測實現過程

第一步:灰度化 

第二步:高斯濾波 

              首先生成二維高斯分布矩陣:

              然后與灰度圖像進行卷積實現濾波:

第三步:計算梯度值和方向 

              求變化率時,對於一元函數,即求導;對於二元函數,求偏導。 數字圖像處理中,用一階有限差分近似求取灰度值的梯度值(變化率)
             (即:使差商(Δf/Δx)(f/x)xyx 方向一階偏導) 。

             

             其中f為圖像灰度值,P代表X方向梯度幅值,Q代表Y方向 梯度幅值,M是該點幅值Θ是梯度方向,也就是角度。

             注:圖像梯度方向與邊緣方向互相垂直:

            

 

第四步:非極大值抑制(NMS)

             通俗意義上是指尋找像素點局部最大值。沿着梯度方向,比較它前面和后面的梯度值。在沿其方向上鄰域的梯度幅值最大,則保留;否則,抑制

             具體參考此文:canny 非極大值抑制 NMS

             可以進行插值來提高結果。

第五步:雙閾值的選取、邊緣連接

  • 選取高閾值2:13:1T=0.3 或 0.2T=0.1 
  • 取出非極大值抑制后的圖像中的最大梯度幅值,重新定義高低閾值。即:×Max,×Ma 。(當然可以自己給定)
  • T01。 
  • TT使8T1) 

 

 

3. Canny檢測Python實現

具體實現略有不同,例如:

高斯矩陣的實現過程、梯度幅值的實現過程、非極大值抑制的角度選取(可以選0,45,90,135)、邊緣檢測的實現過程。

# -*- coding: utf-8 -*-
"""
Created on Thu Dec  7 21:12:41 2017

@author: L.P.S
"""

import matplotlib.pyplot as plt
import numpy as np
import math

img = plt.imread('G:\\360downloads\\lps.png')

sigma1 = sigma2 = 1
sum = 0

gaussian = np.zeros([5, 5])
for i in range(5):
    for j in range(5):
        gaussian[i,j] = math.exp(-1/2 * (np.square(i-3)/np.square(sigma1)           #生成二維高斯分布矩陣
                        + (np.square(j-3)/np.square(sigma2)))) / (2*math.pi*sigma1*sigma2)
        sum = sum + gaussian[i, j]
        
gaussian = gaussian/sum
# print(gaussian)

def rgb2gray(rgb):
    return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])


# step1.高斯濾波
gray = rgb2gray(img)
W, H = gray.shape
new_gray = np.zeros([W-5, H-5])
for i in range(W-5):
    for j in range(H-5):
        new_gray[i,j] = np.sum(gray[i:i+5,j:j+5]*gaussian)   # 與高斯矩陣卷積實現濾波 

# plt.imshow(new_gray, cmap="gray")
     
        
# step2.增強 通過求梯度幅值
W1, H1 = new_gray.shape
dx = np.zeros([W1-1, H1-1])
dy = np.zeros([W1-1, H1-1])
d = np.zeros([W1-1, H1-1])
for i in range(W1-1):
    for j in range(H1-1):   
        dx[i,j] = new_gray[i, j+1] - new_gray[i, j]
        dy[i,j] = new_gray[i+1, j] - new_gray[i, j]        
        d[i, j] = np.sqrt(np.square(dx[i,j]) + np.square(dy[i,j]))   # 圖像梯度幅值作為圖像強度值
         
# plt.imshow(d, cmap="gray")
      
        
# setp3.非極大值抑制 NMS
W2, H2 = d.shape
NMS = np.copy(d)
NMS[0,:] = NMS[W2-1,:] = NMS[:,0] = NMS[:, H2-1] = 0
for i in range(1, W2-1):
    for j in range(1, H2-1):
        
        if d[i, j] == 0:
            NMS[i, j] = 0
        else:
            gradX = dx[i, j]
            gradY = dy[i, j]
            gradTemp = d[i, j]
            
            # 如果Y方向幅度值較大
            if np.abs(gradY) > np.abs(gradX):
                weight = np.abs(gradX) / np.abs(gradY)
                grad2 = d[i-1, j]
                grad4 = d[i+1, j]
                # 如果x,y方向梯度符號相同
                if gradX * gradY > 0:
                    grad1 = d[i-1, j-1]
                    grad3 = d[i+1, j+1]
                # 如果x,y方向梯度符號相反
                else:
                    grad1 = d[i-1, j+1]
                    grad3 = d[i+1, j-1]
                    
            # 如果X方向幅度值較大
            else:
                weight = np.abs(gradY) / np.abs(gradX)
                grad2 = d[i, j-1]
                grad4 = d[i, j+1]
                # 如果x,y方向梯度符號相同
                if gradX * gradY > 0:
                    grad1 = d[i+1, j-1]
                    grad3 = d[i-1, j+1]
                # 如果x,y方向梯度符號相反
                else:
                    grad1 = d[i-1, j-1]
                    grad3 = d[i+1, j+1]
        
            gradTemp1 = weight * grad1 + (1-weight) * grad2
            gradTemp2 = weight * grad3 + (1-weight) * grad4
            if gradTemp >= gradTemp1 and gradTemp >= gradTemp2:
                NMS[i, j] = gradTemp
            else:
                NMS[i, j] = 0
        
# plt.imshow(NMS, cmap = "gray")


# step4. 雙閾值算法檢測、連接邊緣
W3, H3 = NMS.shape
DT = np.zeros([W3, H3])               
# 定義高低閾值
TL = 0.2 * np.max(NMS)
TH = 0.3 * np.max(NMS)
for i in range(1, W3-1):
    for j in range(1, H3-1):
        if (NMS[i, j] < TL):
            DT[i, j] = 0
        elif (NMS[i, j] > TH):
            DT[i, j] = 1
        elif ((NMS[i-1, j-1:j+1] < TH).any() or (NMS[i+1, j-1:j+1]).any() 
              or (NMS[i, [j-1, j+1]] < TH).any()):
            DT[i, j] = 1
        
plt.imshow(DT, cmap = "gray")            

 

 4. 實驗結果

                                      

                    原圖                                                       雙閾值:0.1*max, 0.3*max                                雙閾值:0.2*max, 0.3*max

 

 

 

參考:

算法解剖系列-Canny邊緣檢測原理及實現

Canny邊緣檢測詳解及編程實現

Canny算子中的非極大值抑制(Non-Maximum Suppression)分析

canny算子中非極大值抑制算法的理解

canny算子原理以及實現

圖像學習之如何理解方向梯度直方圖(Histogram Of Gradient)

論文:一種改進非極大值抑制的Canny邊緣檢測算法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM