(1) 低失误概率;(2) 高位置精度;(3) 对每个边缘有唯一的响应。
换句话说边缘检测问题就是要做到: 抑制噪声,精确定位边缘
Canny 边缘检测算法的主要计算步骤包括四个方面:
(1) 利用高斯滤波器对原始图像进行平滑滤波,以提高算法的抗噪性。
(2) 用一阶有限差分近似代替偏导数,计算图像梯度强度和方向。计算梯度可以利用先前的Roberts、 Prewitt、Sobel等算子(文中用的是Prewitt 算子)。其中,方向信息是为了下一步计算需要。
(3) 利用第(2)步梯度方向划分(划分为四个方向 0\ -45\ 90 \45 )进行梯度强度的非极大抑制,获取单像素边缘点。
(4) 双(或滞后)阈值进行边缘的二值化。
目前,Canny提出的以上边缘计算策略已经成为图像边缘检测的精髓,且已融入到各种经典检测算子和一些新的边缘检测方法之中。
有不少学者发表论文,认为Roberts、Prewitt、Sobel等梯度算子不能获取单像素边缘,并进行必要的计算结果对比展示。实际上,以上简单的梯度算子与图像进行卷积滤波,仅仅是对图像的一个锐化过程,得到的仅是原始图像的梯度图,而不是最终的边缘结果,当然不会是单像素边缘。
然而,我们发现Matlab图像处理工具中的各种经典算子,最后得到的处理结果就都是单像素边缘、而且效果与Canny算法的差异不大。这是因为Matlab工具中的各种经典边缘算子,计算过程中引入了非极大抑制(即边缘细化)、双阈值边缘二值化等Canny算法策略。因此,可以获得与Canny算法效果相当的边缘结果。自己编程计算时,一般与算子(模板)进行卷积计算后,进行简单阈值分割,看起来处理效果总是不理想。
import cv2
import numpy as np
m1 = np.array([[-1,0,1],[-1,0,1],[-1,0,1]])
m2 = np.array([[-1,-1,-1],[0,0,0],[1,1,1]])
from matplotlib import pyplot as plt
# 第一步:完成高斯平滑滤波
img = cv2.imread("rice.jpg",0)
img = cv2.GaussianBlur(img,(3,3),2)
# 第二步:完成一阶有限差分计算,计算每一点的梯度幅值与方向
img1 = np.zeros(img.shape,dtype="uint8") # 与原图大小相同
theta = np.zeros(img.shape,dtype="float") # 方向矩阵原图像大小
img = cv2.copyMakeBorder(img,1,1,1,1,borderType=cv2.BORDER_REPLICATE)
rows,cols = img.shape
for i in range(1,rows-1):
for j in range(1,cols-1):
# Gy
Gy = (np.dot(np.array([1, 1, 1]), (m1 * img[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]]))
# Gx
Gx = (np.dot(np.array([1, 1, 1]), (m2 * img[i - 1:i + 2, j - 1:j + 2]))).dot(np.array([[1], [1], [1]]))
if Gx[0] == 0:
theta[i-1,j-1] = 90
continue
else:
temp = (np.arctan(Gy[0] / Gx[0]) ) * 180 / np.pi
if Gx[0]*Gy[0] > 0:
if Gx[0] > 0:
theta[i-1,j-1] = np.abs(temp)
else:
theta[i-1,j-1] = (np.abs(temp) - 180)
if Gx[0] * Gy[0] < 0:
if Gx[0] > 0:
theta[i-1,j-1] = (-1) * np.abs(temp)
else:
theta[i-1,j-1] = 180 - np.abs(temp)
img1[i-1,j-1] = (np.sqrt(Gx**2 + Gy**2))
for i in range(1,rows - 2):
for j in range(1, cols - 2):
if ( ( (theta[i,j] >= -22.5) and (theta[i,j]< 22.5) ) or
( (theta[i,j] <= -157.5) and (theta[i,j] >= -180) ) or
( (theta[i,j] >= 157.5) and (theta[i,j] < 180) ) ):
theta[i,j] = 0.0
elif ( ( (theta[i,j] >= 22.5) and (theta[i,j]< 67.5) ) or
( (theta[i,j] <= -112.5) and (theta[i,j] >= -157.5) ) ):
theta[i,j] = 45.0
elif ( ( (theta[i,j] >= 67.5) and (theta[i,j]< 112.5) ) or
( (theta[i,j] <= -67.5) and (theta[i,j] >= -112.5) ) ):
theta[i,j] = 90.0
elif ( ( (theta[i,j] >= 112.5) and (theta[i,j]< 157.5) ) or
( (theta[i,j] <= -22.5) and (theta[i,j] >= -67.5) ) ):
theta[i,j] = -45.0
# 第三步:进行 非极大值抑制计算
img2 = np.zeros(img1.shape) # 非极大值抑制图像矩阵
for i in range(1,img2.shape[0]-1):
for j in range(1,img2.shape[1]-1):
if (theta[i,j] == 0.0) and (img1[i,j] == np.max([img1[i,j],img1[i+1,j],img1[i-1,j]]) ):
img2[i,j] = img1[i,j]
if (theta[i,j] == -45.0) and img1[i,j] == np.max([img1[i,j],img1[i-1,j-1],img1[i+1,j+1]]):
img2[i,j] = img1[i,j]
if (theta[i,j] == 90.0) and img1[i,j] == np.max([img1[i,j],img1[i,j+1],img1[i,j-1]]):
img2[i,j] = img1[i,j]
if (theta[i,j] == 45.0) and img1[i,j] == np.max([img1[i,j],img1[i-1,j+1],img1[i+1,j-1]]):
img2[i,j] = img1[i,j]
# 第四步:双阈值检测和边缘连接
img3 = np.zeros(img2.shape) #定义双阈值图像
# TL = 0.4*np.max(img2)
# TH = 0.5*np.max(img2)
TL = 50
TH = 100
#关键在这两个阈值的选择
for i in range(1,img3.shape[0]-1):
for j in range(1,img3.shape[1]-1):
if img2[i,j] < TL:
img3[i,j] = 0
elif img2[i,j] > TH:
img3[i,j] = 255
elif (( img2[i+1,j] < TH) or (img2[i-1,j] < TH )or( img2[i,j+1] < TH )or
(img2[i,j-1] < TH) or (img2[i-1, j-1] < TH )or ( img2[i-1, j+1] < TH) or
( img2[i+1, j+1] < TH ) or ( img2[i+1, j-1] < TH) ):
img3[i,j] = 255
cv2.imshow("1",img) # 原始图像
cv2.imshow("2",img1) # 梯度幅值图
cv2.imshow("3",img2) #非极大值抑制灰度图
cv2.imshow("4",img3) # 最终效果图
cv2.imshow("theta",theta) #角度值灰度图
cv2.waitKey(0)
- 实验效果图
rice.jpg 效果图
lena.tiff 效果图
参考博文:http://blog.sciencenet.cn/blog-425437-776470.html