[英]How to calculate a Gaussian kernel matrix efficiently in 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))
This is my current way.这是我目前的方式。 Is there any way I can use matrix operation to do this?有什么办法可以使用矩阵运算来做到这一点? X is the data points. X 是数据点。
I myself used the accepted answer for my image processing, but I find it (and the other answers) too dependent on other modules.我自己将接受的答案用于我的图像处理,但我发现它(和其他答案)过于依赖其他模块。 Therefore, here is my compact solution:因此,这是我的紧凑解决方案:
import numpy as np
def gkern(l=5, sig=1.):
"""\
creates gaussian kernel with side length `l` and a sigma of `sig`
"""
ax = np.linspace(-(l - 1) / 2., (l - 1) / 2., l)
gauss = np.exp(-0.5 * np.square(ax) / np.square(sig))
kernel = np.outer(gauss, gauss)
return kernel / np.sum(kernel)
Edit: Changed arange to linspace to handle even side lengths编辑:将 arange 更改为 linspace 以处理均匀的边长
Edit: Use separability for faster computation, thank you Yves Daoust .编辑:使用可分离性进行更快的计算,谢谢Yves Daoust 。
Do you want to use the Gaussian kernel for eg image smoothing?你想使用高斯核来进行图像平滑吗? If so, there's a function gaussian_filter()
in scipy:如果是这样,scipy 中有一个函数gaussian_filter()
:
Updated answer更新答案
This should work - while it's still not 100% accurate, it attempts to account for the probability mass within each cell of the grid.这应该有效 - 虽然它仍然不是 100% 准确,但它试图解释网格每个单元格内的概率质量。 I think that using the probability density at the midpoint of each cell is slightly less accurate, especially for small kernels.我认为在每个单元格的中点使用概率密度稍微不太准确,尤其是对于小内核。 See https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm for an example.有关示例,请参见https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm 。
import numpy as np
import scipy.stats as st
def gkern(kernlen=21, nsig=3):
"""Returns a 2D Gaussian kernel."""
x = np.linspace(-nsig, nsig, kernlen+1)
kern1d = np.diff(st.norm.cdf(x))
kern2d = np.outer(kern1d, kern1d)
return kern2d/kern2d.sum()
Testing it on the example in Figure 3 from the link:通过链接在图 3 中的示例上对其进行测试:
gkern(5, 2.5)*273
gives给
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 ]])
The original (accepted) answer below accepted is wrong The square root is unnecessary, and the definition of the interval is incorrect.接受下面的原始(接受)答案是错误的 平方根是不必要的,并且区间的定义是错误的。
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
I'm trying to improve on FuzzyDuck's answer here.我正在尝试改进FuzzyDuck 的回答。 I think this approach is shorter and easier to understand.我认为这种方法更短也更容易理解。 Here I'm using signal.scipy.gaussian
to get the 2D gaussian kernel.在这里,我使用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
Plotting it using matplotlib.pyplot
:使用matplotlib.pyplot
绘制它:
import matplotlib.pyplot as plt
plt.imshow(gkern(21), interpolation='none')
You may simply gaussian-filter a simple 2D dirac function , the result is then the filter function that was being used:您可以简单地高斯过滤一个简单的2D dirac 函数,结果就是正在使用的过滤器函数:
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)
A 2D gaussian kernel matrix can be computed with numpy broadcasting,可以使用 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()
For small kernel sizes this should be reasonably fast.对于较小的内核大小,这应该相当快。
Note: this makes changing the sigma parameter easier with respect to the accepted answer.注意:这使得相对于接受的答案更容易更改 sigma 参数。
I tried using numpy only.我尝试只使用 numpy。 Here is the code这是代码
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)
You can visualise the result using:您可以使用以下方法可视化结果:
plt.imshow(get_gauss_kernel(5,1))
If you are a computer vision engineer and you need heatmap for a particular point as Gaussian distribution(especially for keypoint detection on image)如果您是计算机视觉工程师,并且需要将特定点的热图作为高斯分布(尤其是图像上的关键点检测)
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
The usage and output使用和输出
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)最大在 : (2, 2)
kernel shape (10, 10)内核形状 (10, 10)
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)最大 : (40, 25)
kernel shape (50, 100)内核形状 (50, 100)
linalg.norm
takes an axis
parameter. linalg.norm
采用axis
参数。 With a little experimentation I found I could calculate the norm for all combinations of rows with通过一些实验,我发现我可以计算所有行组合的范数
np.linalg.norm(x[None,:,:]-x[:,None,:],axis=2)
It expands x
into a 3d array of all differences, and takes the norm on the last dimension.它将x
扩展为所有差异的 3d 数组,并在最后一个维度上取范数。
So I can apply this to your code by adding the axis
parameter to your Gaussian
:因此,我可以通过将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)
produces产生
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]])
Matching:匹配:
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]])
Building up on Teddy Hartanto's answer.以 Teddy Hartanto 的回答为基础。 You can just calculate your own one dimensional Gaussian functions and then use np.outer
to calculate the two dimensional one.您可以计算自己的一维高斯函数,然后使用np.outer
计算二维一维。 Very fast and efficient way.非常快速有效的方式。
With the code below you can also use different Sigmas for every dimension使用下面的代码,您还可以对每个维度使用不同的西格玛
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
Adapting th accepted answer by FuzzyDuck to match the results of this website: http://dev.theomader.com/gaussian-kernel-calculator/ I now present you with this definition:调整 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))
output:输出:
[[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 ]]
A good way to do that is to use the gaussian_filter function to recover the kernel.一个很好的方法是使用 gaussian_filter 函数来恢复内核。 For instance:例如:
indicatrice = np.zeros((5,5))
indicatrice[2,2] = 1
gaussian_kernel = gaussian_filter(indicatrice, sigma=1)
gaussian_kernel/=gaussian_kernel[2,2]
This gives这给
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]]
As I didn't find what I was looking for, I coded my own one-liner.由于没有找到我要找的东西,我编写了自己的单行代码。 You can modify it accordingly (according to the dimensions and the standard deviation).您可以相应地修改它(根据尺寸和标准偏差)。
Here is the one-liner function for a 3x5 patch for example.例如,这是 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))
You get an output like this:你会得到这样的输出:
[[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 ]]
You can read more about scipy's Gaussian here .您可以在此处阅读有关 scipy 高斯的更多信息。
Yet another implementation.又一个实现。
This is normalized so that for sigma > 1
and sufficiently large win_size
, the total sum of the kernel elements equals 1
.这是标准化的,以便对于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)
To generate a 5x5 kernel:要生成 5x5 内核:
gaussian_kernel(win_size=5, sigma=1)
I took a similar approach to Nils Werner's answer -- since convolution of any kernel with a Kronecker delta results in the kernel itself centered around that Kronecker delta -- but I made it slightly more general to deal with both odd and even dimensions.我对Nils Werner 的回答采取了类似的方法——因为任何 kernel 与 Kronecker delta 的卷积都会导致 kernel 本身以 Kronecker delta 为中心——但我让它稍微更通用一些来处理奇数和偶数维度。 In three lines:三行:
import scipy.ndimage as scim
def gaussian_kernel(dimension: int, sigma: float):
dirac = np.zeros((dimension,dimension))
dirac[(dimension-1)//2:dimension//2+1, (dimension-1)//2:dimension//2+1] = 1.0 / (1 + 3 * ((dimension + 1) % 2))
return scim.gaussian_filter(dirac, sigma=sigma)
The second line creates either a single 1.0
in the middle of the matrix (if the dimension is odd), or a square of four 0.25
elements (if the dimension is even).第二行在矩阵中间创建一个1.0
(如果维数为奇数),或者创建一个由四个0.25
元素组成的正方形(如果维数为偶数)。 The division could be moved to the third line too;该师也可以调到三线; the result is normalised either way.结果以任何一种方式归一化。
For those who like to have the kernel the matrix with one (odd) or four (even) 1.0
element(s) in the middle instead of normalisation, this works:对于那些喜欢 kernel 矩阵的人来说,中间有一个(奇数)或四个(偶数) 1.0
元素而不是标准化,这有效:
import scipy.ndimage as scim
def gaussian_kernel(dimension: int, sigma: float, ones_in_the_middle=False):
dirac = np.zeros((dimension,dimension))
dirac[(dimension-1)//2:dimension//2+1, (dimension-1)//2:dimension//2+1] = 1.0
kernel = scim.gaussian_filter(dirac, sigma=sigma)
divisor = kernel[(dimension-1)//2, (dimension-1)//2] if ones_in_the_middle else 1 + 3 * ((dimension + 1) % 2)
return kernel/divisor
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.