OpenCV 学习(几种基本的低通滤波)
最后更新于:2022-04-01 11:25:19
## OpenCV 学习(几种基本的低通滤波)
对图像进行滤波处理是图像处理中最常见的一种操作类型。而这其中低通滤波(也可以叫做平滑)有事各种滤波处理中最常用的。这里就简单写写 OpenCV 中提供的几种低通滤波方法。
### 均值滤波
这种滤波方法就是取一个像素的邻域内各像素的平均值作为滤波结果。比如下面这个例子:
~~~
cv::blur(image, result, cv::Size(7, 7), cv::Point(-1, -1), cv::BORDER_DEFAULT);
~~~
原始图像是 image, 滤波后的图像是 result ,邻域大小为 5 * 5。 cv::Point(-1, -1) 表明邻域的零位就是邻域的中心,这个是默认值,如果不改变的话可以不填。
cv::BORDER_DEFAULT 是对边界的处理办法,这个一般也不需要改变的。
下面是一副图像滤波前后的对比。
![原始图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1da7517af.jpg "")
![滤波后](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db22dfba.jpg "")
与 blur 函数相关的还有个 boxFilter 函数。这个滤波器的核的各个元素都为 1。normalize = false 时相当于邻域内各像素的数值求和。 normalize = true 时,计算结果等效于 blur 函数。
~~~
cv::boxFilter ( InputArray src,
OutputArray dst,
int ddepth,
Size ksize,
Point anchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT
)
~~~
### 高斯滤波
均值滤波对邻域内各个像素采用统一的权值,这种方式对大多数应用来说不是最佳的。高斯滤波采取邻域内越靠近的值提供越大的权重的方式计算平均值。权重的选取采用高斯函数的形式。高斯函数有个非常好的特点,就是无论在时域还是频域都是钟形的。通过控制 σ 可以控制低通滤波的截止频率。函数原型如下:
~~~
void GaussianBlur( InputArray src,
OutputArray dst, Size ksize,
double sigmaX, double sigmaY=0,
int borderType=BORDER_DEFAULT );
~~~
使用起来很方便,下面是个例子:
~~~
cv::GaussianBlur(image, result, cv::Size(7, 7), 1.5);
~~~
参数image为输入图像,result为输出图像,Size(5,5)定义了核的大小,最后一个参数是高斯滤波的 σ 。从函数原型上可以看到有 sigmaX 和 sigmaY 两个参数。通常情况下 sigmaY 取与 sigmaX 相同的值,这时可以不写出来。也就是用它的默认值 0.
还是刚才的图像,高斯滤波后的结果如下:
![高斯滤波后的结果](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db26e7be.jpg "")
高斯滤波器的大小和 σ 可以只指定一个。另一个会自动选择合适的值。
比如说我们希望 σ=1.5。那么可以直接写为:
~~~
cv::GaussianBlur(image, result, cv::Size(0, 0), 1.5);
~~~
或者要求核的大小为 9 * 9 个点。可以这样写:
~~~
cv::GaussianBlur(image, result, cv::Size(9, 9), 0);
~~~
如果加大 σ 会滤波的更平滑些,但是也会损失更多的细节,当 σ=4 时的滤波结果如下。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db2af429.jpg "")
相关的函数还有个 getGaussianKernel。 这个函数可以计算高斯滤波器的系数,但是它计算的是 1 维滤波器的系数。对于高斯滤波器来说, 2 维系数其实就是横向和竖向两个 1 维滤波器的系数的乘积。这种性质有个专有名词,叫做 seperable filter。
下面是这个函数的一个用例:
~~~
cv::Mat kernel = cv::getGaussianKernel(7, 1.5, CV_32F);
~~~
### 中值滤波器
中值滤波是一种非线性滤波器。它是取邻域内各点的统计中值作为输出。这种滤波器可以有效的去除椒盐噪声。还能保持图像中各物体的边界不被模糊掉。是一种最常用的非线性滤波器。这种滤波器只能使用正方形的邻域。下面是个例子:
~~~
cv::medianBlur(image, result, 7);
~~~
下面是原始图像。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db2e3832.jpg "")
中值滤波的结果如下。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db33146c.jpg "")
同样大小滤波核高斯滤波的结果如下。
![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-26_571f1db35eeef.jpg "")
对比很明显,中值滤波对于去除这些细线更有效。
### 通用滤波器 filter2D
利用这个函数我们可以自定义滤波器的核。
比如下面的代码:
~~~
cv::Mat kernel = (cv::Mat_<float>(3, 3) << 1/9.0, 1/9.0, 1/9.0,
1/9.0, 1/9.0, 1/9.0,
1/9.0, 1/9.0, 1/9.0);
cv::filter2D(image, result, image.depth(), kernel);
~~~
就相当于一个 3 * 3 的均值滤波器。
~~~
cv::blur(image, result, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_DEFAULT);
~~~
当然,如果只是搞个均值滤波器,不需要这么麻烦,直接用 blur 函数就可以了。但是如果我们要设计个很特殊的滤波器时,filter2D 就派上用场了。
### 可分离滤波器 sepFilter2D
一个 2 维滤波器,如果可以分离为x 方向和 y 方向两个独立的 1 维滤波器。那么这个 2 维滤波器就称为 可分离滤波器。比如我们上面介绍的高斯滤波器就是一个典型的可分离滤波器。具有这种性质的滤波器有快速算法,可以比不具有这个性质的普通的滤波器更高效的计算。
这个函数的接口如下:
~~~
void sepFilter2D( InputArray src, OutputArray dst, int ddepth,
InputArray kernelX, InputArray kernelY,
Point anchor=Point(-1,-1),
double delta=0, int borderType=BORDER_DEFAULT );
~~~
与其他滤波器最大的区别就是需要传进 2 个滤波器核,kernelX 和 kernelY。下面举个例子:
~~~
cv::Mat kernel = cv::getGaussianKernel(7, 1.5, CV_32F);
cv::sepFilter2D(image, result, -1, kernel, kernel);
~~~
相当于对 image 进行了一次高斯滤波,也就是说与下面的代码等效。
~~~
cv::GaussianBlur(image, result, cv::Size(7, 7), 1.5);
~~~
大家可以试试,两种方法得到的结果完全相同。