opencv入门之九Opencv边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器

Posted Haven_zhf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv入门之九Opencv边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器相关的知识,希望对你有一定的参考价值。

参考网站:

http://blog.csdn.net/poem_qianmo/article/details/25560901

 

1、边缘检测步骤

  1)滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感。( 通常用高斯滤波 )

  2)增强:增强边缘的基础是确定图像各点领域强度的变化值。增强算法可以将图像灰度点领域强度值有显著变化的点凸显出来。( 可以通过计算梯度幅值来确定 )

  3)检测:经过增强的图像,往往领域中有很多点的梯度值比较大,而特定的应用中,这些点并不是我们要找的边缘点,常通过阈值化方法来筛选。

 

2、canny算子

  最优的边缘检测的算子,其三个主要评价标准:

  1)低错误率:标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。

  2)高定位性:标识出的边缘要与图像中的实际边缘尽可能接近。

  3)最小响应:图像中的边缘只能标识出一次,并且可能存在的图像噪声不应该标识为边缘。

void Canny(InputArray image,
      OutputArray edges,
      double threshold1,   //第一个滞后性阈值
      double threshold2,   //第二个滞后性阈值
      int apertureSize=3,  //应用Sobel算子的孔径大小
      bool L2gradient=false )   //一个计算图像梯度幅值的标识

  注意:这个函数阈值1和阈值2两者的小者用于边缘连接,而大者用来控制强边缘的初始段,推荐的高低阈值比在2:1到3:1之间。

 1 //载入原始图    
 2     Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 3     Mat src1=src.clone();  
 4   
 5     //显示原始图   
 6     imshow("【原始图】Canny边缘检测", src);   
 7   
 8     //----------------------------------------------------------------------------------  
 9     //  一、最简单的canny用法,拿到原图后直接用。  
10     //----------------------------------------------------------------------------------  
11     Canny( src, src, 150, 100,3 );  
12     imshow("【效果图】Canny边缘检测", src);   
13   
14       
15     //----------------------------------------------------------------------------------  
16     //  二、高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色的边缘图  
17     //----------------------------------------------------------------------------------  
18     Mat dst,edge,gray;  
19   
20     // 【1】创建与src同类型和大小的矩阵(dst)  
21     dst.create( src1.size(), src1.type() );  
22   
23     // 【2】将原图像转换为灰度图像  
24     cvtColor( src1, gray, CV_BGR2GRAY );  
25   
26     // 【3】先用使用 3x3内核来降噪  
27     blur( gray, edge, Size(3,3) );  
28   
29     // 【4】运行Canny算子  
30     Canny( edge, edge, 3, 9,3 );  
31   
32     //【5】将g_dstImage内的所有元素设置为0   
33     dst = Scalar::all(0);  
34   
35     //【6】使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中  
36     src1.copyTo( dst, edge);  
37   
38     //【7】显示效果图   
39     imshow("【效果图】Canny边缘检测2", dst);   

 

3、sobel算子

  sobel算子是一个主要用作边缘检测的离散微分算子(discrete differentiation operator)。它sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

void Sobel (  
InputArray src,//输入图  
 OutputArray dst,//输出图  
 int ddepth,//输出图像的深度  
 int dx,    //x方向上的差分阶数
 int dy,    //y方向上的差分阶数
 int ksize=3,    //Sobel核的大小
 double scale=1,  //计算导数值时可选的缩放因子
 double delta=0,    
 int borderType=BORDER_DEFAULT );

  第三个参数:输出图像的深度

    •  若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

  调用Sobel函数的实例代码如下。这里只是教大家如何使用Sobel函数,就没有先用一句cvtColor将原图;转化为灰度图,而是直接用彩色图操作。

 1 //【0】创建 grad_x 和 grad_y 矩阵  
 2     Mat grad_x, grad_y;  
 3     Mat abs_grad_x, abs_grad_y,dst;  
 4   
 5     //【1】载入原始图    
 6     Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 7   
 8     //【2】显示原始图   
 9     imshow("【原始图】sobel边缘检测", src);   
