优化星形轮廓(高斯)拟合算法

时间:2014-12-02 17:11:36

标签: python scipy

我需要将数千个2D高斯函数拟合到CCD图像上的星形轮廓(14x14像素块)并获得质心坐标,长轴和短轴上的FWHM以及长轴的旋转角度。问题是我当前的代码执行时间太长。在i7处理器上几秒钟,我需要它来加快速度。优选尽可能快。我测试了几个高斯拟合函数,看起来AsPyLib中使用的函数是最快的http://www.aspylib.com/doc/aspylib_fitting.html 以下是我试图让自己跑得更快的代码。分析显示大部分时间都花在了mplfit函数内。所以我的问题是,如果这可以加速?我尝试了对代码进行cython化,但它提供了很小的提升。计算时刻(快10倍)并不适合我所拥有的许多图像,因为噪声问题导致估算不可靠。并且可能是由于星形轮廓由于像差而通常远非高斯。 多处理不是一个解决方案,因为新的流程创建开销太高,无法拟合一个星形配置文件。 有什么想法可以进一步了解吗?

import numpy as np
from scipy.optimize import leastsq

def fit_gauss_elliptical(xy, data):
    """
    ---------------------
    Purpose
    Fitting a star with a 2D elliptical gaussian PSF.
    ---------------------
    Inputs
    * xy (list) = list with the form [x,y] where x and y are the integer positions in the complete image of the first pixel (the one with x=0 and y=0) of the small subimage that is used for fitting.
    * data (2D Numpy array) = small subimage, obtained from the full FITS image by slicing. It must contain a single object : the star to be fitted, placed approximately at the center.
    ---------------------
    Output (list) = list with 8 elements, in the form [maxi, floor, height, mean_x, mean_y, fwhm_small, fwhm_large, angle]. The list elements are respectively:
    - maxi is the value of the star maximum signal,
    - floor is the level of the sky background (fit result),
    - height is the PSF amplitude (fit result),
    - mean_x and mean_y are the star centroid x and y positions, on the full image (fit results), 
    - fwhm_small is the smallest full width half maximum of the elliptical gaussian PSF (fit result) in pixels
    - fwhm_large is the largest full width half maximum of the elliptical gaussian PSF (fit result) in pixels
    - angle is the angular direction of the largest fwhm, measured clockwise starting from the vertical direction (fit result) and expressed in degrees. The direction of the smallest fwhm is obtained by adding 90 deg to angle.
    ---------------------
    """

    #find starting values
    dat=data.flatten()  
    maxi = data.max()
    floor = np.ma.median(dat)
    height = maxi - floor
    if height==0.0:             #if star is saturated it could be that median value is 32767 or 65535 --> height=0
        floor = np.mean(dat)
        height = maxi - floor

    mean_x = (np.shape(data)[0]-1)/2
    mean_y = (np.shape(data)[1]-1)/2

    fwhm = np.sqrt(np.sum((data>floor+height/2.).flatten()))
    fwhm_1 = fwhm
    fwhm_2 = fwhm
    sig_1 = fwhm_1 / (2.*np.sqrt(2.*np.log(2.)))
    sig_2 = fwhm_2 / (2.*np.sqrt(2.*np.log(2.)))    

    angle = 0.

    p0 = floor, height, mean_x, mean_y, sig_1, sig_2, angle

    #---------------------------------------------------------------------------------
    #fitting gaussian
    def gauss(floor, height, mean_x, mean_y, sig_1, sig_2, angle):

        A = (np.cos(angle)/sig_1)**2. + (np.sin(angle)/sig_2)**2.
        B = (np.sin(angle)/sig_1)**2. + (np.cos(angle)/sig_2)**2.
        C = 2.0*np.sin(angle)*np.cos(angle)*(1./(sig_1**2.)-1./(sig_2**2.))

        #do not forget factor 0.5 in exp(-0.5*r**2./sig**2.)    
        return lambda x,y: floor + height*np.exp(-0.5*(A*((x-mean_x)**2)+B*((y-mean_y)**2)+C*(x-mean_x)*(y-mean_y)))

    def err(p,data):
        return np.ravel(gauss(*p)(*np.indices(data.shape))-data)

    p = leastsq(err, p0, args=(data), maxfev=200)
    p = p[0]

    #---------------------------------------------------------------------------------
    #formatting results
    floor = p[0]
    height = p[1]
    mean_x = p[2] + xy[0]
    mean_y = p[3] + xy[1]

    #angle gives the direction of the p[4]=sig_1 axis, starting from x (vertical) axis, clockwise in direction of y (horizontal) axis
    if np.abs(p[4])>np.abs(p[5]):

        fwhm_large = np.abs(p[4]) * (2.*np.sqrt(2.*np.log(2.)))
        fwhm_small = np.abs(p[5]) * (2.*np.sqrt(2.*np.log(2.))) 
        angle = np.arctan(np.tan(p[6]))

    else:   #then sig_1 is the smallest : we want angle to point to sig_y, the largest

        fwhm_large = np.abs(p[5]) * (2.*np.sqrt(2.*np.log(2.)))
        fwhm_small = np.abs(p[4]) * (2.*np.sqrt(2.*np.log(2.))) 
        angle = np.arctan(np.tan(p[6]+np.pi/2.))

    output = [maxi, floor, height, mean_x, mean_y, fwhm_small, fwhm_large, angle]
    return output

1 个答案:

答案 0 :(得分:0)

我之前遇到过这种方法,但没有实现它:

http://www.nature.com/nmeth/journal/v9/n7/full/nmeth.2071.html

作为一种捷径,可能值得一看。甚至可以在他的网站http://physics-server.uoregon.edu/~raghu/particle_tracking.html

上复制代码