前面介绍的Sobel算子和Scharr算子存在的问题:
1.要分别计算两个方向(x,y)的边缘,之后将两方向的边缘进行叠加。
2.边缘与方向相关性较大。当我们通过Sobel算子提取x方向检测时,它所能够检测到的边缘都是一个沿着y轴方向的边缘,与检测的方向相反(垂直方向),当进行y方向检测时,得到的边缘是一个水平方向的,垂直的边缘没有办法提取,这就造成了在选择边缘检测的时候需要额外主义边缘的方向性,同时可能出现倾斜的边缘会被反复的检测出来(x方向,y方向检测都有),二当两者边缘检测结果进行叠加时,则会将倾斜的边缘额外加强。
? ? ?因此拉普拉斯算子针对图像边缘的方向性提出一种无边缘的算法。拉普拉斯算子分别对两个方向进行求导然后进行叠加,也就是只要我们使用了拉普拉斯算子,就可以直接提取图像中各个方向的边缘,而无需分别提取x方向和y方向后在进行叠加。所以拉普拉斯算子具有无关方向性的优点,但是由于拉普拉斯算子需要进行两次求导,所以更容易受噪声的影响。
?
由上图拉普拉斯算子矩阵我们可以看出它是一个中心对称的边缘检测算子,因此对于各个方向都可以检测出来。
void cv::Laplacian(InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
·src:输入原图像,可以是灰度图像和彩色图像。
·dst:输出图像,与输入图像src具有相同的尺寸和通道数。
·ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值范围。进行边缘检测时,可能会出现数值较大,较小,负数的情况。
·ksize:滤波器的大小,必须为正奇数,参数默认值为1,表示的时一个3*3尺寸的拉普拉斯边缘检测算子。
·scale:对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放。
·delta:偏值,在计算结果中加上偏值。
·borderType:像素外推法标志。
? ? ?Canny边缘检测是对Sobel算法进行优化的算法,Sobel算子进行边缘检测时得到的边缘信息非常多,而且有些边缘信息较亮,而有些边缘信息较暗,有些边缘信息时存在图像内部,可能是由于光照等因素影响,使得某一个局部区域像素值发生了微小波动,但是通过Sobel算子依然能够将这个波动检测出来,然而此波动却不是真正的边缘,因此Canny算法的思想就是如何去除掉虚假边缘。
Step1:使用高斯滤波平滑图像,目的是尽可能减少出现在图像内部的虚假边缘,常用的高斯滤波平滑图像的滤波器如下图矩阵。
Step2:计算图像中每个像素的梯度方向和幅值。
Step3:应用非极大值抑制算法消除边缘检测带来的杂散响应。
Step4:应用双阀值法划分强边缘和弱边缘,给出较低和较高的阈值,在较低阈值之下的梯度响应舍弃,当梯度的赋值大于较高阈值时,称强边缘,当梯度幅值在阈值之间时,称弱边缘。
Step5:消除孤立的弱边缘,孤立弱边缘指周围没有强边缘的存在,若存在强边缘,那么将弱边缘转化为强边缘。
void cv::Canny(InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize = 3,
bool L2gradient = false
)
·image:输入图像,必须是CV_8U单通道或者三通道的图像。
·edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U,输出图像进行了二值化处理的结果,显示边缘的区域全为最高值255,不是边缘的区域全为0。
·threshold1:第一个滞后阈值。
·threshold2:第二个滞后阈值。
·apertureSize:Sobel算子的直径,参数默认值为3。
·L2gradient:计算图像梯度赋值的标志,使用默认值即可。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv; //opencv的命名空间
using namespace std;
//主函数
int main()
{
//读取图像,黑白图像边缘检测结果较为明显
Mat img = imread("E:/opencv/opencv-4.6.0-vc14_vc15/opencv/lenaGray.png");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat result, result_g, result_G;
//未滤波提取边缘
Laplacian(img, result, CV_16S, 3, 1, 0);
convertScaleAbs(result, result); //取绝对值函数
//滤波后提取Laplacian边缘
GaussianBlur(img, result_g, Size(3, 3), 5, 0); //高斯滤波
Laplacian(result_g, result_G, CV_16S, 3, 1, 0);
convertScaleAbs(result_G, result_G); //取绝对值函数
//显示图像
imshow("result", result);
imshow("result_G", result_G);
cout << "接下来进行Canny比爱能源检测" << endl;
waitKey(0);//等待函数用于显示图像,按下键盘任意键后退出
Mat resultHigh, resultLow, resultG;
//大阈值检测图像边缘
Canny(img, resultHigh, 100, 200, 3);//弱阈值和强阈值较大
//小阈值检测图像边缘
Canny(img, resultLow, 20, 40, 3);//弱阈值和强阈值较小
//高斯模糊后检测图像边缘
GaussianBlur(img, resultG, Size(3, 3), 5);
Canny(resultG, resultG, 100, 200, 3);
//显示图像
imshow("resultHigh", resultHigh);
imshow("resultLow", resultLow);
imshow("resultG", resultG);
waitKey(0);//等待函数用于显示图像,按下键盘任意键后退出
return 0;
}
拉普拉斯边缘检测结果:
?可以看到图像中即提取到了竖直边缘,也提取到了水平边缘,得到的结果中边缘与方向无关,若图像中含有较大的噪声,检测后的图像内部也会出现一些弱小的边缘,这些边缘无法形成条状或带状区域,是一个零散雪花状的区域。
进行高斯滤波后的结果,明显减少了虚假边缘,整个图像结果较为清晰,边缘轮廓也比较准确。
?
?Canny算法得到的结果中,使用较低阈值得到的结果边缘比较多,而使用较高阈值得到的边缘就较少,少了很多虚假边缘。
?
经过高斯滤波后的检测结果,一些虚假的边缘被去除,同时边缘整体信息还是完整保留,虽然Canny算法有一定的优越性,但是我们也要根据实际的情况对图像进行预处理,之后再使用相应的算法得到我们想要的结果。