今天突然想要去试一试opencv里面的高斯模糊怎么实现,虽然以前已经写过很多次关于高斯核的函数了,但是有个好奇点,那就是一般不填sigma这个参数的时候,opencv是怎么计算的。
关于具体的高斯函数的讲解,已经有人写的很详细了,我就不赘述了,所以给大家个链接有兴趣的可以去看看。
http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html
我这里想做的实验是到底opencv中的高斯模糊函数GaussianBlur使用的高斯核是什么。
但是直接获取这个二维的高斯核貌似是不可行的,opencv提供了一维高斯核获取的函数。
具体参考以下页面
那么此处问题就出来了,如何通过一维高斯核生成二维高斯核。
先获取两个一维高斯核,而后对后一个高斯核进行转置,而后第一个高斯核核第二个高斯核通过矩阵相乘就可以得到一个二维高斯核了。
下面是测试代码
def gaussian_kernel_2d_opencv(kernel_size = 3,sigma = 0):
kx = cv2.getGaussianKernel(kernel_size,sigma)
ky = cv2.getGaussianKernel(kernel_size,sigma)
return np.multiply(kx,np.transpose(ky))
然后我需要的是自己写一个二维高斯核函数,此处写法有很多版本,但是我是参考了刚才上面给出的参考链接中的计算方式,同时参考了opencv的源码。
两个地方不一样,一个是opencv当中在计算高斯模版的时候并没有在最后计算除以pi2(sigma**2),而是将每次计算后的数值加起来,最后用1/总和,然后输出的时候将刚才计算的数值乘上本身的像素值。
本来此处写一下公式会比较好理解,但是这里稍微偷懒一下就不给出具体的公式了,大家自行看我下面的代码就OK了。
#kernel_size set (n,n) default
def gaussian_2d_kernel(kernel_size = 3,sigma = 0):
kernel = np.zeros([kernel_size,kernel_size])
center = kernel_size//2
if sigma == 0:
sigma = ((kernel_size-1)*0.5 - 1)*0.3 + 0.8
s = 2*(sigma**2)
sum_val = 0
for i in range(0,kernel_size):
for j in range(0,kernel_size):
x = i-center
y = j-center
kernel[i,j] = np.exp(-(x**2+y**2) / s)
sum_val += kernel[i,j]
#/(np.pi * s)
sum_val = 1/sum_val
return kernel*sum_val
这个计算结果和通过opencv获取一维高斯核通过计算转换为二维高斯核的结果完全一致。此处实验成功。
但是!!通过这个高斯核和图像进行卷积操作的结果和直接通过opencv调用GaussianBlur计算的结果是有所不同的。虽然单纯从数值上来讲,基本都只是差1的大小,但是这说明此函数并不是直接和这个卷积核进行卷积的,而是还做了一点修改。那么具体是什么,我暂时没画时间去查源码,如有了解者希望能够告知我,感激不尽。
最后一个小注意点,上面忘记书写了,卷积核大小在小于等于7的情况下结果是和我自己写的那个函数计算不完全一致的。原因在于opencv源码当中小于等于7的高斯核并不是通过计算给出而是直接给出了结果,所以在小数上有一定差距,估计是效率问题才这么做的。
20190115 修正(源自评论“临京雪月”)
opencv源码当中小于等于7的高斯核并不是通过计算给出而是直接给出了结果。这句话不对,当小于等于7且sigma<=0时候才会使用内置的模板,其余情况同样是计算出来的结果