高斯滤波(Gauss Filter)是线性滤波中的一种。在OpenCV图像滤波处理中,高斯滤波用于平滑图像,或者说是图像模糊处理,因此高斯滤波是低通的。其广泛的应用在图像处理的减噪过程中,尤其是被高斯噪声所污染的图像上。
高斯滤波的基本思想是: 图像上的每一个像素点的值,都由其本身和邻域内其他像素点的值经过加权平均后得到。其具体操作是,用一个核(又称为卷积核、掩模、矩阵)扫描图像中每一个像素点,将邻域内各个像素值与对应位置的权值相称并求和。从数学的角度来看,高斯滤波的过程是图像与高斯正态分布做卷积操作。
注意: 高斯滤波是将二维高斯正态分布放在图像矩阵上做卷积运算。考虑的是邻域内像素值的空间距离关系,因此对彩色图像处理时应分通道进行操作,也就是说操作的图像原矩阵时用单通道数据,最后合并为彩色图像。
一、几个概念
1. 什么是低通滤波、平滑图像、图像模糊处理?
平滑图像与图像模糊处理是相同的含义。平滑处理即是通过操作后,使得图像的像素值与邻域内其他像素值的的变化程度减小。在一张图像上,边缘的像素值是变化程度最剧烈的地方,而其他相对平缓。因此,平滑图像最直观的表现是图像的上物体的边缘轮廓变得模糊。
低通滤波是指仅允许低频率信号通过。一张图像上的大部分能量聚集在低频和中频上,而高频大多是图像中物体的边缘部分,也有可能是高频噪声点。在单通道中,各像素点的取值都在(0~255)中,因此,低通滤波通过一定的阙值设置,有去除高频信号和平缓边缘的效果。
2. 什么是核(又称为卷积核、掩模、矩阵)?
核的本质其实就是一个大小固定、由数值参数组成的数学矩阵,例如一个3*3的核就是一个3*3的矩阵,而矩阵中的数据则为权值。
3. 什么是卷积运算?
卷积运算是指输入图像中某一像素点的邻域的各个值(包括该点)与卷积算子中的值做矩阵相乘运算,最后得到输出值。
卷积算子的公式:
g(i,j) 代表原图像矩阵上的(i,j)点的值,它是输出值。
f(i-k,j-l) 代表原图像矩阵上(i,j)点的邻域中的对应点的值。
h(k,,l) 代表与f(i-k , j-l)这个值在核对应位置的点的值
请看下图:这里用的是f(i-k,j-l)h(k,l)
由上图我们看到,矩阵f是将要进行操作的图像矩阵,当前的(i,j)是(2,2)点。h为核,其以中心(0,0)为参考点。因此f矩阵对应范围即为f(2,2)的邻域。矩阵g为图像输出矩阵,g(2,2)的值为输出值。
注意:卷积算子和相关算子在核上是180度翻转的矩阵,请不要搞混
4.核(卷积核、掩模、矩阵等)
在3的卷积运算中,用到的公式是:
g(i, j) = ∑ f(i-k, j-l)h(k, l) 。其中 k,l代表核上的坐标。而核的坐标明显与数组下标不一致。因此,我们需要做一个转换以满足用数组下标来访问该核的数据。
假设有3*3数组a,它与核的对应关系为
a(0, 0) ===> h(-1, -1) a(0, 1) ===> h(-1, 0) a(0, 2) ===> h(-1, 1)
a(1, 0) ===> h(0, -1) a(1, 1) ===> h(0, 0) a(1, 2) ===> h(0, 1)
a(2, 0) ===> h(1, -1) a(2, 1) ===> h(1, 0) a(2, 2) ===> h(1, 1)
对于3*3数组,其下标是0开始的,假设该数组的参考点(ai, aj)为中心,则有
g(i, j) = ∑ f(i-(k-ai), j-(l-aj))h(k, l)
带入数组参考点(1, 1),则有
g(i, j) = ∑ f(i-(k-1), j-(l-1))h(k, l)
此时,k,l可以从0开始取值
5.图像通道分离与合并(cv::Mat)
// src 原图像,多通道
// [1] 彩色图片通道分离
std::vector<cv::Mat> channels;
cv::split(&src, channels);
// [3] 滤波
// OpenCV中操作
// channels[0] ==> B通道
// channels[1] ==> G通道
// channels[2] ==> R通道
// 省略对各个通道的处理
// [4] 合并返回
cv::merge(channels, *dst);
二、高斯函数
高斯滤波,顾名思义,这是一个建立在高斯正态分布基础上的滤波器。首先我们来了解高斯函数。(图片来源于网络)
一维高斯函数:
可以看到,G(x)的跟sigma的取值有极大的关系。sigma取值越大,图像越平缓,sigma取值越小,图像越尖锐。
二维高斯函数:
二维高斯是构建高斯滤波器的基础。可以看到,G(x,y)在x轴y轴上的分布是一个突起的帽子的形状。这里的sigma可以看作两个值,一个是x轴上的分量sigmaX,另一个是y轴上的分量sigmaY。对图像处理可以直接使用sigma并对图像的行列操作,也可以用sigmaX对图像的行操作,再用sigmaY对图像的列操作。它们是等价的。
当sigmaX和sigmaY取值越大,整个形状趋近于扁平;当sigmaX和sigmaY取值越小,整个形状越突起。
高斯滤波原理就是将上图的二维正态分布应用在二维的矩阵上,G(x,y)的值就是矩阵上的权值,将得到的权值进行归一化,将权值的范围约束在[0,1]之间,并且所有的值的总和为1。
假设一个3*3的核,sigma取值1.5以及sigma取5.0,归一化后其权值分布分别是:
假设一个5*5的核,sigma取值1.5以及sigma取5.0,经归一化后其权值分布分别是:
可以看到,权值的分布是以中间高四周低来分布的。并且距离中心越远,其对中心点的影响就越小,权值也就越小。
因此可以总结:
(1)在核大小固定的情况下,sigma值越大,权值分布越平缓。因此,邻域各个点的值对输出值的影响越大,最终结果造成图像越模糊。
(2)在核大小固定的情况下,sigma值越小,权值分布越突起。因此,邻域各个点的值对输出值的影响越小,图像变化也越小。假如中心点权值为1,其他点权值为0,那么最终结果是图像没有任何变化。
(3)sigma固定时,核越大图像越模糊。
(4)sigma固定时,核越小图像变化越小。
三、高斯滤波器实现
首先看效果:
对于椒盐图作处理
对于高斯噪声图作处理
(1)main函数:读取图片 ==> 高斯滤波 ==> 结果显示
int main(void)
{
// [1] src读入图片
cv::Mat src = cv::imread("Median_pic.jpg");
// [2] dst目标图片
cv::Mat dst;
// [3] 高斯滤波 sigma越大越平越模糊
myGaussianFilter(&src, &dst, 5, 1.5f);
// [4] 窗体显示
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
(2)彩色图像通道分离处理,每个通道都进行高斯滤波,最后合并
void myGaussianFilter(cv::Mat *src, cv::Mat *dst, int n, double sigma)
{
// [1] 初始化
*dst = (*src).clone();
// [2] 彩色图片通道分离
std::vector<cv::Mat> channels;
cv::split(*src, channels);
// [3] 滤波
// [3-1] 确定高斯正态矩阵
double **array = getGaussianArray(n, sigma);
// [3-2] 高斯滤波处理
for (int i = 0; i < 3; i++) {
gaussian(&channels[i], array, n);
}
// [4] 合并返回
cv::merge(channels, *dst);
return ;
}
(3)生成高斯正态分布核(卷积核,掩模等)
/* 获取高斯分布数组 (核大小, sigma值) */
double **getGaussianArray(int arr_size, double sigma)
{
int i, j;
// [1] 初始化权值数组
double **array = new double*[arr_size];
for (i = 0; i < arr_size; i++) {
array[i] = new double[arr_size];
}
// [2] 高斯分布计算
int center_i, center_j;
center_i = center_j = arr_size / 2;
double pi = 3.141592653589793;
double sum = 0.0f;
// [2-1] 高斯函数
for (i = 0; i < arr_size; i++ ) {
for (j = 0; j < arr_size; j++) {
array[i][j] =
//后面进行归一化,这部分可以不用
//0.5f *pi*(sigma*sigma) *
exp( -(1.0f)* ( ((i-center_i)*(i-center_i)+(j-center_j)*(j-center_j)) /
(2.0f*sigma*sigma) ));
sum += array[i][j];
}
}
// [2-2] 归一化求权值
for (i = 0; i < arr_size; i++) {
for (j = 0; j < arr_size; j++) {
array[i][j] /= sum;
printf(" [%.15f] ", array[i][j]);
}
printf("\n");
}
return array;
}
(4)进行高斯滤波操作
/* 高斯滤波 (待处理单通道图片, 高斯分布数组, 高斯数组大小(核大小) ) */
void gaussian(cv::Mat *_src, double **_array, int _size)
{
cv::Mat temp = (*_src).clone();
// [1] 扫描
for (int i = 0; i < (*_src).rows; i++) {
for (int j = 0; j < (*_src).cols; j++) {
// [2] 忽略边缘
if (i > (_size / 2) - 1 && j > (_size / 2) - 1 &&
i < (*_src).rows - (_size / 2) && j < (*_src).cols - (_size / 2)) {
// [3] 找到图像输入点f(i,j),以输入点为中心与核中心对齐
// 核心为中心参考点 卷积算子=>高斯矩阵180度转向计算
// x y 代表卷积核的权值坐标 i j 代表图像输入点坐标
// 卷积算子 (f*g)(i,j) = f(i-k,j-l)g(k,l) f代表图像输入 g代表核
// 带入核参考点 (f*g)(i,j) = f(i-(k-ai), j-(l-aj))g(k,l) ai,aj 核参考点
// 加权求和 注意:核的坐标以左上0,0起点
double sum = 0.0;
for (int k = 0; k < _size; k++) {
for (int l = 0; l < _size; l++) {
sum += (*_src).ptr<uchar>(i-k+(_size/2))[j-l+(_size/2)] * _array[k][l];
}
}
// 放入中间结果,计算所得的值与没有计算的值不能混用
temp.ptr<uchar>(i)[j] = sum;
}
}
}
// 放入原图
(*_src) = temp.clone();
}