Canny边缘检测

  • 前言
  • 一、Canny是什么?
  • 二、算法详细步骤
    • 1. 平滑处理
    • 2. 梯度检测
    • 3. 非极大值抑制
    • 4. 滞后阈值处理
  • 三、函数原型
  • 四、应用实例
  • 五、总结

前言

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括:(i)深度上的不连续;(ii)表面方向不连续;(iii)物质属性变化;(iv)场景照明变化。
边缘检测算子包括:

  • 一阶: Roberts Cross算子,Prewitt算子,Sobel算子,Kirsch算子,罗盘算子;
  • 二阶: Marr-Hildreth,在梯度方向的二阶导数过零点,Canny算子,Laplacian算子。

其中,Canny算子(或者这个算子的变体)是最常用的边缘检测方法。

一、Canny是什么?

Canny边缘检测方法是由Canny于1986年提出的一种被公认为效果较好的边缘检测方法。
在介绍Canny算法的具体流程之前,先说一下边缘检测方法的3项指标:

  • 低失误率,即不能漏检也不能错检;
  • 高的位置精度,即标定的边缘像素点与真正的边缘中心之间距离应该为最小;
  • 每个边缘应该有唯一的响应,即得到单像素宽度的边缘。

Canny的贡献不仅是提出了一种边缘检测算子,而且还给出了衡量边缘检测算子好坏的准则:

  • 信噪比准则;
  • 定位精度准则;
  • 单边缘响应准则。

二、算法详细步骤

下面我们就重点介绍一下Canny算子的实现过程:平滑处理,梯度检测,非极大值抑制和滞后阈值处理。

1. 平滑处理

所有的边缘都极易受到噪声的干扰,为了防止因噪声所引起的错误的检测结果,有必要应用平滑滤波的方法滤除噪声.高斯滤波方法是最常用的滤波方法,二维图像应用二维高斯函数,它的定义为:
G(u,v)=12πσ2e−u2+v22σ2G(u, v) = \frac{1}{2\pi\sigma^2}e^{-\frac{u^2+v^2}{2\sigma^2}} G(u,v)=2πσ21​e−2σ2u2+v2​
式中,σ\sigmaσ表示高斯函数的标准差.只要把输入图像与二维高斯函数进行卷积,即可得到平滑处理后的图像.考虑到数字图像为离散化的形式,我们往往把高斯函数转换为离散化的高斯内核模板的形式,如标准差为1.4的模板尺寸为5*5的归一化高斯内核模板为:
K=1159∗[245424912945121512549129424542]K = \frac{1}{159}* \begin{bmatrix} 2&4&5&4&2\\ 4&9&12&9&4\\ 5&12&15&12&5\\ 4&9&12&9&4\\ 2&4&5&4&2 \end{bmatrix} K=1591​∗⎣⎢⎢⎢⎢⎡​24542​491294​51215125​491294​24542​⎦⎥⎥⎥⎥⎤​

2. 梯度检测

梯度是图像灰度值变化剧烈的地方,它可以通过Roberts算子,Prewitt算子,Sobel算子等最简单的模板检测方法得到.常用的是Sobel算子,它是由两个模板组成:
SGX=[−101−202−101]SGY=[121000−1−2−1]S_{GX} = \begin{bmatrix} -1&0&1\\ -2&0&2\\ -1&0&1 \end{bmatrix} S_{GY} = \begin{bmatrix} 1&2&1\\ 0&0&0\\ -1&-2&-1 \end{bmatrix} SGX​=⎣⎡​−1−2−1​000​121​⎦⎤​SGY​=⎣⎡​10−1​20−2​10−1​⎦⎤​
把这两个模板分别与图像进行卷积运算,则分别得到水平方向的梯度GxG_xGx​和垂直方向的梯度GyG_yGy​.最终的梯度幅值GGG往往是由欧几里得距离(L2L2L2范数)求得:
G=Gx2+Gy2G = \sqrt{G_x^2 + G_y^2} G=Gx2​+Gy2​​
但有时为了简化,梯度值也可由曼哈顿距离(L1L1L1范数)得到:
G=∣Gx∣+∣Gy∣G = |G_x| + |G_y| G=∣Gx​∣+∣Gy​∣
而梯度幅角θ\thetaθ为:
θ=arctan(∣Gy∣∣Gx∣)\theta = arctan(\frac{|G_y|}{|G_x|}) θ=arctan(∣Gx​∣∣Gy​∣​)
由上式得到的角度可以是任意值,但这里我们需要把梯度幅角四舍五入到代表水平方向,垂直方向和2个对角线方向的4个方向上,即0°0\degree0°,45°45\degree45°,90°90\degree90°,135°135\degree135°.例如,梯度幅角为−22.5°-22.5\degree−22.5°~22.5°22.5\degree22.5°时,将被统一设置为0°0\degree0°.

3. 非极大值抑制

