对平滑后的图像使用Sobel 算子计算水平方向和竖直方向的一阶导数(图像梯度)(Gx 和Gy)。根据得到的这两幅梯度图(Gx 和Gy)找到边界的梯度和方向,梯度的方向一般总是与边界垂直。梯度方向被归为四类:垂直,水平,和两个对角线。
在获得梯度的方向和大小之后,应该对整幅图像做一个扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。
现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal 和maxVal。当图像的灰度梯度高于maxVal 时被认为是真的边界,那些低于minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。
A 高于阈值maxVal 所以是真正的边界点,C 虽然低于maxVal 但高于minVal 并且与A 相连,所以也被认为是真正的边界点。而B 就会被抛弃,因为他不仅低于maxVal 而且不与真正的边界点相连。所以选择合适的maxVal和minVal 对于能否得到好的结果非常重要。在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。
在OpenCV 中只需要一个函数:cv2.Canny(),就可以完成以上几步。让我们看如何使用这个函数。这个函数的第一个参数是输入图像。第二和第三个分别是minVal 和maxVal。第三个参数设置用来计算图像梯度的Sobel卷积核的大小,默认值为3。 最后一个参数是L2gradient,它可以用来设定求梯度大小的方程。如果设为True(L2gradient参数表示L2gradient参数表示一个布尔值,如果为真,则使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开方),否则使用L1范数(直接将两个方向导数的绝对值相加),就会使用我们上面提到过的方程,否则使用方程:
代替,默认值为False。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('1.png',0)
edges = cv2.Canny(img,100,200,3)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
#Canny边缘提取
import cv2 as cv
def edge_demo(image):
blurred = cv.GaussianBlur(image, (3, 3), 0)
gray = cv.cvtColor(blurred, cv.COLOR_RGB2GRAY)
# xgrad = cv.Sobel(gray, cv.CV_16SC1, 1, 0) #x方向梯度
# ygrad = cv.Sobel(gray, cv.CV_16SC1, 0, 1) #y方向梯度
# edge_output = cv.Canny(xgrad, ygrad, 50, 150)
edge_output = cv.Canny(gray, 50, 150)
cv.imshow("Canny Edge", edge_output)
dst = cv.bitwise_and(image, image, mask= edge_output)
cv.imshow("Color Edge", dst)
src = cv.imread('E:/imageload/liu.jpg')
cv.namedWindow('input_image', cv.WINDOW_NORMAL) #设置为WINDOW_NORMAL可以任意缩放
cv.imshow('input_image', src)
edge_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
应用场景,使用步骤:
①高斯模糊 - GaussianBlur
②灰度转换 - cvtColor
③计算梯度 – Sobel/Scharr
④非最大信号抑制
⑤高低阈值输出二值图像