10   
11     //【3】求 X方向梯度  
12     Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );  
13     convertScaleAbs( grad_x, abs_grad_x );  
14     imshow("【效果图】 X方向Sobel", abs_grad_x);   
15   
16     //【4】求Y方向梯度  
17     Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );  
18     convertScaleAbs( grad_y, abs_grad_y );  
19     imshow("【效果图】Y方向Sobel", abs_grad_y);   
20   
21     //【5】合并梯度(近似)  
22     addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
23     imshow("【效果图】整体方向Sobel", dst);   
24   

 

4、Laplace算子

  Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad()的散度div()。

  根据图像处理的原理我们知道,二阶导数可以用来进行检测边缘 。 因为图像是 “二维”, 我们需要在两个方向进行求导。使用Laplacian算子将会使求导过程变得简单。

void Laplacian(InputArray src,
        OutputArray dst,
        int ddepth,
        int ksize=1,
        double scale=1,
        double delta=0,
        intborderType=BORDER_DEFAULT );
 1 //【0】变量的定义  
 2     Mat src,src_gray,dst, abs_dst;  
 3   
 4     //【1】载入原始图    
 5     src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 6   
 7     //【2】显示原始图   
 8     imshow("【原始图】图像Laplace变换", src);   
 9   
10     //【3】使用高斯滤波消除噪声  
11     GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );  
12   
13     //【4】转换为灰度图  
14     cvtColor( src, src_gray, CV_RGB2GRAY );  
15   
16     //【5】使用Laplace函数  
17     Laplacian( src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT );  
18   
19     //【6】计算绝对值,并将结果转换成8位  
20     convertScaleAbs( dst, abs_dst );  
21   
22     //【7】显示效果图  
23     imshow( "【效果图】图像Laplace变换", abs_dst );  

 

5、Scharr滤波器

  scharr一般我就直接称它为滤波器,而不是算子。上文我们已经讲到,它在OpenCV中主要是配合Sobel算子的运算而存在的,一个万年备胎。让我们直接来看看函数讲解吧。

void Scharr(  
InputArray src, //源图  
 OutputArray dst, //目标图  
 int ddepth,//图像深度  
 int dx,// x方向上的差分阶数  
 int dy,//y方向上的差分阶数  
 double scale=1,//缩放因子  
 double delta=0,// delta值  
 intborderType=BORDER_DEFAULT )// 边界模式  
 1 //【0】创建 grad_x 和 grad_y 矩阵  
 2     Mat grad_x, grad_y;  
 3     Mat abs_grad_x, abs_grad_y,dst;  
 4   
 5     //【1】载入原始图    
 6     Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
 7   
 8     //【2】显示原始图   
 9     imshow("【原始图】Scharr滤波器", src);   
10   
11     //【3】求 X方向梯度  
12     Scharr( src, grad_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );  
13     convertScaleAbs( grad_x, abs_grad_x );  
14     imshow("【效果图】 X方向Scharr", abs_grad_x);   
15   
16     //【4】求Y方向梯度  
17     Scharr( src, grad_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );  
18     convertScaleAbs( grad_y, abs_grad_y );  
19     imshow("【效果图】Y方向Scharr", abs_grad_y);   
20   
21     //【5】合并梯度(近似)  
22     addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
23   
24     //【6】显示效果图  
25     imshow("【效果图】合并梯度后Scharr", dst);   
26   

 

 

