图像边缘检测的概念和大概原理可以参考我的另一篇博文,链接如下:
https://blog.csdn.net/wenhao_ir/article/details/51743382
这篇博文介绍利用OpenCV的库函数Sobel()对图像作边缘检测。
先对Sobel算子进行大概介绍:
1 sobel算子是以一阶微分为基础的边缘检测,是通过离散微分方法求取图像边缘的边缘检测算子,它在水平方向和垂直方向上对图像作边缘检测运算。
2 Sobel算子结合了高斯平滑滤波的思想,将边缘检测滤波器尺寸由ksize * 1改进为ksize * ksize,提高了对平缓区域边缘的响应。由于结合了高斯平滑滤波的思想,所以sobel算对噪声具有平滑作用。Sobel算子提供水平方向和垂直方向上的边缘信息,但边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
3 使用Sobel边缘检测算子提取图像边缘的过程大致可以分为以下三个步骤:
①按X方向提取边缘;
②按Y方向提取边缘;
③综合两个方向的边缘信息得到整幅图像的边缘。
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)
参数意义如下:
src---待提取边缘的图像。
dst---输出图像,与输入图像src具有相同的尺寸和通道数,数据类型由第三个参数ddepth控制。
ddepth---输出图像的数据类型(深度),根据输入图像的数据类型不同拥有不同的取值,具体的取值如下表所示,当取值为-1时,输出图像的数据类型自动选择。
dx---x 方向上的差分阶数。
dy---y方向上的差分阶数。
ksize---表示Sobel核的大小;必须取1,3,5或7,默认值为3。
scale---对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放。
delta---偏移值,在计算结果中加上偏移值。
borderType---像素外推法选择标志,关于这个参数可以参考我的另一篇博文,链接如下:https://blog.csdn.net/wenhao_ir/article/details/124177989
使用函数Sobel()进行边缘检测的代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1k2wHHJPxh1u_aM6W0ILoNg?pwd=gxti
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
//opencv版本:OpenCV3.0
//VS版本:VS2012
#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>
using namespace cv;
int main( )
{
cv::Mat srcImage = imread("F:/material/images/P0042-building_edge_detection.jpg");
if( !srcImage.data )
return -1;
cv::Mat srcGray;
cvtColor( srcImage, srcGray, CV_RGB2GRAY );
imshow( "srcGray", srcGray);
// 定义边缘图、在x和y方向上运用Sobe算子的结果图
cv::Mat edgeMat, edgeXMat, edgeYMat;
// 在x方向上运用Sobe算子
Sobel( srcGray, edgeXMat, CV_16S, 1, 0, 3, 1,0, BORDER_DEFAULT );
// 在y方向上运用Sobe算子
Sobel( srcGray, edgeYMat, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT );
// 线性变换转换输入数组元素成8位无符号整型
convertScaleAbs(edgeXMat, edgeXMat);
convertScaleAbs(edgeYMat, edgeYMat);
// x与y方向的运算结果叠加
addWeighted(edgeXMat, 0.5, edgeYMat, 0.5, 0, edgeMat);
//结果展示
cv::imshow( "Sobel-edgeX", edgeXMat );
cv::imshow( "Sobel-edgeY", edgeYMat );
cv:imshow( "Sobel-edge", edgeMat );
cv::waitKey(0);
return 0;
}
如果想了解上面代码中用到的函数convertScaleAbs(),可以参见我的另一篇博文,链接如下
https://blog.csdn.net/wenhao_ir/article/details/51336505
运行结果如下:
从上面的结果中我们可以看出,在x方向上应用Sobel算子得到的是目标的垂直方向上的边缘,而在y方向上应用Sobel算子得到的是目标的垂直方向上的边缘。为什么会是这个结果?想想大致的运算过程就知道了,你沿x方向检测到的灰度值变化剧烈的点集当然构成的是垂直方向上的边缘;同样,你沿y方向检测到的灰度值变化剧烈的点集当然构成的是水平方向上的边缘了。
接下来说函数Scharr()。
虽然Sobel算子可以有效的提取图像边缘,但是对图像中较弱的边缘提取效果较差。因此为了能够有效的提取出较弱的边缘,需要将像素值间的差距增大,因此引入Scharr算子。Scharr算子是对Sobel算子差异性的增强,因此两者之间的在检测图像边缘的原理和使用方式上相同。Scharr算子的核在小为3×3。
OpenCV提供了函数Scharr()来实现Scharr算子,其原型如下:
void cv::Scharr(InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
其参数意义和函数Sobel()完全样,只是因为Scharr算子的核大小为固定的3,所以少了个参数ksize。
使用函数Scharr()进行边缘检测的代码如下:
代码中用到的图像下载链接:https://pan.baidu.com/s/1k2wHHJPxh1u_aM6W0ILoNg?pwd=gxti
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
//opencv版本:OpenCV3.0
//VS版本:VS2012
#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>
using namespace cv;
int main( )
{
cv::Mat srcImage = imread("F:/material/images/P0042-building_edge_detection.jpg");
if( !srcImage.data )
return -1;
cv::Mat srcGray;
cvtColor( srcImage, srcGray, CV_RGB2GRAY );
imshow( "srcGray", srcGray);
// 定义边缘图、在x和y方向上运用Scharr算子的结果图
cv::Mat edgeMat, edgeXMat, edgeYMat;
// 在x方向上运用Scharr算子
Scharr( srcGray, edgeXMat, CV_16S, 1, 0, 1,0, BORDER_DEFAULT );
// 在y方向上运用Scharr算子
Scharr( srcGray, edgeYMat, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );
// 线性变换转换输入数组元素成8位无符号整型
convertScaleAbs(edgeXMat, edgeXMat);
convertScaleAbs(edgeYMat, edgeYMat);
// x与y方向的运算结果叠加
addWeighted(edgeXMat, 0.5, edgeYMat, 0.5, 0, edgeMat);
//结果展示
cv::imshow( "Scharr-edgeX", edgeXMat );
cv::imshow( "Scharr-edgeY", edgeYMat );
cv:imshow( "Scharr-edge", edgeMat );
cv::waitKey(0);
return 0;
}
运行结果如下:
从运行结果中我们可看出,Scharr算子确实增强了对弱边缘的检测效果,体现在上面运行结果中就是更多的边缘被检测到了。