写在前面

均值滤波开始,着手实现各种常用的滤波算法。均值滤波是一种线性滤波。图像的空域滤波无非分为两种,线性滤波和非线性滤波。由于我之前对线性、非线性理解不够清晰,这次就好好总结一下吧。

线性滤波:对邻域中的像素的计算为线性运算时,如利用窗口函数进行平滑加权求和的运算,或者某种卷积运算,都可以称为线性滤波。常见的线性滤波有:均值滤波、高斯滤波、盒子滤波、拉普拉斯滤波等等,通常线性滤波器之间只是模版系数不同。

非线性滤波:非线性滤波利用原始图像跟模版之间的一种逻辑关系得到结果,如最值滤波器,中值滤波器。比较常用的有中值滤波器和双边滤波器。

均值滤波的应用场合:

根据冈萨雷斯书中的描述,均值模糊可以模糊图像以便得到感兴趣物体的粗略描述,也就是说,去除图像中的不相关细节,其中“不相关”是指与滤波器模板尺寸相比较小的像素区域,从而对图像有一个整体的认知。即为了对感兴趣的物体得到一个大致的整体的描述而模糊一幅图像,忽略细小的细节。

均值滤波的缺陷:

均值滤波本身存在着固有的缺陷,即它不能很好地保护图像细节,在图像去噪的同时也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。特别是椒盐噪声。

原理

均值滤波的原理很简单,一般是用下面的系数模版与图像做卷积运算,公式表示为:

    

g(x,y)为该邻域的中心像素,n跟系数模版大小有关,一般3*3邻域的模板,n取为9,如:

当然,模板是可变的,一般取奇数,如5*5,7*7等等。

注:在实际处理过程中可对图像边界进行扩充,扩充为0或扩充为邻近的像素值。

       

结果

                                       

                                              原图                                                                                7*7模板

分析:如果采用常规方法,随着模板尺寸的增大,求和计算量也急剧增大,而且模板是逐像素移动的,可见这种效率上的代价是巨大的,下一篇将介绍一种采用积分图处理的快速均值滤波方法,会大大提高执行效率,可以说是工程应用的首选!

下图是常规均值滤波处理一张分辨率为485*528图像的时间(模板15*15):

下图是积分图快速均值滤波处理的时间(模板15*15):

速度提升10倍,爽到爆有木有!

代码(常规方法)

#include <opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
 
void MeanFilater(cv::Mat& src,cv::Mat& dst,cv::Size wsize){
	//图像边界扩充:窗口的半径
	if (wsize.height % 2 == 0 || wsize.width % 2 == 0){
		fprintf(stderr,"Please enter odd size!" );
		exit(-1);
	}
	int hh = (wsize.height - 1) / 2;
	int hw = (wsize.width - 1) / 2;
	cv::Mat Newsrc;
	cv::copyMakeBorder(src, Newsrc, hh, hh, hw, hw, cv::BORDER_REFLECT_101);//以边缘为轴,对称
	dst=cv::Mat::zeros(src.size(),src.type());
 
    //均值滤波
	int sum = 0;
	int mean = 0;
	for (int i = hh; i < src.rows + hh; ++i){
		for (int j = hw; j < src.cols + hw;++j){
 
			for (int r = i - hh; r <= i + hh; ++r){
				for (int c = j - hw; c <= j + hw;++c){
					sum = Newsrc.at<uchar>(r, c) + sum;
				}
			}
			mean = sum / (wsize.area());
			dst.at<uchar>(i-hh,j-hw)=mean;
			sum = 0;
			mean = 0;
		}
	}
 
}
 
int main(){
	cv::Mat src = cv::imread("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Img\\Fig0334(a)(hubble-original).tif");
	if (src.empty()){
		return -1;
	}
	if (src.channels() > 1)
		cv::cvtColor(src,src,CV_RGB2GRAY);
 
	cv::Mat dst;
	cv::Mat dst1;
	cv::Size wsize(7,7);
 
	double t2 = (double)cv::getTickCount();
	MeanFilater(src, dst, wsize); //均值滤波
	t2 = (double)cv::getTickCount() - t2;
	double time2 = (t2 *1000.) / ((double)cv::getTickFrequency());
	std::cout << "FASTmy_process=" << time2 << " ms. " << std::endl << std::endl;
 
	cv::namedWindow("src");
	cv::imshow("src", src);
	cv::namedWindow("dst");
	cv::imshow("dst", dst);
	cv::imwrite("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Image Filtering\\MeanFilter\\Mean_hubble.jpg",dst);
	cv::waitKey(0);
}