Python中特定坐标处强度的二维高斯拟合

Python中特定坐标处强度的二维高斯拟合,python,python-2.7,numpy,scipy,Python,Python 2.7,Numpy,Scipy,我有一组坐标(x,y,z(x,y))来描述坐标x,y处的强度(z)。对于不同坐标下的一组强度,我需要拟合一个使均方误差最小化的二维高斯分布。 数据以numpy矩阵表示,对于每个拟合过程,我将有4、9、16或25个坐标。最终我只需要得到最小均方误差的高斯函数(x_0,y_0)的中心位置。 我发现的所有示例都使用scipy.optimize.curve_fit,但它们的输入数据覆盖整个网格,而不是几个坐标。 任何帮助都将不胜感激 导言 有多种方法可以做到这一点。您可以使用非线性方法(例如,scipy

我有一组坐标(x,y,z(x,y))来描述坐标x,y处的强度(z)。对于不同坐标下的一组强度,我需要拟合一个使均方误差最小化的二维高斯分布。 数据以numpy矩阵表示,对于每个拟合过程,我将有4、9、16或25个坐标。最终我只需要得到最小均方误差的高斯函数(x_0,y_0)的中心位置。 我发现的所有示例都使用scipy.optimize.curve_fit,但它们的输入数据覆盖整个网格,而不是几个坐标。 任何帮助都将不胜感激

导言 有多种方法可以做到这一点。您可以使用非线性方法(例如,
scipy.optimize.curve\u fit
),但这些方法速度较慢,而且不能保证收敛。您可以将问题线性化(快速、唯一的解决方案),但分布“尾部”中的任何噪声都会导致问题。实际上,有一些技巧可以应用于此特定情况,以避免后一个问题。我将展示一些示例,但我现在没有时间演示所有的“技巧”

作为旁注,一般的2D guassian有6个参数,所以你不能用4个点完全拟合。但是,听起来您可能假设x和y之间没有协方差,并且每个方向上的方差相同(即,完美的“圆形”钟形曲线)。如果是这样,那么您只需要四个参数。如果你知道瓜西阶的振幅,你只需要三个。然而,我将从一般的解决方案开始,如果你想的话,你可以在以后简化它

目前,让我们集中精力使用非线性方法解决这个问题(例如,
scipy.optimize.curve\u fit


2D guassian的一般公式是(直接来自维基百科):

其中:

基本上是协方差矩阵的0.5,A是振幅, 及(X)₀, Y₀) 是中心吗


生成简化的样本数据 让我们写出上面的方程式:

import numpy as np
import matplotlib.pyplot as plt

def gauss2d(x, y, amp, x0, y0, a, b, c):
    inner = a * (x - x0)**2 
    inner += 2 * b * (x - x0)**2 * (y - y0)**2
    inner += c * (y - y0)**2
    return amp * np.exp(-inner)
然后让我们生成一些示例数据。首先,我们将生成一些易于拟合的数据:

np.random.seed(1977) # For consistency
x, y = np.random.random((2, 10))
x0, y0 = 0.3, 0.7
amp, a, b, c = 1, 2, 3, 4

zobs = gauss2d(x, y, amp, x0, y0, a, b, c)

fig, ax = plt.subplots()
scat = ax.scatter(x, y, c=zobs, s=200)
fig.colorbar(scat)
plt.show()

请注意,我们没有添加任何噪声,并且分布中心在我们有数据的范围内(即,中心在0.3、0.7,x、y观测值的散布在0和1之间)。现在,让我们继续这个,然后我们将看到当添加噪声并移动中心时会发生什么


非线性拟合 首先,让我们使用
scpy.optimize.curve\u fit
对高斯函数进行非线性最小二乘拟合。(另一方面,您可以使用
scpy.optimize
中的一些其他函数来使用精确的最小化算法)

scipy.optimize
函数预期的函数签名与我们最初在上面编写的函数签名略有不同。我们可以编写一个包装器来“翻译”,但我们只需重新编写
gauss2d
函数即可:

def gauss2d(xy, amp, x0, y0, a, b, c):
    x, y = xy
    inner = a * (x - x0)**2
    inner += 2 * b * (x - x0)**2 * (y - y0)**2
    inner += c * (y - y0)**2
    return amp * np.exp(-inner)
我们所做的只是将自变量(x&y)作为单个2xN数组使用

现在我们需要对guassian曲线的实际参数进行初步猜测。这是可选的(如果我没记错的话,默认值是所有的),但是如果1,1不是特别接近“真”的话,你可能会遇到收敛问题高斯曲线的中心。因此,我们将使用观察到的最大z值的x和y值作为中心的起点。我将其余参数保留为1,但如果您知道它们可能会持续显著不同,请将它们更改为更合理的值

以下是完整的独立示例:

import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt

def main():
    x0, y0 = 0.3, 0.7
    amp, a, b, c = 1, 2, 3, 4
    true_params = [amp, x0, y0, a, b, c]
    xy, zobs = generate_example_data(10, true_params)
    x, y = xy

    i = zobs.argmax()
    guess = [1, x[i], y[i], 1, 1, 1]
    pred_params, uncert_cov = opt.curve_fit(gauss2d, xy, zobs, p0=guess)

    zpred = gauss2d(xy, *pred_params)
    print 'True parameters: ', true_params
    print 'Predicted params:', pred_params
    print 'Residual, RMS(obs - pred):', np.sqrt(np.mean((zobs - zpred)**2))

    plot(xy, zobs, pred_params)
    plt.show()

def gauss2d(xy, amp, x0, y0, a, b, c):
    x, y = xy
    inner = a * (x - x0)**2
    inner += 2 * b * (x - x0)**2 * (y - y0)**2
    inner += c * (y - y0)**2
    return amp * np.exp(-inner)

def generate_example_data(num, params):
    np.random.seed(1977) # For consistency
    xy = np.random.random((2, num))

    zobs = gauss2d(xy, *params)
    return xy, zobs

def plot(xy, zobs, pred_params):
    x, y = xy
    yi, xi = np.mgrid[:1:30j, -.2:1.2:30j]
    xyi = np.vstack([xi.ravel(), yi.ravel()])

    zpred = gauss2d(xyi, *pred_params)
    zpred.shape = xi.shape

    fig, ax = plt.subplots()
    ax.scatter(x, y, c=zobs, s=200, vmin=zpred.min(), vmax=zpred.max())
    im = ax.imshow(zpred, extent=[xi.min(), xi.max(), yi.max(), yi.min()],
                   aspect='auto')
    fig.colorbar(im)
    ax.invert_yaxis()
    return fig

main()

在这种情况下,我们精确地(ish)恢复原始的“true”参数

True parameters:  [1, 0.3, 0.7, 2, 3, 4]
Predicted params: [ 1.   0.3  0.7  2.   3.   4. ]
Residual, RMS(obs - pred): 1.01560615193e-16
我们马上就会看到,情况并非总是如此


增加噪音 让我们给我们的观察添加一些噪声。我在这里所做的就是更改
生成示例数据
函数:

def generate_example_data(num, params):
    np.random.seed(1977) # For consistency
    xy = np.random.random((2, num))

    noise = np.random.normal(0, 0.3, num)
    zobs = gauss2d(xy, *params) + noise
    return xy, zobs
然而,结果却大不相同:

就参数而言:

True parameters:  [1, 0.3, 0.7, 2, 3, 4]
Predicted params: [  1.129    0.263   0.750   1.280   32.333   10.103  ]
Residual, RMS(obs - pred): 0.152444640098
预测中心变化不大,但
b
c
参数变化较大

如果我们将函数中心更改为稍微超出点分散范围的某个位置:

x0, y0 = -0.3, 1.1
在有噪音的情况下,我们最终会变得毫无意义!(它在没有噪音的情况下仍能正常工作。)


在拟合衰减为零的函数时,这是一个常见问题。“尾部”中的任何噪声都可能导致非常糟糕的结果。有许多策略可以解决这一问题。最简单的方法之一是根据观察到的z值对反演进行加权。以下是1D情况的示例:(重点关注线性化问题)如果我稍后有时间,我将为2D案例添加一个这样的示例。

你的出发点是什么。你能展示一些代码来讨论吗?感谢Joe的介绍。这非常有帮助,尽管函数运行时会引发以下错误:RuntimeError:找不到最佳参数:函数调用数已达到maxfev=800。这是否是m我对高斯函数进行过约束了吗?我取了没有2*b*(x-x0)**2*(y-y0)的高斯函数**2并预先指定了方差。我的拟合只适用于振幅,以及x0和y0。@blah1234-这意味着最小化无法收敛。你是在加入噪声时得到了该误差,还是在没有噪声的情况下得到了该误差?另外,如果你删除了其他参数而不更改函数等,您可能会得到该错误,因为它试图最小化不影响解决方案的参数。问题发生在我的脚本中,您的脚本非常好!def gauss2d(xy,amp,x0,y0):x,y=xy varx,vary=5,6 inner=(
True parameters:  [1, -0.3, 1.1, 2, 3, 4]
Predicted params: [  0.546  -0.939   0.857  -0.488  44.069  -4.136]
Residual, RMS(obs - pred): 0.235664449826