def measure_backgrounds(cat_table, ref_im): """ Measure the background for all the sources in cat_table, using ref_im. Parameters ---------- cat_table: astropy Table the catalog in astropy table form, as loaded from a .gst.fits photometry catalog ref_im: imageHDU fits image which will be used to estimate the background Returns ------- measurements: list of float a metric for the background intensity at the star's position (photometry / area) mask: 2d ndarray of bool an array containing a bool for each pixel of the image, True if the pixel was ignored for the background calculations """ if not ref_im: # Return a dumb value return cat_table['F814W_CHI'] w = wcs.WCS(ref_im.header) shp = ref_im.data.shape inner_rad = 30 * units.pixel outer_rad = inner_rad + 20 * units.pixel mask_rad = inner_rad # More elaborate way using an actual image (do not care about the # filter for the moment, just take the image (which was given on the # command line) at face value) ra = cat_table['RA'] dec = cat_table['DEC'] c = astropy.coordinates.SkyCoord(ra * units.degree, dec * units.degree) # Annuli, of which the counts per surface area will be used as # background measurements annuli = pu.SkyCircularAnnulus(c, r_in=inner_rad, r_out=outer_rad) area = annuli.to_pixel(w).area() # A mask to make sure that no sources end up in the background # calculation circles = pu.SkyCircularAperture(c, mask_rad) source_masks = circles.to_pixel(w).to_mask() mask_union = np.zeros(shp) for i, ap_mask in enumerate(source_masks): data_slices = list(ap_mask.bbox.slices) # These slices go outside of the box sometimes! In that case we # will override the slices on both sides. # Default slices (go over the whole mask) mask_slices = [slice(None, None), slice(None, None)] # Adjust the slices for x and y if necessary for j in range(2): # DATA: . . a b c d e f # index - - 0 1 2 3 4 5 # --------------- # MASK: - + + + - # index 0 1 2 3 4 # --> DATA_SLICE [0:stop] # --> MASK_SLICE [2:] data_start = data_slices[j].start if data_start < 0: # Move the start from negative n to zero data_slices[j] = slice(0, data_slices[j].stop) # Move the start from 0 to positive n mask_slices[j] = slice(-data_start, mask_slices[j].stop) # --> we slice over the part of the small mask that # falls on the positive side of the axis data_stop = data_slices[j].stop if data_stop > shp[j]: overflow = data_stop - shp[j] # Move the stop 'overflow' to shp[j] data_slices[j] = slice(data_slices[j].start, shp[j]) # Move the stop to 'overflow' pixels from the end mask_slices[j] = slice(mask_slices[j].start, -overflow) # --> slice over the part that falls below the maximum mask_union[data_slices] += ap_mask.data[mask_slices] # Threshold mask_union = mask_union > 0 phot = pu.aperture_photometry(ref_im.data, annuli, wcs=w, mask=mask_union) return phot['aperture_sum'] / area, mask_union
def measure_backgrounds(cat_table, ref_im, mask_radius, ann_width, cat_filter): """ Measure the background for all the sources in cat_table, using ref_im. Parameters ---------- cat_table: astropy Table the catalog in astropy table form, as loaded from a .gst.fits photometry catalog ref_im: imageHDU fits image which will be used to estimate the background mask_radius : float radius (in pixels) of mask for catalog sources ann_width : float width of annulus (in pixels) for calculating background around each catalog source cat_filter : list or None If list: Two elements in which the first is a filter (e.g. 'F475W') and the second is a magnitude. Catalog entries with [filter]_VEGA > mag will not be masked. If None: all catalog entries will be considered. Returns ------- measurements: list of float a metric for the background intensity at the star's position (photometry / area) """ w = wcs.WCS(ref_im.header) shp = ref_im.data.shape inner_rad = mask_radius * units.pixel outer_rad = inner_rad + ann_width * units.pixel mask_rad = inner_rad # More elaborate way using an actual image (do not care about the # filter for the moment, just take the image (which was given on the # command line) at face value) ra = cat_table['RA'] dec = cat_table['DEC'] c = astropy.coordinates.SkyCoord(ra * units.degree, dec * units.degree) # Annuli, of which the counts per surface area will be used as # background measurements annuli = pu.SkyCircularAnnulus(c, r_in=inner_rad, r_out=outer_rad) area = annuli.to_pixel(w).area() # A mask to make sure that no sources end up in the background # calculation if cat_filter is None: circles = pu.SkyCircularAperture(c, mask_rad) else: circles = pu.SkyCircularAperture(c[ cat_table[cat_filter[0]+'_VEGA'] < float(cat_filter[1]) ], mask_rad) source_masks = circles.to_pixel(w).to_mask() mask_union = np.zeros(shp) for i, ap_mask in enumerate(source_masks): # the masks have a bounding box which we need to take into # account. Here we will calculate the overlap between the mask # and the image (important if the mask is near the edge, so that # the box might go outside of it). data_slices = list(ap_mask.bbox.slices) # These slices go outside of the box sometimes! In that case we # will override the slices on both sides. # Default slices (go over the whole mask) mask_slices = [slice(None, None), slice(None, None)] # Adjust the slices in each dimension for j in range(2): # DATA: . . a b c d e f # index - - 0 1 2 3 4 5 # --------------- # MASK: - + + + - # index 0 1 2 3 4 # --> DATA_SLICE [0:stop] # --> MASK_SLICE [2:] data_start = data_slices[j].start if data_start < 0: # Move the start from negative n to zero data_slices[j] = slice(0, data_slices[j].stop) # Move the start from 0 to positive n mask_slices[j] = slice(-data_start, mask_slices[j].stop) # --> we slice over the part of the small mask that # falls on the positive side of the axis data_stop = data_slices[j].stop if data_stop > shp[j]: overflow = data_stop - shp[j] # Move the stop 'overflow' to shp[j] data_slices[j] = slice(data_slices[j].start, shp[j]) # Move the stop to 'overflow' pixels from the end mask_slices[j] = slice(mask_slices[j].start, -overflow) # --> slice over the part that falls below the maximum mask_union[tuple(data_slices)] += ap_mask.data[tuple(mask_slices)] # Threshold mask_union = mask_union > 0 # also mask NaNs mask_union[np.isnan(ref_im.data)] = True # Save the masked reference image hdu = fits.PrimaryHDU(np.where(mask_union, 0, ref_im.data), header=ref_im.header) hdu.writeto('masked_reference_image.fits', overwrite=True) # Do the measurements phot = pu.aperture_photometry(ref_im.data, annuli, wcs=w, mask=mask_union) return phot['aperture_sum'] / area