如何在numpy中有效地计算高斯核矩阵?

时间:2015-04-19 15:15:45

标签: python numpy

def GaussianMatrix(X,sigma):
    row,col=X.shape
    GassMatrix=np.zeros(shape=(row,row))
    X=np.asarray(X)
    i=0
    for v_i in X:
        j=0
        for v_j in X:
            GassMatrix[i,j]=Gaussian(v_i.T,v_j.T,sigma)
            j+=1
        i+=1
    return GassMatrix
def Gaussian(x,z,sigma):
    return np.exp((-(np.linalg.norm(x-z)**2))/(2*sigma**2))

这是我目前的方式。有什么方法可以使用矩阵运算来做到这一点吗? X是数据点。

13 个答案:

答案 0 :(得分:22)

你想使用高斯内核吗?图像平滑?如果是这样,scipy中有一个函数gaussian_filter()

更新回答

这应该有效 - 虽然它仍然不是100%准确,但它试图考虑网格中每个单元格内的概率质量。我认为使用每个单元中点的概率密度稍微不准确,特别是对于小内核。有关示例,请参阅https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm

def gkern(kernlen=21, nsig=3):
    """Returns a 2D Gaussian kernel."""

    lim = kernlen//2 + (kernlen % 2)/2
    x = np.linspace(-lim, lim, kernlen+1)
    kern1d = np.diff(st.norm.cdf(x))
    kern2d = np.outer(kern1d, kern1d)
    return kern2d/kern2d.sum()

从链接中对图3中的示例进行测试:

gkern(5,1)*273

给出

array([[ 1.0278445 ,  4.10018648,  6.49510362,  4.10018648,  1.0278445 ],
       [ 4.10018648, 16.35610171, 25.90969361, 16.35610171,  4.10018648],
       [ 6.49510362, 25.90969361, 41.0435344 , 25.90969361,  6.49510362],
       [ 4.10018648, 16.35610171, 25.90969361, 16.35610171,  4.10018648],
       [ 1.0278445 ,  4.10018648,  6.49510362,  4.10018648,  1.0278445 ]])

以下接受的原始(已接受)答案错误 平方根是不必要的,并且间隔的定义是不正确的。

import numpy as np
import scipy.stats as st

def gkern(kernlen=21, nsig=3):
    """Returns a 2D Gaussian kernel array."""

    interval = (2*nsig+1.)/(kernlen)
    x = np.linspace(-nsig-interval/2., nsig+interval/2., kernlen+1)
    kern1d = np.diff(st.norm.cdf(x))
    kernel_raw = np.sqrt(np.outer(kern1d, kern1d))
    kernel = kernel_raw/kernel_raw.sum()
    return kernel

答案 1 :(得分:16)

您可以简单地对一个简单的2D dirac function进行高斯过滤,结果就是正在使用的过滤函数:

import numpy as np
import scipy.ndimage.filters as fi

