opencv学习9-13——高斯滤波,中值滤波,均值滤波,motion滤波(运动模糊),Max-Min滤波

Q9:高斯滤波

高斯滤波器是用于图像平滑处理的一种手段,性质为线性平滑,可对噪声进行过滤,达到降噪的目的。如果有一个像素点的值远高于周围的点则可能是噪声或高频的边缘,高斯滤波对用该点周围多个点与其做加权平均等于用周边的值拉低了这个高值,也就是所谓平滑。高斯滤波在数学上的体现就是对整个图像像素值通过加权平均重赋值的操作。

加权平均可以理解为不同部分按照不同的阈值进行计算再相加的结果。

例:期末考试的成绩

科一:80分,10%

科二:90分,40%

科三:70分,50%

加权平均=80*10%+90*40%+70*50%。10%,40%,50%代表阈值。当所有阈值相等时就变成了算术平均,因此算术平均看作是加权平均的一种情况。

高斯滤波的加权平均是通过创建窗口实现的,也可以称为模板,通过模板遍历图像的每一个像素,计算模板中相邻像素的加权平均赋给模板的中心像素。

在高斯滤波中,模板的定义通常适用窗口设计,还有一种傅里叶变换的方式,如果窗口设计的参数较大计算量过大,可以使用傅里叶,一般使用窗口模板法。该方法最关键的就是高斯核,也就是设计模板窗口的尺寸n*n,n必须是奇数,因为高斯滤波的计算方法就是将计算值赋给窗口模板的中心值,若是偶数,则无法捕获中心值。如下所示,为3*3的窗口。

以中心点为原点,建立坐标系,各个位置的像素用坐标表示,接下来就要带入高斯函数,标准差决定滤波的效果,过大时,高斯图形越矮胖,过小,高斯图像越高瘦。由于图像的长宽可能不是滤波器大小的整数倍,因此我们需要在图像的边缘补0,并且卷积核(可以称为权值,即通过高斯函数带入上述建立的坐标和指定标准差求出的值)要进行归一化操作

代码如下

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include "math.h"
#include"iostream"
using namespace std;
using namespace cv;
#define PI 3.1415926
#define sigma 1.3
Mat Gaussian_filter(Mat img,int Kernel_size){
    //卷积核矩阵(二维数组)
	double **K=new double*[Kernel_size];
	for(int i=0;i<Kernel_size;i++){
		K[i]=new double[Kernel_size];
	} 
	int imgrow = img.rows;
    int imgcol = img.cols;
	int channel = img.channels();
	Mat out = Mat::zeros(imgrow,imgcol,CV_8UC3);
	int x = 0;
	int y = 0;
	int dot = floor((float)(Kernel_size/2));
	double K_sum = 0;
	for(int x=0;x<Kernel_size;x++){
		for(int y=0;y<Kernel_size;y++){
			K[x][y]=0;
		}
	}
	for(int i=0;i<Kernel_size;i++){
		for(int j=0;j<Kernel_size;j++){
			y = i-dot;
			x = j-dot;
			K[i][j] =  1 / (2*PI*sigma*sigma) * exp( - (x*x + y*y) / (2*sigma*sigma));//一维高斯函数
			K_sum += K[i][j];
		}
	}
	//归一化
	for(int i=0;i<Kernel_size;i++){
		for(int j=0;j<Kernel_size;j++){
			K[i][j] /=K_sum;
			cout << K[i][j]<< "    ";
		}
		cout << endl;
	}
	//加权平均,构建滤波器
	int v = 0;
	for(int i=floor((float)(Kernel_size/2));i<imgrow-floor((float)(Kernel_size/2));i++){
		for(int j=floor((float)(Kernel_size/2));j<imgcol-floor((float)(Kernel_size/2));j++){
			for(int k=0;k<channel;k++){
				v=0;
				for(int i_=-dot;i_<dot+1;i_++){
					for(int j_=-dot;j_<dot+1;j_++){
							v += img.at<Vec3b>(i+i_,j+j_)[k]*K[i_+dot][j_+dot];
					}
				}
				out.at<Vec3b>(i,j)[k] = v;			
			}
		}
	}
    for(int i=0;i<Kernel_size;i++){
		delete[] K[i];
	}
	delete[] K;
	return out;
}
void main()
{    
    Mat img = imread("ck567.jpg",IMREAD_COLOR);
    Mat out = Gaussian_filter(img,5);
	imshow("Gaussian-filter",out);
	imshow("original-pic",img);
	waitKey(0);
	destroyAllWindows();    
}

