数字图像处理3之空间滤波(平滑空间滤波器)
“滤波”是指接受(通过)或者拒绝一定的频率成分。例如,通过低频的滤波器称为低通滤波器,低通滤波器的最终效果是模糊(平滑)一幅图像。
某些邻域处理工作是操作邻域的图像像素值以及相应的与邻域有相同维数的子图像的值。这些子图像可以被称之为滤波器、掩膜、核、模板或者窗口,其中前三个词是更为普遍的术语。在滤波器子图像中的值是系数值,而不是像素值。
一、空间滤波的机理
空间滤波器由(1)一个邻域(通常是一个较小的矩形)、(2)对该邻域所包围图像像素执行的预定义操作组成。滤波产生一个新像素,新像素的坐标等于邻域中心的坐标,像素的值是滤波操作的结果。
滤波器的中心访问输入图像中的每个像素后,就生成了处理(滤波)之后的图像。如果在图像像素上执行的是线性操作,则该滤波器称为线性空间滤波器;否则,滤波器就称为非线性空间滤波器。
通过下面使用3*3邻域的线性空间滤波的过程可以展示出空间滤波的机理。空间滤波处理就是在待处理图像中逐点的移动掩模。在每一点处,滤波器在该点的响应通过事先定义的关系来计算。
在图像中的任意一点,滤波器的响应是滤波器系数与由该滤波器所包围的图像像素的乘积之和:
下面,可以将上述公式推广到任意大小的滤波器。
对于大小为的掩模,我们假设m=2a+1且n=2b+1,其中a、b均为非负整数。在后续的讨论中,处理的掩模的长和宽都为奇数。一般来说,在M*N的图像f上,用大小的滤波器掩模进行线性滤波可以由下式给出:
为了得到一幅完整的经过滤波处理的图像,必须对和依次应用公式,这样就确保了能够对图像中的所有像素进行处理。
非线性空间滤波处理也是基于领域处理,且掩模滑过一幅图像的机理和上面论述的线性滤波的机理一样。然而,一般说来,非线性空间滤波处理所考虑的是领域像素点的值,而不能直接使用上式中所描述的乘积求和中的系数。
当滤波中心靠近图像轮廓的时候,会发生掩模中的某些行或者列处于图像平面之外的情况。于是就有了下面的两种处理方法:
方法1:
最简单的方法就是将掩模中心点的移动范围限制在距离图像边缘不小于个像素处。但是这样就会出现图像边缘像素没有被处理的情况。如果要保持与原图像一样的大小,可以直接将未处理的图像边缘像素直接复制到结果图像。
方法2:
在图像边缘以外再补上行和列灰度为0的像素点(其灰度也可以是其它值),或者将边缘像素带复制补在图像之外。
二、几种重要的滤波器
1、平滑空间滤波器
平滑滤波器用于模糊处理和降低噪声。模糊处理经常用于预处理中,例如,在提取大的目标之前去除图像中的一些琐碎的细节、以及连接直线与曲线的缝隙。通过线性滤波器和非线性滤波器的模糊处理可以降低噪声。
平滑线性滤波器
1、平滑线性滤波器的响应关系
平滑线性滤波器的输出(响应)是包含在滤波器模板邻域内的像素的简单平均值。因此,这些滤波器也被称为均值滤波器。
2、平滑滤波器的应用
平滑滤波器使用滤波器模板确定的邻域内像素的平均灰度值来代替图像中每个像素的值,这种处理的结果降低了图像灰度的“尖锐”变化。由于典型的随机噪声就是由于灰度级的尖锐变化组成,因此,常见的平滑处理应用就是降低噪声。
3、均值滤波处理的负面影响
由于图像边缘(几乎总是一幅图像希望有的特性)也是由图像灰度尖锐变化带来的特性,所以均值滤波处理还是存在着不希望的边缘模糊的负面效应。
下面来看两种计算均值的方法,两种不同的方法也就对应了两种不同的滤波器。
1、盒状滤波器
下面是一个的滤波器,表格中的数字都是滤波器系数。
1 | 1 | 1 |
1 | 1 | 1 |
1 | 1 | 1 |
假设上面的邻域包围的像素依次为,可计算出由该模板定义的邻域内像素灰度的平均值为,这也就是滤波器在邻域内的响应值。可以看到,这里滤波器的系数全为1。我们将所有系数都相等的空间均值滤波器也称为盒状滤波器。
2、加权平均滤波器(书上并没有这样的术语,为了记忆自己取得)
下面是一个的滤波器,表格中的数字都是滤波器系数。
1 | 2 | 1 |
2 | 4 | 2 |
1 | 2 | 1 |
该模板产生所谓的加权平均,使用这一术语是指用不同的系数乘以像素,这样,从权值上来看,一些像素比另一些像素更为重要。对于上面这一种掩模,处于掩模中心位置的像素比其它任何像素的权值都要大,因此在均值计算中给定的这一像素显得更为重要,而距离掩模中心较远的其它像素就显得不太重要。
假设上面的邻域包围的像素依次为,可计算出由该模板定义的领域内像素灰度的响应值为:。
推广:
一幅图像经过一个(m,n是奇数)的加权均值滤波器滤波的过程可以用下式给出:
那么空间均值处理有哪些应用呢?
空间均值处理的一个重要应用是,为了得到图像中的感兴趣物体的粗略描述而模糊一幅图像,这样,那些较小物体的强度与背景混合在一起了,较大物体变的像“斑点”而易于检测。掩模的大小由那些即将融入背景中去的物件尺寸来决定。
考虑下图,从左往右分别为1、2、3:
图1是绕地球轨道上的Hubble望远镜拍摄下来的一幅图像。
图2是应用的均值滤波器模板对该图像处理后的结果。可以看到图像中的一部分小物体要么已经融入背景,要么亮度明显降低。
使用图2的最高亮度的25%的阈值,并使用图2的阈值函数处理的结果如图3所示。可以看到图像中最大、最亮的物体基本表达出来了,而一些小物件都已经看不到了。
像这样的利用阈值处理并基于物体亮度来消除某些物体的操作是很典型的。
统计排序滤波器
统计排序滤波器是一种非线性空间滤波器。这种滤波器的响应以滤波器包围的图像区域中所包含的像素排序(排队)为基础,然后使用统计排序结果决定的值来代替中心像素的值。
这一类滤波器中最常见的滤波器就是中值滤波器,如其名称所暗示的那样,它用像素邻域内灰度的中值来代替邻域中心的像素值。
下面来仔细的介绍中值滤波器:
中值滤波器是用像素邻域内灰度的中值来代替该像素的值。
一个数值集合的中值是这样的数值,即在数值集合中,有一半小于或等于,还有一半大于或等于 。为了对一幅图像上的某个点做中值滤波处理,首先将邻域内的像素分类排序,确定其中值,然后将中值赋予滤波后图像中的相应像素点。
中值滤波器对处理脉冲噪声非常有效,这种噪声也称为椒盐噪声,因为这种噪声是以黑白点的形式叠加在图像上的。中值滤波器的使用非常普遍,因为对于一定类型的随机噪声,它提供了一种优秀的去噪能力,而且比相同尺寸的线性平滑滤波器的模糊程度明显要低。
在图像处理中,尽管中值滤波器是使用的最为广泛的统计排序滤波器,但是这并不意味着它是唯一的。同样,可以在排序之后取最大值来代替相应的像素点的灰度值,对应的滤波器称为最大值滤波器;或者在排序之后取最小的像素值来代替相应的像素点的灰度值,对应的滤波器称为最小值滤波器。
下面来看一个利用中值滤波器降噪的例子。
下图是一张受椒盐噪声污染的电路板的X光图像:
经过均值滤波后的图像如下(可以看到,噪声有了改善,但是还是有明显的椒盐噪声):
经过中值滤波后的图像如下(可以看到椒盐噪声有了明显的改善,性能明显好于均值滤波):
下面自己动手来实现一个简单中值滤波器吧!
实现代码:
#include <iostream>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
//返回中值
int getMeanValue(vector<int> num)
{
//先进行插入排序,然后取第5个数为中值,count用来指示sortNum中的元素的个数
int sortNum[9], count=0;
for (vector<int>::iterator it = num.begin(); it != num.end(); it++)
{
int temp = *it;
//将temp插入到sortNum中,小的在前面
if (count == 0)
{
sortNum[0] = temp;
count++;
}
else if (temp >= sortNum[count - 1])
{
sortNum[count] = temp;
count++;
}
else
{
//查找插入位置
int k;
for (k = count - 1; k>=0 && sortNum[k] > temp; k--)
{
sortNum[k + 1] = sortNum[k];
}
//插入
sortNum[k + 1] = temp;
count++;
}
}
return sortNum[4];
}
int main(int argc, char **argv)
{
cv::Mat srcImage, dstImage;
int rows, cols;
if (argc < 2)
{
cout << "please input image_path" << endl;
return -1;
}
srcImage = cv::imread(argv[1], 0); //加载图像
if (!srcImage.data)
{
cout << "load image failed" << endl;
return -1;
}
cv::imshow("srcImage", srcImage);
//初始化dstImage
dstImage = cv::Mat(srcImage.size(), srcImage.type());
rows = srcImage.rows;
cols = srcImage.cols;
//直接将srcImage的边缘像素带赋值到dstImage中
//复制第0行和最后一行
for (int j = 0; j < cols; j++)
{
dstImage.at<uchar>(0, j) = srcImage.at<uchar>(0, j);
dstImage.at<uchar>(rows - 1, j) = srcImage.at<uchar>(rows - 1, j);
}
//复制第0列和最后一列
for (int i = 0; i < rows; i++)
{
dstImage.at<uchar>(i, 0) = srcImage.at<uchar>(i, 0);
dstImage.at<uchar>(i, cols - 1) = srcImage.at<uchar>(i, cols - 1);
}
//进行中值滤波
int dir[][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1} }, meanValue;
vector<int> num(9);
for (int i = 1; i<rows - 1; i++)
{
for (int j = 1; j < cols - 1; j++)
{
//每次处理完一个领域都要将num清空,否则num会溢出
num.clear();
//查找邻域元素
for (int k = 0; k < 9; k++)
{
num.push_back((int)srcImage.at<uchar>(i + dir[k][0], j + dir[k][1]));
}
//找中值
meanValue = getMeanValue(num);
//用中值代替输出图像相应位置的像素
dstImage.at<uchar>(i, j) = (uchar)meanValue;
}
}
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
运行结果:
可以看到效果和上面给出的利用中值滤波器去除椒盐噪声的结果相同。
好了,平滑空间滤波器就先学习到这里了。下一篇开始学习锐化空间滤波器。
学习锐化空间滤波器之前,先来做个总结吧。今天学了很多内容,有些记得不清楚了,先来看下面一张图吧。
这是数字图像已经学了的内容。加油,还有很多很难得内容没有学。