> 技术文档 > OpenCV(18):Canny边缘检测流程(Canny算子)

OpenCV(18):Canny边缘检测流程(Canny算子)

Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的 最优算法, 最优边缘检测的三个主要评价标准是:

  1. 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
  2. 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
  3. 最小响应: 图像中的边缘只能标识一次。

18.1 最优边缘准则

Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:

  1. 最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;
  2. 最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;
  3. 检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。

18.2 算法实现步骤详解

Canny边缘检测算法可以分为以下5个步骤:

第一步减噪声:边缘检测对噪声非常敏感,利用5*5高斯滤波器进行操作

第二步图像梯度:利用sobel求X、Y的梯度,得到边缘梯度和方向,梯度方向与边缘垂直

第三步应用非极大值(Non_Maxinum Supperession)抑制以消除边缘检测带来的杂散影响。该点是8邻域的边缘梯度幅值最大值,则该点保留,否则就剔除

点A在垂直方向边缘上,梯度方向正交于边缘。点B与点C在梯度方向,检测点A是否点B和点C形成局部极大值,如果是,进入下阶段,否则设置为0。

第四步应用双阈值(Double-Thresold)检测来确定真实和潜在的边缘

滞后阈值:梯度大于最大值是边界,低于最小值是非边界,剔除掉。介于最小值和最大值之间的边界取决于连接性,与只是真实边界相连为边界,不相连予以剔除

第五步:通过抑制孤立的弱边缘最终完成边缘检测

边缘A高于最大值,被认为是真边缘。边缘C低于最大值,与边缘A相连,被认为是真边缘。边缘B没有与真边缘相连接,予以剔除。

第一步: 消除噪声的实现

应用高斯滤波来平滑(模糊)图像,目的是去除噪声。

高斯滤波器是将高斯函数离散化,将滤波器中对应的横纵坐标索引代入到高斯函数,从而得到对应的值。

二维的高斯函数如下:其中 (x , y)为坐标, σ 为标准差。

不同尺寸的滤波器,得到的值也不同,下面是 (2k+1)x(2k+1)  滤波器的计算公式 :

常见的高斯滤波器大小为 5×5 σ = 1.4  ,其近似值为:

第二步:计算梯度强度和方向的实现

接下来,我们要寻找边缘,即灰度强度变化最强的位置,(一道黑边一道白边中间就是边缘,它的灰度值变化是最大的)。在图像中,用梯度来表示灰度值的变化程度和方向。

常见方法采用Sobel滤波器【水平x和垂直y方向】在计算梯度和方向

1. 水平方向的Sobel算子Gx:用来检测 y 方向的边缘。

2. 垂直方向的Sobel算子Gy:用来检测 x 方向的边缘( 边缘方向和梯度方向垂直

3. 采用下列公式计算梯度和方向:


梯度方向近似到四个可能角度之一(一般 0, 45, 90, 135)

第三步:非最大抑制的实现

利用非最大抑制技术NMS来消除边误检,这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。

原理:遍历梯度矩阵上的所有点,并保留边缘方向上具有极大值的像素

这一步的目的是将模糊(blurred)的边界变得清晰(sharp)。通俗的讲,就是保留了每个像素点上梯度强度的极大值,而删掉其他的值。对于每个像素点,进行如下操作:

  1. 将其梯度方向近似为以下值中的一个(0,45,90,135,180,225,270,315)(即上下左右和45度方向)
  2. 比较该像素点,和其梯度方向正负方向的像素点的梯度强度
  3. 如果该像素点梯度强度最大则保留,否则抑制(删除,即置为0)

         

例如下图:点 A 位于图像边缘垂直方向. 梯度方向 垂直于边缘. B 和点 C 位于梯度方向. 因此,检查点 A 和点 B,点 C,确定点A是否是局部最大值. 如果点 A 是局部最大值,则继续下一个阶段;如果点 A 不是局部最大值,则其被抑制设为0

最后会保留一条边界处最亮的一条细线

第四步:滞后阈值的实现

最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

这个阶段决定哪些边缘是真正的边缘,哪些边缘不是真正的边缘

经过非极大抑制后图像中仍然有很多噪声点。Canny算法中应用了一种叫双阈值的技术。即设定一个阈值上界maxVal和阈值下界minVal,图像中的像素点如果大于阈值上界则认为必然是边界(称为强边界,strong edge),小于阈值下界则认为必然不是边界,两者之间的则认为是候选项(称为弱边界,weak edge),需进行进一步处理——如果与确定为边缘的像素点邻接,则判定为边缘;否则为非边缘。

应用双阈值的方法来决定可能的(潜在的)边界。

  1. 如果某一像素位置的幅值超过 阈值maxVal, 该像素被保留为边缘像素。
  2. 如果某一像素位置的幅值小于 阈值minVal, 该像素被排除。
  3. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 阈值的像素时被保留。

第五步:利用滞后技术来跟踪边界

这个阶段是进一步处理弱边界。

大体思想是,和强边界相连的弱边界认为是边界,其他的弱边界则被抑制。

由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。

18.3 cv2.Canny()函数——Canny算子

函数原型:

edge=cv2.Canny(image,threshold1,threshold2[,edgs[,apertureSize[,L2gradient]]])

参数:

  1. image - 输入图片,必须为单通道的灰度图
  2. threshold1 和 threshold2 - 分别对应于阈值 minVal 和 maxVal
  3. apertureSize - 用于计算图片提取的 Sobel kernel 尺寸. 默认为 3.
  4. L2gradient - 指定计算梯度的等式。该参数默认为 False,采用的梯度计算公式为:当参数为 True 时,其精度更高;采用 梯度计算公式(1)(2),


18.4 示例

import cv2import numpy as npimport matplotlib.pyplot as plt# 第一步读取图片img = cv2.imread(\'C:\\\\Users\\\\xxx\\\\Downloads\\\\picture1.jpeg\', cv2.IMREAD_GRAYSCALE)# 第二步:使用cv2.sobel进行sobel算子计算sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0)sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1)sobel_x = cv2.convertScaleAbs(sobel_x)sobel_y = cv2.convertScaleAbs(sobel_y)sobel_xy = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)# 第三步:使用cv2.scharr进行scharr算子计算scharr_x = cv2.Scharr(img, cv2.CV_64F, 1, 0)scharr_y = cv2.Scharr(img, cv2.CV_64F, 0, 1)scharr_x = cv2.convertScaleAbs(scharr_x)scharr_y = cv2.convertScaleAbs(scharr_y)scharr_xy = cv2.addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0)# 第四步: 使用cv2.laplacian 拉普拉斯算子计算lapkacian = cv2.Laplacian(img, cv2.CV_64F)lapkacian = cv2.convertScaleAbs(lapkacian)# 第五步: 使用cv2.Canny 算子计算canny = cv2.Canny(img,100,200)# 第六步:比较原图和四种算子的效果names = [\'Original\',\'sobel\',\'scharr\',\'lapkacian\',\'canny\']images = [img,sobel_xy,scharr_xy,lapkacian,canny]plt.figure(figsize=(19.2,9.6))for i in range(2): for j in range(3): plt.subplot(2,3,i*3+j+1),plt.imshow(images[i*3+j]) plt.title(names[i*3+j],fontsize=30), plt.xticks([]), plt.yticks([]) num=i*3+j if num >= len(names)-1: breakplt.show()

运行结果如下:

宠物用品批发