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])
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")