图像增强算法实现--图像的椒盐噪声、随机噪声、高斯噪声、均值滤波、中值滤波以及高斯滤波

(1)算法描述:

本程序为了更好地实现模块化的设计,将加噪声和滤波分成两个独立程序。

椒盐噪声是由图像传感器,传输信道,解码处理等产生的黑白相间的亮暗点噪声。椒盐噪声是指两种噪声,一种是盐噪声(salt noise),另一种是胡椒噪声(pepper noise)。盐=白色,椒=黑色。前者是高灰度噪声,后者属于低灰度噪声。一般两种噪声同时出现,呈现在图像上就是黑白杂点。本算法控制噪声的数量,随机生成黑白杂点,在图像坐标内随机分布。

随机噪声,又称背景噪声,由时间上随机产生的大量起伏骚扰积累而造成的,其值在给定瞬间内不能预测的噪声。说实话没大理解,后来在老师的ppt课件上发现了随机值脉冲噪声,感觉它的效果和椒盐噪声很像。又百度了一下,发现脉冲噪声的特点是无规则。于是本算法基本和椒盐噪声的实现相似,可以控制噪声数量,随机生成黑白杂点,但此黑白杂点也是随机色值(a,b,c)(255-a,255-b,255-c,其中abc0-30的随机数,这些杂点在图像坐标内随机分布。

高斯噪声,是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声。本算法通过Box-Muller变换可以产生Gaussian噪音。

对于三种滤波算法,opencv提供了封装好的函数,分别是medianBlur(),blur(),GaussianBlur().值得借鉴和参考。

中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为gx,y=med{f(x-k,y-l),(k,lW)} ,其中,f(x,y)g(x,y)分别为原始图像和处理后图像。中值滤波法对消除椒盐噪声非常有效。在给定阙值时,中值滤波的工程又叫做超限中值滤波。

超限领域平均法滤波也称为线性滤波,其采用的主要方法为邻域平均法。线性滤波的基本原理是用均值代替原图像中的各个像素值,即对待处理的当前像素点(xy),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(xy),作为处理后图像在该点上的灰度个gxy),即个gxy=1/m fxy) m为该模板中包含当前像素在内的像素总个数。

高斯滤波实质上是一种信号的滤波器,其用途是信号的平滑处理,人们知道数字图像用于后期应用,其噪声是最大的问题,由于误差会累计传递等原因,很多图像处理教材会在很早的时候介绍Gauss滤波器,用于得到信噪比SNR较高的图像(反应真实信号)。与此相关的有Gauss-Laplace变换,其实就是为了得到较好的图像边缘,先对图像做Gauss平滑滤波,剔除噪声,然后求二阶导矢,用二阶导的过零点确定边缘,在计算时也是频域乘积=>空域卷积。滤波器就是建立的一个数学模型,通过这个模型来将图像数据进行能量转化,能量低的就排除掉,噪声就是属于低能量部分。


(2)实验结果,界面图:

添加噪声:

整体运行截图:


 左上:原图窗口        右上:加椒盐噪声

左下:加随机噪声         右下:加高斯噪声


滤波模板一:

    对添加椒盐噪声的图片进行处理,滤波模板为3*3,效果如下,可以看出超限中值滤波的效果是最好的。




滤波模板二:

    对添加椒盐噪声的图片进行处理,滤波模板为5*5,效果如下,可以看出超限中值滤波的效果是最好的。



滤波模板三:

对添加椒盐噪声的图片进行处理,滤波模板为7*7,效果如下。



滤波模板四:

对添加椒盐噪声的图片进行处理,滤波模板为9*9,效果如下.



滤波模板五:

对添加椒盐噪声的图片进行处理,滤波模板为15*15,效果如下.图像基本模糊。




滤波模板六:

对添加高斯噪声的图片进行处理,滤波模板为5*5,效果如下高斯滤波效果较好。



(3)关键代码:

