循序渐进之(六)空间域图像增强之平滑空间滤波

                         循序渐进之(六)空间域图像增强之平滑空间滤波

 

冈萨雷斯第二版《数字图像处理》,平滑空间滤波是3.6章节的内容。

下面图片来自于图像平滑处理,6种滤波总结的综合示例,具体相关原理也可通过本博客学习。

1.均值滤波(方框滤波),只是参数不同这里放在一起。。

滤波问题,还有个边界处理的小问题,这一块可以看一下:图像处理之其他杂项(六)之Mat格式图像处理相关小事(像素越界,边界处理),,在现阶段opencv+VS中好像不存在这个问题(后续更正:好吧,这句话不正确,有些情况不存在越界问题,自动填充数值,有些情况越界报错,具体情况具体分析),而且如下面代码中那样,如果将边界问题考虑到,会造成掩模扩大后图像一定边界范围内的像素处理不到的情况。

#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
void meanFilterS(Mat &src, int n);
void meanFilterSB(Mat &src, int n);
int main()
{
	Mat originImage=imread("E://匹配.jpg",0);
	Mat src1=originImage.clone(),src2 = originImage.clone();
	meanFilterSB(src1, 25);
	meanFilterS(src2, 25);
	imshow("原图", originImage);
	imshow("有边界滤波",src1);
	imshow("无边界滤波", src2);
	waitKey(0);
	return 0;
}
void meanFilterSB(Mat &src, int n)//考虑边界情况
{
	for (int i = (n - 1) / 2; i < src.rows - (n - 1) / 2; i++)
	{
		for (int j = (n - 1) / 2; j < src.cols - (n - 1) / 2; j++)
		{
			int total = 0;
			int x;
			int y;
			for (x = i - ((n - 1) / 2); x < i + ((n - 1) / 2); x++)
				for (y = j - ((n - 1) / 2); y < j+ ((n - 1) / 2); y++)
				{
					total += src.at<uchar>(x, y);
				}
			src.at<uchar>(i, j) = total / n / n;
		}
	}
}
void meanFilterS(Mat &src, int n)//不考虑边界情况
{
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j <src.cols; j++)
		{
			int total = 0;
			int x;
			int y;
			for (x = i - ((n - 1) / 2); x < i  + ((n - 1) / 2); x++)
				for (y = j - ((n - 1) / 2); y < j  + ((n - 1) / 2); y++)
				{				
					total += src.at<uchar>(x, y);
				}
			src.at<uchar>(i, j) = total / n / n;
		}
	}
}

处理结果如下:

2.高斯滤波

高斯滤波总体原理和均值滤波类似,只是平均分布系数分布符合正态分布,,滤波更加合理化。

重点公式:

 程序如下:

