边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。 这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。 边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。
OpenCV提供了相关函数来实现各种边缘检测算子,具体的各种边缘检测的算子的算法原理请大家自行百度,下面分别介绍并给出示例代码。
一、Canny边缘检测算子
OpenCV提供了Canny函数来实现Canny边缘检测算子,函数原型如下:
C++: void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
参数意义如下:
image:输入图像,其类型要求只能是8位的
edges:边缘输出,其类型要求是单通道的8位图像,和输入图像有相同的大小。
threshold1:第一个滞后性阈值。
threshold2:第二个滞后性阈值。
apertureSize:使用Sobel算子的孔径大小,有默认值为3。
L2gradient:是否使用 L_2 norm =\sqrt{(dI/dx)^2 + (dI/dy)^2来计算图像的梯度幅值,默认值为false,表示不使用L_2 norm来计算梯度幅值,此时使用L_1 norm来计算梯度幅值。
补充说明:两个滞后性阈值中较小的那个用来确定边缘连接,较大的那个用来初始化强边缘的检测,详细原理可参见链接:http://en.wikipedia.org/wiki/Canny_edge_detector
使用Canny函数进行边缘检测的代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1hs8ImVU 密码:irnd
//opencv版本:OpenCV3.0
//VS版本:VS2013
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat src = imread("20.jpg");
Mat src_1 = imread("20.jpg", 0);
Mat gray = imread("20.jpg", 0);
imshow("原图", src);
//----------------------------------------------------------------------------------
// 一、最简单的canny用法,拿到原图后直接用。
//----------------------------------------------------------------------------------
Canny(src_1, src_1, 100, 150, 3);
imshow("简单用法的Canny边缘检测结果", src_1);
//----------------------------------------------------------------------------------
// 二、高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色的边缘图
//----------------------------------------------------------------------------------
Mat dst, edge;
// 【1】创建与src同类型和大小的矩阵(dst)
dst.create(gray.size(), gray.type());
// 【2】先用使用 3x3内核来降噪
blur(gray, edge, Size(3, 3));
// 【3】运行Canny算子
Canny(edge, edge, 100, 150, 3);
imshow("高阶用法的Canny边缘检测结果", edge);
//【4】将g_dstImage内的所有元素设置为0
dst = Scalar::all(0);
//【5】使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中
src.copyTo(dst, edge);
//【6】显示效果图
imshow("高阶用法的合成图", dst);
waitKey(0);
return 0;
}
代码运行截果如下图所示:
二、Sobel算子
OpecnCV提供了Sobel函数来计算图像的一阶,二阶,三阶导数,使用的算子是扩展了的Sobel算子。
函数原型如下:
C++: 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 )
参数意义如下:
src:源图像
dst:目标图像,和源图的大小和通道数一样。
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
当 ddepth = -1时,目标图像和源图像的深度一样,当深度是8位时,程序会对数作截断处理。
dx:x方向的求导阶数
dy:y方向的求导阶数
ksize:扩展Sobel算子的大小,可以的取值为1,3,5,7,值得注意的是,当ksize=CV_SCHARR (-1)时,使用3×3的Scharr 滤波器进行计算,这样精度更高。Scharr在x方向和y方向上的核算子矩阵为:
Scharr函数的原型如下:
C++: void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
这里就不对Scharr的参数多做介绍了,也不单独给Scharr的使用示例,因为它就是Sobel算子的一个特例,和Sobel算子的使用上,除了没有ksize这个参数外,其它都一样。
scale:可选的导数计算比例因子,默认不使用比例因子。
borderType:边界处理方法,具体的详见帖子thread-177-1-1.html
值得注意的是:Sobel算子在计算中进行了高斯平滑和差分运算,这样或多或少对噪声有消除作用。可以设置(xorder = 1, yorder = 0, ksize = 3)或(xorder = 0, yorder = 1, ksize = 3)来计算在x或y方向上的一阶导数。
一阶导数的核算子如下:
二阶导数的核算子如下:
使用Sobel函数计算图像边缘的代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1gfAggZh 密码:dnol
//opencv版本:OpenCV3.0
//VS版本:VS2013
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include
#include
#include
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//【0】创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y,dst;
//【1】载入原始图
Mat src = imread("21.jpg");
//【2】显示原始图
imshow("【原始图】sobel边缘检测", src);
//【3】求 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);
//【4】求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);
//【5】合并梯度(近似)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );
imshow("【效果图】整体方向Sobel", dst);
waitKey(0);
return 0;
}
运行结果如下图所示:
三、Laplace算子
OpecnCV提供了Laplacian函数来计算图像的拉普拉斯运算。
函数原型如下:
C++: void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
参数意义如下:
src:源图像
dst:目标图像,和源图像的大小和通道数一样。
ddepth:目标图像深度。
ksize:核算子的大小,用计算二阶导数时使用。
delta:可选的delta值,结果进行存储前的附加值、
borderType:边界处理方法,具体的详见帖子thread-177-1-1.html
这个函数实际上就是把使用Sobel在x方向和y方向算子计算出的二阶层数进行合成,从而得到自己的计算结果。
使用Laplacian函数计算图像边缘的代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1slJptsL 密码:tiki
//opencv版本:OpenCV3.0
//VS版本:VS2013
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
//-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include
#include
#include
//-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main()
{
//【0】变量的定义
Mat src, src_gray, dst, abs_dst;
//【1】载入原始图
src = imread("22.jpg");
//【2】显示原始图
imshow("【原始图】图像Laplace变换", src);
//【3】使用高斯滤波消除噪声
GaussianBlur(src, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
//【4】转换为灰度图
cvtColor(src, src_gray, CV_RGB2GRAY);
//【5】使用Laplace函数
Laplacian(src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT);
//【6】计算绝对值,并将结果转换成8位
convertScaleAbs(dst, abs_dst);
//【7】显示效果图
imshow("【效果图】图像Laplace变换", abs_dst);
waitKey(0);
return 0;
}
运行结果如下图所示: