铅笔

在你的害怕中坚持的越多,你就会越自信
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

图像梯度–Sobel算子

Posted on 2020-01-09 14:50  黑色の铅笔  阅读(7760)  评论(0编辑  收藏  举报

1.Sobel算子

  • 卷积的作用除了实现图像模糊或者去噪,还可以寻找一张图像上所有梯度信息,这些梯度信息是图像的最原始特征数据,进一步处理之后就可以生成一些比较高级的特征用来表示一张图像实现基于图像特征的匹配,图像分类等应用。
  • Sobel算子是一种很经典的图像梯度提取算子,其本质是基于图像空间域卷积,背后的思想是图像一阶导数算子的理论支持。
  • sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。(Laplacian边缘检测并不局限于水平方向或垂直方向,这是Laplacian与soble的区别)

2.Sobel算子的实现

Sobel本质是基于图像空间域卷积,先来理解下卷积的基础知识。

2.1卷积

    1. 待处理的数字图像可以看做是一个大矩阵,图像的每个像素对应着矩阵的每个元素,假设我们的图像分辨率为1024x768,那么对应大矩阵的的行数则为1024,列数为768.
    2. 用于滤波(卷积核不止用于滤波)的小矩阵(也叫卷积核),一般是一个方阵,也就是行数和列数相同,比如常见的边缘检测Sobel算子为3x3的矩阵。
    3. 滤波就是对于大矩阵中的每个像素,计算它周围和滤波器矩阵对应位置元素的乘积,最后把乘积结果相加到一起,最终得到的值就作为该像素的新值。这样就对一个像素完成了一次滤波 
      卷积示意图

注:对图像大矩阵和滤波小矩阵的对应位置元素进行相乘再求和的操作就叫做卷积。(Convolution)或协相关(Correlation). 
协相关(Correlation)和卷积(Convolution)很类似, 两者唯一的差别就是卷积在计算前需要翻转卷积核, 协相关则不需要翻转

2.2Sobel算子

该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。其公式如下: 

 

 

 

其中:

  • I代表原始图像,
  • Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值
  • G表示图像的每一个像素的横向及纵向灰度值 有两种计算方式
  • Scharr,比Sobel算子的值更大,因此对于灰度变化更为敏感,会得到较强的边缘强度,但是也会损失一些细节

3.实战

3.1 api解释

OpenCV实现了基于Sobel算子提取图像梯度的API,表示与解释如下

 1 void Sobel(
 2            InputArray src, 
 3            OutputArray dst, int ddepth,
 4            int dx, 
 5             int dy,
 6             int ksize = 3,
 7            double scale = 1, double delta = 0,
 8            int borderType = BORDER_DEFAULT );
 9 
10 @param src 为输入图像,填Mat类型即可。
11 @param dst 即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
12 @param ddepth 输出图像的深度。
13 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F 
14 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F 
15 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F 
16 若src.depth() = CV_64F, 取ddepth = -1/CV_64F
17 @param dx x 方向上的差分阶数。
18 @param dy y方向上的差分阶数。
19 @param ksize 有默认值3,表示Sobel核的大小;必须取1,3,5或7。
20 @param scale 计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。我们可以在文档中查阅getDerivKernels的相关介绍,来得到这个参数的更多信息。
21 @param delta 表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
22 @param borderType 边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate处得到更详细的信息

 

3.2代码

 1 #include <opencv2/opencv.hpp>
 2 #include <iostream>
 3 
 4 using namespace cv;
 5 using namespace std;
 6 
 7 int main(int artc, char** argv) {
 8             Mat src = imread("src_sobel.jpg");
 9     if (src.empty()) {
10         printf("could not load image...\n");
11         return -1;
12     }
13     namedWindow("input", CV_WINDOW_AUTOSIZE);
14     imshow("input", src);
15     Mat grad_x, grad_y;
16     Mat dst;
17     Sobel(src, grad_x, CV_32F, 1, 0, 3, 1, 0, BORDER_DEFAULT);
18     Sobel(src, grad_y, CV_32F, 0, 1, 3, 1, 0, BORDER_DEFAULT);
19 
20     convertScaleAbs(grad_x, grad_x);
21     convertScaleAbs(grad_y, grad_y);
22 
23     add(grad_x, grad_y, dst, Mat(), CV_16S);
24     convertScaleAbs(dst, dst);
25 
26     imshow("grad_x", grad_x);
27     imshow("grad_y", grad_y);
28     imshow("gradient", dst);
29 
30     waitKey(0);
31 
32     return 0;
33 }
原图:

 

 

x方向sobel:检测水平梯度边缘,已经将竖直方向上的边缘梯度剔除

 

 

y方向sobel:检测竖直梯度边缘,已经将水平方向上的边缘梯度剔除
 

 

 

梯度综合图像(两者像素相加即可得到合并后的图像)
 

 

 

 
 
参考
卷积及常用卷积核的类型:https://www.cnblogs.com/xiaojianliu/p/9075872.html
sobel:https://www.cnblogs.com/freeblues/p/5738987.html
https://blog.csdn.net/linqianbi/article/details/78673903
https://blog.csdn.net/qq_30460949/article/details/90046408
https://wx.zsxq.com/dweb2/index/group/551551828124?from=mweb&type=detail