图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)

一、图像平滑

        图像平滑的目的之一是消除噪声,二是模糊图像。

        从信号频谱的角度来看,信号缓慢变化的部分在频率域表现为低频,迅速变化的部分表现为高频。图像在获取、储存、处理、传输过程中,会受到电气系统和外界干扰而存在一定程度的噪声,图像噪声使图像模糊,甚至淹没图像特征,给分析带来困难。

二、模板卷积

        模板卷积是数字图像处理常用的一种邻域运算方式,模板卷积可以实现图像平滑、图像锐化、边缘检测等功能。

        模板可以是一小幅图像,也可以是一个滤波器。

        模板卷积的基本步骤:

     (1)模板在输入图像上移动,让模板中心依次与输入图像的每个像素重合;

      (2)模板系数与跟模板重合的输入图像的对应像素相乘,再将乘积相加;

      (3)把结果赋予输图像,其像素位置与模板中心在输入图像上的位置一致;

      具体过程如下图所示(原图像进行了边界扩展,目的是保持卷积后图像与原图像大小相同)

图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第1张图片

       图像边界问题:

     (1)当模板超出图像边界时不做处理;

     (2)扩充图像,可以复制原图像边界像素或利用常数填充图像边界。

      计算结果可能超出灰度范围,对于8位灰度图像,当计算结果超出 [0, 255] 时,可以简单的将其值置为0或255.

       常用模板有Box模板和高斯模板

图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第2张图片

以下为均值滤波与高斯滤波代码


#include 
#include 
#include 
#include 
#include 

int main()
{
	//smooth with GAUSS and BOX
	const char *filename = "building.bmp";	
	IplImage *inputimage = cvLoadImage(filename, -1);	//载入图片
	IplImage *smoothimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);//声明去噪图片
	IplImage *extensionimage = cvCreateImage(cvSize(inputimage->width+2, inputimage->height+2), IPL_DEPTH_8U, inputimage->nChannels);//声明边界扩展图片
	cvCopyMakeBorder(inputimage, extensionimage, cvPoint(1, 1), IPL_BORDER_REPLICATE);	//进行边界扩展,原图像在新图像的(1,1)位置,IPL_BORDER_REPLICATE表示扩展为边界像素
	

	int gauss[3][3] = { 1,2,1,2,4,2,1,2,1 };	//声明高斯模板

	int box[3][3] = { 1,1,1,1,1,1,1,1,1 };	//声明Box模板

	//进行模板卷积
	for (int i = 0; i < inputimage->height; i++)
	{
		for (int j = 0; j < inputimage->width; j++)
		{
			for (int k = 0; k < inputimage->nChannels; k++)
			{
				float temp = 0;
				for (int m = 0; m < 3; m++)
				{
					for (int n = 0; n < 3; n++)	
					{
						temp += gauss[m][n] * (unsigned char)extensionimage->imageData[(i + m)*extensionimage->widthStep + (j + n)* extensionimage->nChannels + k];
						//temp += box[m][n] * (unsigned char)extensionimage->imageData[(i + m)*extensionimage->widthStep + (j + n)* extensionimage->nChannels + k];
					}
				}
				smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k] = temp / 16.0;	//gauss模板
				//smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k] = temp / 9.0;	//box模板
			}
			
		}
	}

	cv::Mat smooth = cv::cvarrToMat(smoothimage); 
	cv::imwrite("buildingsmooth.bmp", smooth);	//将去噪后的图片保存到当前目录下
	cvNamedWindow("inputimage", 1);
	cvNamedWindow("smoothimage", 1);

	cvShowImage("inputimage", inputimage);
	cvShowImage("smoothimage", smoothimage);
	cvWaitKey(0);

	cvDestroyWindow("inputimage");
	cvDestroyWindow("smoothimage");

	cvReleaseImage(&inputimage);
	cvReleaseImage(&smoothimage);
}

以下为程序运行结果,从左到右依次为:原噪声图像---均值滤波图像---高斯滤波图像

图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第3张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第4张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第5张图片
三、中值滤波

       中值滤波是一种非线性滤波,它能在滤除噪声的同时很好的保持图像边缘

      中值滤波的原理:把以当前像素为中心的小窗口内的所有像素的灰度按从小到大排序,取排序结果的中间值作为该像素的灰度值。

