OpenCV 笔记 – 边缘检测(Sobel、Laplace、Canny)
一、Sobel 算子
1、简介
Sobel 算子是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度近似值。在图像的任何一点使用此算子,将会产生该点对应的梯度矢量或是其法矢量。算子其实是一个模板,用模板与图像做卷积(协相关)运算。
Sobel 模板(x 和y 方向):
求出近似梯度:
梯度方向:
2、特点
- 具有一定的噪声鲁棒性
- 有边缘方向信息
3、OpenCV 中的 Sobel 函数
函数原型:
void Sobel (InputArray src,
OutputArray dst,
int ddepth,
int dx, int dy,
int ksize=3,
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT);
参数详解:
-
第一个参数:InputArray 类型的src,为输入图像,填Mat类型即可。
-
第二个参数:OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
-
第三个参数:int类型的ddepth,输出图像的深度,支持如下src.depth()和ddepth的组合
-
若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
-
若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
-
若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
-
若src.depth() = CV_64F, 取ddepth = -1/CV_64F
-
第四个参数:int类型dx,x 方向上的差分阶数。
-
第五个参数:int类型dy,y方向上的差分阶数。
-
第六个参数:int类型ksize,有默认值3,表示Sobel核的大小。必须取1,3,5或7。
-
第七个参数:double类型的scale,计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。我们可以在文档中查阅getDerivKernels的相关介绍,来得到这个参数的更多信息。
-
第八个参数:double类型的delta,表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
-
第九个参数: int类型的borderType,边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate处得到更详细的信息。
函数使用:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y,dst;
// 载入原始图
Mat src = imread("1.jpg");
// 显示原始图
imshow("【原始图】sobel边缘检测", src);
// 求 X方向梯度
Sobel(src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs(grad_x, abs_grad_x );
imshow("【效果图】X方向Sobel", abs_grad_x);
// 求Y方向梯度
Sobel(src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y );
imshow("【效果图】Y方向Sobel", abs_grad_y);
// 合并梯度(近似)
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
imshow("【效果图】Sobel算子", dst);
waitKey(0);
return 0;
}
二、Laplace 算子
1、简介
拉普拉斯算子一个二阶微分算子。
Laplace 模板:
2、特点
- 旋转不变性
- 没有了边缘的方向信息
- 双倍加强了噪声的影响
3、OpenCV 中的 Sobel 函数
函数原型:
void Laplacian(InputArray src,
OutputArray dst,
int ddepth,
int ksize=1,
double scale=1,
double delta=0,
intborderType=BORDER_DEFAULT);
三、Canny 边缘提取
1、特点
检测标准:不丢失重要的边缘,没有虚假的边缘
定位标准:实际边缘与检测到的边缘位置之间的偏差最小
单响应标准:将多个响应降低为单个边缘响应
2、步骤
1) 使用高斯滤波器,以平滑图像,滤除噪声。
2) 计算图像中每个像素点的梯度强度和方向。
可以使用多种边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平 Gx 和垂直 Gy 方向的一阶导数值,由此便可以确定像素点的梯度G和方向theta 。
3) 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
对图像进行梯度计算后,仅仅基于梯度值提取的边缘仍然很模糊。非极大值抑制则可以帮助将局部最大值之外的所有梯度值抑制为0,对梯度图像中每个像素进行非极大值抑制的算法是:
- 将当前像素的梯度强度与沿正负梯度方向上的两个像素进行比较。
- 如果当前像素的梯度强度与另外两个像素相比最大,则该像素点保留为边缘点,否则该像素点将被抑制。
通常为了更加精确的计算,在跨越梯度方向的两个相邻像素之间使用线性插值来得到要比较的像素梯度。
4) 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
为了解决由于噪声和颜色变化引起的一些边缘像素的杂散响应,必须用弱梯度值过滤边缘像素,并保留具有高梯度值的边缘像素,可以通过选择高低阈值来实现。如果边缘像素的梯度值高于高阈值,则将其标记为强边缘像素;如果边缘像素的梯度值小于高阈值并且大于低阈值,则将其标记为弱边缘像素;如果边缘像素的梯度值小于低阈值,则会被抑制。阈值的选择取决于给定输入图像的内容。
5) 通过抑制孤立的弱边缘最终完成边缘检测。
对于弱边缘像素,因为这些像素可能是从真实边缘提取也可能是因噪声或颜色变化引起的。为了获得准确的结果,应该抑制由后者引起的弱边缘。通常,由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,通过查看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
3、OpenCV 中 Canny 函数
函数原型:
void Canny(InputArray image,
OutputArray edges,
double threshold1,
double threshold2,
int apertureSize=3,
bool L2gradient=false)
参数详解:
- 第一个参数:InputArray 类型的 image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像
- 第二个参数:OutputArray 类型的 edges,输出的边缘图,需要和源图片有一样的尺寸和类型
- 第三个参数:double 类型的 threshold1,第一个滞后性阈值
- 第四个参数:double 类型的 threshold2,第二个滞后性阈值
- 第五个参数:int 类型的 apertureSize,表示应用 Sobel 算子的模板大小,默认值3
- 第六个参数:bool 类型的 L2gradient,一个计算图像梯度幅值的标识,默认值false
函数使用:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//载入原始图
Mat src = imread("1.jpg");
//进行Canny边缘检测
Canny(src, src, 150, 100, 3 );
//显示边缘检测的结果图
imshow("【效果图】Canny边缘检测", src);
waitKey(0);
return 0;
}