首先,了解什么是模糊,可以看一下这篇,图像模糊--快速均值滤波http://blog.csdn.net/zhonghuan1992/article/details/41131183
看完上面的那篇blog,应该就知道什么是模糊了,那么什么是高斯滤波,或者说是高斯模糊呢?
简单地替换一下就好了,均值滤波,就是取平均值,那么高斯滤波,取得是什么?
高斯滤波,会比均值滤波复杂一点,它不是简单地平均而已,而是加权平均,加权平均的意思,可以看下嘛。
注意,上面的图显示的是一个5*5的区域,不过,里面的数值,不再是图像的像素点了,而是这个区域的每一个点得权重,权重是什么意思呢?就是重要的程度,怎么体现呢?当我们用上面的卷积核(这里给这个区域的权重表取了一个响亮的名字,卷积核),卷积核上面的值和对应位置的点的像素相乘,再相加,便是更新的值。
O(u,v) = a(1,1)*I(1,1)+a(1,2)*I(1,2)+....+a(5,5)*I(5,5)
很好理解吧。
注意一下,上面的卷积核是归一化了,所以上面计算的时候我们才直接相加。
好了,现在我们该考虑,这个卷积核是怎么来的了。这个是这里的关键点。看下面图
上面的图片,可以看到,高斯函数就是一个二维的正态分布,什么,忘了正态分布神马了?看这里
也可以从上面的图中,看到,图像以中点开始,向四周散开,非常好的形态。而高斯卷积核就是通过这个高斯函数计算出来的。好了,这样的话,高斯滤波就解决了。
就这样简单嘛,是滴,你木有听错,就是这样简单,不用想复杂啦。那么,在代码中我们,如何实现它呢?看下面的实现吧,代码来自网络上c++版的(因为本人直接写得优化版的,懒呐): 来自(http://blog.csdn.net/zddblog/article/details/7450033)
void GaussianSmooth2D(const Mat &src, Mat &dst, double sigma) { if(src.channels() != 1) return; //确保sigma为正数 sigma = sigma > 0 ? sigma : 0; //高斯核矩阵的大小为(6*sigma+1)*(6*sigma+1) //ksize为奇数 int ksize = cvRound(sigma * 3) * 2 + 1; // dst.create(src.size(), src.type()); if(ksize == 1) { src.copyTo(dst); return; } dst.create(src.size(), src.type()); //计算高斯核矩阵 double *kernel = new double[ksize*ksize]; double scale = -0.5/(sigma*sigma); const double PI = 3.141592653; double cons = -scale/PI; double sum = 0; for(int i = 0; i < ksize; i++) { for(int j = 0; j < ksize; j++) { int x = i-(ksize-1)/2; int y = j-(ksize-1)/2; kernel[i*ksize + j] = cons * exp(scale * (x*x + y*y)); sum += kernel[i*ksize+j]; // cout << " " << kernel[i*ksize + j]; } // cout <<endl; } //归一化 for(int i = ksize*ksize-1; i >=0; i--) { *(kernel+i) /= sum; } //图像卷积运算,无边缘处理 for(int j = 0; j < src.cols-ksize; j++) { for(int i = 0; i < src.rows-ksize; i++) { double acc = 0; for(int m = 0; m < ksize; m++) { for(int n = 0; n < ksize; n++) { acc += *(srcData + src.step * (i+n) + src.channels() * (j+m)) * kernel[m*ksize+n]; } } *(dstData + dst.step * (i + (ksize - 1)/2) + (j + (ksize -1)/2)) = (int)acc; } } delete []kernel; }
下面,我们考虑一下,高斯函数,其实这个函数有一些不错的性质值得探讨,这里,说一个,它的行列可分离性,这个有什么用呢?简单的说,他可以化简我们计算时候的复杂度。看下面的图:
有什么用呐,当然是优化了复杂度啊,原先,我们都是计算一个区域的x和y,这样很耗时间,现在,先计算x,再计算y,就能够简化了,具体看代码吧,仔细看,就能理解了,因为不太好表述,语言是python
# -*- coding: utf-8 -*- __author__ = 'zhonghuan' import numpy as np import cv2 import math class MyGaussianBlur(): #初始化 def __init__(self, sigma): self.length=int(math.floor(6*sigma-1)/2*2+1) self.sigma=sigma #高斯的计算公式,根据高斯函数计算的。 def calcTemplate(self): a=np.zeros(self.length); k=(self.length)/2; sum = 0.0; cons = 1/(math.sqrt(2*math.pi)*self.sigma); cons2 = -1/(2*self.sigma*self.sigma); for i in range(0,self.length): x = i-k; #和中间节点的距离,就是x a[i]= cons * math.exp((x*x)*cons2); sum=sum+a[i]; a/=sum; return a; #滤波函数 def filter(self, src, template): dstSize = [np.size(src,0),np.size(src,1)]; if src.ndim==3: dstSize.append(3) len = self.length; center = len/2; print len; dst=np.array(np.zeros(dstSize),src.dtype) temp=np.array(np.zeros(dstSize),src.dtype) # print np.size(src,0),np.size(src,1); #这里就是利用行列可分离性,先计算x for x in range(0,np.size(src,0)): for y in range(0,np.size(src,1)): ary = 0; sum = 0; for i in range(-center,center): if(y+i>=0 and y+i < np.size(src,1)): ary =ary + template[i+center]*src[x,y+i]; sum =sum + template[i+center]; temp[x,y]=ary/sum; return temp; #再计算y for y in range(0,np.size(src,1)): for x in range(0,np.size(src,0)): ary = 0; sum = 0; for i in range(-center,center): if(x+i>=0 and x+i < np.size(src,0)): ary =ary + template[i+center]*temp[x+i]; sum =sum + template[i+center]; dst[x,y]=ary/sum; return dst; s=10 #sigma数值,自己自由调整 GBlur=MyGaussianBlur(sigma=s)#声明高斯模糊类 img=cv2.imread('author.jpg')#打开图片 # img = cv2.imread('m.png') template = GBlur.calcTemplate() print template GBimg = GBlur.filter(img,template); cv2.imshow("src",img); cv2.imshow("dst",GBimg); cv2.waitKey(20000); cv2.destroyAllWindows()