以下是具体实现代码:


#include 
#include 
#include 
#include 
#include 

int median(int *input)	//中值函数,借助了简单的排序(起泡法)
{
	for (int i = 0; i < 8; i++)
	{
		for (int j = 0; j < 8 - i; j++)
		{
			if (input[j] > input[j + 1])
			{
				int k;
				k = input[j];
				input[j] = input[j + 1];
				input[j + 1] = k;
			}
		}
	}
	return input[4];
}


int main()
{
	//smooth with median filter
	const char *filename = "dinner.bmp";
	IplImage *inputimage = cvLoadImage(filename, -1);
	IplImage *smoothimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);
	IplImage *extensionimage = cvCreateImage(cvSize(inputimage->width + 2, inputimage->height + 2), IPL_DEPTH_8U, inputimage->nChannels);
	cvCopyMakeBorder(inputimage, extensionimage, cvPoint(1, 1), IPL_BORDER_REPLICATE);	//进行边界扩展,原图像在新图像的(1,1)位置,IPL_BORDER_REPLICATE表示扩展为边界像素

	for (int i = 0; i < smoothimage->height; i++)
	{
		for (int j = 0; j < smoothimage->width; j++)
		{
			for (int k = 0; k < smoothimage->nChannels; k++)
			{
				int temp[9] = {};
				int s = 0;
				for (int m = 0; m < 3; m++)
				{
					for (int n = 0; n < 3; n++)
					{
						temp[s++] = (unsigned char)extensionimage->imageData[(i + m)*extensionimage->widthStep + (j + n)* extensionimage->nChannels + k];
					}
				}
				int m;
				m = median(temp);	//得到中值
				smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k] = m;
			}
		}
	}

	cv::Mat smooth = cv::cvarrToMat(smoothimage);
	cv::imwrite("dinnersmooth.jpg", smooth);	//将去噪后的图片保存到当前目录下

	cvNamedWindow("inputimage", 1);
	cvNamedWindow("smoothimage", 1);

	cvShowImage("inputimage", inputimage);
	cvShowImage("smoothimage", smoothimage);
	cvWaitKey(0);

	cvDestroyWindow("inputimage");
	cvDestroyWindow("smoothimage");

	cvReleaseImage(&inputimage);
	cvReleaseImage(&smoothimage);
}


以下是实现结果,从左到右依次是 原噪声图像---中值滤波后图像

图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第6张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第7张图片

四、双边滤波

        双边滤波不仅考虑了像素信息,也考虑到了像素位置信息

        具体公式如下:

                               Wij = exp(-||Pij-Pi||^2/2\sigma1^2)* exp(-||Cij-Ci||^2/2\sigma2^2)

       Wij为当前像素权值,Pij为当前像素信息,Pi为当前像素邻域均值;Cij为当前像素位置信息,Ci为当前像素平均位置信息,\sigma1\sigma2分别为当前像素信息、当前像素位置的标准差。

具体算法过程与上述算法类似,具体代码如下:

#include 
#include 
#include 
#include 
#include
#include "opencv2/opencv.hpp"

float avg(float *a)		//计算平均数
{
	float temp = 0.0;
	for (int m = 0; m < 9; m++)
	{
		temp += a[m];
	}
	return temp;
}

float var(float *b,float c)		//计算方差
{
	float temp = 0.0;
	for (int m = 0; m < 9; m++)
	{
		float a = 0;
		a = b[m] > c ? (b[m] - c) : (c - b[m]);
		temp = temp + a * a;
	}
	return temp / 9.0;
}
	