添加三种噪声:

#include <opencv/cv.h>
#include <opencv/highgui.h> 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
#define TWO_PI 6.2831853071795864769252866 
Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3;//存储图片的Mat类型

static void salt(cv::Mat &image, int n);       //给图像添加椒盐噪声
static void AddGaussianNoise(cv::Mat& image);     //给图像加高斯噪声
static void random(cv::Mat& image);              //给图像加随机噪声

int main()
{
	// 载入原图
	g_srcImage = imread("OpenCvDemo.jpg", 1);
	if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; }
	//克隆原图到三个Mat类型中
	g_dstImage1 = g_srcImage.clone();
	g_dstImage2 = g_srcImage.clone();
	g_dstImage3 = g_srcImage.clone();
	//显示原图
	namedWindow("【<0>原图窗口】", 1);
	imshow("【<0>原图窗口】", g_srcImage);
	//=================【<1>加椒盐噪声】==================
	namedWindow("【<1>加椒盐噪声】", 1);
	salt(g_dstImage1, 4000);
	imshow("【<1>加椒盐噪声】", g_dstImage1);
	//=================【<2>加随机噪声】==================
	namedWindow("【<2>加随机噪声】", 1);
	random(g_dstImage2);
	imshow("【<2>加随机噪声】", g_dstImage2);
	
	//=================【<3>加高斯噪声】==================
	namedWindow("【<3>加高斯噪声】", 1);
	AddGaussianNoise(g_dstImage3);
	imshow("【<3>加高斯噪声】", g_dstImage3);
	//imwrite("加椒盐噪声.jpg", g_dstImage1);
    //cvSaveImage("加椒盐噪声.jpg", (CvArr*)&g_dstImage1);
    //cvSaveImage("加随机噪声.jpg", (CvArr*)&g_dstImage2);
	//cvSaveImage("加高斯噪声.jpg", (CvArr*)&g_dstImage3);
	//try {
	//	imwrite("加椒盐噪声.png",g_srcImage);
	//}
	//catch (runtime_error& ex) {
	//	fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
	//	return 1;
	//}
	cout << endl << "\t给图像加三种噪声的效果~\n\n"
		<< "【<0>原图窗口】           【<1>加椒盐噪声】\n\n"
		<< "【<2>加随机噪声】          【<3>加高斯噪声】\n\n"
		<< "\n\t\t by蔡真真  2015-12-19";

	//按下“q”键时,程序退出
	while (char(waitKey(1)) != 'q') {}
	return 0;
}

//给图像加椒盐噪声
static void salt(cv::Mat &image, int n)
{
	int i, j;
	for (int k = 0; k < n; k++)   //盐噪声(salt noise)
	{
		i = rand() % image.cols;
		j = rand() % image.rows;
		if (image.channels() == 1)
			image.at<uchar>(j, i) = 255;
		else if (image.channels() == 3)
		{
			image.at<cv::Vec3b>(j, i)[0] = 255;
			image.at<cv::Vec3b>(j, i)[1] = 255;
			image.at<cv::Vec3b>(j, i)[2] = 255;
		}
	}
	for (int k = 0; k < n; k++)  //胡椒噪声(pepper noise)
	{
		i = rand() % image.cols;
		j = rand() % image.rows;
		if (image.channels() == 1)
			image.at<uchar>(j, i) = 0;
		else if (image.channels() == 3)
		{
			image.at<cv::Vec3b>(j, i)[0] = 0;
			image.at<cv::Vec3b>(j, i)[1] = 0;
			image.at<cv::Vec3b>(j, i)[2] = 0;
		}
	}
}

//给图像加高斯噪声
double generateGaussianNoise()
{
	static bool hasSpare = false;
	static double rand1, rand2;

	if (hasSpare)
	{
		hasSpare = false;
		return sqrt(rand1) * sin(rand2);
	}

	hasSpare = true;

	rand1 = rand() / ((double)RAND_MAX);
	if (rand1 < 1e-100) rand1 = 1e-100;
	rand1 = -2 * log(rand1);
	rand2 = (rand() / ((double)RAND_MAX)) * TWO_PI;

	return sqrt(rand1) * cos(rand2);
}