结果输出权值矩阵

该段代码在代码初始的地方有两个define,直接通过修改这两个参数得到不同情况下的高斯滤波图像,Kernel_size表示高斯核,即设计窗口的尺寸,为3*3,修改时注意该参数一定为奇数,sigma为高斯过程的标准差。

#define Kernel_size 3
#define sigma 1.3

opencv官方提供了API,如下所示

void GaussianBlur(InputArray src, 
                  OutputArray dst, 
                  Size ksize, 
                  double sigmaX, 
                  double sigmaY=0, 
                  int borderType=BORDER_DEFAULT);


src,输入图像。

dst,即目标图像,返回的图像

ksize,高斯内核的大小是由sigma计算而来。

sigmaX,表示高斯核函数在X方向的的标准偏差。

sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,
       如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。

 程序代码如下

void main(){
    namedWindow("Orginnal-pic", WINDOW_AUTOSIZE);
    namedWindow("Gaussian-Image", WINDOW_AUTOSIZE);
    Mat img = imread("ck567.jpg", IMREAD_COLOR);
    imshow("Orginal-pic", img);
    Mat out;
    GaussianBlur(img, out, Size(5, 5), 3, 3);
    GaussianBlur(out, out, Size(5, 5), 3, 3);
    imshow("Gaussian-Image", out);
    waitKey(0);
    destroyAllWindows();   
}

Q10:中值滤波

相对于高斯滤波来说,中值滤波的原理就非常简单了,它是创建好窗口后,将邻域内元素的中值赋给中心元素,达到平滑某个凸跳的噪声,为非线性,代码如下。

Mat Medieum_filter(Mat img, intKernel_size){
    double **K=new double*[Kernel_size];
	for(int i=0;i<Kernel_size;i++){
		K[i]=new double[Kernel_size];
	} 
	int imgrow = img.rows;
    int imgcol = img.cols;
	int channel = img.channels();
	Mat out = Mat::zeros(imgrow,imgcol,CV_8UC3);
	int x = 0;
	int y = 0;
	int dot = floor((float)(Kernel_size/2));
	int count;
	double K[Kernel_size*Kernel_size]; 
	for(int x=0;x<Kernel_size*Kernel_size;x++){
			K[x]=0;
	}
	int v = 0;
	for(int i=floor((float)(Kernel_size/2));i<imgrow-floor((float)(Kernel_size/2));i++){
		for(int j=floor((float)(Kernel_size/2));j<imgcol-floor((float)(Kernel_size/2));j++){
			for(int k=0;k<channel;k++){
				count=0;
				for(int i_=-dot;i_<dot+1;i_++){
					for(int j_=-dot;j_<dot+1;j_++){
							K[count++] = img.at<Vec3b>(i+i_,j+j_)[k];
					}
				}
				sort(K,K+Kernel_size*Kernel_size);
				out.at<Vec3b>(i,j)[k] = K[(int)(floor((float)count/2))+1];
			}
		}
	}
	for(int i=0;i<Kernel_size;i++){
		delete[] K[i];
	}
	delete[] K;
    return out
}
void main(){
	Mat img = imread("ck567.jpg",IMREAD_COLOR);
    Mat out = Medieum_filter(img,5);
	imshow("Medieum-filter",out);
	imshow("original-pic",img);
	waitKey(0);
	destroyAllWindows();
}

代码可以直接从高斯滤波修改而来,去掉计算权值的部分,只要遍历每个窗口将值赋给一个数组,将数组中的中值赋给像素即可,这里用到sort()函数,用来排序的函数。两个参数为初始位置和结束位置。

sort(K,K+Kernel_size*Kernel_size);

Q11:均值滤波

均值滤波就是将均值赋给中心像素,直接上代码。

