예제 #1
0
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