예제 #1
0
def raw_aperture_photometry(sci_path,
                            rms_path,
                            mask_path,
                            ra,
                            dec,
                            apply_calibration=False):

    import photutils
    from astropy.coordinates import SkyCoord
    from astropy.io import fits
    from astropy.table import vstack
    from astropy.wcs import WCS

    ra = np.atleast_1d(ra)
    dec = np.atleast_1d(dec)
    coord = SkyCoord(ra, dec, unit='deg')

    with fits.open(sci_path, memmap=False) as shdu:
        header = shdu[0].header
        swcs = WCS(header)
        scipix = shdu[0].data

    with fits.open(rms_path, memmap=False) as rhdu:
        rmspix = rhdu[0].data

    with fits.open(mask_path, memmap=False) as mhdu:
        maskpix = mhdu[0].data

    apertures = photutils.SkyCircularAperture(coord, r=APERTURE_RADIUS)
    phot_table = photutils.aperture_photometry(scipix,
                                               apertures,
                                               error=rmspix,
                                               wcs=swcs)

    pixap = apertures.to_pixel(swcs)
    annulus_masks = pixap.to_mask(method='center')
    maskpix = [annulus_mask.cutout(maskpix) for annulus_mask in annulus_masks]

    magzp = header['MAGZP']
    apcor = header[APER_KEY]

    # check for invalid photometry on masked pixels
    phot_table['flags'] = [
        int(np.bitwise_or.reduce(m, axis=(0, 1))) for m in maskpix
    ]

    phot_table['zp'] = magzp + apcor
    phot_table['obsjd'] = header['OBSJD']
    phot_table['filtercode'] = 'z' + header['FILTER'][-1]

    # rename some columns
    phot_table.rename_column('aperture_sum', 'flux')
    phot_table.rename_column('aperture_sum_err', 'fluxerr')

    return phot_table
예제 #2
0
def _get_apertures(grid):
    # NOTE: we assume all apertures have the same radius
    r = np.unique(grid.radiuses)
    assert r.shape[0] == 1, "Regions have different radiuses!"

    apertures = photutils.SkyCircularAperture(
        SkyCoord(ra=grid.centers_ra * u.deg,
                 dec=grid.centers_dec * u.deg,
                 frame='icrs'), r[0] * u.arcsec)

    return apertures
예제 #3
0
def test_recovered_flux_aperture(science_image):

    with fits.open(science_image) as hdul:
        header = hdul[0].header
        wcs = WCS(header)
        footprint = wcs.calc_footprint()

    # realize fakes randomly throughout the footprint
    rx = rng.uniform(size=N_FAKE)
    ry = rng.uniform(size=N_FAKE)

    # keep things bright
    mag = rng.uniform(low=15, high=18, size=N_FAKE)

    minra, mindec = footprint.min(axis=0)
    maxra, maxdec = footprint.max(axis=0)

    coord = SkyCoord(minra + (maxra - minra) * rx,
                     mindec + (maxdec - mindec) * ry,
                     unit='deg')

    fakes.inject_psf(science_image, mag, coord)

    with fits.open(science_image) as hdul:
        data = hdul[-2].data
        header = hdul[0].header
        table = hdul[-1].data

    # subtract off the background
    sigma_clip = SigmaClip(sigma=3.0)
    bkg = MedianBackground(sigma_clip)
    bkg_val = bkg.calc_background(data)
    bkgsub = data - bkg_val

    APERTURE_RADIUS = 3 * u.pixel

    for row in table:
        ra, dec, mag = row['fake_ra'], row['fake_dec'], row['fake_mag']
        coord = SkyCoord(ra, dec, unit='deg')
        apertures = photutils.SkyCircularAperture(coord, r=APERTURE_RADIUS)
        phot_table = photutils.aperture_photometry(bkgsub, apertures, wcs=wcs)
        flux = phot_table['aperture_sum'][0]

        assert abs(-2.5 * np.log10(flux) + header['MAGZP'] + header['APCOR4'] -
                   mag) < 0.05
예제 #4
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
예제 #5
0
outf.writeto('dendrograms_min1mJy_diff1mJy_mask_pruned.fits', clobber=True)