void main(){
	Mat img = imread("ck567.jpg",IMREAD_COLOR);
	int imgrow = img.rows;
    int imgcol = img.cols;
	int channel = img.channels();
	Mat out = Mat::zeros(imgrow,imgcol,CV_8UC3);
	int x = 0;
	int y = 0;
	int dot = floor((float)(Kernel_size/2));
	int count;
	double K[Kernel_size*Kernel_size]; 
	for(int x=0;x<Kernel_size*Kernel_size;x++){
			K[x]=0;
	}
	int v = 0;
	int Sum = 0;
	for(int i=floor((float)(Kernel_size/2));i<imgrow-floor((float)(Kernel_size/2));i++){
		for(int j=floor((float)(Kernel_size/2));j<imgcol-floor((float)(Kernel_size/2));j++){
			for(int k=0;k<channel;k++){
				count = 0;
				Sum = 0;
				for(int i_=-dot;i_<dot+1;i_++){
					for(int j_=-dot;j_<dot+1;j_++){
						K[count] = img.at<Vec3b>(i+i_,j+j_)[k];
						Sum += K[count];
						count++;
					}
				}
				out.at<Vec3b>(i,j)[k] = (int)(Sum/(Kernel_size*Kernel_size));
			}
		}
	}
	imshow("Mean-filter",out);
	imshow("original-pic",img);
	waitKey(0);
	destroyAllWindows();
}

Q12:motion滤波

motion滤波与均值滤波和中值滤波类似,均是采用窗口设计,计算窗口的中心值,区别就是中心值的方法不同,motion滤波是仅计算窗口矩阵的主对角线元素的均值,其他元素不参与计算,以3*3为例,如下所示。中心值就是(x1+x2+x3)/3,滤波后的图像每一个像素值都是由该像素点和邻域的窗口元素按照这种规则计算而来。

代码如下

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include "math.h"
#include"iostream"
using namespace std;
using namespace cv;
#define Kernel_size 3
void main(){
	Mat img = imread("ck567.jpg",IMREAD_COLOR);
	int imgrow = img.rows;
    int imgcol = img.cols;
	int channel = img.channels();
	Mat out = Mat::zeros(imgrow,imgcol,CV_8UC3);
	int dot = floor((float)(Kernel_size/2));
	int Sum = 0;
	for(int i=floor((float)(Kernel_size/2));i<imgrow-floor((float)(Kernel_size/2));i++){
		for(int j=floor((float)(Kernel_size/2));j<imgcol-floor((float)(Kernel_size/2));j++){
			for(int k=0;k<channel;k++){
				Sum = 0;
				for(int i_=-dot;i_<dot+1;i_++){
					for(int j_=-dot;j_<dot+1;j_++){
						if(i_==j_){
							Sum += img.at<Vec3b>(i+i_,j+j_)[k];
						}
					}
				}
				out.at<Vec3b>(i,j)[k] = (int)((double)(Sum/Kernel_size));
			}
		}
	}
	imshow("motion-filter",out);
	imshow("original-pic",img);
	waitKey(0);
	destroyAllWindows();
}

这里用#define Kernel_size来给窗口赋值,因此在程序中的均值为Sum/Kernel_size。

Q13:Max-Min滤波

Max-Min滤波顾名思义就是将窗口中的最大值减去最小值赋给中心值,这个应用在边缘检测中,而且进行该滤波是添加的是灰度图像,程序如下

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<opencv2/opencv.hpp>
#include "math.h"
#include"iostream"
using namespace std;
using namespace cv;
#define Kernel_size 3
void main(){
	Mat img = imread("ck567.jpg",IMREAD_GRAYSCALE);
	int imgrow = img.rows;
    int imgcol = img.cols;
	int channel = img.channels();
	Mat out = Mat::zeros(imgrow,imgcol,CV_8UC1);
	int dot = floor((float)(Kernel_size/2));
	int count;
	double K[Kernel_size*Kernel_size]; 
	for(int x=0;x<Kernel_size*Kernel_size;x++){
			K[x]=0;
	}
	for(int i=floor((float)(Kernel_size/2));i<imgrow-floor((float)(Kernel_size/2));i++){
		for(int j=floor((float)(Kernel_size/2));j<imgcol-floor((float)(Kernel_size/2));j++){
				count=0;
				for(int i_=-dot;i_<dot+1;i_++){
					for(int j_=-dot;j_<dot+1;j_++){
							K[count++] = img.at<uchar>(i+i_,j+j_);
					}
				}
				sort(K,K+Kernel_size*Kernel_size);
				out.at<uchar>(i,j) = K[Kernel_size*Kernel_size-1]-K[0];
			}
		}
	imshow("Max-Min-filter",out);
	imshow("original-pic",img);
	waitKey(0);
	destroyAllWindows();
}

程序中使用sort()函数进行排序,取首尾两个元素就是最大值和最小值,不需要一一比较。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值