图像模糊,也可以称为图像滤波,主要是为了去除图像中明显的噪声点;
这一节主要介绍两种滤波方式: 均值滤波和高斯滤波;
重点介绍一下两者的原理,并使用OpenCV提供的API进行测试;
卷积计算
其实,不管是均值滤波,还是高斯滤波,其核心计算是卷积操作;
计算方式如下图所示,通过一个卷积核在图像上以滑动窗口的形式进行计算:
相应位置元素相乘后,累加,再取平均;每一次卷积计算的表达式如下:
\[g(i, j) = \dfrac{1}{k\times l}\sum_{k,l}f(i +k , j+l)h(k, l) \]其中,\(k ,l\)表示卷积核的尺寸;\(h\)表示卷积核;
因此,卷积计算的核心是卷积核的选择;另外,卷积核的尺寸最好是奇数,因为需要对局部视野中心进行赋值;
均值滤波
均值滤波的卷积核如下所示:
均值滤波的卷积核中所有的元素大小相同,且经过归一化;
OpenCV中均值滤波API blur
使用的介绍:
void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
-
src
表示需要被执行均值滤波处理的图像 -
dst
表示滤波后的图像 -
ksize
表示卷积核的尺寸 - 其他保持默认即可;
举例, blur
的使用(完整代码在文章最后):
Mat dst;
blur(src, dst, Size(3, 3), Point(-1, -1)); // filter2D
高斯滤波
高斯滤波的卷积核不同于均值滤波;
高斯滤波卷积核是通过高斯函数计算出来的,计算方式如下:
\[G(x, y) = \dfrac{1}{2\pi \sigma^2}e^{-\dfrac{x^2+y^2}{2\sigma^2}} \]将各个位置的坐标带入到高斯函数中,计算得到卷积核,其位置坐标如下所示:
假设,卷积核尺寸选择\(3\times3\),\(\sigma=0.8\),则计算得到的卷积核为:
上面卷积核的计算方式如下:
- 将位置坐标代码高斯函数中,计算得到每个位置的结果;
- 将所有位置的值除以所有位置的总和;
这里提供一下python实现的计算方式:
import math
import numpy as np
sigma = 0.8;
def calc_val(x, y, sigma):
val = (1/(2*math.pi*sigma**2))*(math.e**(-(x**2+y**2)/(2*sigma**2)))
return val
a_11 = calc_val(-1, 1, sigma)
a_12 = calc_val(0, 1, sigma)
a_13 = calc_val(1, 1, sigma)
a_21 = calc_val(-1, 0, sigma)
a_22 = calc_val(0, 0, sigma)
a_23 = calc_val(1, 0, sigma)
a_31 = calc_val(-1, -1, sigma)
a_32 = calc_val(0, -1, sigma)
a_33 = calc_val(1, -1, sigma)
res = np.array([[a_11, a_12, a_13],
[a_21, a_22, a_23],
[a_31, a_32, a_33]])
res = res/np.sum(res)
print(res)
那么,由高斯函数计算得到卷积核之后,有两种使用方式:
1. 小数卷积核
小数卷积核指的是直接使用计算得到卷积核用于后续卷积计算;
2. 整数卷积核
整数卷积核指的是将卷积核进行归一化处理;
- 首先,先将左上角缩放到1,其他位置上的值再乘以这个缩放系数;
得到:
计算方式,python
实现如下:
# 归一化, 代码接着上面的来
scale = 1 / a_11
res = res * scale
print(res)
- 其次, 对计算后的卷积核,取整,再除以和,如下;
从以上的描述过程可以看出,高斯卷积核最重要的参数是高斯分布的标准差\(\sigma\),它代表了数据的离散程度;
- 如果\(\sigma\)较小,得到的高斯核中心系数就越大,周围系数越小,则对图像的平滑效果不好;
- 如果\(\sigma\)较大,则高斯核中的各个系数相差不大,则对图像的平滑效果较好;
上面介绍了,如何得到高斯核,那么接下来,看一下OpenCV中提供的GaussianBlur
的使用:
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
-
src
表示需要模糊的图像; -
dst
表示模糊的输出图像; -
ksize
表示卷积核的尺寸; -
sigmaX
表示在X方向的标准偏差; -
sigmaY
表示在Y方向上的标准偏差;
需要注意的是,如果sigmaY=0
,则将其设为sigmaX
;如果两者都为0,则两者由卷积核的尺寸进行计算:
注意:这里提到的sigmaX, sigmaY
如何与上面叙述的高级核的构造过程如何产生关系,还不得知,以后弄明白了再补充;
示例:
Mat dst_gaussian;
int sigma = 3;
GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);
最后,完整的程序如下:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
// 读取图像
Mat src = imread("/home/chen/dataset/lena.jpg");
if (src.empty()){
cout << "cloud not load image." << endl;
return -1;
}
// 均值模糊
Mat dst;
blur(src, dst, Size(3, 3), Point(-1, -1)); // filter2D
// 高斯模糊
Mat dst_gaussian;
int sigma = 3;
GaussianBlur(src, dst_gaussian, Size(3, 3), sigma, sigma);
// 显示图像
char input_title[] = "src";
char output_title[] = "src_blur";
char output_title_gaussian[] = "src_gaussian_blur";
namedWindow(input_title, WINDOW_AUTOSIZE);
imshow(input_title, src);
namedWindow(output_title, WINDOW_AUTOSIZE);
imshow(output_title, dst);
namedWindow(output_title_gaussian, WINDOW_AUTOSIZE);
imshow(output_title_gaussian, dst_gaussian);
waitKey(0);
return 0;
}
输出:
Reference: