天天看點

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

一、圖像平滑

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

        從信号頻譜的角度來看,信号緩慢變化的部分在頻率域表現為低頻,迅速變化的部分表現為高頻。圖像在擷取、儲存、處理、傳輸過程中,會受到電氣系統和外界幹擾而存在一定程度的噪聲,圖像噪聲使圖像模糊,甚至淹沒圖像特征,給分析帶來困難。

二、模闆卷積

        模闆卷積是數字圖像處理常用的一種鄰域運算方式,模闆卷積可以實作圖像平滑、圖像銳化、邊緣檢測等功能。

        模闆可以是一小幅圖像,也可以是一個濾波器。

        模闆卷積的基本步驟:

     (1)模闆在輸入圖像上移動,讓模闆中心依次與輸入圖像的每個像素重合;

      (2)模闆系數與跟模闆重合的輸入圖像的對應像素相乘,再将乘積相加;

      (3)把結果賦予輸圖像,其像素位置與模闆中心在輸入圖像上的位置一緻;

      具體過程如下圖所示(原圖像進行了邊界擴充,目的是保持卷積後圖像與原圖像大小相同)

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

       圖像邊界問題:

     (1)當模闆超出圖像邊界時不做處理;

     (2)擴充圖像,可以複制原圖像邊界像素或利用常數填充圖像邊界。

      計算結果可能超出灰階範圍,對于8位灰階圖像,當計算結果超出 [0, 255] 時,可以簡單的将其值置為0或255.

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

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

以下為均值濾波與高斯濾波代碼

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>

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);
}
           

以下為程式運作結果,從左到右依次為:原噪聲圖像---均值濾波圖像---高斯濾波圖像

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
三、中值濾波

       中值濾波是一種非線性濾波,它能在濾除噪聲的同時很好的保持圖像邊緣

      中值濾波的原理:把以目前像素為中心的小視窗内的所有像素的灰階按從小到大排序,取排序結果的中間值作為該像素的灰階值。

以下是具體實作代碼:

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>

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);
}


           

以下是實作結果,從左到右依次是 原噪聲圖像---中值濾波後圖像

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

四、雙邊濾波

        雙邊濾波不僅考慮了像素資訊,也考慮到了像素位置資訊

        具體公式如下:

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

       Wij為目前像素權值,Pij為目前像素資訊,Pi為目前像素鄰域均值;Cij為目前像素位置資訊,Ci為目前像素平均位置資訊,

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波

分别為目前像素資訊、目前像素位置的标準差。

具體算法過程與上述算法類似,具體代碼如下:

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <stdio.h>
#include<math.h>
#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次的圖像

圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波
圖像去噪經典算法(均值濾波,高斯濾波,雙邊濾波,中值濾波)一、圖像平滑二、模闆卷積 三、中值濾波四、雙邊濾波