【数字图像处理】线性滤波、最大值滤波,最小值滤波、中值滤波、高频补偿滤波(vs2017+openCV)

版权声明:本文为博主原创文章,转载请标明出处 https://blog.csdn.net/C2681595858/article/details/83240827

一、实验原理

1、线性滤波

① 不管是低通线性滤波还是高通线性滤波原理都是一样的,用图一所示的滤波器模板进行加权处理,将最终得到的R值赋给w5对应的像素。
②低通线性滤波和高通线性滤波不同之处就在于:

  • 低通线性滤波w1+w2+…+w9 = 1,且w1~w9全都>=0
  • 高通线性滤波w1+w2+…+w9 =0.二阶导数滤波(拉普拉斯)属于线性滤波。

③高频补偿滤波
原图像减去模糊处理后的图像,再将相减得到的模板乘以一个权重加给原图像。公式表式如下:
在这里插入图片描述

在这里插入图片描述

2、非线性滤波

①中值滤波

  • 没有模板参数,但有模板大小,用模板去滤波,每次不再是求加权和,而是将模板内的灰度值进行排序,取中间位置的灰度值赋给w5.这种滤波器对处理椒盐噪声非常有用

②最大值滤波

  • 和中值滤波的不同,最大值滤波器取模板内最大的灰度值赋给w5.并且不需要排序,可以直接求最大值。

③最小值滤波

  • 和最大值滤波器相反,取最小灰度值赋给w5

④一阶导数滤波

  • 首先进行线性滤波再将滤波得到的结果进行模和操作(非线性操作)

3、标定方式

在图像进行锐化处理的过程中,有可能会出现图像灰度值大于255或者小于0的情况,所以遇到这种情况我们需要将图像的灰度值通过一些处理控制在0-255之间。在这次实验中我原本使用了课本中的标定方法,但是结果却很让人失望,所以最后直接改用截断的方法处理,即:大于255都置为255,小于0的都置为0。进一步说明在结果分析中。

二、设计思路

1、线性滤波器

从实验原理我们可以知道,线性滤波原理都一样,也就是说均值滤波器、基本高通滤波器、拉普拉斯滤波器、高频补偿滤波器原理都一样,但是我们可以只用一个函数来实现这几个滤波器吗?当然不行请看下面的分析:

  • ①不管是低通还是高通,线性滤波都是一个原理为什么使用了两个函数分别去实现低通和高通?
    答:
    低通滤波器的模板各系数之和为1,且他们的取值都在0-1之间,所以处理后得到的加权平均值不会发生溢出的状况,得到结果后可以直接以Mat的形式保存。
    而高通滤波器不一样,它的模板各个系数之和为0,有正有负,虽然也是线性滤波,但是处理后可能发生溢出的状况。比如下面的情形:
    在这里插入图片描述

    左上为图像,右上为滤波器,滤波后-4x255,发生溢出。如果是线性(均值)滤波器则255/9,不会发生溢出。
    说了这些其实就是说高通滤波器要多做一步溢出的处理。由于Mat中如果发生了溢出,那么保存的就不在是计算出的那个负数或者超过255的数。所以我们必须用一个int类型的数组(源程序中用名为imageMat)来保存计算后发生溢出的图像,然后在用算法(源程序中函数名为scale)处理这个发生溢出的图像,让他的所有值在0-255内。

  • ②这样一来:低通线性滤波器一个函数,基本高通滤波器和拉普拉斯滤波器可以使用同一个函数,高频补偿滤波器一个函数。其实在真正代码实现的时候基本高通滤波器和拉普拉斯滤波器也分别使用了一个函数,因为拉普拉斯变换后的图像,加上原图像,这样拉普拉斯处理的效果能够得到很好的体现。

  • ③高频补偿滤波器:是对基本线性滤波器的一个线性使用,所以另写函数,调用已经写好的一些函数。

2、 边缘处理的镜像算法是如何实现的?

答:这个问题很有价值吗?我认为还是值得一提的,因为我在写程序的时候,这部分还是挺费力的。

  • ①首先新建图像长和宽都要加上滤波器大小减一(按照所有滤波器都是奇数长度来处理)。
  • ②在处理四个角落,因为他们最好处理,直接赋值128;然后中间部分也好处理把原图像搬过来就可以。最麻烦的就是四个边缘。特别是右边缘和下边缘,下面直说这两个边缘的处理:
    我们要清楚的是,输入是一个正常图像,输出是一个经过边缘对称的图像,现在我们已经有了这个输出图像的框架,接下来只需要填满这个框架。
    【处理前图像】
    在这里插入图片描述
    就下边缘来说,我们现在的目的是根据眼前这个图像中的坐标,求出原图像中对应的镜像后的坐标。首先绿色那块是搬过来的原图像,下边缘是蓝色箭头所指的那条线开始的。假如说我们现在要求蓝色箭头指向的位置的像素值。首先我们减去边缘宽度的2倍,那么坐标现在把这个坐标放在原图像中对应的应该是橙色箭头所在位置,如果我们把这个位置的像素值赋给蓝色箭头处的像素,那么我们最后得到的图像将不是镜像处理的,而是直接平移原图像边缘一部分到新图像边缘。所以为了得到镜像结果。我们进行如下处理:把我们得到的目前的这个坐标沿着原图像中从下边缘开始宽度为新加边缘宽度的对称轴,做对称,这样我们就到达了黑色箭头指向的位置,刚好就是我们需要的镜像位置。
  • ③求对称点的方法:
    首先算出对称轴的坐标,然后用对称轴坐标减去该点坐标,再将得到的结果加到对称轴坐标上去。