static void AddGaussianNoise(Mat& image)
{
	CV_Assert(image.depth() != sizeof(uchar));
	int channels = image.channels();
	int nRows = image.rows;
	int nCols = image.cols * channels;
	if (image.isContinuous()){
		nCols *= nRows;
		nRows = 1;
	}
	int i, j;
	uchar* p;
	for (i = 0; i < nRows; ++i){
		p = image.ptr<uchar>(i);
		for (j = 0; j < nCols; ++j){
			double val = p[j] + generateGaussianNoise() * 128;
			if (val < 0)
				val = 0;
			if (val > 255)
				val = 255;
			p[j] = (uchar)val;
		}
	}
}

//给图像加随机噪声
static void random(cv::Mat& image)
{
	int n = rand() % 1000 + 2000;
	int a, b, c;
	int i, j;
	for (int k = 0; k < n-1500; k++)   
	{
		a = rand() % 30;
		b = rand() % 30;
		c = rand() % 30;
		i = rand() % image.cols;
		j = rand() % image.rows;
		if (image.channels() == 1)
			image.at<uchar>(j, i) = 255;
		else if (image.channels() == 3)
		{
			image.at<cv::Vec3b>(j, i)[0] = 255-a;
			image.at<cv::Vec3b>(j, i)[1] = 255-b;
			image.at<cv::Vec3b>(j, i)[2] = 255-c;
		}
	}
	for (int k = 0; k < n; k++)  //胡椒噪声(pepper noise)
	{
		a = rand() % 30;
		b = rand() % 30;
		c = rand() % 30;
		i = rand() % image.cols;
		j = rand() % image.rows;
		if (image.channels() == 1)
			image.at<uchar>(j, i) = 0;
		else if (image.channels() == 3)
		{
			image.at<cv::Vec3b>(j, i)[0] = a;
			image.at<cv::Vec3b>(j, i)[1] = b;
			image.at<cv::Vec3b>(j, i)[2] = c;
		}
	}
}


