步骤
1.平滑:模糊削弱噪声。
2. 计算梯度,用Sobel算子计算像素的梯度,计算得到垂直梯度和水平梯度,即为Gx和Gy, 像素的总梯度是
3. 细化边缘:用非极大值抑制,局部最大值保留为边缘。参考坐标如下:
4. 双阈值抑制:根据大小两个阈值(由编码者给定),如果某点的像素高于大阈值的确定为边缘,保留。低于小阈值的不是边缘,置为0。小阈值<某点的像素<=大阈值,遍历这个像素相邻的8个像素,如果8个中有一个的像素>大阈值,则说明,某点的像素与强边缘相连,则保留某点的像素。
代码实现:
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
def showImage(name,img):
plt.imshow(img,cmap="gray")
plt.title(name)
plt.show()
# 检测轮廓
def canny(img,th1,th2):
#平滑
img2=cv.GaussianBlur(img,(5,5),2)
#计算梯度
gradx=cv.Sobel(img,cv.CV_64F,1,0)
grady=cv.Sobel(img,cv.CV_64F,0,1)
# 计算每个像素的总梯度
R=np.sqrt((gradx)**2+(grady)**2)
# 计算梯度的方向 (gradx+1e-3)防止分母为0
T=np.arctan(grady/(gradx+1e-3))
#细化边缘
(h,w)=R.shape
img_thin=np.zeros_like(R)
# 细化边缘按照梯度方向,非极大值抑制
for i in range(1, h - 1):
for j in range(1, w - 1):
thetha = T[i, j]
# 像素和左右的比较
if -np.pi / 8 <= thetha < np.pi / 8:
if R[i, j] == np.max([R[i, j], R[i][j - 1], R[i][j + 1]]):
img_thin[i, j] = R[i, j]
# 像素和右上角和左下角比价
elif -3 * np.pi / 8 <= thetha < -np.pi / 8:
if R[i, j] == np.max([R[i, j], R[i + 1][j - 1], R[i - 1][j + 1]]):
img_thin[i, j] = R[i, j]
# 像素和右下角、左上角比较
elif np.pi / 8 <= thetha < 3 * np.pi / 8:
if R[i, j] == np.max([R[i, j], R[i + 1][j + 1], R[i - 1][j - 1]]):
img_thin[i, j] = R[i, j]
# 像素和上下比较
else:
if R[i, j] == np.max([R[i, j], R[i - 1][j], R[i + 1][j]]):
img_thin[i, j] = R[i, j]
(h,w)=img_thin.shape
#细化边缘,双边阈值
edge=np.zeros_like(img_thin,dtype=np.uint8)
for i in range(1,h-1):
for j in range(1,w-1):
#大于th2是强边缘,保留
if img_thin[i,j]>=th2:
edge[i,j]=255
#th1<像素<=th2,周围8像素有强边缘的话,保留,没有默认0
elif img_thin[i,j]>th1:
around=img_thin[i-1:i+2,j-1:j+2]
if around.max()>=th2:
edge[i,j]=255
return edge;
if __name__=="__main__":
img=cv.imread("../image/goldpig.jpg",0)
edge1=canny(img,75,200)
showImage("goldpig",img)
showImage("goldpig2",edge1)
原图和轮廓图对比: