Пример #1
0
def fit_im(im, psf, weight=None, dq=None, psfderiv=True,
           nskyx=0, nskyy=0, refit_psf=False, fixedstars=None,
           verbose=False, miniter=4, maxiter=10, blist=None):
    if fixedstars is not None and len(fixedstars['x']) > 0:
        fixedpsflist = {'psfob': fixedstars['psfob'], 'ind': fixedstars['psf']}
        fixedmodel = build_model(fixedstars['x'], fixedstars['y'],
                                 fixedstars['flux'], im.shape[0], im.shape[1],
                                 psflist=fixedpsflist,
                                 offset=fixedstars['offset'])
    else:
        fixedmodel = numpy.zeros_like(im)

    if isinstance(weight, int):
        weight = numpy.ones_like(im)*weight

    im = im
    model = numpy.zeros_like(im)+fixedmodel
    xa = numpy.zeros(0, dtype='f4')
    ya = xa.copy()
    lsky = numpy.median(im[weight > 0])
    hsky = numpy.median(im[weight > 0])
    msky = 0
    passno = numpy.zeros(0, dtype='i4')
    guessflux, guesssky = None, None
    titer = -1
    lastiter = -1

    roughfwhm = psfmod.neff_fwhm(psf(im.shape[0]//2, im.shape[1]//2))
    roughfwhm = numpy.max([roughfwhm, 3.])

    while True:
        titer += 1
        hsky = sky_im(im-model, weight=weight, npix=20)
        lsky = sky_im(im-model, weight=weight, npix=10*roughfwhm)
        if titer != lastiter:
            # in first passes, do not split sources!
            blendthresh = 2 if titer < 2 else 0.2
            xn, yn = peakfind(im-model-hsky,
                              model-msky, weight, dq, psf,
                              keepsat=(titer == 0),
                              blendthreshhold=blendthresh)
            if len(xa) > 0 and len(xn) > 0:
                keep = neighbor_dist(xn, yn, xa, ya) > 1.5
                xn, yn = (c[keep] for c in (xn, yn))
            if (titer == 0) and (blist is not None):
                xnb, ynb = add_bright_stars(xn, yn, blist, im)
                xn = numpy.concatenate([xn, xnb]).astype('f4')
                yn = numpy.concatenate([yn, ynb]).astype('f4')
            xa, ya = (numpy.concatenate([xa, xn]).astype('f4'),
                      numpy.concatenate([ya, yn]).astype('f4'))
            passno = numpy.concatenate([passno, numpy.zeros(len(xn))+titer])
            if verbose:
                print('Iteration %d, found %d sources.' % (titer+1, len(xn)))
        else:
            xn, yn = numpy.zeros(0, dtype='f4'), numpy.zeros(0, dtype='f4')
        if titer != lastiter:
            if (titer == maxiter-1) or (
                    (titer >= miniter-1) and (len(xn) < 100)) or (
                    len(xa) > 40000):
                lastiter = titer + 1
        sz = get_sizes(xa, ya, im-hsky, weight=weight, blist=blist)
        if guessflux is not None:
            guess = numpy.concatenate([guessflux, numpy.zeros_like(xn),
                                       guesssky])
        else:
            guess = None
        sky = hsky if titer >= 2 else lsky
        # in final iteration, no longer allow shifting locations; just fit
        # centroids.
        tpsfderiv = psfderiv if lastiter != titer else False
        psfs = build_psf_list(xa, ya, psf, sz, psfderiv=tpsfderiv)
        flux, model, msky = fit_once(im-sky, xa, ya, psfs, psfderiv=tpsfderiv,
                                     weight=weight, guess=guess,
                                     nskyx=nskyx, nskyy=nskyy)

        model += fixedmodel
        centroids = compute_centroids(xa, ya, psfs, flux[0], im-(sky+msky),
                                      im-model-sky,
                                      weight)
        xcen, ycen, stamps = centroids
        if titer == lastiter:
            tflux, tskypar = unpack_fitpar(flux[0], len(xa), False)
            stats = compute_stats(xa-numpy.round(xa), ya-numpy.round(ya),
                                  stamps[0], stamps[2],
                                  stamps[3], stamps[1],
                                  tflux)
            stats['flags'] = extract_im(xa, ya, dq).astype('i4')
            stats['sky'] = extract_im(xa, ya, sky+msky).astype('f4')
            break
        guessflux, guesssky = unpack_fitpar(flux[0], len(xa),
                                            psfderiv)
        if refit_psf and len(xa) > 0:
            # how far the centroids of the model PSFs would
            # be from (0, 0) if instantiated there
            # this initial definition includes the known offset (since
            # we instantiated off a pixel center), and the model offset
            xe, ye = psfmod.simple_centroid(
                psfmod.central_stamp(stamps[4], censize=stamps[0].shape[-1]))
            # now we subtract the known offset
            xe -= xa-numpy.round(xa)
            ye -= ya-numpy.round(ya)
            if hasattr(psf, 'fitfun'):
                psffitfun = psf.fitfun
                npsf = psffitfun(xa, ya, xcen+xe, ycen+ye, stamps[0],
                                 stamps[1], stamps[2], stamps[3], nkeep=200)
                if npsf is not None:
                    npsf.fitfun = psffitfun
            else:
                shiftx = xcen + xe + xa - numpy.round(xa)
                shifty = ycen + ye + ya - numpy.round(ya)
                npsf = find_psf(xa, shiftx, ya, shifty,
                                stamps[0], stamps[3], stamps[1])
            # we removed the centroid offset of the model PSFs;
            # we need to correct the positions to compensate
            xa += xe
            ya += ye
            psf = npsf
        xcen, ycen = (numpy.clip(c, -3, 3) for c in (xcen, ycen))
        xa, ya = (numpy.clip(c, -0.499, s-0.501)
                  for c, s in zip((xa+xcen, ya+ycen), im.shape))
        fluxunc = numpy.sum(stamps[2]**2.*stamps[3]**2., axis=(1, 2))
        fluxunc = fluxunc + (fluxunc == 0)*1e-20
        fluxunc = (fluxunc**(-0.5)).astype('f4')
        # for very bright stars, fluxunc is unreliable because the entire
        # (small) stamp is saturated.
        # these stars all have very bright inferred fluxes
        # i.e., 50k saturates, so we can cut there.
        keep = (((guessflux/fluxunc > 3) | (guessflux > 1e5)) &
                cull_near(xa, ya, guessflux))
        xa, ya = (c[keep] for c in (xa, ya))
        passno = passno[keep]
        guessflux = guessflux[keep]
        # should probably also subtract these stars from the model image
        # which is used for peak finding.  But the faint stars should
        # make little difference?

    if fixedmodel is not None:
        model += fixedmodel
    flux, skypar = unpack_fitpar(flux[0], len(xa), False)
    stars = OrderedDict([('x', xa), ('y', ya), ('flux', flux)] +
                        [(f, stats[f]) for f in stats])
    res = (stars, skypar, model+sky, sky+msky, psf)
    return res
Пример #2
0
def find_psf(xcen,
             shiftx,
             ycen,
             shifty,
             psfstack,
             weightstack,
             imstack,
             stampsz=59,
             nkeep=100):
    """Find PSF from stamps."""
    # let's just go ahead and correlate the noise
    xr = numpy.round(shiftx)
    yr = numpy.round(shifty)
    psfqf = (numpy.sum(psfstack * (weightstack > 0), axis=(1, 2)) /
             numpy.sum(psfstack, axis=(1, 2)))
    totalflux = numpy.sum(psfstack, axis=(1, 2))
    timflux = numpy.sum(imstack, axis=(1, 2))
    toneflux = numpy.sum(psfstack, axis=(1, 2))
    tmedflux = numpy.median(psfstack, axis=(1, 2))
    tfracflux = toneflux / numpy.clip(timflux, 100, numpy.inf)
    tfracflux2 = (
        (toneflux - tmedflux * psfstack.shape[1] * psfstack.shape[2]) /
        numpy.clip(timflux, 100, numpy.inf))
    okpsf = ((numpy.abs(psfqf - 1) < 0.03) & (tfracflux > 0.5) &
             (tfracflux2 > 0.2))
    if numpy.sum(okpsf) > 0:
        shiftxm = numpy.median(shiftx[okpsf])
        shiftym = numpy.median(shifty[okpsf])
        okpsf = (okpsf & (numpy.abs(shiftx - shiftxm) < 1.) &
                 (numpy.abs(shifty - shiftym) < 1.))
    if numpy.sum(okpsf) <= 5:
        print('Fewer than 5 stars accepted in image, keeping original PSF')
        return None
    if numpy.sum(okpsf) > nkeep:
        okpsf = okpsf & (totalflux > -numpy.sort(-totalflux[okpsf])[nkeep - 1])
    psfstack = psfstack[okpsf, :, :]
    weightstack = weightstack[okpsf, :, :]
    totalflux = totalflux[okpsf]
    xcen = xcen[okpsf]
    ycen = ycen[okpsf]
    shiftx = shiftx[okpsf]
    shifty = shifty[okpsf]
    for i in range(psfstack.shape[0]):
        psfstack[i, :, :] = shift(psfstack[i, :, :], [-shiftx[i], -shifty[i]])
        if (numpy.abs(xr[i]) > 0) or (numpy.abs(yr[i]) > 0):
            weightstack[i, :, :] = shift(weightstack[i, :, :],
                                         [-xr[i], -yr[i]],
                                         mode='constant',
                                         cval=0.)
        # our best guess as to the PSFs & their weights
    # select some reasonable sample of the PSFs
    totalflux = numpy.sum(psfstack, axis=(1, 2))
    psfstack /= totalflux.reshape(-1, 1, 1)
    weightstack *= totalflux.reshape(-1, 1, 1)
    tpsf = numpy.median(psfstack, axis=0)
    tpsf = psfmod.center_psf(tpsf)
    if tpsf.shape == stampsz:
        return tpsf
    xc = numpy.arange(tpsf.shape[0]).reshape(-1, 1) - tpsf.shape[0] // 2
    yc = xc.reshape(1, -1)
    rc = numpy.sqrt(xc**2. + yc**2.)
    stampszo2 = psfstack[0].shape[0] // 2
    wt = numpy.clip((stampszo2 + 1 - rc) / 4., 0., 1.)
    overlap = (wt != 1) & (wt != 0)

    def objective(par):
        mod = psfmod.moffat_psf(par[0],
                                beta=2.5,
                                xy=par[2],
                                yy=par[3],
                                deriv=False,
                                stampsz=tpsf.shape[0])
        mod /= numpy.sum(mod)
        return ((tpsf - mod)[overlap]).reshape(-1)

    from scipy.optimize import leastsq
    par = leastsq(objective, [4., 3., 0., 1.])[0]
    modpsf = psfmod.moffat_psf(par[0],
                               beta=2.5,
                               xy=par[2],
                               yy=par[3],
                               deriv=False,
                               stampsz=stampsz)
    modpsf /= numpy.sum(psfmod.central_stamp(modpsf))
    npsf = modpsf.copy()
    npsfcen = psfmod.central_stamp(npsf, tpsf.shape[0])
    npsfcen[:, :] = tpsf * wt + (1 - wt) * npsfcen[:, :]
    npsf /= numpy.sum(npsf)
    return psfmod.SimplePSF(npsf, normalize=-1)
Пример #3
0
def compute_centroids(x, y, psflist, flux, im, resid, weight):
    # define c = integral(x * I * P * W) / integral(I * P * W)
    # x = x/y coordinate, I = isolated stamp, P = PSF model, W = weight
    # Assuming I ~ P(x-y) for some small offset y and expanding,
    # integrating by parts gives:
    # y = 2 / integral(P*P*W) * integral(x*(I-P)*W)
    # that is the offset we want.

    # we want to compute the centroids on the image after the other sources
    # have been subtracted off.
    # we construct this image by taking the residual image, and then
    # star-by-star adding the model back.
    centroidsize = 19
    psfs = [numpy.zeros((len(x), centroidsize, centroidsize), dtype='f4')
            for i in range(len(psflist))]
    for j in range(len(psflist)):
        for i in range(len(x)):
            psfs[j][i, :, :] = psfmod.central_stamp(psflist[j][i],
                                                    censize=centroidsize)
    stampsz = psfs[0].shape[-1]
    stampszo2 = (stampsz-1)/2
    dx = numpy.arange(stampsz, dtype='i4')-stampszo2
    dx = dx.reshape(-1, 1)
    dy = dx.copy().reshape(1, -1)
    xp = numpy.round(x).astype('i4')
    yp = numpy.round(y).astype('i4')
    # subtracting to get to the edge of the stamp, adding back to deal with
    # the padded image.
    xe = xp - stampszo2 + stampszo2
    ye = yp - stampszo2 + stampszo2
    resid = numpy.pad(resid, [stampszo2, stampszo2], constant_values=0.,
                      mode='constant')
    weight = numpy.pad(weight, [stampszo2, stampszo2], constant_values=0.,
                       mode='constant')
    im = numpy.pad(im, [stampszo2, stampszo2], constant_values=0.,
                   mode='constant')
    repeat = len(psflist)
    residst = numpy.array([resid[xe0:xe0+stampsz, ye0:ye0+stampsz]
                           for (xe0, ye0) in zip(xe, ye)])
    weightst = numpy.array([weight[xe0:xe0+stampsz, ye0:ye0+stampsz]
                            for (xe0, ye0) in zip(xe, ye)])
    psfst = psfs[0] * flux[:len(x)*repeat:repeat].reshape(-1, 1, 1)
    imst = numpy.array([im[xe0:xe0+stampsz, ye0:ye0+stampsz]
                        for (xe0, ye0) in zip(xe, ye)])
    if len(x) == 0:
        weightst = psfs[0].copy()
        residst = psfs[0].copy()
        imst = psfs[0].copy()
    modelst = psfst.copy()
    if len(psflist) > 1:
        modelst += psfs[1]*flux[1:len(x)*repeat:repeat].reshape(-1, 1, 1)
        modelst += psfs[2]*flux[2:len(x)*repeat:repeat].reshape(-1, 1, 1)
    cen = []
    ppw = numpy.sum(modelst*modelst*weightst, axis=(1, 2))
    pp = numpy.sum(modelst*modelst, axis=(1, 2))
    for dc in (dx, dy):
        xrpw = numpy.sum(dc[None, :, :]*residst*modelst*weightst, axis=(1, 2))
        xmmpm = numpy.sum(dc[None, :, :]*(modelst-psfst)*modelst, axis=(1, 2))
        cen.append(2*xrpw/(ppw + (ppw == 0.))*(ppw != 0.) +
                   2*xmmpm/(pp + (pp == 0.))*(pp != 0.))
    xcen, ycen = cen
    norm = numpy.sum(modelst, axis=(1, 2))
    norm = norm + (norm == 0)
    psfqf = numpy.sum(modelst*(weightst > 0), axis=(1, 2)) / norm
    m = psfqf < 0.5
    xcen[m] = 0.
    ycen[m] = 0.
    if (len(psflist) > 1) and numpy.sum(m) > 0:
        ind = numpy.flatnonzero(m)
        # just use the derivative-based centroids for this case.
        fluxnz = flux[repeat*ind]
        fluxnz = fluxnz + (fluxnz == 0)
        xcen[ind] = flux[repeat*ind+1]/fluxnz
        ycen[ind] = flux[repeat*ind+2]/fluxnz
    # stamps: 0: neighbor-subtracted images,
    # 1: images,
    # 2: psfs with shifts
    # 3: psfs without shifts
    res = (xcen, ycen, (modelst+residst, imst, modelst, weightst, psfst))
    return res
Пример #4
0
def compute_centroids(x,
                      y,
                      psflist,
                      flux,
                      im,
                      resid,
                      weight,
                      derivcentroids=False):
    # define c = integral(x * I * P * W) / integral(I * P * W)
    # x = x/y coordinate, I = isolated stamp, P = PSF model, W = weight
    # Assuming I ~ P(x-y) for some small offset y and expanding,
    # integrating by parts gives:
    # y = 2 / integral(P*P*W) * integral(x*(I-P)*W)
    # that is the offset we want.

    # we want to compute the centroids on the image after the other sources
    # have been subtracted off.
    # we construct this image by taking the residual image, and then
    # star-by-star adding the model back.
    centroidsize = 19
    psfs = [
        numpy.zeros((len(x), centroidsize, centroidsize), dtype='f4')
        for i in range(len(psflist))
    ]
    for j in range(len(psflist)):
        for i in range(len(x)):
            psfs[j][i, :, :] = psfmod.central_stamp(psflist[j][i],
                                                    censize=centroidsize)
    stampsz = psfs[0].shape[-1]
    stampszo2 = (stampsz - 1) / 2
    dx = numpy.arange(stampsz, dtype='i4') - stampszo2
    dx = dx.reshape(-1, 1)
    dy = dx.copy().reshape(1, -1)
    xp = numpy.round(x).astype('i4')
    yp = numpy.round(y).astype('i4')
    # subtracting to get to the edge of the stamp, adding back to deal with
    # the padded image.
    xe = xp - stampszo2 + stampszo2
    ye = yp - stampszo2 + stampszo2
    resid = numpy.pad(resid, [stampszo2, stampszo2],
                      constant_values=0.,
                      mode='constant')
    weight = numpy.pad(weight, [stampszo2, stampszo2],
                       constant_values=0.,
                       mode='constant')
    im = numpy.pad(im, [stampszo2, stampszo2],
                   constant_values=0.,
                   mode='constant')
    repeat = len(psflist)
    residst = numpy.array([
        resid[xe0:xe0 + stampsz, ye0:ye0 + stampsz]
        for (xe0, ye0) in zip(xe, ye)
    ])
    weightst = numpy.array([
        weight[xe0:xe0 + stampsz, ye0:ye0 + stampsz]
        for (xe0, ye0) in zip(xe, ye)
    ])
    psfst = psfs[0] * flux[:len(x) * repeat:repeat].reshape(-1, 1, 1)
    imst = numpy.array([
        im[xe0:xe0 + stampsz, ye0:ye0 + stampsz] for (xe0, ye0) in zip(xe, ye)
    ])
    if len(x) == 0:
        weightst = psfs[0].copy()
        residst = psfs[0].copy()
        imst = psfs[0].copy()
    modelst = psfst.copy()
    if len(psflist) > 1:
        modelst += psfs[1] * flux[1:len(x) * repeat:repeat].reshape(-1, 1, 1)
        modelst += psfs[2] * flux[2:len(x) * repeat:repeat].reshape(-1, 1, 1)
    cen = []
    ppw = numpy.sum(modelst * modelst * weightst, axis=(1, 2))
    pp = numpy.sum(modelst * modelst, axis=(1, 2))
    for dc in (dx, dy):
        xrpw = numpy.sum(dc[None, :, :] * residst * modelst * weightst,
                         axis=(1, 2))
        xmmpm = numpy.sum(dc[None, :, :] * (modelst - psfst) * modelst,
                          axis=(1, 2))
        cen.append(2 * xrpw / (ppw + (ppw == 0.)) * (ppw != 0.) + 2 * xmmpm /
                   (pp + (pp == 0.)) * (pp != 0.))
    xcen, ycen = cen
    norm = numpy.sum(modelst, axis=(1, 2))
    norm = norm + (norm == 0)
    psfqf = numpy.sum(modelst * (weightst > 0), axis=(1, 2)) / norm
    # how should we really be doing this?  derivcentroids is the first order
    # approximation to the right thing.  the centroid computation that I do
    # otherwise should be unbiased but noisier than optimal for significantly
    # offset peaks.  Vakili, Hogg (2016) say that I should convolve with the
    # PSF and interpolate to the brightest point with some polynomial.  I
    # expected this to be slow (convolving thousands of stamps individually
    # with the PSF each iteration), but the spread_model code worked pretty
    # well, so this is probably a worthwhile thing to try.  if it worked, it
    # would obviate some of the code mess above, and be optimal, so that
    # sounds worthwhile.
    if not derivcentroids:
        m = psfqf < 0.5
    else:
        m = numpy.ones(len(xcen), dtype='bool')
    xcen[m] = 0.
    ycen[m] = 0.
    if (len(psflist) > 1) and numpy.sum(m) > 0:
        ind = numpy.flatnonzero(m)
        # just use the derivative-based centroids for this case.
        fluxnz = flux[repeat * ind]
        fluxnz = fluxnz + (fluxnz == 0)
        xcen[ind] = flux[repeat * ind + 1] / fluxnz
        ycen[ind] = flux[repeat * ind + 2] / fluxnz
    # stamps: 0: neighbor-subtracted images,
    # 1: images,
    # 2: psfs with shifts
    # 3: psfs without shifts
    res = (xcen, ycen, (modelst + residst, imst, modelst, weightst, psfst))
    return res