基于opencv2利用卷积算子实现高斯模糊

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ling_robe/article/details/79970084

    我们都知道,卷积是一种好玩的东西,通过卷积可以实现很多功能,例如photoshop中的各种滤镜也可以通过对一副图像进行卷积获得。而目前大火的机器学习中的CNN算法也是利用卷积来实现的,至于CNN的算法以后再讲,今天主要是介绍卷积的原理以及利用它来实现高斯模糊。

    我们先来看下卷积是将两个矩阵相乘在求和,而具体原理可在这篇文章中看。而图像中高斯模糊则是根据二维的正态分布(高斯分布)来产生高斯模糊卷积子。

    一维的正态分布:,其中我们的u取0。

    二维的正态分布

        

    利用这个二维高斯模糊公式,我们可以产生N*N的卷积核,然后利用这个核与图片中的每个像素进行卷积便是高斯模糊。

    这个过程可以通过以下几张图来理解,假如我产生一个3*3的卷积核,取1,因此我们就可以产生以下的矩阵:

                                            

我们取图像中的某个像素周围3*3的矩阵:

                                            

而矩阵中的78对应的是(0,0)坐标,而这个矩阵我们是围绕着这个像素点在半径为1的范围内取得矩阵,因此我们卷积获得的值也应替代该值。

通过卷积:(0.0585*0+0.0965*0+0.0585*7+0.0965*4+0.1592*78+0.0965*8+0.0585*69+0.0965*2+0.0585*4)=18.4486

    因此78这个位置的值也就变为18.4486。高斯模糊便是通过对每个像素以半径r范围取一N*N矩阵与N*N卷积核进行卷积并改变改像素点的值以获得滤波效果。

以下便是灰度图的高斯模糊程序:

#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<math.h>

#define sigma 1   //定义sigma的大小越大越模糊
#define PI 3.14159
#define N 5 //定义卷积核大小

int main()
{
	float liv_conv[N][N] = {0}; //定义卷积核

	cv::Mat lMv_src = cv::imread("E:\\temp\\3D\\pad.jpg");//打开图片

	cv::resize(lMv_src,lMv_src,cv::Size(lMv_src.cols/4,lMv_src.rows/4),0,0,cv::INTER_LINEAR);//因为我的图片太大,不方便显示,我就利用这个函数缩小

	cv::Mat lMv_gray, lMv_OutPut = cv::Mat::zeros(lMv_src.rows, lMv_src.cols, CV_8UC1); //定义灰度图,与输出图像
	cv::cvtColor(lMv_src,lMv_gray,CV_BGR2GRAY);//将彩色图转换灰度图

	int liv_n = N/2; //获取半径
	float all = 0.0; 

	/**************
	产生卷积核
	***************/
	for(int i = 0; i<N; i++)
	{
		for(int j = 0; j<N; j++)
		{
			liv_conv[i][j] = exp(-((i-liv_n)*(i-liv_n)+(j-liv_n)*(j-liv_n))/(2.0*sigma*sigma))/(2*PI*sigma*sigma);//二维正态分布公式
			all+=liv_conv[i][j];
		}
	}
	/******************
	逐个像素进行卷积
	*******************/
	for(int i = 0; i<lMv_gray.rows-N; i++)
	{
		for(int j = 0; j<lMv_gray.cols-N; j++)
		{
			float lfv_sum = 0.0;
			for(int y = 0; y<N; y++)
			{
				for(int x = 0; x<N; x++)
				{
					lfv_sum+=lMv_gray.at<uchar>(i+y,j+x)*liv_conv[y][x];//相称求和
				}
			}
			lMv_OutPut.at<uchar>(i,j) = lfv_sum/all;
		}
	}

	cv::imshow("Gaussian",lMv_OutPut);//显示高斯模糊后的图像
	cv::imshow("Src",lMv_gray);//显示原图
	cv::waitKey(0);
	return 0;
}

原图:


sigma = 1:


sigma = 10:


sigma = 0.1:



其实彩色图片的高斯模糊的原理是一样,程序也是基本一样的:

#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<math.h>

#define sigma 1  //定义sigma的大小越大越模糊
#define PI 3.14159
#define N 5 //定义卷积核大小

int main()
{
	float liv_conv[N][N] = {0}; //定义卷积核

	cv::Mat lMv_src = cv::imread("E:\\temp\\3D\\pad.jpg");//打开图片

	cv::resize(lMv_src,lMv_src,cv::Size(lMv_src.cols/2,lMv_src.rows/2),0,0,cv::INTER_LINEAR);//因为我的图片太大,不方便显示,我就利用这个函数缩小

	cv::Mat lMv_gray, lMv_OutPut = cv::Mat::zeros(lMv_src.rows, lMv_src.cols, CV_8UC3); //定义灰度图,与输出图像
	cv::cvtColor(lMv_src,lMv_gray,CV_BGR2GRAY);//将彩色图转换灰度图

	int liv_n = N/2; //获取半径
	float all = 0.0; 

	/**************
	产生卷积核
	***************/
	for(int i = 0; i<N; i++)
	{
		for(int j = 0; j<N; j++)
		{
			liv_conv[i][j] = exp(-((i-liv_n)*(i-liv_n)+(j-liv_n)*(j-liv_n))/(2.0*sigma*sigma))/(2*PI*sigma*sigma);//二维正态分布公式
			all+=liv_conv[i][j];
		}
	}
	/******************
	逐个像素进行卷积
	*******************/
	for(int i = 0; i<lMv_gray.rows-N; i++)
	{
		for(int j = 0; j<lMv_gray.cols-N; j++)
		{
			float lfv_sum_b = 0.0, lfv_sum_g = 0.0, lfv_sum_r = 0.0;
			for(int y = 0; y<N; y++)
			{
				for(int x = 0; x<N; x++)
				{
					lfv_sum_b+=lMv_src.at<cv::Vec3b>(i+y,j+x)[0]*liv_conv[y][x];//相称求和
					lfv_sum_g+=lMv_src.at<cv::Vec3b>(i+y,j+x)[1]*liv_conv[y][x];
					lfv_sum_r+=lMv_src.at<cv::Vec3b>(i+y,j+x)[2]*liv_conv[y][x];
				}
			}
			lMv_OutPut.at<cv::Vec3b>(i,j)[0] = lfv_sum_b/all;
			lMv_OutPut.at<cv::Vec3b>(i,j)[1] = lfv_sum_g/all;
			lMv_OutPut.at<cv::Vec3b>(i,j)[2] = lfv_sum_r/all;
		}
	}

	cv::imshow("Gaussian",lMv_OutPut);//显示高斯模糊后的图像
	cv::imshow("Src",lMv_src);//显示原图
	cv::waitKey(0);
	return 0;
}

结果:


然后我们加入椒盐噪声测试下性能:

#define sigma 3  //定义sigma的大小越大越模糊
#define PI 3.14159
#define N 8 //定义卷积核大小
#define NN 1600
/*************************
创建椒盐噪点
*************************/
for(int i = 0; i<NN; i++)
{
	int x = rand()%(lMv_src.cols-1)+1; //创建随机数
        int y = rand()%(lMv_src.rows-1)+1;
	lMv_src.at<cv::Vec3b>(y,x)[0] = 255;
	lMv_src.at<cv::Vec3b>(y,x)[1] = 255;
	lMv_src.at<cv::Vec3b>(y,x)[2] = 255;
}

由此看来效果还是可以的,接下来我会跟大家分享去模糊的算法。

欢迎喜欢图像处理或人工智能的朋友通过邮箱lingrobepy@gmail.com来联系我,一起交流。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页