int main()
{
	// bilateral filter
	const char *filename = "building.bmp";
	IplImage *inputimage = cvLoadImage(filename, -1);
	IplImage *smoothimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);
	IplImage *extensionimage = cvCreateImage(cvSize(inputimage->width + 2, inputimage->height + 2), IPL_DEPTH_8U, inputimage->nChannels);

	
	//边界扩展
	int z = 0;
	while (z <30)	//进行30次循环,进行一次双边滤波效果不大
	{
	//IplImage *extensionimage = cvCreateImage(cvSize(inputimage->width + 2, inputimage->height + 2), IPL_DEPTH_8U, inputimage->nChannels);
	//IplImage *smoothimage = cvCreateImage(cvSize(inputimage->width, inputimage->height), IPL_DEPTH_8U, inputimage->nChannels);
		cvCopyMakeBorder(inputimage, extensionimage, cvPoint(1, 1), IPL_BORDER_REPLICATE);	//进行边界扩展,原图像在新图像的(1,1)位置,IPL_BORDER_REPLICATE表示扩展为边界像素
	
		for (int i = 0; i < smoothimage->height; i++)
		{
			for (int j = 0; j < smoothimage->width; j++)
			{
				for (int k = 0; k < smoothimage->nChannels; k++)
				{
					float average, variance = 0;	
					float shuzu[9] = {};
					int pos = 0;
					for (int m = -1; m < 2; m++)
					{
						for (int n = -1; n < 2; n++)
						{
							shuzu[pos++] = (unsigned char)extensionimage->imageData[(i + 1 + m)*extensionimage->widthStep + (j + 1 + n) * extensionimage->nChannels + k] / 9;
						}
					}
					average = avg(shuzu);				//计算均值
					variance = var(shuzu, average);		//计算方差
					float root = sqrt(variance);
					if (variance < 1)	//防止相似像素间的方差过小,造成错误
					{
						smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k]
							= (unsigned char)extensionimage->imageData[(i + 1)*extensionimage->widthStep + (j + 1) * extensionimage->nChannels + k];
						//printf("%f\n", variance);
					}
					else
					{
						float temp = 0.0;
						float w = 0.0;

						float num1[9] = { 1.414,1,1.414,1,0,1,1.414,1,1.414 };	//位置信息
						int s = 0;

						for (int m = -1; m < 2; m++)
						{
							for (int n = -1; n < 2; n++)
							{
								float e, e1, e2, den1, num2, den2;
								//num1 = (m ^ 2 + n ^ 2) ^ (1/2);
								den1 = 0.182;
								e1 = exp(-num1[s++] / den1);
								unsigned char x, y;
								x = (unsigned char)extensionimage->imageData[(i + 1)*extensionimage->widthStep + (j + 1) * extensionimage->nChannels + k];
								y = (unsigned char)extensionimage->imageData[(i + 1 + m)*extensionimage->widthStep + (j + 1 + n)* extensionimage->nChannels + k];
								num2 = x < y ? (y - x) : (x - y);	//像素信息转换为无符号字符型0-255之间,保证差值为整数
								num2 = pow(num2, 2);
								den2 = variance;
								e2 = exp(-num2 / (2 * den2));
								e = e1 * e2;
								w += e;
								temp += e * (unsigned char)extensionimage->imageData
									[(i + 1 + m)*extensionimage->widthStep + (j + 1 + n)* extensionimage->nChannels + k];
							}
						}

						smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k] = temp / w;
					}
				}
			}
		}

		for (int i = 0; i < smoothimage->height; i++)
		{
			for (int j = 0; j < smoothimage->width; j++)
			{
				for (int k = 0; k < smoothimage->nChannels; k++)
				{
					inputimage->imageData[i*inputimage->widthStep + j * inputimage->nChannels + k]
						= smoothimage->imageData[i*smoothimage->widthStep + j * smoothimage->nChannels + k];
				}
			}
		}
		z++;
	}
	cvNamedWindow("inputimage", 1);
	cvNamedWindow("smoothimage", 1);
	
	cvShowImage("inputimage", inputimage);
	cvShowImage("smoothimage", smoothimage);
	
	cvWaitKey(0);

	cv::Mat smooth = cv::cvarrToMat(smoothimage);
	cv::imwrite("buildingsmooth.bmp", smooth);
	/*const char *file = "G:\\flower_filter.bmp";
	cvSaveImage(file, smoothimage);*/

	cvDestroyWindow("inputimage");
	cvDestroyWindow("smoothimage");

	cvReleaseImage(&inputimage);
	cvReleaseImage(&smoothimage);
}

具体实现效果如下,从左到右依次为 原噪声图像---双边滤波15次后的图像---双边滤波后30次的图像---双边滤波后40次的图像

图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第8张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第9张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第10张图片图像去噪经典算法(均值滤波,高斯滤波,双边滤波,中值滤波)_第11张图片

你可能感兴趣的:(opencv)