边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。 这些包括(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 <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/imgproc/types_c.h> #include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui_c.h> #include <iostream> 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 <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/imgproc/types_c.h> #include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui_c.h> #include <iostream> using namespace cv; using namespace std; //-----------------------------------【头文件包含部分】--------------------------------------- // 描述:包含程序所依赖的头文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空间声明部分】--------------------------------------- // 描述:包含程序所使用的命名空间 //----------------------------------------------------------------------------------------------- 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 <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/imgproc/types_c.h> #include <opencv2/highgui/highgui.hpp> #include <opencv2/highgui/highgui_c.h> #include <iostream> using namespace cv; using namespace std; //-----------------------------------【头文件包含部分】--------------------------------------- // 描述:包含程序所依赖的头文件 //---------------------------------------------------------------------------------------------- #include <opencv2/opencv.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> //-----------------------------------【命名空间声明部分】--------------------------------------- // 描述:包含程序所使用的命名空间 //----------------------------------------------------------------------------------------------- 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; }运行结果如下图所示: