canny边缘检测是一个流行的边缘检测算法,它分为几个步骤
1.消除噪声
由于边缘检测对图像中的噪声很敏感,第一步是用5x5高斯滤波器去除图像中的噪声,在前面图像平滑的文章中已经讲过。
2.梯度值和方向的计算
第二步,在水平方向和垂直方向用Sobel算子对平滑后的图像进行梯度计算,得到水平方向(GX)和垂直方向(Gy)上的一阶导数。
梯度值和方向的公式:
梯度方向总是垂直于边缘的,在canny边缘检测算法中,梯度方向被简化到4个方向角度,0,45,90,135。
3.非极大值抑制
在获得梯度幅值和方向后,对图像进行全面扫描,以消除可能不在边缘上的像素。在每个像素上,检查像素是否是其邻域内沿梯度方向的局部最大值。例如下面的边缘梯度:
C,A,B三个梯度值都在梯度方向上,对比这三个梯度,只保留最大的A进入下一阶段的计算,而C,B都设置为0,因此canny边缘检测得到的都是轮廓比较细的边缘,精确度也相对较高。
4.滞后阈值
这个阶段决定了哪些像素是真正的边缘,哪些不是边缘。为此设定了两个阈值,minVal和maxVal。任何强度梯度大于maxVal的边缘都肯定是边缘,而minVal以下的边缘肯定是非边缘的,因此被丢弃。处于这两个阈值之间的人根据其连通性分为边缘或非边缘,如果它们连接到“确定边缘”像素,则它们被视为边缘的一部分,否则它们也会被丢弃。如下图:
边缘像素A位于maxVal之上,因此被认为是“确定边缘”。虽然边缘像素C的梯度值在maxVal以下,但它与A相连接,因此也被认为是有效边,我们得到了AC这条完整的曲线。但是对于边缘像素B,虽然它高于minVal,并且与边缘C梯度所处的区域相同,但它没有连接到任何“确定边缘”,因此被丢弃。因此,为了得到正确的结果,我们必须合理地选择minVal和maxVal,这是非常重要的。Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。
这一阶段也消除了小段像素噪声的干扰,边缘一定都是长线段。
所以canny最终得到的是图像中的梯度值大的强边缘部分。
5.opencv的canny边缘计算函数
opencv提供了函数cv2.canny()中来执行上述所有步骤。
void cv::Canny (
InputArray image, //待处理图像
OutputArray edges, //边缘结果
double threshold1, //minVal
double threshold2, //maxVal
int apertureSize = 3,//sobel内核大小
bool L2gradient = false //是否使用更精确的梯度值计算函数(默认是第2节图中的公式)
)
示例:
img = cv2.imread('test.jpg', 0)
edges = cv2.Canny(img, 75, 150)
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()
根据图像的具体情况,选择合适的minVal/maxVal是非常重要的。
本文链接:https://www.zoucz.com/blog/2019/04/19/6efc15c0-629e-11e9-90b5-eb40e9720ed0/