目录
一、前言
二、算子
1、咋理解算子
2、算子定义
三、Sobel算子
1、讲解
2、API
3、代码展示
4、执行结果
四、Scharr算子
1、讲解
2、API
3、代码展示
4、执行结果
继续填坑。
如果想看其他有关于OpenCV学习方法介绍、学习教程、代码实战、常见报错及解决方案等相关内容,可以直接看我的OpenCV分类:
【OpenCV系列】:https://blog.csdn.net/shuiyixin/article/category/7581855
如果你想了解更多有关于计算机视觉、OpenCV、机器学习、深度学习等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!
今天开始,我们来了解一下算子的概念,这个名字可能大家刚开始入门计算机视觉,会听到,但是会感觉别扭,就觉得,这是个啥?
听着不太对劲,是不是说错了?
其实呢,算子本身来源于数学。
啊!数学
啊!数学
啊!数学
如果你想掌控全世界,那你一定要会数学。
好,崇拜完数学之后,我们来了解一下算子。
简单来说,算子就是一个东西通过算子之后之后,变成了另一个东西。
这个熟悉不,大家想想函数,一个自变量的值,通过映射,得到了函数值。一个图像,经过卷积操作,变成另一个图像,一块肉,经过加工,变成了香肠。
这样大家是不是就更容易理解啦。
趁热打铁,我们来看一下算子的概念:
狭义的算子实际上是指从一个函数空间到另一个函数空间(或它自身)的映射。
广义的算子的定义只要把上面的空间推广到一般空间,可以是向量空间,赋范向量空间,内积空间,或更进一步,Banach空间,Hilbert空间都可以。算子还可分为有界的与无界的,线性的与非线性的等等类别。
讲完了算子,我们来说一个具体的算子Sobel算子,可以让大家理解地更深。
Sobel算子又叫索贝尔算子,是计算机视觉领域的一种重要处理方法。主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。Sobel算子是把图像中每个像素的上下左右四领域的灰度值加权差,在边缘处达到极值从而检测边缘。
Sobel算子所采用的算法是先进行加权平均,然后进行微分运算,算子的计算方法如下:
对于一个二元函数f(x,y)来说,x有三个取值,y有三个取值,那我们就可以构造一个3×3的矩阵,矩阵的中心点为(x,y),向上向左为减一,向右向下为加一。我们用矩阵来表示一下上面的式子:
由此,我们设计两个核,一个是x方向上的,一个是y方向上的,设计的两个核是3×3的矩阵,分别是:
所以,如果对于一个图像I,我们能通过这两个核分别计算该图像X方向和Y方向的梯度:
我们也能根据两个方向的梯度计算总的梯度:
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
);
函数参数含义如下:
(1)InputArray类型的src ,输入图像。
(2)OutputArray类型的dst ,输出图像,图像的大小、通道数和输入图像相同。
(3)int类型的ddepth,输出图像深度,请参阅@ref filter_depth“组合”;如果是8位输入图像,则会导致导数截断。
(4)int类型的dx,导数x的阶数。
(5)int类型的dy,导数y的阶数。
(6)int类型的ksize,扩展Sobel内核的大小;它必须是1、3、5或7。
(7)double类型的scale,计算派生值的可选比例因子;默认情况下,不应用缩放(有关详细信息,请参见cv::getDerivKernels。
(8)double类型的delta,在将筛选的像素存储到dst中之前添加到这些像素的可选值。说的有点专业了其实就是给所选的像素值添加一个值delta。
(9)int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT。
在这里,我们用到一个很简单的函数:
void convertScaleAbs(
InputArray src,
OutputArray dst,
double alpha= 1,
double belt= 0,
);
我们这个函数只需要设置前两个参数,这个函数可以计算图像src的像素绝对值,输出到图像dst。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, gx, gy, src;
img = imread("E:/image/girl2.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
Sobel(img, gx, CV_16S, 1, 0);
Sobel(img, gy, CV_16S, 0, 1);
convertScaleAbs(gx, gx);
convertScaleAbs(gy, gy);
addWeighted(gx, 0.5, gy, 0.5, 0, src);
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
当然,对导数比较熟悉的同学呢,我们都知道,导数是对于连续函数来说的,现在的这个不是连续的函数呀,所以这种方式,其实只是一个近似解。但是这种近似解,对于上面的来说又不是特别的精确,所以,在opencv中,采用更加精确的Scharr算子:
void Scharr(
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
);
函数参数含义如下:
(1)InputArray类型的src ,输入图像。
(2)OutputArray类型的dst ,输出图像,图像的大小、通道数和输入图像相同。
(3)int类型的ddepth,输出图像深度,请参阅@ref filter_depth“组合”;如果是8位输入图像,则会导致导数截断。
(4)int类型的dx,导数x的阶数。
(5)int类型的dy,导数y的阶数。
(6)double类型的scale,计算派生值的可选比例因子;默认情况下,不应用缩放(有关详细信息,请参见cv::getDerivKernels。
(7)double类型的delta,在将筛选的像素存储到dst中之前添加到这些像素的可选值。说的有点专业了其实就是给所选的像素值添加一个值delta。
(8)int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, gx, gy, src;
img = imread("E:/image/girl2.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
Scharr(img, gx, CV_16S, 1, 0);
Scharr(img, gy, CV_16S, 0, 1);
convertScaleAbs(gx, gx);
convertScaleAbs(gy, gy);
addWeighted(gx, 0.5, gy, 0.5, 0, src);
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
大家也可以自己尝试一下呀,一定要多做练习!