#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
Mat generateGaussianTemplate(int ksize, double sigma);
void gaussianBlur(Mat &src,Mat &gaussian);
int main()
{
	Mat originImage = imread("E://匹配.jpg", 0);
	imshow("原图", originImage);
	Mat copyImage1=originImage.clone();
	Mat copyImage2,copyImage3;
	copyImage1.convertTo(copyImage2, CV_32FC1,1.0/255);//第三个系数的作用是把float类型的像素值归一化到0.0到1.0
	copyImage1.convertTo(copyImage3, CV_32FC1,1.0/255);
	Mat gaussian1=generateGaussianTemplate(5,0.8);
	gaussianBlur(copyImage2, gaussian1);
	imshow("高斯滤波图σ=0.8", copyImage2);
	Mat gaussian2 = generateGaussianTemplate(5, 1.8);
	gaussianBlur(copyImage3, gaussian2);
	imshow("高斯滤波图σ=1.8", copyImage3);
	waitKey(0);
	return 0;
}
Mat generateGaussianTemplate( int ksize, double sigma)//计算高斯滤波的内核矩阵
{
	Mat window(ksize,ksize,CV_32FC1);
	static const double pi = 3.1415926;
	int center = ksize / 2; 
	double x2, y2;
	double sum = 0;
	for (int i = 0; i < ksize; i++)
	{
		x2 = pow(i - center, 2);
		for (int j = 0; j < ksize; j++)
		{
			y2 = pow(j - center, 2);
			float g = exp(-(x2 + y2) / (2 * sigma * sigma));			
			g /= sqrt(2 * pi )* sigma;//看了好几版的正态分布的函数,这个系数有好几种写法,其中不必纠结于系数sqrt(2 * pi )* sigma,因为它只是!一个! 常数!并不会影响互相之间的比例关系,并且最终都要进行归一化
			sum += g;
			window.at<float>(i,j) = g;
		}			
	}
	for (int i = 0; i < ksize; i++)
	{
		for (int j = 0; j < ksize; j++)
		{
			window.at<float>(i, j) /= sum;//归一化
		}
	}	
	return window;
}
void gaussianBlur(Mat &src,Mat &gaussian)
{
	for (int i = (gaussian.rows - 1) / 2; i < src.rows - (gaussian.rows - 1) / 2 ; i++)//这里就要考虑边界滤波的越界问题,否则会出现错误
	{
		for (int j = (gaussian.cols - 1) / 2; j < src.cols - (gaussian.cols - 1) / 2 ; j++)
				{
					float temp = 0.0;
					int o = 0;
					for (int x = i - (gaussian.rows - 1) / 2; x < i + (gaussian.rows - 1) / 2 + 1; x++)
					{
						int p = 0;
						for (int y = j - (gaussian.cols - 1) / 2; y < j + (gaussian.cols - 1) / 2 + 1; y++)
						{
							temp += src.at<float>(x, y)*gaussian.at<float>(o, p);							
							p++;
						}
						o++;
					}
					src.at<float>(i, j) = temp;
				}
	}
}

处理结果如下:

3.中值滤波

取一个滤波核范围内的中间值,这个好理解,所以对于突变的极大值极小值(椒盐噪声)有很好的滤波效果。

#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
unsigned char lvbo(unsigned char D[]);
void meanFilter(Mat &src);
void salt(cv::Mat image, int n);
const int n = 3;
const int m = n*n;
unsigned char block[m];
int main()
{
	
	Mat originImage = imread("E://匹配.jpg", 0);
	imshow("原图", originImage);
	Mat saltImage=originImage.clone();
	salt(saltImage,10000);
	imshow("盐噪声图", saltImage);
	Mat src = saltImage.clone();
	meanFilter(src);
	imshow("中值滤波图", src);
	waitKey(0);
	return 0;	
}
unsigned char lvbo(unsigned char D[])//排序取中间值
{
	     unsigned int temp;
	     int i, j;
	
		     for (i = 0; i<m; i++)
		     {
		         for (j = i+1; j<m; j++)
			         {
			             if (D[i]>D[j])
				             {
				                 temp = D[i];
				                 D[i] = D[j];
				                 D[j] = temp;
				             }
			         }
		     }
	
		     return D[4];
}
void meanFilter(Mat &src)
{
	//for (int i = (n- 1) / 2; i < src.rows - (n - 1) / 2; i++)//这里就要考虑边界滤波的越界问题,否则会出现错误
	//{
		//for (int j = (n - 1) / 2; j < src.cols - (n - 1) / 2; j++)
	for (int i = 0; i < src.rows; i++)//这里边界问题没有考虑,也不会报错,,有些奇怪。。哪些情况会有问题?现在看没有什么规律
	{
		 for (int j =0; j < src.cols; j++)
		{			
			int k = 0;
			for (int x = i - (n - 1) / 2; x < i + (n - 1) / 2 + 1; x++)
			{
				for (int y = j - (n - 1) / 2; y < j + (n - 1) / 2 + 1; y++)
				{
					block[k] = src.at<uchar>(x, y);
					k++;
				}
			}
			unsigned char meanValue = lvbo(block);
			int x = meanValue;
			src.at<uchar>(i, j) = meanValue;
		}
	}
}