for image, name in ((contfile,''), (radio_image,'KUband')):
    data = image[0].data.squeeze()
    mywcs = wcs.WCS(image[0].header).celestial

    keys = ['{1}cont_flux{0}arcsec'.format(rr.value, name) for rr in radii]
    columns = {k:[] for k in (keys)}
    log.info("Doing aperture photometry on {0}".format(name))
    for ii, row in enumerate(ProgressBar(pruned_ppcat)):
        size = max(radii)*2.2
        xc,yc = row['x_cen'], row['y_cen']
        position = coordinates.SkyCoord(xc, yc, frame='fk5', unit=(u.deg,u.deg))
        cutout = Cutout2D(data, position, size, mywcs, mode='partial')
        for rr in radii:
            aperture = photutils.SkyCircularAperture(positions=position,
                                                     r=rr).to_pixel(cutout.wcs)
            aperture_data = photutils.aperture_photometry(data=cutout.data,
                                                          apertures=aperture,
                                                          method='exact')
            flux_jybeam = aperture_data[0]['aperture_sum']
            #flux_jybeam_average = flux_jybeam / aperture.area()
            #flux_jysr_average = flux_jybeam_average / beam.sr.value
            #flux_jysr = flux_jysr_average * aperture.area()
            #flux_jy = (flux_jysr * (pixel_scale**2).to(u.sr).value)
            columns['{1}cont_flux{0}arcsec'.format(rr.value, name)].append(flux_jybeam/ppbeam)

    for k in columns:
        if k not in pruned_ppcat.keys():
            pruned_ppcat.add_column(Column(name=k, data=columns[k]))

    for k in pruned_ppcat.colnames:
