数字图像处理系列(三)---图像去噪

图像去噪


图像噪声介绍

1.什么是图像噪声

图像中各种妨碍人们对其信息接受的因素即可称为图像噪声(即图像数据中的干扰信息)

2.图像噪声的来源

  • 图像获取过程
  • 数字图像传输的过程

3.特点

  • 噪声的分布和大小不规则
  • 噪声和原图像有相关性
  • 噪声具有叠加性

4.分类

1.高斯噪声(它的幅度分布服从高斯分布,而它的功率谱密度又是均匀分布)

根据Box-Muller变换原理,建设随机变量U1、U2来自独立的处于(0,1)之间的均匀分布,则经过下面两个式子产生的随机变量Z0,Z1服从标准高斯分布

上式中Z0,Z1满足正态分布,其中均值为0,方差为1,变量U1和U2可以修改为下式:

2.椒盐噪声(强烈变化引起的突然产生不规则的白点和黑点)

通过随机获取像素点并设置为高亮度点和低灰度点来实现

代码如下

# include<limits.h>
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void salt_noise(Mat& image, Mat& salt_image, const int n);//椒盐噪声
double addgassian_noise(double mu,double sigma);//高斯噪声的实现
void gassian_noise(Mat& image, Mat& gassian_image);//高斯噪声

int main()
{
	Mat image = imread("d:/texting.jpg", 0);
	if (image.empty())
	{
		cout << "未找到文件" << endl;
		return -1;
	}

	Mat salt_image;
	salt_noise(image, salt_image, 5000);

	Mat gassian_image;

	gassian_noise(image, gassian_image);
	
	imshow("image", image);
	imshow("salt_image", salt_image);
	imshow("gassian_image", gassian_image);
	waitKey(0);

	return 0;
}

void salt_noise(Mat& image, Mat& salt_image, const int n)
{
	salt_image = image.clone();

	int i, j;
	for (int x=0; x < n; ++x)
	{
		i = rand() % image.rows;
		j = rand() % image.cols;

		if (salt_image.channels() == 1)
		{
			salt_image.at<uchar>(i, j) = 255;
		}
		else
		{
			salt_image.at<Vec3b>(i, j)[0] = 255;
			salt_image.at<Vec3b>(i, j)[1] = 255;
			salt_image.at<Vec3b>(i, j)[2] = 255;
		}
	}

	for (int x=0; x < n; ++x)
	{
		i = rand() % image.rows;
		j = rand() % image.cols;

		if (salt_image.channels() == 1)
		{
			salt_image.at<uchar>(i, j) = 0;
		}
		else
		{
			salt_image.at<Vec3b>(i, j)[0] = 0;
			salt_image.at<Vec3b>(i, j)[1] = 0;
			salt_image.at<Vec3b>(i, j)[2] = 0;
		}
	}
}


double addgassian_noise(double mu, double sigma)
{
	//定义小值
	const double min_number = numeric_limits<double>::min();
	static double z0, z1;
	static bool flag = false;
	flag = !flag;

	if (!flag)
		return z1 * sigma + mu;
	double u1, u2;
	
	do
	{
		u1 = rand() * (1.0 / RAND_MAX);
		u2 = rand() * (1.0 / RAND_MAX);
	} while (u1 <= min_number);

	z0 = sqrt(-2.0 * log(u1)) * cos(2 * CV_PI * u2);
	z1 = sqrt(-2.0 * log(u1)) * sin(2 * CV_PI * u2);
	return z0 * sigma + mu;
}

void gassian_noise(Mat& image, Mat& gassian_image)
{
	gassian_image = image.clone();
	for (int row=0; row < image.rows; ++row)
	{
		for (int col=0; col < image.cols; ++col)
		{
			int x = int(gassian_image.at<uchar>(row, col) + addgassian_noise(2, 0.8) * 32);

			if (x < 0)
				x = 0;
			if (x > 255)
				x = 255;
			gassian_image.at<uchar>(row,col) = (uchar)x;
		}
	}
}

效果如下:


局部运算

为了减少噪声到来的影响,因此很多时候我们采用很多的方法进行处理,而其中最有效的方法的使用局部运算,即使用像素和像素周围的像素进行运算,从而综合单个像素对于图像过于明显的影响。

1.邻域平均法(box模板)

