OpenCV计算机图像视觉基础学习笔记10——详解Sobel算子

似乎很快就能学到边缘检测等实际需要用的内容了。倒不如先把中间的东西学完,自然推进就好。


Sobel算子

Sobel算子是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度。梯度越大的地方,越可能是图像的边缘。

比如在一幅图的一小块区域当中出现了黑与白之间的渐变,像是人脸轮廓与头发之间的渐变,这种渐变通常看起来是瞬间的,其实它们在中间总会有一个很短的变化过程,或者说,像素发生了跃迁。在头发区域是黑色的,在人脸区域是白色的,而它们之间总会有一段颜色的变化,这个变化是按照慢-快-慢的规律进行的。如果我们把像素渐变的这个过程抽象成函数,也许它看起来像是一个三次函数。

那么它总能找到一个变化率最高的点,不难理解,只需一次求导就可以得到这个点。Sobel算子便是会判断这里为图像的边缘。

Sobel算子的功能集合了高斯平滑和微分求导两个方面,所以又称一阶微分算子、求导算子。在水平和垂直两个方向上求导,可以得到图像X方向与Y方向的梯度图像。所以说,只要说到求图像的梯度图像,想到使用Sobel算子就好。

它是怎么实现的呢?简单来说,它会扩大选定方向上的差异,通过给算子两侧赋予一定的值,让被处理的图片的位置上的像素值出现更大的差来扩大差异,达到一个凸显轮廓的效果。

水平梯度上:

垂直梯度上:

最终图像梯度(上式为准确定义公式,下式是为了减少计算机的计算量采用的近似公式):

然而在实际应用中,Sobel算子在kernel=3时不是很准确,OpenCV中采用了改进版本的Scharr算子,它的算子如下:

Sobel算子与Scharr算子的相关API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Sobel (
InputArray src, // 输入图像
OutputArray dst,// 输出图像,大小与输入图像一致
int depth, // 输出图像深度 一定要比输出图像的深度大或者深度相等,不可以比他小。当然,遇事不决写-1。-1代表与原来深度相同。
int dx, // X方向,几阶导数
int dy, // Y方向,几阶导数.
int ksize, // SOBEL算子kernel大小,必须是1、3、5、7、9等奇数
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)

Scharr (
InputArray src, // 输入图像
OutputArray dst, // 输出图像,大小与输入图像一致
int depth, // 输出图像深度.
int dx, // X方向,几阶导数
int dy, // Y方向,几阶导数.
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)

对Sobel算子的应用

一般可以用来做边缘提取(和上一节说的那个边缘不是一个边缘= =)。

边缘是什么?是像素值发生跃迁的地方,是图像的显著特征之一。在图像特征提取、对象检测、模式识别等方面都有重要的作用。

那么如何捕捉/提取边缘呢?只需要对图像求它的一阶导数就可以。

我们知道,Δ = f(x) – f(x-1),Δ越大,说明像素在x方向变化越大,边缘信号越强。

应用操作分四步,首先做一次高斯模糊,使图片变得平滑,得到一个降噪的效果。然后使用cvtColor()把图片转化成灰度,再对图片求一下x方向和y方向上的梯度。最后混合一下xy方向上的梯度就可以了。一般叫混合之后的图像为振幅图像。信号越强,振幅也就越大。

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
Mat src, dst, dst2, dst3;
int ksize = 0;

src = imread("D:/pic2.png");
if (!src.data) {
cout << "could not load image..." << endl;
return -1;
}
char input_WIN[] = "input image";
char outputx_WIN[] = "output x image";
char outputy_WIN[] = "output y image";

int borderType = BORDER_DEFAULT;
imshow(input_WIN, src);

Mat gray_src;
GaussianBlur(src, dst, Size(3, 3), 0, 0);
cvtColor(dst, gray_src, CV_BGR2GRAY);

Mat xgrad, ygrad;
Sobel(gray_src, xgrad, CV_16S, 0, 1, 3);
Sobel(gray_src, ygrad, CV_16S, 1, 0, 3);

// Performs a look-up table transform of an array.
// 没看懂函数原型解释讲的是什么玩意,不过它可以处理显示不正常。。
// 似乎是在灰度图像上进行一个彩色绘制达到显示图像的效果?
convertScaleAbs(xgrad, xgrad);
convertScaleAbs(ygrad, ygrad);

imshow(outputx_WIN, xgrad);
imshow(outputy_WIN, ygrad);


waitKey(0);
return 0;
}

这样做出来的效果似乎和之前用的Sobel算子效果不太一样,问题出现在哪里呢?

是深度问题。我们把深度设为-1再试试。

可以看到似乎颜色的“力道”稍浅了一些。当“力道”较大时,图片处理后的细节就较为明显。

最后让我们用addWeighted把两个方向上的图片拼合到一起。

最终的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
Mat src, dst, dst2, dst3;
int ksize = 0;

src = imread("D:/pic2.png");
if (!src.data) {
cout << "could not load image..." << endl;
return -1;
}
char input_WIN[] = "input image";
char outputx_WIN[] = "output x image";
char outputy_WIN[] = "output y image";
char output_WIN[] = "final image";

int borderType = BORDER_DEFAULT;
imshow(input_WIN, src);

Mat gray_src;
GaussianBlur(src, dst, Size(3, 3), 0, 0);
cvtColor(dst, gray_src, CV_BGR2GRAY);

Mat xgrad, ygrad, final;
Sobel(gray_src, xgrad, -1, 0, 1, 3);
Sobel(gray_src, ygrad, -1, 1, 0, 3);

// Performs a look-up table transform of an array.
// 没看懂函数原型解释讲的是什么玩意,不过它可以处理显示不正常。。
// 似乎是在灰度图像上进行一个彩色绘制达到显示图像的效果?
convertScaleAbs(xgrad, xgrad);
convertScaleAbs(ygrad, ygrad);

//imshow(outputx_WIN, xgrad);
//imshow(outputy_WIN, ygrad);
addWeighted(xgrad, 0.5, ygrad, 0.5, 0, final);

imshow(output_WIN, final);
waitKey(0);
return 0;
}

-------------本文结束,感谢您的阅读转载请注明原作者及出处-------------


本文标题:OpenCV计算机图像视觉基础学习笔记10——详解Sobel算子

文章作者:Shawn Zhou

发布时间:2019年08月25日 - 18:08

最后更新:2019年08月26日 - 20:08

原始链接:http://shawnzhou.xyz/2019/08/25/19-08-25-01/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

知识无价,码字不易。对您有用,敬请打赏。金额随意,感谢关心。
0%