1.理论
(1)去噪
边缘检测容易受到图像中噪声的影响,故首先需要用5x5的高斯滤波器去除图像中的噪声。
(2)计算图像的强度梯度
将去噪(平滑)后的图像由sobel内核分别在水平和垂直方向上求导(一阶微分),得到Gx和Gy。根据这两幅梯度图,求得每个像素的边缘梯度大小和方向。
(3)非极大值抑制
得到梯度大小和方向后,对图像进行全扫描,去除可能不构成边缘的任何不需要的像素。故在每个像素处,检查像素在梯度方向上是否为其邻域内的局部最大值。
如下图,A点位于边缘上,即垂直线,其梯度方向与边缘线方向垂直,点B和点C位于梯度方向上,所以检查A与B、C以查看A是否是局部最大值,如果是进入下一步,如果不是则置为0。
(4)滞后阈值法
此阶段在边缘线中哪些是真正需要检测的边缘,哪些不是。对此,需要两个阈值,minVal和maxVal。任何强度梯度大小大于maxVal的边缘都被认为是要留下的边缘,而那些梯度值小于minVal的边缘都被丢弃,认为是非边缘。剩下的梯度值位于这两个阈值之间的根据它们的连通性再将其划分到边缘或非边缘中。如果它们能够连接到已被确认为边缘的一些边缘上,则也将它们视为边缘的一部分,否则丢弃。
如下图,边缘A梯度值大于maxValue,被置为‘sure-edge’,尽管边缘C梯度值小于maxVal,但由于C与A连接,故C也被置为是有效边缘,最终得到整条曲线。而边缘B尽管其梯度值大于minVal,但由于其不能连接到任意一条'sure-edge'上,所以B被丢掉。
这个阶段基于边缘是长线的假设,也消除了小像素噪声。
OpenCV代码:
# 以灰度格式读取图像,检测边缘时靠强度(亮度)变化,与颜色不太相关
img_yuan_gray = cv2.imread('image/7E_BIR9IL{}5%0RKIK_$F62.png', cv2.IMREAD_GRAYSCALE)
print(img_yuan_gray.dtype)
'''
Canny边缘检测
'''
img_edgs_canny = cv2.Canny(img_yuan_gray,100,300,apertureSize=3,L2gradient=True)
cv2.imshow('img_yuan_gray', img_edgs_canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
原图如下:
梯度图:
结果如下:
附注: