canny边缘检测是一个流行的边缘检测算法,它分为几个步骤

1.消除噪声

由于边缘检测对图像中的噪声很敏感,第一步是用5x5高斯滤波器去除图像中的噪声,在前面图像平滑的文章中已经讲过。

2.梯度值和方向的计算

第二步,在水平方向和垂直方向用Sobel算子对平滑后的图像进行梯度计算,得到水平方向(GX)和垂直方向(Gy)上的一阶导数。

梯度值和方向的公式:
image.png

梯度方向总是垂直于边缘的,在canny边缘检测算法中,梯度方向被简化到4个方向角度,0,45,90,135。

3.非极大值抑制

在获得梯度幅值和方向后,对图像进行全面扫描,以消除可能不在边缘上的像素。在每个像素上,检查像素是否是其邻域内沿梯度方向的局部最大值。例如下面的边缘梯度:

image.png

C,A,B三个梯度值都在梯度方向上,对比这三个梯度,只保留最大的A进入下一阶段的计算,而C,B都设置为0,因此canny边缘检测得到的都是轮廓比较细的边缘,精确度也相对较高。

4.滞后阈值

这个阶段决定了哪些像素是真正的边缘,哪些不是边缘。为此设定了两个阈值,minVal和maxVal。任何强度梯度大于maxVal的边缘都肯定是边缘,而minVal以下的边缘肯定是非边缘的,因此被丢弃。处于这两个阈值之间的人根据其连通性分为边缘或非边缘,如果它们连接到“确定边缘”像素,则它们被视为边缘的一部分,否则它们也会被丢弃。如下图:

image.png

边缘像素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()

image.png

根据图像的具体情况,选择合适的minVal/maxVal是非常重要的。

☞ 参与评论