使用以一个点为中心f(x,y)和其周围的n*n的像素进行取平均,然后再赋值给f(x,y),这样该位置就变成g(x,y)。即其就是一个平均的概念

代码如下:

/**********************************************************
*项目名称:使用均值滤波进行图像去噪
*缺点: 1.图像模糊,2.计算量大
*
*函数:void average_filter(Mat& src, Mat& dst,const int& Border);
*      描述:无返回值,src是输入的图像,dst是改变后输出的图像,Border表示模板大小 Border*Border
*
*作者:***
*日期:2020.04.06
*修改:使用积分图技术进行减少计算量(未完成)
***********************************************************/

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

void average_filter(Mat& src, Mat& dst,const int& Border);
int main(void)
{
	Mat image = imread("d:/模糊的图片.PNG", 0);
	
	if (image.empty())
	{
		cout << "未找到指定的文件" << endl;
		return -1;
	}

	// 生成一个大小一样的全黑的图像
	Mat average_image = Mat::zeros(image.size(), CV_8UC1); 
	//模板的大小
	const int border = 7;

	//自定义均值滤波函数
	average_filter(image,average_image, border); 

	Mat output_blur;
	//库的均值滤波函数
	blur(image, output_blur, Size(7, 7));

	imshow("image", image);
	imshow("average_image", average_image);
	imshow("output_blur", output_blur);

	waitKey(0);
	return 0;
}

void average_filter(Mat& src, Mat& dst, const int& Border)
{
	int width = src.rows;
	int height = src.cols;

	cout << width << endl;
	cout << height << endl;

	int row,col;
	int sum=0;//表示模板内的像素的总和
	

	for (row = 0; row < width ; ++row)
	{
		for (col =0; col < height; ++col)
		{
			if (row >= (Border - 1) / 2 && row < width - (Border - 1) / 2 && col >= (Border - 1) / 2 && col < height - (Border - 1) / 2)
			{
				for (int i = -(Border - 1) / 2; i <= (Border - 1) / 2; ++i)
					for (int j = -(Border - 1) / 2; j <= (Border - 1) / 2; ++j)
					{
						sum = sum+src.at<uchar>(row + i, col + j);
					}
				dst.at<uchar>(row, col) = sum / (Border * Border);
				sum = 0;
			}
			else
			{
				dst.at<uchar>(row, col) = src.at<uchar>(row, col);
			}
		}
	}
}

2.中值滤波法

其步骤为:

  1. 将窗口中心与图中某个象素位置重合(和均值一样取n*n)
  2. 提取出该模板的个个像素灰度值
  3. 进行排序
  4. 取出中间的值
  5. 进行赋值给窗口的f(x,y),得到新的值g(x,y)

代码如下:

/**********************************************************
*项目名称:使用中值滤波进行图像去噪
*缺点: 图像中点、线、尖角细节较多,则不宜采用中值滤波
*
*函数:void median_filter(Mat& src, Mat& dst,const int& Border);
*      描述:无返回值,src是输入的图像,dst是改变后输出的图像,Border表示模板大小 Border*Border
*
*作者:***
*日期:2020.04.06
*修改:使用多种排列的算法进行处理(未完成)
***********************************************************/

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

void median_filter(Mat& src, Mat& dst, const int& Border);
int main(void)
{
	Mat image = imread("d:/模糊的图片.PNG", 0);

	if (image.empty())
	{
		cout << "未找到指定的文件" << endl;
		return -1;
	}

	// 生成一个大小一样的全黑的图像
	Mat median_image = Mat::zeros(image.size(), CV_8UC1);
	//模板的大小
	const int border = 5;

	//自定义均值滤波函数
	median_filter(image, median_image, border);

	Mat output_blur;
	//库的均值滤波函数
	//blur(image, output_blur, Size(7, 7));

	imshow("image", image);
	imshow("median_image", median_image);
	//imshow("output_blur", output_blur);

	waitKey(0);
	return 0;
}

