3 种边缘检测算子 灰度或结构等信息的突变位置是图像的边缘,图像的边缘有幅度和方向属性,沿边缘方向像素变化缓慢,垂直边缘方向像素变化剧烈。因此,边缘上的变化能通过梯度计算出来。 一阶导数的梯度算子 对于二维的图像,梯度定义为一个向量, Gx对于x方向的梯度,Gy对应y方向的梯度,向量的幅值本来是 mag(f)?=?(Gx2?+?Gy2)1/2,为简化计算,一般用mag(f)=|Gx|+|Gy|近似,幅值同时包含了x而后y方向的梯度信息。梯度的方向为 α?=?arctan(Gx/Gy) 。 由于图像的数字离散特性,所以梯度微分运算用差分代替,并且用小的空域模板和图像进行卷积近似计算梯度,由于模板的不同,因此衍生处多种梯度算子:Roberts算子、Sobel算子和Prewitt算子。 Sobel与Prewitt算子模板 平滑模板都有一个特点,即模板内所有平滑值的和为0,因此梯度计算的步骤是:
高斯拉普拉斯算子 上面的一阶导数算子,是各向异性的,因此分x方向和y方向的梯度值,而高斯拉普拉斯算子是对图像求二阶导数,边缘对应二阶导数的过零点。 由上式可知,xy进行互换的结果是一样的,所以拉普拉斯算子没有x方向和y方向的区分,拉普拉斯算子对应图像中的差分运算是: 也可以通过卷积模板实现,
相对于一阶导数,高斯拉普拉斯算子(Laplacian of Gaussian, LOG算子)由于求二阶导数,很容易将点噪声判别为边界,因此常在使用LOG算子前先用高斯平滑滤波器去除正态分布的噪声,二维高斯分布为: 其中 σ 为高斯分布标准差,决定高斯滤波器的宽度,用该函数对图像平滑滤波,可以减少椒盐噪声对LOG算子的影响。 Canny 算子 1983,MIT,Canny提出的边缘检测三个标准:
Canny算子力图在抗噪声干扰与精度之间寻求最佳方案,Canny算子有相关的复杂理论,其基本的步骤是:
OpenCV 中相关源码 Sobel算子及LOG算子的源码在/modules/imgproc/src/deriv.cpp中,Canny算子实现在/modules/imgproc/src/canny.cpp中。 经过之前的基础准备,感觉只要知道什么时候该用什么OpenCV函数,其它的一切都变得简单起来了。于是感觉学着去探索OpenCV的源码对自己的受益会更大,就从这里开始吧。 deriv.cpp中有Sobel算子的源码: getSobelKernels是实际创建卷积模板的函数,被上面的getDerivKernels调用,不妨看看OpenCV中Sobel创建的卷积模板是啥样的,下面只是getSobelKernels函数的一部分 ksize表示卷积核的大小,之前理论分析中取的是3×3的模板,对应到if( ksize == 3 ),order变量确定对x梯度方向的卷积模板进行赋值还是y梯度方向的卷积进行赋值,因此,当且仅当Sobel函数的输入实参中dx=1时才计算Gx方向的梯度,dy=1时才计算dy方向的梯度。OpenCV没有给出Prewiit算子的源码,但可以自己通过修改替换getDerivKernels函数实现Prewiit的功能。 LOG算子也可以进行相同的分析,这里就不写下来了。再看Canny算子, C++版本的Canny算子实际就是调用原来C版本中的函数,只是进行了下封装而已,在cvCanny函数中我看到这么几行代码: Canny就是调用Sobel算子计算x方向的梯度Gx和y方向的梯度Gy。计算梯度角度和非最大化抑制的代码有些长 最后就是使用双阈值跟踪边界,形成连续边缘 这其中貌似用到了栈对邻域梯度信息进行保存,以上详细的实现没做太多的分析,但流程就摆在那里了。请注意,OpenCV中的Canny实现包含了Canny算子的3个步骤,唯独没有第一步中的高斯平滑滤波,因此调用前得先使用高斯平滑滤波。 试试身手 Sobel算子的源码: 简单的换一下函数,就是LOG算子的源码: Canny算子的源码也很简单,只不过使用了GaussianBlur进行高斯平滑 请注意,上面的Sobel和LOG算子代码都没有在计算结果后使用阈值判断是否属于边界,而直接显示了边缘信息。 Sobel边缘检测结果:右上为x梯度结果,左下为y梯度结果,右下为G(x,y)梯度结果 Laplace边缘检测结果 Canny边缘检测结果 Canny检测算子的效果还是很不错的 关注「CPP开发者」 看更多精选C/C++技术文章 ↓↓↓
|
|