Ejemplo n.º 1
0
def flux_weighted_centroid(img, box_edge, init=None, to_plot=False):
    """
    Compute the flux-weighted centroid as described on p. 105 of
    Howell (2006).

    inputs
    ------
    img: array-like

    box_edge: odd integer
        number of pixels in a box edge

    init: array-like, n=2, optional
        initial position at which to center the fitting box. 
        if None, the central pixel of the image will be chosen.

    to_plot: boolean 
        If True, produce a diagnostic plot of flux vs. pixel
        Default = False

    """

    if (box_edge % 2)==0:
        logging.warning("even box edge given; increasing by one")
        L = box_edge / 2
        box_edge += 1
    else:
        L = (box_edge - 1) / 2

    if init is None:
        init = np.asarray(np.shape(img) / 2.0, int)
        raw_init = init
    else:
        raw_init = np.copy(init)
        init = np.asarray(np.round(init), int)
#    print init, raw_init

    sub_img = img[init[1]-L:init[1]+L+1, init[0]-L:init[0]+L+1]

    Isum = np.sum(sub_img, axis=0)
    Jsum = np.sum(sub_img, axis=1)

#    print np.shape(img)
    xedge = np.arange(np.shape(img)[1])[init[0]-L:init[0]+L+1]
    yedge = np.arange(np.shape(img)[0])[init[1]-L:init[1]+L+1]
#    print xedge
#    print yedge

    Ibar = (1.0 / box_edge) * np.sum(Isum)
    Jbar = (1.0 / box_edge) * np.sum(Jsum)
#    print Ibar, Jbar

    Ibar_diff = Isum - Ibar
#    print Ibar_diff, np.sum(Ibar_diff)
    Jbar_diff = Jsum - Jbar
#    print Jbar_diff, np.sum(Jbar_diff)

    xc_top = np.sum((Ibar_diff * xedge)[Ibar_diff>0])
    xc_bot = np.sum(Ibar_diff[Ibar_diff>0])

    yc_top = np.sum((Jbar_diff * yedge)[Jbar_diff>0])
    yc_bot = np.sum(Jbar_diff[Jbar_diff>0])

    xc = xc_top / xc_bot
    yc = yc_top / yc_bot

#    logging.debug("init %.2f %.2f c %.2f %.2f", init[0], init[1], xc, yc)


    if to_plot:
        plt.figure(figsize=(8,10))
        grid = (4,2)
        fake_mask = np.ones_like(img)
        ax1 = plt.subplot2grid(grid, (0,0), rowspan=2)
        plot.stamp(img, fake_mask, ax=ax1)
        ax2 = plt.subplot2grid(grid, (0,1), rowspan=2)
        plot.stamp(img[init[0]-L:init[0]+L+1, init[1]-L:init[1]+L+1], 
                   fake_mask[init[0]-L:init[0]+L+1, init[1]-L:init[1]+L+1],  
                   ax=ax2)

        ax3 = plt.subplot2grid(grid, (2,0), colspan=2)
        ax3.step(xedge, Isum, lw=2, where="mid")
        ax4 = plt.subplot2grid(grid, (3,0), colspan=2)
        ax4.step(yedge, Jsum, lw=2, where="mid")

        ax3.axhline(Ibar,color="g", ls=":", lw=3)
        ax4.axhline(Jbar,color="g", ls=":", lw=3)

        ax3.axvline(raw_init[0],color="k", ls="--", lw=2)
        ax4.axvline(raw_init[1],color="k", ls="--", lw=2)
        ax3.axvline(xc,color="r", lw=2)
        ax4.axvline(yc,color="r", lw=2)

        ax3.set_xlabel("X Pixel")
        ax4.set_xlabel("Y Pixel")
        ax3.set_ylabel("Flux")
        ax3.set_ylabel("Flux")

        plot.centroids(ax1, init=init, coords=(xc,yc))
        ax2.set_xticklabels(np.append(xedge,xedge[-1]+1))
        ax2.set_yticklabels(np.append(yedge,yedge[-1]+1))

#        plt.suptitle("TEST",fontsize="large")
        plt.tight_layout()

    return np.array([xc, yc])
