Scipy leastsq() 函数的开销

4 投票
1 回答
551 浏览
提问于 2025-04-17 13:50

我正在开发一个图像分析程序,发现程序的瓶颈在于多次尝试将一个2D高斯函数拟合到一个小窗口(20x20像素)上。执行时间的90%都花在这段代码上。

我使用的是scipy食谱中提供的代码来解决这个问题:

   def gaussian(height, center_x, center_y, width_x, width_y):
           """Returns a gaussian function with the given parameters"""
        width_x = float(width_x)
        width_y = float(width_y)
        return lambda x,y: height*exp(
                    -(((center_x-x)/width_x)**2+((center_y-y)/width_y)**2)/2)


def moments(data):
    """Returns (height, x, y, width_x, width_y)
    the gaussian parameters of a 2D distribution by calculating its
    moments """
    total = data.sum()
    X, Y = indices(data.shape)
    x = (X*data).sum()/total
    y = (Y*data).sum()/total
    col = data[:, int(y)]
    width_x = sqrt(abs((arange(col.size)-y)**2*col).sum()/col.sum())
    row = data[int(x), :]
    width_y = sqrt(abs((arange(row.size)-x)**2*row).sum()/row.sum())
    height = data.max()
    return height, x, y, width_x, width_y


def fitgaussian(data):
    """Returns (height, x, y, width_x, width_y)
    the gaussian parameters of a 2D distribution found by a fit"""
    params = moments(data)
    errorfunction = lambda p: ravel(gaussian(*p)(*indices(data.shape)) -
                                 data)
    p, success = optimize.leastsq(errorfunction, params, maxfev=50, ftol=1.49012e-05)
    return p

通过将errorfunction()和gaussian()这两个函数合并,我成功将执行时间缩短了一半。这样每次leastsq()调用errorfunction()时,只需要调用一个函数,而不是两个。

这让我觉得剩下的执行时间大部分是因为函数调用的开销,因为leastsq()算法在调用errorfunction()时会产生额外的时间消耗。

有没有办法减少这种函数调用的开销呢?我对此感到困惑,因为leastsq()需要一个函数作为输入。

如果我的描述让人困惑,我先在这里道个歉。我是机械工程师,现在正在学习Python。如果还有其他信息能帮助到你,请告诉我。

1 个回答

1

因为指数函数是单调的,你可以用高斯函数的对数作为你的误差函数,比如:

def log_gaussian(height, center_x, center_y, width_x, width_y):
    """Returns a gaussian function with the given parameters"""
    width_x = float(width_x)
    width_y = float(width_y)
    log_height = log(height)
    return lambda x,y: (log_height - 
             (((center_x-x)/width_x)**2 - ((center_y-y)/width_y)**2)/2)

这样做的话,每次迭代只需要调用一次对数函数,而不是每处理一行数据就调用一次指数函数。

撰写回答