void salt(cv::Mat image, int l)
{

	int i, j;
	for (int k = 0; k < l / 2; k++)
	{

		// rand() is the random number generator
		i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
		j = std::rand() % image.rows;

		if (image.type() == CV_8UC1)
		{ // gray-level image

			image.at<uchar>(j, i) = 255; //at方法需要指定Mat变量返回值类型,如uchar等

		}
		else if (image.type() == CV_8UC3)
		{ // color image

			image.at<cv::Vec3b>(j, i)[0] = 255; //cv::Vec3b为opencv定义的一个3个值的向量类型
			image.at<cv::Vec3b>(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2
			image.at<cv::Vec3b>(j, i)[2] = 255;
		}
	}
}

 处理结果如下:

4.双边滤波

双边滤波相对于高斯滤波,系数项多了一个值域参数,因为其他的滤波只考虑空间分布,一视同仁,对于边缘也有同样的模糊效果,这是我们不愿意看到的,系数项的作用是对于疑似边缘的处理进行对应调节,滤波合理化,减小模糊效果。

下面图片来自:双边滤波器的原理及实现

代码参考:C++实现双边滤波

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

using namespace std;
using namespace cv;


double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD);
double ColorFactor(int x, int y, double sigmaR);
Mat BilateralFilter(Mat &inputImg, int filterSize, double sigmaR, double sigmaD);


int main()
{
	Mat image = imread("E:\\匹配.jpg",0);
	imshow("原图", image);
	Mat bilateralImage = image.clone();
	Mat dst = BilateralFilter(bilateralImage, 15, 50, 10);
	Mat out;
	imshow("双边滤波图", dst);
	bilateralFilter(image, out, 15, 50, 10);
	imshow("opencv自带",out);
	waitKey(0);
	return 0;
}


double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD)
{
	double absX = pow(abs(x1 - x2), 2);
	double absY = pow(abs(y1 - y2), 2);
	return exp(-(absX + absY) / (2 * pow(sigmaD, 2)));
}


double ColorFactor(int x, int y, double sigmaR)
{
	double distance = pow(abs(x - y), 2);
	return exp(-distance / (2 * pow(sigmaR, 2)));
}


Mat BilateralFilter(Mat &inputImg, int filterSize, double sigmaR, double sigmaD)
{
	int len;
	if (filterSize % 2 != 1 || filterSize <= 0)
	{
		cerr << "Filter Size must be a positive odd number!" << endl;
		return inputImg;
	}
	len = filterSize / 2;


	Mat resultGrayImg =inputImg.clone();
	for (int i = 20; i<inputImg.rows; i++)
	{
		for (int j = 20; j<inputImg.cols; j++)
		{
			double k = 0.0;
			double f = 0.0;
			//不对边缘进行处理
			if ((i - len) >= 0 && (i + len)<inputImg.rows && (j - len) >= 0 && (j + len)<inputImg.cols)
			{
				for (int r = i - len; r <= i + len; r++)
				{
					for (int c = j - len; c <= j + len; c++)
					{
						f = f+ inputImg.ptr<uchar>(r)[c] * SpaceFactor(i, j, r, c, sigmaD)*ColorFactor(inputImg.ptr<uchar>(i)[j], inputImg.ptr<uchar>(r)[c], sigmaR);					
						k += SpaceFactor(i, j, r, c, sigmaD)*ColorFactor(inputImg.ptr<uchar>(i)[j], inputImg.ptr<uchar>(r)[c], sigmaR);
						
					}
				}
				int value = f / k;
				int a = resultGrayImg.ptr<uchar>(i)[j];
				if (value < 0)
					value = 0;
				else if (value > 255)
					value = 255;
				resultGrayImg.ptr<uchar>(i)[j] = value;
			}
		}
	}
	return resultGrayImg;
}

处理结果如下:

 

 

猜你喜欢

转载自blog.csdn.net/coming_is_winter/article/details/88368881
今日推荐