You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复2D高斯拟合天文图像失效问题?技术问询

解决2D高斯拟合因量级差异失败的优化方案

我之前处理天文图像拟合时也踩过这个量级差异的坑——像素值是1e-4量级,而坐标是几百甚至上千,直接代入高斯函数会导致指数部分溢出成几乎为0的数,拟合算法根本找不到有效的优化方向。给你几个针对性的优化步骤,结合你的代码来调整:

1. 对坐标做中心化+归一化,缩小量级差异

你的高斯函数里直接用原始x、y坐标(比如0到几百),而sigma是1e-4量级,代入(x-x0)**2会得到超大的数值,指数部分直接变成负无穷,函数输出几乎为0。解决方法是用相对于亮斑中心的偏移量代替原始坐标:

修改你的gaussian函数和拟合逻辑:

def gaussian(xycoor, x0, y0, sigma_x, sigma_y, amp, offset):
    # 改用相对于中心的偏移量,大幅缩小坐标量级
    x, y = xycoor
    dx = x - x0
    dy = y - y0
    # 拆分x/y方向的sigma,适配非圆对称的天文源
    exponent = -(dx**2/(2*sigma_x**2) + dy**2/(2*sigma_y**2))
    return amp * np.exp(exponent) + offset

2. 修正初始猜测和参数边界的核心错误

你之前的初始sigma用了np.std(image),这是像素值的标准差,完全不是高斯的空间宽度!空间sigma应该是亮斑在x/y方向的半宽,比如先提取亮斑周围的区域来计算:

def fit(image):
    med = np.median(image)
    image = image - med
    # 修正索引顺序:np.unravel_index返回的是(y, x)
    y0_idx, x0_idx = np.unravel_index(np.argmax(image), image.shape)
    # 提取亮斑周围20x20的区域,估计空间sigma
    crop = image[y0_idx-10:y0_idx+10, x0_idx-10:x0_idx+10]
    crop_y, crop_x = np.where(crop > np.max(crop)*0.5)  # 取半最大值以上的像素
    sigma_x_guess = np.std(crop_x) if len(crop_x) > 0 else 2.0
    sigma_y_guess = np.std(crop_y) if len(crop_y) > 0 else 2.0
    
    x = np.arange(0, image.shape[1], 1)
    y = np.arange(0, image.shape[0], 1)
    xx, yy = np.meshgrid(x, y)
    
    # 修正初始猜测:sigma是空间宽度,不是像素值标准差
    guess = [x0_idx, y0_idx, sigma_x_guess, sigma_y_guess, np.max(image), 0]
    # 修正参数边界:sigma不能太小,坐标不能超出图像范围
    low = [0, 0, 0.5, 0.5, 0, -np.max(image)]
    upper = [image.shape[1]-1, image.shape[0]-1, 20, 20, np.max(image)*2, np.max(image)]
    bounds = [low, upper]
    
    params, pcov = curve_fit(gaussian, (xx.ravel(), yy.ravel()), image.ravel(), 
                             p0=guess, bounds=bounds, maxfev=10000)
    return params

3. 改用更鲁棒的拟合库(推荐lmfit)

scipy的curve_fit对量级差异很敏感,而lmfit可以自动处理参数缩放,还能更灵活地约束参数。你已经导入了lmfit.models.GaussianModel,可以用它构建2D高斯模型:

from lmfit import Model

def gaussian2d(x, y, x0, y0, sigma_x, sigma_y, amp, offset):
    dx = x - x0
    dy = y - y0
    exponent = -(dx**2/(2*sigma_x**2) + dy**2/(2*sigma_y**2))
    return amp * np.exp(exponent) + offset

def fit_with_lmfit(image):
    med = np.median(image)
    image = image - med
    y0_idx, x0_idx = np.unravel_index(np.argmax(image), image.shape)
    crop = image[y0_idx-10:y0_idx+10, x0_idx-10:x0_idx+10]
    crop_y, crop_x = np.where(crop > np.max(crop)*0.5)
    sigma_x_guess = np.std(crop_x) if len(crop_x) > 0 else 2.0
    sigma_y_guess = np.std(crop_y) if len(crop_y) > 0 else 2.0
    
    x = np.arange(image.shape[1])
    y = np.arange(image.shape[0])
    xx, yy = np.meshgrid(x, y)
    
    # 创建lmfit模型并设置参数约束
    model = Model(gaussian2d, independent_vars=['x', 'y'])
    params = model.make_params(
        x0=x0_idx, y0=y0_idx,
        sigma_x=sigma_x_guess, sigma_y=sigma_y_guess,
        amp=np.max(image), offset=0,
        sigma_x_min=0.5, sigma_y_min=0.5,
        x0_min=0, x0_max=image.shape[1]-1,
        y0_min=0, y0_max=image.shape[0]-1
    )
    # 执行拟合并输出报告(方便调试)
    result = model.fit(image.ravel(), params, x=xx.ravel(), y=yy.ravel())
    print(result.fit_report())
    return result.params.valuesdict()

然后修改主调用逻辑:

data = fits.getdata('AzTECC100.fits')
med = np.median(data)
data = data - med
data = data[0,0,:,:]
# 改用lmfit拟合
params_dict = fit_with_lmfit(data)
# 提取参数用于绘图
parameters = [params_dict['x0'], params_dict['y0'], params_dict['sigma_x'], params_dict['sigma_y']]
plotting(data, parameters)

4. 额外调试技巧

  • 拟合前确认背景扣除是否到位(你用median扣除的方法没问题),避免噪声干扰。
  • 打印拟合参数和协方差矩阵,检查sigma是否在预期的空间宽度范围内(比如1-10像素)。
  • 可以先绘制初始猜测的高斯函数和图像对比,确认初始形状和真实源接近。

这样修改后,坐标的量级(相对于中心的偏移,几个像素)和sigma的量级(1-10像素)匹配,高斯函数的输出值也会和图像的像素值(1e-4)匹配,拟合算法就能正常收敛了。

内容的提问来源于stack exchange,提问作者Samuel Clyne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:37:10