Ejemplo n.º 2
0
def run_one(filename, output_f=None, extract_companions=False, fw_box=None):
    """ 
    Extract a light curve for one star. 

    Inputs
    ------
    filename: string
        filename for target pixel file

    output_f: string (optional)
        if provided, statistics from the extraction will be printed to this file

    extract_companions: boolean (optional; default=False)
        if true, extract light curves for any other DAOFIND objects
        detected in the pixel stamp.

    fw_box: odd integer (optional)
        size of the box within which to calculate the flux-weighted centroid.
        Must be odd. Defaults to the shortest dimension of the pixel stamp,
        or 9 if larger than 9. 

    """

    # Clip part of the filename for use in saving outputs
    outfilename = filename.split("/")[-1][:-14]
    logging.info(outfilename)
    # Retrieve the data
    table, times, pixels, maskmap, maskheader, kpmag = tpf_io.get_data(filename)

    # Use the RA/Dec from the header as the initial position for calculation
    init = centroid.init_pos(maskheader)
    logging.info("init %f %f", init[0], init[1])

    # If not provided, calculate the maximum possible centroid box size.
    # Maximum possible box is 9, unless set larger by the user with fw_box
    if fw_box is None:
        min_ax = np.argmin(np.shape(maskmap))
        min_box = np.shape(maskmap)[min_ax]
        if min_box>=9:
           min_box = 9
           logging.debug("min_box set to 9")
        elif min_ax==0:
            for row in maskmap:
                row_len = len(np.where(row>0)[0])
                if row_len < min_box:
                    min_box = row_len
                    logging.debug("new min_box %d", min_box)
        else:
            for i in range(np.shape(maskmap)[1]):
                col = maskmap[:, i]
                col_len = len(np.where(col>0)[0])
                if col_len < min_box:
                    min_box = row_len
                logging.debug("new min_box %d", min_box)
        
        fw_box = (min_box / 2) * 2 + 1
    
    logging.info("fw box %d",fw_box)

    # Co-add all the sub-images and calculate the flux-weighted centroid
    coadd = np.sum(pixels,axis=0)
    coords = centroid.flux_weighted_centroid(coadd, fw_box, init=init)

    # Background of the co-added stampl is just the sigma-clipped median
    mean, median, std = sigma_clipped_stats(coadd, mask=(maskmap==0),
                                            sigma=3.0, iters=3)
    coadd_bkgd = median

    # Warn the user if the calculated initial coordinates are too far
    # from the initial coordinates (indicting that there's a brighter nearby 
    # star pulling the centroid off of the target)
    if np.sqrt((coords[0] - init[0])**2 + (coords[1] - init[1])**2)>1:
        logging.warning("Centroid (%f %f) far from init (%f %f)", 
                        coords[0], coords[1], init[0], init[1])
    else:
        logging.debug("coords %f %f", coords[0], coords[1])

    # Set keyword arguments for DAOFIND - trying to find anything other 
    # real sources in the image
    dkwargs = {"fwhm":2.5, "threshold":coadd_bkgd,  
               "sharplo":0.01,"sharphi":5.0}
    sources, n_sources = centroid.daofind_centroid(coadd, None, dkwargs)
    if n_sources==0:
        logging.info("bkgd: %f max: %f", coadd_bkgd, max(coadd.flatten()))
    logging.debug("sources")
    logging.debug(sources)

    # Set the minimum and maximum aperture sizes for the extraction
    # and the step in aperture sizes
    ap_min, ap_max, ap_step = 2, 7, 0.5
    radii = np.arange(ap_min, ap_max, ap_step)

    # Always use circular apertures, I think ap_type is now defunct actually...
    ap_type = "circ"
    phot.make_circ_lc(pixels, maskmap, times, init, radii,
                 "lcs/{}.csv".format(outfilename), fw_box)

    # get the EPIC ID
    epic = outfilename.split("-")[0][4:]
    logging.info(epic)

    # Plot an informational plot with the co-added stamp, possibly a DSS image,
    # centroid motion, and location on the K2 FOV 
#    plot.plot_four(epic, filename, coadd, maskmap, maskheader, init, coords, 
#                   sources, ap=None, campaign=4)


    # If an output filename is provided, save the output
    if output_f is not None:
        output_f.write("\n{},{}".format(outfilename,epic))
        output_f.write(",{0:.6f},{1:.6f}".format(maskheader["RA_OBJ"],
                                                 maskheader["DEC_OBJ"]))
        print type(init[0]), type(init[1])
        print init[0].dtype, init[1].dtype
        print init[0], init[1]
        output_f.write(",{0:.6f},{1:.6f}".format(np.float64(init[0]),
                                                 np.float64(init[1])))
        output_f.write(",{0:.2f},{1}".format(kpmag, n_sources))
        output_f.write(",{0:.2f},{1:.6f}".format(dkwargs["fwhm"], 
                                                 dkwargs["threshold"]))
        output_f.write(",{0:.2f},{1:.2f}".format(dkwargs["sharplo"],  
                                                 dkwargs["sharphi"]))
        output_f.write(",{0:.2f},{1}".format(fw_box,ap_type))
        output_f.write(",{0:.2f},{1:.2f},{2:.2f}".format(ap_min, ap_max,   
                                                         ap_step))

    # If desired, also extract light curves for companions
    if extract_companions:
        for i, source in enumerate(sources):
            # Calculate the separation between this source and the target
            sep = np.sqrt((init[0] - source["xcentroid"])**2 +
                          (init[1] - source["ycentroid"])**2)

            # If the source is close to the center, just skip (it's the target)
            if sep<3:
                continue
            
            # If the source is further away, compute a lightcurve for it.
            sletter = alphas[i]

            init2 = np.array([source["xcentroid"], source["ycentroid"]])
            coords2 = centroid.flux_weighted_centroid(coadd, 3, init=init2)
            radii = np.arange(0.5, (sep / 2.0) + 0.1 ,0.5)

            ax = plot.stamp(coadd, maskmap)
            plot.centroids(ax, init2, coords2, sources)
            plot.apertures(ax, init2, radii)
            plt.savefig("plot_outputs/{}{}_stamp.png".format(outfilename,
                                                              sletter))
            plt.title(outfilename+sletter)

            phot.make_circ_lc(pixels, maskmap, times, init2[::-1], radii,
                              "lcs/{}{}.csv".format(outfilename, sletter))
            #plot.lcs("lcs/{}{}.csv".format(outfilename, sletter), epic=epic)
    
            plt.close("all")