6、最后是综合实践

  1 //******************************【程序说明】*****************************
  2 //    程序名称:opencv边缘检测:Canny算子,Sobel算子,laplace算子,Scharr滤波器
  3 //    opencv版本:2.4.13
  4 //    日期:2017/9/22
  5 //**********************************************************************
  6 
  7 
  8 //******************************【头文件包含部分】*****************************
  9 //    描述:包含程序所依赖的头文件
 10 //*****************************************************************************
 11 //#include <opencv2/core/core.hpp>
 12 #include <opencv2/highgui/highgui.hpp>
 13 #include <opencv2/imgproc/imgproc.hpp>
 14 //#include <iostream>
 15 
 16 
 17 //******************************【命名空间声明部分】*****************************
 18 //    描述:包含程序所使用的命名空间
 19 //*****************************************************************************
 20 using namespace cv;
 21 
 22 
 23 //******************************【全局变量声明部分】*****************************
 24 //    描述:全局变量声明
 25 //*****************************************************************************
 26 Mat g_srcImage, g_srcGrayImage, g_dstImage;    //原始图、原图的灰度图、效果图
 27 
 28 //Canny边缘检测相关变量
 29 Mat g_cannyDetectedEdges;
 30 int g_cannyLowThreshold = 1;    //TrackBar位置参数
 31 
 32 //Sobel边缘检测相关变量
 33 Mat g_sobelGradient_X, g_sobelGradient_Y;
 34 Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
 35 int g_sobelkernelSize = 1;    //TrackBar位置参数
 36 
 37 //Laplacian相关变量
 38 Mat g_LaplacianAbs;
 39 int g_LaplaciankerneSize = 1;//TrackBar位置参数
 40 
 41 //Scharr滤波器相关变量
 42 Mat g_scharrGradient_X, g_scharrGradient_Y;
 43 Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;
 44 
 45 
 46 //******************************【全局函数声明部分】*****************************
 47 //    描述:全局函数声明
 48 //*****************************************************************************
 49 static void ShowHelpText();                //帮助文字显示
 50 static void on_Canny( int, void* );    //回调函数
 51 static void on_Sobel ( int, void* );    //回调函数
 52 static void on_Lapacian( int, void* );        //回调函数
 53 void Scharr();    //封装Scharr边缘检测相关代码的函数
 54 
 55 
 56 //******************************【main()部分】*****************************
 57 //    描述:控制台应用程序的入口函数,我们的程序从这里开始
 58 //*****************************************************************************
 59 int main(int argc, char** argv )
 60 {
 61     //【0】初始化
 62     system("color 2F");
 63     ShowHelpText();
 64 
 65     //【1】读取原图
 66     g_srcImage = imread( "1.jpg", 1 );    //工程目录下需要有一张名为1.jpg的素材图
 67     if(!g_srcImage.data) { printf("Oh,no,读取srcImage错误!!!!\n"); return false; }
 68 
 69     //【2】显示原图
 70     namedWindow("【原图窗口】", 1 );
 71     imshow("【原图窗口】", g_srcImage );
 72 
 73     //【3】创建与src同类型和大小的矩阵(dst)
 74     g_dstImage.create( g_srcImage.size(), g_srcImage.type() );
 75 
 76     //【4】先去除噪声,然后将原图像转换为灰度图像
 77     GaussianBlur( g_srcImage, g_srcImage, Size(3,3), 0, 0, BORDER_DEFAULT );
 78     cvtColor( g_srcImage, g_srcGrayImage, CV_BGR2GRAY );
 79 
 80     //【5】创建三个窗口
 81     namedWindow("【效果图】Canny边缘检测", CV_WINDOW_AUTOSIZE);
 82     namedWindow("【效果图】Sobel边缘检测", CV_WINDOW_AUTOSIZE);
 83     namedWindow("【效果图】Laplacian边缘检测", CV_WINDOW_AUTOSIZE);
 84 
 85     //【6】创建轨迹条trackbar
 86     createTrackbar( "参数值:", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny );
 87     createTrackbar( "参数值:", "【效果图】Sobel边缘检测", &g_sobelkernelSize, 3, on_Sobel );
 88     createTrackbar( "参数值:", "【效果图】Laplacian边缘检测", &g_LaplaciankerneSize, 3, on_Lapacian );
 89     
 90     //【7】调用回调函数
 91     on_Canny( 0, 0 );
 92     on_Sobel( 0, 0 );
 93     on_Lapacian( 0, 0 );
 94 
 95     //【8】调用封装了Scharr边缘检测代码的函数
 96     Scharr();
 97 
 98     //【9】轮询获取按键信息,按下键盘Q或者ESC,程序退出
 99     while(1)
100     {
101         int c;
102         c = waitKey(0);
103         if( (char)c==q || (char)c==27 )    break;
104     }
105 
106     return 0;
107 }
108 
109 
110 
111 //******************************【ShowHelpText()部分】*****************************
112 //    描述:输出一些帮助信息
113 //*****************************************************************************
114 static void ShowHelpText()
115 {
116     //输出一些帮助信息  
117     printf( "\n\n\t嗯。运行成功,请调整滚动条观察图像效果~\n\n"  
118              "\t按下“q”键时,程序退出~!\n"  
119              "\n\n\t\t\t\t by浅墨" );  
120 }
121 
122 
123 
124 //******************************【on_Canny()部分】*****************************
125 //    描述:Canny边缘检测窗口滚动条的回调函数
126 //*****************************************************************************
127 static void on_Canny( int, void* )
128 {
129     //【1】先使用3*3内核来降噪
130     blur( g_srcGrayImage, 
131         g_cannyDetectedEdges, 
132         Size(3,3) );
133 
134     //【2】运行我们的Canny算子
135     Canny( g_cannyDetectedEdges, 
136         g_cannyDetectedEdges, 
137         g_cannyLowThreshold, 
138         g_cannyLowThreshold*3,
139         3 );
140 
141     //【3】先将g_dstImage内的所有元素设置为0
142     g_dstImage = Scalar::all(0);
143         
144     //【4】使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷贝到目标图g_dstImage中
145     g_srcImage.copyTo( g_dstImage, g_cannyDetectedEdges );
146         
147     //【5】显示效果图
148     imshow( "【效果图】Canny边缘检测", g_dstImage );
149 }
150 
151 
152 
153 //******************************【on_Sobel()部分】*****************************
154 //    描述:Sobel边缘检测窗口滚动条的回调图
155 //*****************************************************************************
156 static void on_Sobel( int, void* )
157 {
158     //【1】求X方向梯度
159     Sobel( g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelkernelSize+1), 1, 1,BORDER_DEFAULT );
160     convertScaleAbs( g_sobelGradient_X, g_sobelAbsGradient_X );    //计算绝对值,并将结果转换成8位
161 
162     //【2】求Y方向梯度
163     Sobel( g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2*g_sobelkernelSize+1), 1, 1,BORDER_DEFAULT );
164     convertScaleAbs( g_sobelGradient_Y, g_sobelAbsGradient_Y );    //计算绝对值,并将结果转换成8位
165 
166     //【3】合并梯度
167     addWeighted( g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage );
168 
169     //【4】显示效果图
170     imshow( "【效果图】Sobel边缘检测", g_dstImage );
171 }
172 
173 
174 //******************************【on_Lapacian()部分】*****************************
175 //    描述:Lapacian边缘检测窗口滚动条的回调图
176 //*****************************************************************************
177 static void on_Lapacian( int, void* )
178 {
179     //【1】使用Laplacian函数
180     Laplacian( g_srcGrayImage, g_dstImage, CV_16S, (2*g_LaplaciankerneSize+1), 1, 0, BORDER_DEFAULT );
181     
182     //【2】计算绝对值,并将结果转换成8位
183     convertScaleAbs( g_dstImage, g_LaplacianAbs );
184     
185     //【3】显示效果图
186     imshow("【效果图】Laplacian边缘检测", g_LaplacianAbs );
187 }
188 
189 
190 
191 //******************************【Scharr()部分】*****************************
192 //    描述:Scharr边缘检测相关代码的函数
193 //*****************************************************************************
194 static void Scharr()
195 {
196     //【1】求X方向梯度
197     Scharr( g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );
198     convertScaleAbs( g_scharrGradient_X, g_scharrAbsGradient_X );    //计算绝对值,并将结果转换成8位
199 
200     //【2】求Y方向梯度
201     Scharr( g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );
202     convertScaleAbs( g_scharrGradient_Y, g_scharrAbsGradient_Y );    //计算绝对值,并将结果转换成8位
203 
204     //【3】合并梯度
205     addWeighted( g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage );
206 
207     //【4】显示效果图
208     imshow( "【效果图】Scharr滤波器", g_dstImage );
209 }

 

以上是关于opencv入门之九Opencv边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV | OpenCV实战从入门到精通系列三 --canny边缘检测

OpenCV入门指南第三篇Canny边缘检测

OpenCV3入门图像边缘检测

opencv边缘检测的入门剖析

OpenCV入门指南第七篇 线段检测与圆检测

opencv入门项目——车道线检测