这一步骤的目的是使边缘细化.由上一步得到的边缘图像十分模糊,不符合好的边缘检测中的第三个指标,而非极大值抑制可以抑制那些局部不是梯度幅值最大值的边缘,而保留下来的具有局部最大值的像素点正是灰度值变化最剧烈的地方.这里的局部最大值是由在3*3的邻域内的梯度方向上比较梯度值得到的.例如:

  1. 当梯度方向为0°0\degree0°时,图像的边缘是南-北方向,则在3*3的邻域内,当前像素与其左右两侧像素的梯度值进行比较,如果当前像素的梯度幅值最大,则保留,否则剔除;
  2. 当梯度方向为90°90\degree90°时,图像的边缘是东-西方向,则在3*3的邻域内,当前像素与其上下两侧像素的梯度值进行比较,如果当前像素的梯度幅值最大,则保留,否则剔除;
  3. 当梯度方向为135°135\degree135°时,图像的边缘是东北-西南方向,则在3*3的邻域内,当前像素与其左上角和右下角像素的梯度值进行比较,如果当前像素的梯度幅值最大,则保留,否则剔除;
  4. 当梯度方向为45°45\degree45°时,图像的边缘是东南-西北方向,则在3*3的邻域内,当前像素与其右上角和左下角像素的梯度值进行比较,如果当前像素的梯度幅值最大,则保留,否则剔除.

还需说明的是,梯度方向的符号与非极大值抑制的结果无关,即无论是东-西方向还是西-东方向,两者是一样的.

4. 滞后阈值处理

由上一步得到的边缘仍有一小部分由于噪声或者颜色变化的影响而不是真正的边缘,这种现象的表现形式是尽管这些边缘的梯度幅值是局部最大值,但与其他边缘比,他们的梯度幅值很小,也就是绝对梯度幅值很小.处理它们也很简单,采用阈值法即可.但Canny采用的双阈值的方法,即设置高,低两个阈值,当梯度幅值大于高阈值时,该边缘为强边缘;当梯度幅值小于低阈值时,该边缘需要被剔除;当梯度值介于高,低阈值之间时,该边缘为弱边缘.
强边缘毫无疑问是需要被保留下来的,而弱边缘则需要采用边缘跟踪的方法来判断其是否为真正的边缘.在弱边缘的3*3的邻域内,如果有强边缘,则说明该弱边缘是属于这个强边缘的,所以需要被保留,否则被剔除掉.

三、函数原型

Canny函数的原型为:

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)

image表示输入图像;
edges表示输出边缘图像;
threshold1和threshold2表示滞后阈值法中所需要的高,低两个阈值;
apertureSize表示孔径尺寸,即Sobel算子的尺寸大小,OpenCV是采用Sobel算子来计算图像的梯度的,该值默认为3;
L2gradient表示在计算梯度幅值时是用L2范数还是用L1范数,该值默认为false,即采用L1范数.

源码解析内容太多,暂时不做详细整理,但是可以列一下源码中的几个特点:

  • 它没有执行经典Canny方法的步骤1,即滤波平滑这一步被省略,当然我们可以事先进行平滑处理后再运行Canny函数;
  • 只遍历了一次图像,就完成了步骤2和步骤3,具体过程是计算当前遍历行的梯度幅值,同时计算前一行的梯度幅值,并完成了3*3邻域内的非极大值抑制;
  • 步骤3和步骤4的内容并不是完全分开执行的,而是交错在了一起;
  • 在步骤4的边缘跟踪法中,程序是寻找强边缘的33邻域内的弱边缘的方法,而不是寻找弱边缘的33邻域内的强边缘.

四、应用实例

在进行边缘检测之前,我们先应用GaussianBlur函数对图像进行高斯平滑滤波,设它的标准差为1.6,而高斯内核尺寸是由该标准差确定的.

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"
#include <iostream>using namespace cv;
using namespace std;int main(int argc, char** argv){Mat src, dst, edge;src = imread("happynewyear.jpeg");if(!src.data)return -1;GaussianBlur(src, dst, Size(0, 0), 1.6); //高斯滤波//Canny方法,高阈值为60, 低阈值为25Canny(dst, edge, 25, 60);namedWindow("Canny", WINDOW_AUTOSIZE);imshow("Canny", edge);imwrite("Canny.jpg", edge);waitKey(0);return 0;
}

我们看下效果(糟心的2020过去了,祝大家新年快乐!新的一年,继续奋斗!):
有几点需要注意一下:

  • imwrite
  bool imwrite(const string& filename, InputArray img, const vector<int>& params=vector<int>() )

其中,filename: 需要写入的文件名,会自己创建(eg. imwrite(“1.jpeg”, src)),注意要带后缀;

  • 编译cpp

    g++ happynewyear.cpp -o happynewyear `pkg-config opencv --cflags --libs`
    

    注意,pkg-config opencv --cflags --libs外面的引号是键盘左上角1旁边的那个引号.

五、总结

总体来讲,Canny边缘检测相对比较成熟,调用起来也比较方便.如果不是要针对边缘检测做研究和进一步优化的话,直接调用就可以了.如果需要对源码做优化,虽然原理相对比较简单,但是还是要对源码进行解读才可以.