void median_filter(Mat& src, Mat& dst, const int& Border)
{
	int width = src.rows;
	int height = src.cols;

	cout << width << endl;
	cout << height << endl;

	int row, col;
	int* border = new int[Border * Border];

	for (int i = 0; i < Border * Border; ++i)
	{
		border[i] = i;
	}
	for (int i = 0; i < Border * Border; ++i)
	{
		//cout<<border[i]<<endl;
	}
	//cout << sizeof(border);//8

	int median = 0;




	for (row = 0; row < width; ++row)
	{
		for (col = 0; col < height; ++col)
		{
			if (row >= (Border - 1) / 2 && row < width - (Border - 1) / 2 && col >= (Border - 1) / 2 && col < height - (Border - 1) / 2)
			{
				
				for (int i = -(Border - 1) / 2; i <= (Border - 1) / 2; ++i)
				{
					for (int j = -(Border - 1) / 2; j <= (Border - 1) / 2; ++j)
					{
						//进行赋值到中间数组里面进行排序
						border[(i+ (Border - 1) / 2)+(j+ (Border - 1) / 2) *Border]= src.at<uchar>(row + i, col + j);
						
					}
				}


				//进行排序
				for (int i = 1; i < Border * Border; ++i)
				{
					for (int j = 1; j < Border * Border-i; ++j)
					{
						if (border[j - 1] > border[j])
						{
							median = border[j - 1];
							border[j - 1] = border[j];
							border[j] = median;
						}
						median = 0;
					}
				}

				//排序完进行赋值
				dst.at<uchar>(row, col) = border[(Border * Border - 1) / 2];
			}
			else
			{
				dst.at<uchar>(row, col) = src.at<uchar>(row, col);
			}
		}
	}
}

3.高斯滤波

模板运算的数学含义是卷积运算,使用高斯滤波来解决图像去噪的问题,只有基本掌握模板运算和高斯滤波的公式就可以。

代码如下:

//*************************************************
//第三周作业:编写代码,实现高斯滤波,模板大小为3*3
//
//Version: 0.1
//author : ***
//date   : 2020/03/29
//***********************************************
#include <iostream>
#include<opencv2/opencv.hpp>
#include <math.h>


using namespace std;
using namespace cv;


double** getGuassionArray(int size, double sigma) {
	int i, j;
	double sum = 0.0;
	int center = size; //以第一个点的坐标为原点,求出中心点的坐标

	double** arr = new double* [size];//建立一个size*size大小的二维数组
	for (i = 0; i < size; ++i)
		arr[i] = new double[size];

	for (i = 0; i < size; ++i)
		for (j = 0; j < size; ++j) {
			arr[i][j] = exp(-((i - center) * (i - center) + (j - center) * (j - center)) / (sigma * sigma * 2));
			sum += arr[i][j];
		}
	for (i = 0; i < size; ++i)
		for (j = 0; j < size; ++j)
			arr[i][j] /= sum;
	return arr;
}
//**********************************
//第一个参数是_src表示输入
//第二个参数是_dst表示输出
void myGaussian(const Mat _src, Mat& _dst) {
	if (_src.empty())
	{
		cout << "未找到有效文件" << endl;
		return;
	}

	double** arr;
	Mat tmp(_src.size(), _src.type());
	for (int i = 0; i < _src.rows; ++i)
		for (int j = 0; j < _src.cols; ++j) {
			//保证边缘不进行处理
			if ((i - 1) > 0 && (i + 1) < _src.rows && (j - 1) > 0 && (j + 1) < _src.cols) {
				arr = getGuassionArray(3, 1);//自定义得到的权值数组
				tmp.at<Vec3b>(i, j)[0] = 0;
				tmp.at<Vec3b>(i, j)[1] = 0;
				tmp.at<Vec3b>(i, j)[2] = 0;
				for (int x = 0; x < 3; ++x) {
					for (int y = 0; y < 3; ++y) {
						tmp.at<Vec3b>(i, j)[0] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[0];
						tmp.at<Vec3b>(i, j)[1] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[1];
						tmp.at<Vec3b>(i, j)[2] += arr[x][y] * _src.at<Vec3b>(i + 1 - x, j + 1 - y)[2];
					}
				}
			}
		}
	tmp.copyTo(_dst);
}

void main() {
	Mat image = imread("D:/图片/壁纸/动漫/1.jpg",1);

	Mat Gaussian;
	Mat image1;
	GaussianBlur(image, image1, Size(3, 3), 1); //自带的高斯滤波
	myGaussian(image, Gaussian);

	imshow("原图", image);
	imshow("opencv自带的高斯滤波", image1);
	imshow("自定义高斯滤波", Gaussian);
	waitKey(0);
}

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值