梯度是一个量变化的速度,在数学中通常使用求导、求偏导获取梯度或者某一方向上的梯度。
在数字图像中梯度可以看为像素值分别在x,y方向上的变化速度,因为数字图像的离散型,以及像素是最小处理单元的特性,求数字图像的梯度时,不需要求导,只需要进行加减运算即可。(其实就是求导的差分近似形式).如下图所示:
这就使得可以通过简单的卷积运算得到图像的边缘信息。
因此就有了一些基本的梯度算子:Sobel算子、Scharr算子、Roberts算子、拉普拉斯算子,都属于一阶微分算子
一、一阶微分算子
1.1 Sobel算子
如上式,G_x 与G_y 分别表示对图像A进行横向和纵向梯度检测得到的结果。(注意,是横向的梯度,不是横向的边缘)
取二者平方和即可得到图像上每一点的梯度值,即在该点同时计算x方向与y方向的梯度。
该点的梯度方向可以通过取这两个值的比的反正切arctan得到:
前面也说了,Sobel算子并不是真正的导数,只是对函数的差分近似,或者说是局部拟合,而差分近似的阶数越高,拟合就更加准确。因此大的核能够更好的近似导数,可以消除部分噪声影响。但是导数变化比较剧烈时,核太大会导致结果偏差大。
1.2 Scharr算子
Scharr算子是Sobel算子的一种特殊形式。
其核的形式如下:
前面提到了,Sobel算子中核越大就能够更好的近似导数,准确度也更高。因此,在核比较小时如3×3时,Sobel核的准确度较差,使用Scharr算子代替3×3的Sobel核能够提高精度。因为加大了在x方向或y方向的权重,使得梯度角(梯度方向)不会距离x或y方向太远,因此误差也不会太大。例如,只求x方向的梯度时,核的中心点的x方向的两个权值远大于其它权值,这使得求得的梯度更靠近x方向,一定程度减小了误差。
OpenCV函数使用:
void cv::Sobel(
cv::InputArray src,
cv::OutputArray dst,
int ddepth, //输出图的深度或类型
int xorder, //求导顺序取值可为:0,1,2 。0表示不求导
int yorder, //同上
cv::Size ksize = 3,//核大小,必须为奇数
double scale = 1,
double delta = 0,
int borderType = cv::BORDER_DEFAULT
)
将ksize 设为cv::SCHARR可转为使用Scharr算子
1.3 Roberts算子
Roberts算子的核如上图所示,是一种简单的交叉差分算法,在求±45°的梯度时最有效。
相比于一般的水平竖直方向的差分算子,Roberts算子能够有效地保留边缘的角点,并且计算速度较快。缺点是对细节敏感导致对噪声也十分敏感。
一阶微分算子即是对图像的灰度变化情况进行求导,获得的边缘比较粗糙,并且信息较少。二阶微分算子是对灰度变化的导数求导,对灰度值变化更为敏感,获得的边缘更加准确。
二、二阶微分算子
2.1 拉普拉斯算子
拉普拉斯算子可由二阶导数定义:
而在数字图像中离散化,用二阶差分表示为:
所以拉普拉斯算子可以表示为:
其卷积核如下图所示:
拉普拉斯算子法其实是一种图像边缘增强算子,常用于图像锐化,在增强边缘的同时也增强了噪声,因此使用前需要进行平滑或滤波处理
如下图,可以看出,在函数值发生突变的情况时,二阶导数能够增强突变点与其两侧的对比度。在数字图像中就是图像边缘处得到了增强,因此实现了图像的锐化。
OpenCV函数使用
Void cv::Laplacian(
cv::InputArray src,
cv::OutputArray dst,
int ddepth,
cv::Size ksize = 3,
double scale = 1,
double delta = 0,
int borderType = cv::BORDER_DEFAULT
);
其实二阶微分算子还有LOG的,但其实和拉普拉斯算子差别不是特别大,就不再详细进行说明了
二阶微分算子检测的边缘更加准确,对边缘的定位能力更强。但是相较于一阶微分算子,不能保留梯度的方向信息,并且对噪声更为敏感。 1986年提出Canny算子能够较好的兼顾这几方面,成为最常用也最有效稳定的边缘检测算法。
三、Canny算法
算法流程
① 去噪。使用高斯滤波对图像进行平滑去除噪声。滤波详情可见
https://blog.csdn.net/qq_38574198/article/details/109030375
② 检测初始边缘。使用Sobel算子进行边缘检测,得到初始的边缘信息。
③ 非极大值抑制细化。利用非极大值抑制算法,只保留局部极大梯度,抑制其它非极大的梯度。
在Sobel算子进行边缘检测后得到的边缘图可以视为梯度图,每个位置的梯度大小可以根据其灰度值得到。
如图,点C处的梯度方向为蓝线,然后对比C点的灰度值与其正负梯度方向上相邻位置(点dTmp1和dTmp2)的灰度值,只有当C的灰度值大于其它两个点的灰度值时,才视C点为边缘点,否则将其置0. 而因为正负梯度方向上的位置不一定正对单个像素点,这两个位置的灰度值往往通过其相邻的点进行线性插值得到。例如g1,g2插值得到dTmp1的灰度值
④ 双阈值筛选。
定义一个高阈值和一个低阈值。梯度强度低于低阈值的像素点被抑制,不作为边缘点;高于高阈值的像素点被定义为强边缘,保留为边缘点;处于高低阈值之间的定义为弱边缘,留待进一步处理。
⑤ 边缘连接(孤立点筛选)。
因为还是有部分噪声的存在,噪声在梯度图中算是 “假边缘“。通常而言,由真实边缘引起的弱边缘像素点将连接到强边缘像素点,而噪声响应则未连接。通过查看弱边缘像素及其8个邻域像素,可根据其与强边缘的连接情况来进行判断。一般,可定义只要其中邻域像素其中一个为强边缘像素点,则该弱边缘就可以保留为强边缘,即真实边缘点。
OpenCV 函数使用
Void cv::Canny(
Cv::InputArray image, //输入图片,必须单通道格式
Cv::OutputArray edges, //输出
Double threshold1, //双阈值中的低阈值
Double threshold2, //高阈值
Int apertureSize = 3, //Sobel算子处理的核的大小
Bool L2gradient = false //计算方向梯度时使用1范数(快,精度较低)还是2范数(精度高)
)