def gkern2(kernlen=21, nsig=3):
    """Returns a 2D Gaussian kernel array."""

    # create nxn zeros
    inp = np.zeros((kernlen, kernlen))
    # set element at the middle to one, a dirac delta
    inp[kernlen//2, kernlen//2] = 1
    # gaussian-smooth the dirac, resulting in a gaussian filter mask
    return fi.gaussian_filter(inp, nsig)

答案 2 :(得分:13)

我自己使用了已接受的答案进行图像处理,但我发现它(以及其他答案)过于依赖其他模块。此外,接受的答案有时会产生最终具有大量零条目的内核。

因此,这是我的紧凑型解决方案:

import numpy as np


def gkern(l=5, sig=1.):
    """
    creates gaussian kernel with side length l and a sigma of sig
    """

    ax = np.arange(-l // 2 + 1., l // 2 + 1.)
    xx, yy = np.meshgrid(ax, ax)

    kernel = np.exp(-0.5 * (np.square(xx) + np.square(yy)) / np.square(sig))

    return kernel / np.sum(kernel)

答案 3 :(得分:8)

我想在这里改进FuzzyDuck's answer。我认为这种方法更短,更容易理解。在这里,我使用signal.scipy.gaussian来获取2D高斯内核。

import numpy as np
from scipy import signal

def gkern(kernlen=21, std=3):
    """Returns a 2D Gaussian kernel array."""
    gkern1d = signal.gaussian(kernlen, std=std).reshape(kernlen, 1)
    gkern2d = np.outer(gkern1d, gkern1d)
    return gkern2d

使用matplotlib.pyplot绘图:

import matplotlib.pyplot as plt
plt.imshow(gkern(21), interpolation='none')

Gaussian kernel plotted using matplotlib

答案 4 :(得分:4)

可以使用numpy广播计算2D高斯核矩阵,

def gaussian_kernel(size=21, sigma=3):
    """Returns a 2D Gaussian kernel.
    Parameters
    ----------
    size : float, the kernel size (will be square)

    sigma : float, the sigma Gaussian parameter

    Returns
    -------
    out : array, shape = (size, size)
      an array with the centered gaussian kernel
    """
    x = np.linspace(- (size // 2), size // 2)
    x /= np.sqrt(2)*sigma
    x2 = x**2
    kernel = np.exp(- x2[:, None] - x2[None, :])
    return kernel / kernel.sum()

对于小内核大小,这应该相当快。

注意:这使得更容易根据接受的答案更改sigma参数。

答案 5 :(得分:3)

linalg.norm需要axis个参数。通过一些实验,我发现我可以用

计算所有行组合的范数
np.linalg.norm(x[None,:,:]-x[:,None,:],axis=2)

它将x扩展为包含所有差异的3d数组,并在最后一维上采用标准。

因此,我可以将axis参数添加到您的Gaussian中,将其应用于您的代码:

def Gaussian(x,z,sigma,axis=None):
    return np.exp((-(np.linalg.norm(x-z, axis=axis)**2))/(2*sigma**2))

x=np.arange(12).reshape(3,4)
GaussianMatrix(x,1)

产生

array([[  1.00000000e+00,   1.26641655e-14,   2.57220937e-56],
       [  1.26641655e-14,   1.00000000e+00,   1.26641655e-14],
       [  2.57220937e-56,   1.26641655e-14,   1.00000000e+00]])

匹配

Gaussian(x[None,:,:],x[:,None,:],1,axis=2)

array([[  1.00000000e+00,   1.26641655e-14,   2.57220937e-56],
       [  1.26641655e-14,   1.00000000e+00,   1.26641655e-14],
       [  2.57220937e-56,   1.26641655e-14,   1.00000000e+00]])

答案 6 :(得分:2)

建立Teddy Hartanto的答案。您可以计算自己的一维高斯函数,然后使用np.outer计算二维函数。非常快速有效的方式。

使用下面的代码,您还可以为每个维度使用不同的Sigmas

import numpy as np
def generate_gaussian_mask(shape, sigma, sigma_y=None):
    if sigma_y==None:
        sigma_y=sigma
    rows, cols = shape

    def get_gaussian_fct(size, sigma):
        fct_gaus_x = np.linspace(0,size,size)
        fct_gaus_x = fct_gaus_x-size/2
        fct_gaus_x = fct_gaus_x**2
        fct_gaus_x = fct_gaus_x/(2*sigma**2)
        fct_gaus_x = np.exp(-fct_gaus_x)
        return fct_gaus_x

    mask = np.outer(get_gaussian_fct(rows,sigma), get_gaussian_fct(cols,sigma_y))
    return mask

答案 7 :(得分:2)

我尝试仅使用numpy。这是代码

def get_gauss_kernel(size=3,sigma=1):
    center=(int)(size/2)
    kernel=np.zeros((size,size))
    for i in range(size):
       for j in range(size):
          diff=np.sqrt((i-center)**2+(j-center)**2)
          kernel[i,j]=np.exp(-(diff**2)/(2*sigma**2))
    return kernel/np.sum(kernel)

您可以使用以下方式显示结果:

plt.imshow(get_gauss_kernel(5,1))

Here is the output

答案 8 :(得分:0)

如果您是计算机视觉工程师,并且需要特定点的热图为高斯分布(尤其是图像上的关键点检测)

def gaussian_heatmap(center = (2, 2), image_size = (10, 10), sig = 1):
    """
    It produces single gaussian at expected center
    :param center:  the mean position (X, Y) - where high value expected
    :param image_size: The total image size (width, height)
    :param sig: The sigma value
    :return:
    """
    x_axis = np.linspace(0, image_size[0]-1, image_size[0]) - center[0]
    y_axis = np.linspace(0, image_size[1]-1, image_size[1]) - center[1]
    xx, yy = np.meshgrid(x_axis, y_axis)
    kernel = np.exp(-0.5 * (np.square(xx) + np.square(yy)) / np.square(sig))
    return kernel

用法和输出

kernel = gaussian_heatmap(center = (2, 2), image_size = (10, 10), sig = 1)
plt.imshow(kernel)
print("max at :", np.unravel_index(kernel.argmax(), kernel.shape))
print("kernel shape", kernel.shape)

max at:(2,2)

内核形状(10,10)

Gaussian distribution at mean (2,2) and sigma 1.0

kernel = gaussian_heatmap(center = (25, 40), image_size = (100, 50), sig = 5)
plt.imshow(kernel)
print("max at :", np.unravel_index(kernel.argmax(), kernel.shape))
print("kernel shape", kernel.shape)

max at:(40,25)

内核形状(50,100)

Gaussian distribution mean at

答案 9 :(得分:0)

由于我没有找到想要的东西,所以我编写了自己的单行代码。您可以进行相应的修改(根据尺寸和标准偏差)。

例如,这是3x5补丁的单线功能。

from scipy import signal

def gaussian2D(patchHeight, patchWidth, stdHeight=1, stdWidth=1):
    gaussianWindow = signal.gaussian(patchHeight, stdHeight).reshape(-1, 1)@signal.gaussian(patchWidth, stdWidth).reshape(1, -1)
    return gaussianWindow

print(gaussian2D(3, 5))

您将获得如下输出:

[[0.082085   0.36787944 0.60653066 0.36787944 0.082085  ]
[0.13533528  0.60653066 1.         0.60653066 0.13533528]
[0.082085   0.36787944 0.60653066 0.36787944 0.082085  ]]

您可以了解有关scipy的高斯here的更多信息。

答案 10 :(得分:0)

使FuzzyDuck接受的答案适应该网站的结果:http://dev.theomader.com/gaussian-kernel-calculator/现在,我向您介绍这个定义:

import numpy as np
import scipy.stats as st

def gkern(kernlen=21, sig=3):
    """Returns a 2D Gaussian kernel."""

    x = np.linspace(-(kernlen/2)/sig, (kernlen/2)/sig, kernlen+1)
    kern1d = np.diff(st.norm.cdf(x))
    kern2d = np.outer(kern1d, kern1d)
    return kern2d/kern2d.sum()

print(gkern(kernlen=5, sig=1))

输出:

[[0.003765   0.015019   0.02379159 0.015019   0.003765  ]
 [0.015019   0.05991246 0.0949073  0.05991246 0.015019  ]
 [0.02379159 0.0949073  0.15034262 0.0949073  0.02379159]
 [0.015019   0.05991246 0.0949073  0.05991246 0.015019  ]
 [0.003765   0.015019   0.02379159 0.015019   0.003765  ]]

答案 11 :(得分:0)

一个好的方法是使用gaussian_filter函数恢复内核。 例如:

indicatrice = np.zeros((5,5))
indicatrice[2,2] = 1
gaussian_kernel = gaussian_filter(indicatrice, sigma=1)
gaussian_kernel/=gaussian_kernel[2,2]

这给

array[[0.02144593, 0.08887207, 0.14644428, 0.08887207, 0.02144593],
       [0.08887207, 0.36828649, 0.60686612, 0.36828649, 0.08887207],
       [0.14644428, 0.60686612, 1.        , 0.60686612, 0.14644428],
       [0.08887207, 0.36828649, 0.60686612, 0.36828649, 0.08887207],
       [0.02144593, 0.08887207, 0.14644428, 0.08887207, 0.02144593]]

答案 12 :(得分:0)

又一个实现。

这是标准化的,因此对于 sigma > 1 和足够大的 win_size,内核元素的总和等于 1

def gaussian_kernel(win_size, sigma):
    t = np.arange(win_size)
    x, y = np.meshgrid(t, t)
    o = (win_size - 1) / 2
    r = np.sqrt((x - o)**2 + (y - o)**2)
    scale = 1 / (sigma**2 * 2 * np.pi)
    return scale * np.exp(-0.5 * (r / sigma)**2)

要生成 5x5 内核:

gaussian_kernel(win_size=5, sigma=1)