예제 #6
0
def aperture_photometry(calibratable,
                        ra,
                        dec,
                        apply_calibration=False,
                        assume_background_subtracted=False,
                        use_cutout=False,
                        direct_load=None):

    import photutils
    from astropy.coordinates import SkyCoord
    from astropy.io import fits
    from astropy.table import vstack
    from astropy.wcs import WCS

    ra = np.atleast_1d(ra)
    dec = np.atleast_1d(dec)
    coord = SkyCoord(ra, dec, unit='deg')

    if not use_cutout:

        wcs = calibratable.wcs

        apertures = photutils.SkyCircularAperture(coord, r=APERTURE_RADIUS)

        # something that is photometerable implements mask, background, and wcs
        if not assume_background_subtracted:
            pixels_bkgsub = calibratable.background_subtracted_image.data
        else:
            pixels_bkgsub = calibratable.data

        bkgrms = calibratable.rms_image.data
        mask = calibratable.mask_image.data

        phot_table = photutils.aperture_photometry(pixels_bkgsub,
                                                   apertures,
                                                   error=bkgrms,
                                                   wcs=wcs)

        phot_table['zp'] = calibratable.header['MAGZP'] + calibratable.header[
            'APCOR4']
        phot_table['obsjd'] = calibratable.header['OBSJD']
        phot_table['filtercode'] = 'z' + calibratable.header['FILTER'][-1]

        pixap = apertures.to_pixel(wcs)
        annulus_masks = pixap.to_mask(method='center')
        maskpix = [
            annulus_mask.cutout(mask.data) for annulus_mask in annulus_masks
        ]

    else:
        phot_table = []
        maskpix = []
        for s in coord:

            if direct_load is not None and 'sci' in direct_load:
                sci_path = direct_load['sci']
            else:
                if assume_background_subtracted:
                    sci_path = calibratable.local_path
                else:
                    sci_path = calibratable.background_subtracted_image.local_path

            if direct_load is not None and 'mask' in direct_load:
                mask_path = direct_load['mask']
            else:
                mask_path = calibratable.mask_image.local_path

            if direct_load is not None and 'rms' in direct_load:
                rms_path = direct_load['rms']
            else:
                rms_path = calibratable.rms_image.local_path

            with fits.open(sci_path, memmap=True) as f:
                wcs = WCS(f[0].header)

            pixcoord = wcs.all_world2pix([[s.ra.deg, s.dec.deg]], 0)[0]
            pixx, pixy = pixcoord

            nx = calibratable.header['NAXIS1']
            ny = calibratable.header['NAXIS2']

            xmin = max(0, pixx - 1.5 * APERTURE_RADIUS.value)
            xmax = min(nx, pixx + 1.5 * APERTURE_RADIUS.value)

            ymin = max(0, pixy - 1.5 * APERTURE_RADIUS.value)
            ymax = min(ny, pixy + 1.5 * APERTURE_RADIUS.value)

            ixmin = int(np.floor(xmin))
            ixmax = int(np.ceil(xmax))

            iymin = int(np.floor(ymin))
            iymax = int(np.ceil(ymax))

            ap = photutils.CircularAperture([pixx - ixmin, pixy - iymin],
                                            APERTURE_RADIUS.value)

            # something that is photometerable implements mask, background, and wcs
            with fits.open(sci_path, memmap=True) as f:
                pixels_bkgsub = f[0].data[iymin:iymax, ixmin:ixmax]

            with fits.open(rms_path, memmap=True) as f:
                bkgrms = f[0].data[iymin:iymax, ixmin:ixmax]

            with fits.open(mask_path, memmap=True) as f:
                mask = f[0].data[iymin:iymax, ixmin:ixmax]

            pt = photutils.aperture_photometry(pixels_bkgsub, ap, error=bkgrms)

            annulus_mask = ap.to_mask(method='center')
            mp = annulus_mask.cutout(mask.data)
            maskpix.append(mp)

            phot_table.append(pt)

        phot_table = vstack(phot_table)

    if apply_calibration:
        magzp = calibratable.header['MAGZP']
        apcor = calibratable.header[APER_KEY]

        phot_table['mag'] = -2.5 * np.log10(
            phot_table['aperture_sum']) + magzp + apcor
        phot_table['magerr'] = 1.0826 * phot_table[
            'aperture_sum_err'] / phot_table['aperture_sum']

    # check for invalid photometry on masked pixels
    phot_table['flags'] = [
        int(np.bitwise_or.reduce(m, axis=(0, 1))) for m in maskpix
    ]

    # rename some columns
    phot_table.rename_column('aperture_sum', 'flux')
    phot_table.rename_column('aperture_sum_err', 'fluxerr')

    return phot_table
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
예제 #8
0
def funcdrz_aperture_photometry_on_crop(drz, obs, tar, source='qso', fp_image='drz_crop.fits', radii=([1., 2., 3., 4., ])*u.arcsec, stretch='linear', vmin=None, vmax=None): 
	""" measure aperture photometry of images
	"""
	s = drz.sources[source]

	# wcs from drz.fits
	w = wcs.WCS(fits.getheader(drz.directory+'drz.fits', 1))

	# read crop
	fp = s.directory+fp_image
	fp_root = os.path.splitext(fp)[0]
	data = fits.getdata(fp)

	# define aperture
	pos_sky = ac.SkyCoord(s.ra, s.dec, frame='icrs', unit='deg')
	apt_sky = [pu.SkyCircularAperture(pos_sky, r=r) for r in radii]
	apt_pix = [apt_sky.to_pixel(wcs=w) for apt_sky in apt_sky]

	# transform from drz.fits pix coordinate to crop pix coordinate
	pos_pix_crop = apt_pix[0].positions[0] - np.array([s.x, s.y]) + np.array(data.shape[::-1])//2

	apertures_pix_crop = copy.copy(apt_pix)
	for aperture in apertures_pix_crop:
		aperture.positions = np.array([pos_pix_crop])

	phot = at.Table(pu.aperture_photometry(data, apertures_pix_crop))
	phot['xcenter'].unit = None
	phot['ycenter'].unit = None

	# reorganize phot table
	phot.remove_column('id')
	phot.rename_column('xcenter', 'xc_drzcrop')
	phot.rename_column('ycenter', 'yc_drzcrop')
	phot['xc_drz'] = apt_pix[0].positions[0][0]
	phot['yc_drz'] = apt_pix[0].positions[0][1]
	phot['ra'] = s.ra
	phot['dec'] = s.dec
	phot['fn_image'] = fp_image
	cols = ['fn_image', 'ra', 'dec', 'xc_drz', 'yc_drz', 'xc_drzcrop', 'yc_drzcrop', ]
	if len(radii) == 1:
		cols += ['aperture_sum']
	else: 
		cols += ['aperture_sum_'+str(i) for i in range(len(radii))]

	phot = phot[cols]

	# write photometric results
	phot.write(fp_root+'_aphot.csv', overwrite=True)

	# write radii csv
	radii_df = pd.DataFrame({'tag': ['aperture_sum_'+str(i) for i in range(len(radii))], 
							'radii': [str(radii[i]) for i in range(len(radii))]})
	radii_df = radii_df[['tag','radii']]
	radii_df.to_csv('radii.csv', index=False)

	# visual
	v = tinyfit.visual.visual(fp)
	v.plot(fn_out=fp_root+'_aphot.pdf', colorbar=True, vmin=vmin, vmax=vmax, stretch=stretch)
	for a in apertures_pix_crop:
		a.plot()
	plt.savefig(fp_root+'_aphot.pdf')
	plt.close()