滤波算法源代码:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat g_srcImage, g_dstImage1, g_dstImage2, g_dstImage3;//存储图片的Mat类型
int g_nBoxFilterValue = 3;  //超限中值滤波参数值
int g_nMeanBlurValue = 3;  //均值滤波参数值
int g_nGaussianBlurValue = 3;  //高斯滤波器滤波参数值
//四个轨迹条的回调函数
static void on_medianBlur(int, void *);		//超限中值滤波
static void on_MeanBlur(int, void *);		//超限邻域平均法滤波
static void on_GaussianBlur(int, void *);	//高斯滤波器滤波
int main()
{
	// 载入原图
	g_srcImage = imread("OpenCvDemo.jpg", 1);
	if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; }

	//克隆原图到三个Mat类型中
	g_dstImage1 = g_srcImage.clone();
	g_dstImage2 = g_srcImage.clone();
	g_dstImage3 = g_srcImage.clone();

	//显示原图
	namedWindow("【<0>原图窗口】", 1);
	imshow("【<0>原图窗口】", g_srcImage);

	//=================【<1>方框滤波】==================
	namedWindow("【<1>超限中值滤波】", 1);
	createTrackbar("内核值:", "【<1>超限中值滤波】", &g_nBoxFilterValue, 40, on_medianBlur);
	on_medianBlur(g_nBoxFilterValue, 0);

	//=================【<2>超限邻域平均法滤波】==================
	namedWindow("【<2>超限邻域平均法滤波】", 1);
	createTrackbar("内核值:", "【<2>超限邻域平均法滤波】", &g_nMeanBlurValue, 40, on_MeanBlur);
	on_MeanBlur(g_nMeanBlurValue, 0);

	//=================【<3>高斯滤波器滤波】=====================
	namedWindow("【<3>高斯滤波器滤波】", 1);
	createTrackbar("内核值:", "【<3>高斯滤波器滤波】", &g_nGaussianBlurValue, 40, on_GaussianBlur);
	on_GaussianBlur(g_nGaussianBlurValue, 0);

	cout << endl << "\t给图像滤波的效果~\n\n"
		<< "【<0>原图窗口】               【<1>超限中值滤波】\n\n"
		<< "【<2>超限邻域平均法滤波】     【<3>高斯滤波器滤波】\n\n"
		<< "\n\t\t    by蔡真真  2015-12-19";

	//按下“q”键时,程序退出
	while (char(waitKey(1)) != 'q') {}
	return 0;
}
//描述:超限中值滤波操作的回调函数
static void on_medianBlur(int, void *)
{
	if (g_nBoxFilterValue % 2 == 0)
	{
		g_nBoxFilterValue++;
	}
	//方框滤波操作
	medianBlur(g_srcImage, g_dstImage1,g_nBoxFilterValue);   //g_nBoxFilterValue值必须为奇数
	//显示窗口
	imshow("【<1>超限中值滤波】", g_dstImage1);
}
//描述:超限邻域平均法滤波操作的回调函数
static void on_MeanBlur(int, void *)
{
	//超限邻域平均法滤波操作
	blur(g_srcImage, g_dstImage2, Size(g_nMeanBlurValue + 1, g_nMeanBlurValue + 1), Point(-1, -1));
	//显示窗口
	imshow("【<2>超限邻域平均法滤波】", g_dstImage2);
}
//描述:高斯滤波器滤波操作的回调函数
static void on_GaussianBlur(int, void *)
{
	//高斯滤波器滤波操作
	GaussianBlur(g_srcImage, g_dstImage3, Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);
	//显示窗口
	imshow("【<3>高斯滤波器滤波】", g_dstImage3);
}


(4)完成作业体会,总结:

原本是想用MFC整合成一个程序的,后来考虑到要设计成多种模板,便将加噪声和滤波分开写,这样效果更明显。对于加噪声这个程序,遇到的困难之一在于不理解随机噪声和椒盐噪声的区别,网上的相关资料太少,查阅老师的课件也觉得两者的效果是差不多的,我觉得这点区别在程序中很难体现,只能依靠自己的理解来区别这两种算法。第二个难题是高斯噪声的实现,原本想要写高斯分布函数来实现的,以图像的中点为期望值向四周分布,实现后发现想法是错误的。最后借鉴了维基百科的算法,通过Box-Muller变换可以产生Gaussian噪音。第三个难题是如何保存处理后的图像,本程序用的图像类型是cv::Mat,因此应该调用imwrite()函数,但是程序停止工作,而将cv::Mat转换成const cvArr*类型后,调用cvSaveImage()函数,程序也停止工作。后来尝试抛出异常处理,发现异常并未抛出,程序的确保存图片了,只是保存的图片格式有错。网友断言,这是imwrite()这个函数本身的原因。对于滤波程序的实现,最大的收获是学会了使用轨迹条,原本是想通过数组固定几种模板的,后来发现使用轨迹条可以自由的选择滤波模板参数,当然在参值的传递那里遇见了很多困难,最终还是一一克服了。其实这个作业在以前李豪杰老师的多媒体设计导论课上也做过,也研究过滤波的几种算法,是基于MFC框架进行编程的。而这一次,用的是opencv,很多算法的实现都有现成的封装函数。完成这一部分的作业花了一整天的时间,说真心话,刚开始是有点懊悔的,比重10分的东西花了一整天,感觉完成大作业更是遥遥无期了。但是不可否认,今天真的学到了很多,可以说是从无到有的转变。真的非常庆幸选择了树莓这个专业方向。


  • 14
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值