自动驾驶感知-车道线系列(二)——Canny边缘检测相关推荐

  1. 自动驾驶感知-车道线系列(一)——车道线基础流程实现

    车道线基础流程实现 OpenCV的安装 图片的读取,展示和保存 Canny边缘检测 ROI_Mask 霍夫变换 离群值过滤 最小二乘拟合 直线绘制 视频流读写 总结 最近开始接触车道线相关的工作,由于 ...

  2. 自动驾驶感知-车道线系列(三)——霍夫变换

    霍夫变换 一.什么是霍夫变换 二.基本原理 1. 用霍夫变换拟合直线 2. 霍夫变换中的难点 三.函数原型 四.应用实例 五.总结 一.什么是霍夫变换 霍夫变换是一种特征检测(feature extr ...

  3. 【自动驾驶】车道线拟合算法---最小二乘法拟合直线

    概览 关于自动驾驶车道线拟合算法,常用的方法有B样条.三次样条插值.Ransac.最小二乘法等等. 但是针对于高精度地图的车道线拟合,由于车道线坐标点已知,所以不需要有控制点进行约束,那么B样条.贝塞 ...

  4. 【Python】自动驾驶检测车道线

    检测车道线-- 检测尼玛的车道线,别检测了,除了深度学习,那些什么颜色阈值.梯度阈值.霍夫检测.透视变换都不靠谱-- 反正是老板和师兄们说的--现在车道线检测在深度学习里面已经很成熟了--没必要自己再 ...

  5. 自动驾驶:车道线检测及车位线检测

    文章目录 环境搭建 anaconda 步骤 使用颜色分割 边缘检测方法 霍夫变换 + canny 以下是部分源码 环境搭建 anaconda 步骤 从视频获取每一帧 使每帧灰度 使用Canny算法检测 ...

  6. 自动驾驶——ADAS车道线方程推导

    很长时间,对ADAS中的控制方程不求甚解,在高校读书的时候也没使用过这种方式.而且下述公式在我之前的技术博客提到多次,今日就彻底把它说明白了吧. 对公式(8)的手写推导如下: 上述公式(8)的推导精度 ...

  7. 【camera】自动驾驶感知系统实现(车道线检测和拟合、目标检测与跟踪、道路可行驶区域分割、深度估计、图像视野到BEV空间映射、像平面到地平面映射)

    自动驾驶感知系统实现(车道线检测和拟合.目标检测与跟踪.道路可行驶区域分割.深度估计.图像视野到BEV空间映射.像平面到地平面映射) 项目下载地址:项目下载地址 推理引擎下载地址:推理引擎下载地址 支 ...

  8. Apollo星火计划学习笔记——第六讲上自动驾驶感知基础(I)

    文章目录 1. 感知的作用 2. 常见传感器 2.1 相机 2.1.1 相机的种类 2.1.2 相机的特点 2.2 激光雷达 2.2.1 激光雷达系统构成与方案 2.2.2 激光雷达光源 2.2.3 ...

  9. BEV感知,是下一代自动驾驶感知算法风向吗?

    1 背景 在现代自动驾驶任务中,决策规划模块依赖于多个感知.预测任务模块以提供充足的环境信息,其中感知任务不仅需要检测场景中的动态物体,还需要识别道路边界.人行横道.车道线.路标等静态元素.而预测任务 ...

最新文章

  1. Flex与.NET互操作(十二):FluorineFx.Net的及时通信应用(Remote Shared Objects)(三)
  2. 《Windows驱动开发技术详解》读书笔记(一)
  3. 【干货】深入B端SaaS产品设计核心理念
  4. 【今日互联网大事儿】网易陌陌开火,孰是孰非我们慢慢看
  5. pycharm里怎么关闭一个项目_【周末分享】一个完整的项目复盘到底要怎么做?...
  6. 《Linux内核修炼之道》——2.2 编译内核
  7. matlab freqz m,freqz(freqz和freqs区别)
  8. 中国5G基站将带动155GWh以上锂电池需求
  9. 计算机软件考试成绩查询时间,2020年计算机软考什么时候出成绩,怎么查成绩?|...
  10. 服务器有无线网卡么,全面认识服务器网卡:理论篇
  11. 超大背包(挑战编程之01背包)
  12. 纵向表格转为横向表格
  13. DSP28377s系统时钟配置注意事项
  14. 刘天栋:开源是打破内卷的最好方式
  15. 自下而上语法制导翻译过程
  16. 分享2个在线图表制作工具
  17. 中国家电闯入国外运营费成本比中国高
  18. 51单片机(ESP8266模块)
  19. thinkphp5 使用ElasticSearch 做搜索
  20. 用C语言设计简易银行系统

热门文章

  1. 2017 Multi-University Training Contest - Team 7:1005. Euler theorem(答案是(n+3)/2)
  2. 人脸检测caffe下步骤
  3. python聊天机器人
  4. 静电场求电场强度E和电势U的方法
  5. 简单小知识 记录一下 慢慢更新积累
  6. POJ 1753 Flip Game 简单BFS
  7. Asynchronous HTTP Requests in Android Using Volley
  8. 水晶报表提示“出现打印错误。将停止打印”的原因,非page_Init
  9. vue路由传参丢失问题
  10. Enterprise Library 缓存应用程序块快速入门