3、 关于Mat传值后处理,会影响原图像的说明?

  • 在做给图像加椒盐噪声的时候,遇到了一个问题,就是将原图像以传值的方 式传进函数,然后加上椒盐噪声返回出来,发现后面使用的所用图像都加上了椒盐噪声。但是我是按值传递进去的啊,不应该影响原图像,百度了一下,opencv按值传递也会影响原图像,解决办法是,使用原图像的拷贝时,需要用clone()或者copyTo()函数,而不能直接“=”赋值。

三、实验过程

1、椒盐噪声

为了体现出中值滤波器处理椒盐噪声的强大功能,另写了一个函数Mat addSaltAndPepperNoise(Mat image_in, float rate = 0.2);来给图像imag_in加上椒盐噪声,然后返回出来。

2、特别注意

  • 为了方便使用不同的模板进行拉普拉斯处理,使用了如下方法:
    需要传入将要进行Laplacian处理的图片,以及选择要用哪个模板处理,具体如下: Mat Spafilt::laplacian(Mat &image_in, int id);
    Image_in是需要传进去的待处理图片,id是选择用哪个模板处理(在1-4中选),具体模板选择如下:
    在这里插入图片描述

3、设计文件

spatial_filtering.cpp文件500多行放在这里不是很好,有需要的同学可以到这里下载,其实有了设计文件以及详细的注释,自己写实现部分也不是很难。
【spatial_filtering.hpp】

#pragma once
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;

class Spafilt {
public:
	Spafilt();
	~Spafilt();

	/*
	线性低通滤波器
	@image_in:将要处理的图片。
	@filter_in[]:使用的滤波器模板。
	@sizeOfFilter_in:滤波器大小
	@return:处理后的图片
	*/
	Mat linearFilter(Mat &image_in, float filter_in[], int sizeOfFilter_in);
	
	/*
	给图片加上椒盐噪声
	@Image_in:将要处理的图片
	@rate:噪声程度,建议(0-0.5)
	@return: 加上噪声后的图片
	*/
	Mat addSaltAndPepperNoise(Mat image_in, float rate = 0.2);

	/*
	中值滤波器
	@image_in:将要处理的图片。
	@sizeOfFiter_in;滤波器大小
	@return:处理后的图片。
	*/
	Mat medianFilter(Mat &image_in, int sizeOfFilter_in = 3);

	/*
	最大值滤波器
	@image_in:将要处理的图片。
	@sizeOfFiter_in;滤波器大小
	@return:处理后的图片。
	*/
	Mat maxFilter(Mat &image_in, int sizeOfFilter_in = 3);

	/*
	最小值滤波器
	@image_in:将要处理的图片。
	@sizeOfFiter_in;滤波器大小
	@return:处理后的图片。
	*/
	Mat minFilter(Mat &image_in, int sizeOfFilter_in = 3);

	/*
	基本高通滤波器
	@image_in:将要处理的图片。
	@filter_in[]:使用的滤波器模板。
	@sizeOfFilter_in:滤波器大小
	@return:处理后的图片
	*/
	Mat basicHighPassFilter(Mat &image_in, float filter_in[], int sizeOfFilter_in);

	/*
	罗伯特算子
	@image_in:将要处理的图片
	@return:处理后的图片
	*/
	Mat roberts(Mat &image_in);

	/*
	prewitt算子
	@image_in:将要处理的图片
	@return:处理后的图片
	*/
	Mat prewitt(Mat &image_in);

	/*
	Sobel算子
	*/
	Mat sobel(Mat &image_in);

	/*
	@image_in:将要处理的图片
	@id:选择使用哪个模板处理,id应该是1-4中的某一整数,详见实验报告。
	@return: Laplacian处理后的图片
	*/
	Mat laplacian(Mat &image_in, int id);
	
	/*
	高频补偿滤波器
	@k;高频补偿加模板的权重,默认为1
	*/
	Mat highBoostFilter(Mat &image_in, float k = 1);
	
	/*
	@原本是一个协助我调试程序的函数
	@但是最终它担任了使用截断方式标定的任务
	@在Mat scale(int k = 255);中调用了
	*/
	Mat Debug();
private:
	Mat imageSource;//待处理的图片
	Mat imageAfterBorderProcess;//对边缘进行处理后的图片。
	int* imageMat;//存放经过某些处理后,灰度值超过0-255区间的图片,等待后续处理或应用
	int filterSize;//滤波器大小
	float *filter;//滤波器模板

