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滤波器的主要内容,如果未能解决你的问题,请参考以下文章