	/*
	设置滤波器大小及模板,这里只考虑方形的滤波器(长宽相等)
	@size_i:滤波器大小,只能是方形滤波器,传入任一边长度
	@fil:滤波器模板
	*/
	void setFilter(int size_i, float *fil);

	/*
	设置滤波器大小这里只考虑方形的滤波器(长宽相等)
	@size_i:滤波器大小,只能是方形滤波器,传入任一边长度
	*/
	void setFilterSize(int size_i);

	/*
	设置将要处理的图片
	@image_i:将要处理的图片
	*/
	void setImage(Mat &image_i);

	/*
	对图像的边缘进行处理
	* 处理的是imageSource图片
	* 处理后的结果存放在imageAfterBorderProcess中
	* 采用镜像处理办法,角落处的值设为128
	*/
	void borderProcessing();

	/*
	* 用来求两幅图像的带权的差或者和,
	* 可以在图像和图像或图像和数组之间运算
	* 用一个系数来控制加或者减或者其他系数
	* 结果放在数组imageMat中
	@coefficient:系数,决定两幅图像之间进行怎么样的操作
	@image_in: 若插入该参数,则进行imageSource和image_in之间的运算,
	           否则进行imageSource和imageMat之间运算
	*/
	void addOrSubtractOfTwoImage(float coefficient, Mat* image_in = NULL);

	/*
	* 使用线性滤波器对图片进行滤波
	* 由于高通滤波器模板处理后灰度值有可能溢出,所以结果放在imageMat中,待进一步处理
	* 使用私有变量中的filter对imageSource进行滤波
	*/
	void preHighPassFilter();

	/*
	用来将数组内的所有的灰度值标定到指定灰度区间并以Mat的形式返回出来
	不修改数组内容
	@参数k:表示标定的上界
	下界为零
	@return:返回标定后的图像
	*/
	Mat scale(int k = 255);

	/*
	非线性高通3x3滤波器
	@filterR:大小为3x3的滤波器模板
	@filterC:大小为3x3的滤波器模板
	@return:处理后经过标定的图像
	*/
	Mat nonLinearHighPass3x3filter(float* filterR, float* filterC);

};

【main.cpp】

#include "spatial_filtering.h"
int main()
{
	Mat image = imread("pic.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	//被图片读入折磨的情况。
	//验证边缘处理的正确性
	//验证加和标定的正确性。
	//数据类型
	imshow("source_image", image);
	Spafilt spt;

	//线性低通滤波器
	float filter[9];
	for (int counter = 0; counter < 9; counter++)
		filter[counter] = (float)1 / 9;
	imshow("linearFilter",spt.linearFilter(image, filter, 3));

	//中值滤波器
	Mat image_noise = spt.addSaltAndPepperNoise(image, 0.2);
	imshow("beforeMedaiFiltering", image_noise);
	imshow("afterMedaiFiltering", spt.medianFilter(image_noise, 3));

	//最大值滤波器
	imshow("maxFilter", spt.maxFilter(image,3));

	//最小值滤波器
	imshow("mixFilter", spt.minFilter(image, 3));

	//基本高通滤波器
	float highFilter[9] = { 1,  1, 1,  1, -8, 1, 1, 1, 1};
	imshow("BasicHighPassFilter", spt.basicHighPassFilter(image, highFilter, 3));

	//Roberts交叉算子
	imshow("Roberts", spt.roberts(image));

	//prewitt算子
	imshow("Prewitt", spt.prewitt(image));

	//Sobel算子
	imshow("Sobel", spt.sobel(image));

	//Laplacian锐化处理
	imshow("Laplacian", spt.laplacian(image, 3));

	//高频补偿滤波器
	imshow("highBoost", spt.highBoostFilter(image, 1));

	waitKey(0);
	return 0;
}

四、结果分析

.cpp实现文件

1、可疑问题

刚开始标定使用了是课本中介绍的方法,原理如下:
在这里插入图片描述
在这里插入图片描述
就是找出最大值和最小值, min(f)就是最小值,max(fm)就是极差,当f为最大值的时候,fm = max(fm).这样fs = k. 当f = min(f)的时候fs = 0.这样整个图像的灰度值就控制在0-k之间了。
但是我处理后所有用了标定处理的图像显得非常灰蒙蒙的,效果不是很好,像下面这样:
在这里插入图片描述
上为原图,下为高频补偿处理后的图像
在这里插入图片描述
仔细分析,出现这个现象的原因很可能是,上式中max(fm)非常大,然后f-fm能够达到max(fm)的像素个数又是极个别的。所以导致图像处理以后灰度值集中在了128附近。导致上述结果。所以使用了在图像标定部分说的截断的方法。

2、结果展示

【原图像】
在这里插入图片描述
【均值滤波】
在这里插入图片描述
【椒盐噪声】
在这里插入图片描述
【中值滤波器】
在这里插入图片描述
【最大值滤波器】
在这里插入图片描述
【最小值滤波】
在这里插入图片描述
【基本高通滤波】
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/C2681595858/article/details/83240827