예제 #1
0
 def plot_sky_masks(self,
                    r1,
                    r2,
                    minp=0.0,
                    maxp=99.9,
                    cols=5,
                    figsize=(11, 2.5)):
     aps = CircularAnnulus([self._stars.xcentroid, self._stars.ycentroid],
                           r1, r2)
     fig, axs = subplots(int(ceil(self.nstars / cols)),
                         cols,
                         figsize=figsize,
                         sharex=True,
                         sharey=True)
     for m, ax in zip(aps.to_mask(), axs.flat):
         d = where(m.data.astype('bool'), m.cutout(self.reduced),
                   nan)  #m.multiply(self.reduced)
         ax.imshow(d,
                   cmap=cm.gray_r,
                   origin='image',
                   norm=sn(d,
                           stretch='linear',
                           min_percent=minp,
                           max_percent=maxp))
     fig.tight_layout()
예제 #2
0
def photometry_custom(image_data, radpix, wcs):
    #aperture = CircularAperture((image_data.shape[0]/2,image_data.shape[0]/2), radpix[19])
    position = [image_data.shape[0] / 2, image_data.shape[0] / 2]
    apertures = [CircularAperture(position, r=r) for r in radpix]
    annulus_aperture = CircularAnnulus(position,
                                       r_in=radpix[19],
                                       r_out=radpix[19] + radpix[16])
    phot_table = aperture_photometry(image_data, apertures, wcs=wcs)

    annulus_masks = annulus_aperture.to_mask(method='center')
    annulus_data = annulus_masks.multiply(image_data)
    mask = annulus_masks.data
    annulus_data_1d = annulus_data[mask > 0]
    _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
    bkg_mean = median_sigclip

    #sky_aperture = to_sky(apertures,wcs)
    phot_array = np.zeros(20)
    bkg_sum_array = np.zeros(20)
    for i in range(0, 20):
        phot_array[i] = phot_table['aperture_sum_' + str(i)][0]
        bkg_sum_array[i] = bkg_mean * apertures[i].area

    final_sum = phot_array - bkg_sum_array

    scale = np.mean(proj_plane_pixel_scales(wcs))
    phot_Jy_custom = final_sum

    print('Backgorud outer radius =', radpix[19] + radpix[16], 'pixels')
    print(phot_table)
    return (phot_Jy_custom)
예제 #3
0
def aper_phot(data, header, positions, aper, data_out, gain=1, rdnoise=0):
    exptime = header['EXPTIME']
    apertures = CircularAperture(positions, r=aper[0])
    annulus_apertures = CircularAnnulus(positions, r_in=aper[1], r_out=aper[2])
    annulus_masks = annulus_apertures.to_mask(method='center')

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)
    bkg_intensity = np.array(bkg_median) * apertures.area() * gain
    phot = aperture_photometry(data, apertures)
    phot['signal_intensity'] = phot['aperture_sum'] * gain - bkg_intensity
    phot['SNR'] = phot['signal_intensity'] / (phot['signal_intensity'] +
                                              bkg_intensity + rdnoise**2)**0.5
    phot['magnitude'] = -2.5 * np.log10(
        phot['signal_intensity'] / exptime) + 24
    phot['delta_magnitude'] = 2.5 * np.log10(1 + 1 / phot['SNR'])

    for col in phot.colnames:
        phot[col].info.format = '%.4f'  # for consistent table output

    imageshow(data, positions, aper=aper)
    return phot
예제 #4
0
def _fiber_fracflux(psf, x_centroid=None, y_centroid=None, fib_rad_pix=None):

    # not really sure if this edge case will ever happen ??
    if (np.sum(psf) <= 0):
        return np.nan, np.nan, np.nan

    if fib_rad_pix is None:
        fib_rad_pix = 3.567 * (1.52 / 1.462)  # pixels

    position = (x_centroid, y_centroid)

    aperture = CircularAperture(position, r=fib_rad_pix)
    annulus_aperture = CircularAnnulus(position, r_in=25.0, r_out=40.0)
    annulus_mask = annulus_aperture.to_mask(method='center')

    annulus_data = annulus_mask.multiply(psf)
    annulus_data_1d = annulus_data[annulus_mask.data > 0]

    _, bkg_median, std_bg = sigma_clipped_stats(annulus_data_1d)

    phot = aperture_photometry(psf, aperture)

    aper_bkg_tot = bkg_median * _get_area_from_ap(aperture)

    numerator = phot['aperture_sum'][0] - aper_bkg_tot  # aper flux
    denominator = np.sum(psf)

    frac = numerator / denominator

    return frac, numerator, denominator
예제 #5
0
    def _make_apertures(self, colpos, rowpos):
        """Create an apertures, bg annular, and annular mask object for 
           aperture photometry.
           
        See https://photutils.readthedocs.io/en/stable/aperture.html#sigma-clipped-median-within-a-circular-annulus
        """

        positions = np.transpose((colpos, rowpos))
        
        # Radius of circular source region radius
        ap_radius = math.ceil(self._ap_fwhm_mult * self._search_fwhm)
        
        # BG outer annulus radius. By making this 1.5 times ap_radius
        # the BG annulus has an area 1.25 times the inner circle, so
        # we should get decent sampling.
        outer_radius = math.ceil(1.5 * ap_radius) 
        
        self._logger.debug(f'Radius of circular aperture photometry is {ap_radius} pixels.')
        self._logger.debug(f'Local BG estimation in annulus outer radius {outer_radius} pixels, inner radius {ap_radius} pixels.')
        
        apertures         = CircularAperture(positions, r=ap_radius)
        bg_apertures      = CircularAnnulus(positions, r_in=ap_radius, 
            r_out=outer_radius)
        bg_aperture_masks = bg_apertures.to_mask(method='center')
        
        return apertures, bg_apertures, bg_aperture_masks
예제 #6
0
    def _aper_local_background(self):
        """
        Estimate the local background and error using a circular annulus
        aperture.

        The local backround is the sigma-clipped median value in the
        annulus.  The background error is the standard error of the
        median, sqrt(pi / 2N) * std.
        """
        bkg_aper = CircularAnnulus(
            self.xypos, self.aperture_params['bkg_aperture_inner_radius'],
            self.aperture_params['bkg_aperture_outer_radius'])
        bkg_aper_masks = bkg_aper.to_mask(method='center')
        sigclip = SigmaClip(sigma=3)

        nvalues = []
        bkg_median = []
        bkg_std = []
        for mask in bkg_aper_masks:
            bkg_data = mask.multiply(self.model.data.value)
            bkg_data_1d = bkg_data[mask.data > 0]
            values = sigclip(bkg_data_1d, masked=False)
            nvalues.append(values.size)
            bkg_median.append(np.median(values))
            bkg_std.append(np.std(values))

        nvalues = np.array(nvalues)
        bkg_median = np.array(bkg_median)
        # standard error of the median
        bkg_median_err = np.sqrt(np.pi / (2. * nvalues)) * np.array(bkg_std)

        bkg_median <<= self.model.data.unit
        bkg_median_err <<= self.model.data.unit

        return bkg_median, bkg_median_err
예제 #7
0
def nb_cogphot(nbima, nbvar, xc, yc, maxrad=15, growthlim=1.025, plots=False):

    from photutils import CircularAperture, CircularAnnulus
    from photutils import aperture_photometry
    from astropy.stats import sigma_clipped_stats as scl
    from astropy.convolution import convolve, Box2DKernel

    rad = np.arange(1, maxrad + 1)
    phot = np.zeros_like(rad, dtype=float)
    photerr = np.zeros_like(rad, dtype=float)
    growth = np.zeros_like(rad, dtype=float)

    skyaper = CircularAnnulus((xc - 1, yc - 1),
                              r_in=maxrad,
                              r_out=np.max([1.5 * maxrad, 20]))
    skymask = skyaper.to_mask(method='center')
    skydata = skymask.multiply(nbima)[skymask.data > 0]

    bkg_avg, bkg_med, _ = scl(skydata)

    for ii, r in enumerate(rad):

        aper = CircularAperture((xc - 1, yc - 1), r=r)
        phot[ii] = (aperture_photometry(
            nbima, aper))['aperture_sum'][0] - bkg_med * aper.area
        photerr[ii] = np.sqrt((aperture_photometry(nbvar,
                                                   aper))['aperture_sum'][0])
        if ii < 3:
            growth[ii] = 100
        else:
            growth[ii] = phot[ii] / phot[ii - 1]

    rlim = np.argmin(growth > growthlim) - 1

    fluxarr = phot[rlim]
    errarr = photerr[rlim]
    radarr = rad[rlim]

    if plots:
        fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(5, 5))
        ax.imshow(convolve(nbima, Box2DKernel(5)),
                  vmin=-2,
                  vmax=30,
                  origin='lower')
        #ax.imshow(tmpnbima, vmin=-2, vmax=30, origin='lower')
        circ = plt.Circle((xc - 1, yc - 1),
                          rad[rlim],
                          color='r',
                          fill=False,
                          lw=3)
        ax.add_artist(circ)
        ax.set_xlim(xc - 50, xc + 50)
        ax.set_ylim(yc - 50, yc + 50)
        plt.show()

        plt.plot(rad, phot)
        plt.show()

    return fluxarr, errarr, radarr
예제 #8
0
def do_photometry(image,
                  source_table,
                  aperture_radius=3,
                  subtract_background=False,
                  annulus_radii=(6, 8)):
    """Perform aperture photometry on the input data using the source
    locations specified in the source_table

    Parameters
    ----------
    image : numpy.ndarray
        2D image

    source_table : astropy.table.Table
        Table of source locations (i.e. output from photutils's
        DAOStarFinder)

    subtract_background : bool
        If True, use photutils to estimate and subtract background

    Returns
    -------
    source_table : astropy.table.Table
        Updated source_table including photometry results
    """
    # Create list of tuples giving source locations
    positions = [(tab['xcentroid'], tab['ycentroid']) for tab in source_table]

    apertures = CircularAperture(positions, r=aperture_radius)

    # If background is to be subtracted, calculate the sigma-clipped median
    # value of the pixels in the annuli around the sources
    if subtract_background:
        annulus_apertures = CircularAnnulus(positions,
                                            r_in=annulus_radii[0],
                                            r_out=annulus_radii[1])
        annulus_masks = annulus_apertures.to_mask(method='center')

        bkg_median = []
        for mask in annulus_masks:
            annulus_data = mask.multiply(image)
            annulus_data_1d = annulus_data[mask.data > 0]
            _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
            bkg_median.append(median_sigclip)
        bkg_median = np.array(bkg_median)

    # Do the photometry and add to the input table
    phot_table = aperture_photometry(image, apertures)
    source_table['aperture_sum'] = phot_table['aperture_sum']

    if subtract_background:
        # Add columns with that background subtraction info
        source_table['annulus_median'] = bkg_median
        source_table['aper_bkg'] = bkg_median * apertures.area
        source_table['aper_sum_bkgsub'] = source_table[
            'aperture_sum'] - source_table['aper_bkg']
    return source_table
예제 #9
0
def do_Circ_phot(pos, FWHM, ap_min=3., ap_factor=1.5, rin=None, rout=None, \
                   sky_nsigma=3., sky_iter=10):
    '''
    In rigorous manner, we should use
    error = sqrt (flux / epadu + area * stdev**2 + area**2 * stdev**2 / nsky)
    as in http://stsdas.stsci.edu/cgi-bin/gethelp.cgi?phot .
    Here flux == aperture_sum - sky, i.e., flux from image_reduc.
        stdev should include sky error AND ronoise.
    '''
    if rin == None:
        rin  = 4 * FWHM
    if rout == None:
        rout = 6 * FWHM
    N = len(pos)
    if pos.ndim == 1:
        N = 1
    an       = CircAn(pos, r_in=rin, r_out=rout)
    ap_size  = np.max([ap_min, ap_factor*FWHM])
    aperture = CircAp(pos, r=ap_size)
    flux     = aperture.do_photometry(image_reduc, method='exact')[0]
    # do phot and get sum from aperture. [0] is sum and [1] is error.
#For test:
#N=len(pos_star_fit)
#an       = CircAn(pos_star_fit, r_in=4*FWHM_moffat, r_out=6*FWHM_moffat)
#ap_size  = 1.5*FWHM_moffat
#aperture = CircAp(pos_star_fit, r=ap_size)
#flux     = aperture.do_photometry(image_reduc, method='exact')[0]
    flux_ss  = np.zeros(N)
    error    = np.zeros(N)
    for i in range(0, N):
        mask_an    = (an.to_mask(method='center'))[i]
        #   cf: test = mask_an.cutout(image_reduc) <-- will make cutout image.
        sky_an     = mask_an.apply(image_reduc)
        all_sky    = sky_an[np.nonzero(sky_an)]
        # only annulus region will be saved as np.ndarray
        msky, stdev, nsky, nrej = sky_fit(all_sky, method='Mode', mode_option='sex')
        area       = aperture.area()
        flux_ss[i] = flux[i] - msky*area  # sky subtracted flux
        error[i]   = np.sqrt( flux_ss[i]/gain \
                           + area * stdev**2 \
                           + area**2 * stdev**2 / nsky )
# To know rejected number, uncomment the following.
#        plt.imshow(sky_an, cmap='gray_r', vmin=-20)
#        plt.colorbar()
#        plt.show()
#        mask_ap  = (aperture.to_mask(method='exact'))[i]
#        star_ap  = mask_ap.apply(image_reduc)
#        plt.imshow(star_ap)
#        plt.colorbar()
#        plt.show()
#        plt.cla()
        if pos.ndim > 1:
            print('\t[{x:7.2f}, {y:7.2f}], {nsky:3d} {nrej:3d} {msky:7.2f} {stdev:7.2f} {flux:7.1f} {ferr:3.1f}'.format(\
                          x=pos[i][0], y=pos[i][1], \
                          nsky=nsky, nrej=nrej, msky=msky, stdev=stdev,\
                          flux=flux_ss[i], ferr=error[i]))
    return flux_ss, error
예제 #10
0
파일: phot.py 프로젝트: desihub/gfa_reduce
def pmgstars_forced_phot(xcentroid, ycentroid, image, elg=False,
                         bgs=False):

    assert(len(xcentroid) > 0)
    assert(len(ycentroid) > 0)

    # create the apertures
    # get the fluxes

    print('Attempting to do forced aperture photometry')

    # shouldn't happen...
    assert(not (elg and bgs))

    if elg or bgs:
        par = common.gfa_misc_params()

        param = 'exp_kernel_filename' if elg else 'devauc_kernel_filename'

        fname = os.path.join(os.environ[par['meta_env_var']],
                             par[param])

        kern = fits.getdata(fname) # non-optimal to repeatedly read this...

        image = ndimage.convolve(image, kern, mode='constant')

    positions = list(zip(xcentroid, ycentroid))

    # the 1.52/1.462 factor comes from David Schlegel's request
    # to have gfa_reduce fiber flux fraction related quantities
    # be referenced to a 1.52 asec diameter aperture, even though
    # the angular diameter corresponding to a 107 um fiber at the
    # GFA focal plane position is smaller (1.462 asec using GFA
    # platescale geometric mean); see SurveySpeed wiki page for 1.52 value
    radius = 3.567*(1.52/1.462) # pixels

    apertures = CircularAperture(positions, r=radius)
    annulus_apertures = CircularAnnulus(positions, r_in=60.0, r_out=65.0)
    annulus_masks = annulus_apertures.to_mask(method='center')

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(image)
        annulus_data_1d = annulus_data[mask.data > 0]
        # this sigma_clipped_stats call is actually the slow part !!
        _, median_sigclip, std_bg = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)

    bkg_median = np.array(bkg_median)
    phot = aperture_photometry(image, apertures)

    aper_bkg_tot = bkg_median*_get_area_from_ap(apertures[0])

    aper_fluxes = np.array(phot['aperture_sum']) - aper_bkg_tot

    return aper_fluxes
예제 #11
0
 def load_bkg_cutout(self, manual_mask=False, col_mask_start=0, col_mask_end=0,
                     row_mask_start=0, row_mask_end=0):
     '''       
     Only need to manually mask the background if the number of bad 
     pixels in the background annulus is more than half of the total;
     Otherwise *median absolute deviation*(or percentiles) should give a 
     robust estimate of the background noise.
     
     imgpath = pobj.imgpath
     pixX = pobj.pixX
     pixY = pobj.pixY  
     # bad_threshold = pobj.bad_threshold
     r_bkg_in = pobj.r_bkg_in
     r_bkg_out = pobj.r_bkg_out
     '''
     imgpath = self.imgpath              
     pixX = self.pixX
     pixY = self.pixY
     # bad_threshold = self.bad_threshold
     r_bkg_in = self.r_bkg_in
     r_bkg_out = self.r_bkg_out
     
     dt = fits.open(imgpath)[1].data        
     positions = [(pixX, pixY)]
     annulus_aperture = CircularAnnulus(positions, 
                                        r_in = r_bkg_in, r_out = r_bkg_out)
     annulus_masks = annulus_aperture.to_mask(method='center')
     annulus_data = annulus_masks[0].multiply(dt)
     
     bkg_fn = deepcopy(annulus_data)
     bad_bkg_mask = np.isnan(annulus_data)
     if manual_mask == True:    
         bad_bkg_mask[r_bkg_out+row_mask_start:r_bkg_out+row_mask_end, 
                      r_bkg_out+col_mask_start:r_bkg_out+col_mask_end] = True
     nbad_bkg = np.sum(bad_bkg_mask)
         
     self.nbad_bkg = nbad_bkg
     
     setnan = annulus_masks[0].data==0
     bkg_fn[setnan] = np.nan
     # bkgstd = np.nanstd(bkg_fn) 
     
     temp = bkg_fn.ravel()
     temp = temp[~np.isnan(temp)]
     bkgstd = 0.5 * (np.percentile(temp, 84.13)-np.percentile(temp, 15.86))
     # bkgstd = np.median(abs(temp - np.median(temp)))
     bkgmed = np.median(temp)
     
     self.bkgstd = bkgstd 
     self.bkg_fn = bkg_fn
     self.bkgmed = bkgmed
     
     if self.verbose == True:
         print ('\t bkgstd pixel RMS in original diff-image cutout = %.2f DN'%(self.bkgstd))
         print ('\t bkgmed pixel in original diff-image cutout = %.2f DN'%(self.bkgmed))
예제 #12
0
def psf_radial_reduce(img, reduction: Callable[[np.ndarray], float] = np.mean):
    # get center of image.
    xcenter, ycenter = centroid_quadratic(img)
    # last radius in pixel where ring is fully in image
    extent = np.min(img.shape)/2

    radii = np.linspace(0.1, extent, int(extent))
    values = []

    for r_in, r_out in zip(radii, radii[1:]):
        aper = CircularAnnulus([xcenter, ycenter], r_in, r_out)
        mask = aper.to_mask('center')
        values.append(reduction(mask.get_values(img)))


    return radii[:-1], np.array(values)/np.max(img)
예제 #13
0
def pc_aper_phot(im, cat, one_aper=False, bg_sigclip=False):

    im = im.astype(float)

    par = common.pc_params()

    positions = list(zip(cat['xcentroid'], cat['ycentroid']))

    radii = par['aper_phot_objrad'] if not one_aper else [par['aper_phot_objrad_best']]
    ann_radii = par['annulus_radii'] # should have 2 elements - inner and outer

    apertures = [CircularAperture(positions, r=r) for r in radii]
    annulus_apertures = CircularAnnulus(positions, r_in=ann_radii[0],
                                        r_out=ann_radii[1])
    annulus_masks = annulus_apertures.to_mask(method='center')

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(im)
        annulus_data_1d = annulus_data[mask.data > 0]
        if bg_sigclip:
            # this sigma_clipped_stats call is actually the slow part !!
            _, median_sigclip, std_bg = sigma_clipped_stats(annulus_data_1d)
            bkg_median.append(median_sigclip)
        else:
            bkg_median.append(np.median(annulus_data_1d))

    bkg_median = np.array(bkg_median)
    phot = aperture_photometry(im, apertures)

    for i, aperture in enumerate(apertures):
        aper_bkg_tot = bkg_median*_get_area_from_ap(aperture)
        cat['aper_sum_bkgsub_' + str(i)] = phot['aperture_sum_' + str(i)] - aper_bkg_tot

        cat['aper_bkg_' + str(i)] = aper_bkg_tot

    cat['sky_annulus_area_pix'] = _get_area_from_ap(annulus_apertures)
    cat['sky_annulus_median'] = bkg_median

    flux_adu = np.zeros((len(cat), len(radii)), dtype=float)

    for i in range(len(radii)):
        flux_adu[:, i] = cat['aper_sum_bkgsub_' + str(i)]

    cat['flux_adu'] = flux_adu

    return cat
예제 #14
0
def ap_phot(image, star_tbl, read_noise, exposure, r=1.5, r_in=1.5, r_out=3.):
    '''
        Given an image, go do some aperture photometry
    '''
    from astropy.stats import sigma_clipped_stats
    from photutils import aperture_photometry, CircularAperture, CircularAnnulus
    from photutils.utils import calc_total_error

    # Build apertures from star_tbl
    positions = np.transpose([star_tbl['x'], star_tbl['y']])
    apertures = CircularAperture(positions, r=r)
    annulus_apertures = CircularAnnulus(positions, r_in=r_in, r_out=r_out)
    annulus_masks = annulus_apertures.to_mask(method='center')

    # Get backgrounds in annuli
    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(image)
        annulus_data_1d = annulus_data[mask.data > 0]
        _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip.value)
    bkg_median = np.array(bkg_median) * image.unit

    # Set error
    error = calc_total_error(image.value, read_noise / exposure.value,
                             exposure.value)
    error *= image.unit
    # Perform aperture photometry
    result = aperture_photometry(image, apertures, error=error)
    result['annulus_median'] = bkg_median
    result['aper_bkg'] = bkg_median * apertures.area()
    result['aper_sum_bkgsub'] = result['aperture_sum'] - result['aper_bkg']

    # To-do: fold an error on background level into the aperture photometry error

    for col in result.colnames:
        result[col].info.format = '%.8g'  # for consistent table output


#    print("Aperture photometry complete")

    return result, apertures, annulus_apertures
예제 #15
0
def CircleMaskPhometry(data, location, index=2):
    Mimgdata = np.copy(data)
    Mlocatin = np.copy(location)

    Mapeture = CircularAperture(Mlocatin, r=6)
    Mannuals = CircularAnnulus(Mlocatin, r_in=8., r_out=11.)

    Eannuals_masks = Mannuals.to_mask(method='center')

    bkg_median = []
    for mask in Eannuals_masks:
        Eannuals_data = mask.multiply(Mimgdata)
        Eannulus_data_1d = Eannuals_data[mask.data > 0]
        meandata, median_sigclip, _ = sigma_clipped_stats(Eannulus_data_1d)
        bkg_median.append(median_sigclip)

    bkg_median = np.array(bkg_median)
    phot = aperture_photometry(Mimgdata, Mapeture)
    phot['annulus_median'] = bkg_median
    phot['aper_bkg'] = bkg_median * Mapeture.area
    phot['aper_sum_bkgsub'] = phot['aperture_sum'] - phot['aper_bkg']

    Mpositionflux = np.transpose(
        (phot['xcenter'], phot['ycenter'], phot['aper_sum_bkgsub']))

    displayimage(Mimgdata, 3, index)
    Mapeture.plot(color='blue', lw=0.5)
    Mannuals.plot(color='red', lw=0.2)
    plt.pause(0.001)
    plt.clf()
    #Mannulus_data = Eannuals_masks[0].multiply(Mimgdata)
    #displayimage(Mannulus_data,3,index+1)

    flux_sum = phot['aper_sum_bkgsub']
    magstar = 25 - 2.5 * np.log10(abs(flux_sum / 1))

    return Mpositionflux, magstar
예제 #16
0
파일: photometry.py 프로젝트: lgrcia/prose
class PhotutilsAperturePhotometry(Block):
    r"""
    Aperture photometry using the :code:`CircularAperture` and :code:`CircularAnnulus` of photutils_ with a wide range of apertures. By default annulus goes from 5 fwhm to 8 fwhm and apertures from 0.1 to 10 times the fwhm with 0.25 steps (leading to 40 apertures).

    The error (e.g. in ADU) is then computed following:

    .. math::
    
        \sigma = \sqrt{S + (A_p + \frac{A_p}{A_n})(b + r^2 + \frac{gain^2}{2}) + scint }


    .. image:: images/aperture_phot.png
        :align: center
        :width: 110px

    with :math:`S` the flux (ADU) within an aperture of area :math:`A_p`, :math:`b` the background flux (ADU) within an annulus of area :math:`A_n`, :math:`r` the read-noise (ADU) and :math:`scint` is a scintillation term expressed as:


    .. math::

        scint = \frac{S_fd^{2/3} airmass^{7/4} h}{16T}

    with :math:`S_f` a scintillation factor, :math:`d` the aperture diameter (m), :math:`h` the altitude (m) and :math:`T` the exposure time.

    The positions of individual stars are taken from :code:`Image.stars_coords` so one of the detection block should be used, placed before this one.

    For more details check https://photutils.readthedocs.io/en/stable/aperture.html

    |write| 
    
    - ``Image.stars_coords``
    - ``Image.apertures_area``
    - ``Image.sky``
    - ``Image.fluxes``
    - ``Image.annulus_area``
    - ``Image.annulus_rin``
    - ``Image.annulus_rout``
    - ``Image.apertures_radii``
    - ``Image.fluxes``

    |modify| 

    Parameters
    ----------
    apertures : ndarray or list, optional
        apertures in fraction of fwhm, by default None, i.e. np.arange(0.1, 8, 0.25)
    r_in : int, optional
        radius of the inner annulus in fraction of fwhm, by default 5
    r_out : int, optional
        radius of the outer annulus in fraction of fwhm, by default 8
    scale: bool or float:
        Multiplication factor applied to `apertures`.
        - if True: `apertures` multiplied by image.fwhm, varying for each image
        - if False: `apertures` not multiplied
        - if float: `apertures` multiplied `scale` and held fixed for all images
    """
    def __init__(self,
                 apertures=None,
                 r_in=5,
                 r_out=8,
                 scale=True,
                 sigclip=2.,
                 **kwargs):

        super().__init__(**kwargs)
        if apertures is None:
            self.apertures = np.arange(0.1, 8, 0.25)
        else:
            self.apertures = apertures

        self.annulus_inner_radius = r_in
        self.annulus_outer_radius = r_out
        self.annulus_final_rin = None
        self.annulus_final_rout = None
        self.aperture_final_r = None

        self.n_apertures = len(self.apertures)
        self.n_stars = None
        self.circular_apertures = None
        self.annulus_apertures = None
        self.annulus_masks = None

        self.circular_apertures_area = None
        self.annulus_area = None
        self.scale = scale
        self.sigclip = sigclip

        self._has_fix_scale = not isinstance(self.scale, bool)

    def set_apertures(self, stars_coords, fwhm=1):

        self.annulus_final_rin = self.annulus_inner_radius * fwhm
        self.annulus_final_rout = self.annulus_outer_radius * fwhm
        self.aperture_final_r = fwhm * self.apertures

        self.annulus_apertures = CircularAnnulus(
            stars_coords,
            r_in=self.annulus_final_rin,
            r_out=self.annulus_final_rout,
        )
        if callable(self.annulus_apertures.area):
            self.annulus_area = self.annulus_apertures.area()
        else:
            self.annulus_area = self.annulus_apertures.area

        self.circular_apertures = [
            CircularAperture(stars_coords, r=r) for r in self.aperture_final_r
        ]

        # Unresolved buf; sometimes circular_apertures.area is a method, sometimes a float
        if callable(self.circular_apertures[0].area):
            self.circular_apertures_area = [
                ca.area() for ca in self.circular_apertures
            ]
        else:
            self.circular_apertures_area = [
                ca.area for ca in self.circular_apertures
            ]

        self.annulus_masks = self.annulus_apertures.to_mask(method="center")
        self.n_stars = len(stars_coords)

    def run(self, image):
        try:
            if self._has_fix_scale:
                self.set_apertures(image.stars_coords, self.scale)
            elif self.scale:
                self.set_apertures(image.stars_coords, image.fwhm)
            else:
                self.set_apertures(image.stars_coords)
        except ZeroDivisionError:  # temporary
            image.discard = True
            return None

        bkg_median = []
        for mask in self.annulus_masks:
            annulus_data = mask.multiply(image.data)
            if annulus_data is not None:
                annulus_data_1d = annulus_data[mask.data > 0]
                _, median_sigma_clip, _ = sigma_clipped_stats(
                    annulus_data_1d, sigma=self.sigclip)
                bkg_median.append(median_sigma_clip)
            else:
                bkg_median.append(0.)

        bkg_median = np.array(bkg_median)

        image.apertures_area = self.circular_apertures_area
        image.annulus_sky = bkg_median
        image.sky = bkg_median.mean()
        image.fluxes = np.zeros((self.n_apertures, self.n_stars))
        image.annulus_area = self.annulus_area
        image.annulus_rin = self.annulus_final_rin
        image.annulus_rout = self.annulus_final_rout
        image.apertures_radii = self.aperture_final_r

        data = image.data.copy()
        data[data < 0] = 0

        photometry = aperture_photometry(data, self.circular_apertures)
        fluxes = np.array([
            photometry[f"aperture_sum_{a}"] -
            (bkg_median * self.circular_apertures_area[a])
            for a in range(len(self.apertures))
        ])

        # dummy values if negative or nan
        fluxes[np.isnan(fluxes)] = 1
        fluxes[fluxes < 0] = 1

        image.fluxes = fluxes

        self.compute_error(image)
        image.header["sky"] = np.mean(image.sky)

    def compute_error(self, image):

        image.errors = np.zeros((self.n_apertures, self.n_stars))

        for i, aperture_area in enumerate(self.circular_apertures_area):
            area = aperture_area * (1 + aperture_area / self.annulus_area)
            image.errors[i, :] = image.telescope.error(
                image.fluxes[i],
                area,
                image.sky,
                image.exposure,
                airmass=image.get("keyword_airmass"),
            )

    def citations(self):
        return "astropy", "photutils"
예제 #17
0
    async def __call__(self, image: Image) -> Image:
        """Do aperture photometry on given image.

        Args:
            image: Image to do aperture photometry on.

        Returns:
            Image with attached catalog.
        """
        loop = asyncio.get_running_loop()

        # no pixel scale given?
        if image.pixel_scale is None:
            log.warning("No pixel scale provided by image.")
            return image

        # fetch catalog
        if image.catalog is None:
            log.warning("No catalog in image.")
            return image
        sources = image.catalog.copy()

        # get positions
        positions = [(x - 1, y - 1) for x, y in sources.iterrows("x", "y")]

        # perform aperture photometry for diameters of 1" to 8"
        for diameter in [1, 2, 3, 4, 5, 6, 7, 8]:
            # extraction radius in pixels
            radius = diameter / 2.0 / image.pixel_scale
            if radius < 1:
                continue

            # defines apertures
            aperture = CircularAperture(positions, r=radius)
            annulus_aperture = CircularAnnulus(positions,
                                               r_in=2 * radius,
                                               r_out=3 * radius)
            annulus_masks = annulus_aperture.to_mask(method="center")

            # loop annuli
            bkg_median = []
            for m in annulus_masks:
                annulus_data = m.multiply(image.data)
                annulus_data_1d = annulus_data[m.data > 0]
                _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
                bkg_median.append(median_sigclip)

            # do photometry
            phot = await loop.run_in_executor(
                None,
                partial(aperture_photometry,
                        image.data,
                        aperture,
                        mask=image.mask,
                        error=image.uncertainty))

            # calc flux
            bkg_median_np = np.array(bkg_median)
            aper_bkg = bkg_median_np * aperture.area
            sources["fluxaper%d" % diameter] = phot["aperture_sum"] - aper_bkg
            if "aperture_sum_err" in phot.columns:
                sources["fluxerr%d" % diameter] = phot["aperture_sum_err"]
            sources["bkgaper%d" % diameter] = bkg_median_np

        # copy image, set catalog and return it
        img = image.copy()
        img.catalog = sources
        return img
예제 #18
0
def compare_rate_images(subarray_file,
                        fullframe_file,
                        out_dir='./',
                        subarray_threshold=100,
                        fullframe_threshold=100,
                        max_separation=3.,
                        aperture_radius=5,
                        bkgd_in_radius=6,
                        bkgd_out_radius=8,
                        subarray_rate_file=None,
                        fullframe_rate_file=None):
    """MAIN FUNCTiON FOR COMPARING SOURCE RATES IN RATE FILES

    Parameters
    ----------
    subarray_file : str
        Fits file containing the subarray data to be compared

    fullframe_file : sr
        Fits file containing the full frame data to be compared

    out_dir : str
        Output directory into which products are saved

    subarray_threshold : float
        Number of sigma above the background needed to identify a source

    fullframe_threshold : float
        Number of sigma above the background needed to identify a source

    max_separation : float
        Maximum allowed separation between sources, in pixels, between the subarray
        and full frame data in order for a source to be considered a match.

    aperture_radius : int
        Aperture radius (in pixels) to use for photometry

    bkgd_in_radius : int
        Inner radius (in pixels) to use for photometry background subtraction

    bkgd_out_radius : int
        Outer radius (in pixels) to use for photometry background subtraction

    Returns
    -------
    sub_sources : astropy.table.Table
        Table of source positions, equivalent full frame positions, and photometry
        results from the data
    """
    # Read in data
    subarray = datamodels.open(subarray_file)
    fullframe = datamodels.open(fullframe_file)

    # Quick test to see if using the rate files rather than the cal files
    # makes any difference in the LW discrepancy between fullframe and subarray data
    #if subarray_rate_file is not None:
    #    subarray_rate = datamodels.open(subarray_rate_file)
    #else:
    #    subarray_rate = subarray

    #if fullframe_rate_file is not None:
    #    fullframe_rate = datamodels.open(fullframe_rate_file)
    #else:
    #    fullframe_rate = fullframe

    # Define coord transform functions
    sub_xy_to_radec = subarray.meta.wcs.get_transform('detector', 'world')
    ff_radec_to_xy = fullframe.meta.wcs.get_transform('world', 'detector')
    ff_xy_to_radec = fullframe.meta.wcs.get_transform('detector', 'world')

    # Check to be sure the two images overlap
    ff_min_x = 0
    ff_min_y = 0
    ff_max_x = fullframe.meta.subarray.xsize - 1
    ff_max_y = fullframe.meta.subarray.ysize - 1
    cornerx = [
        0, 0, subarray.meta.subarray.xsize, subarray.meta.subarray.xsize
    ]
    cornery = [
        0, subarray.meta.subarray.ysize, subarray.meta.subarray.ysize, 0
    ]
    cornerra, cornerdec = sub_xy_to_radec(cornerx, cornery)
    ffcornerx, ffcornery = ff_radec_to_xy(cornerra, cornerdec)
    overlap = False
    for fcx, fcy in zip(ffcornerx, ffcornery):
        if ((fcx > ff_min_x) & (fcx < ff_max_x) & (fcy > ff_min_y)
                and (fcy < ff_max_y)):
            overlap = True
    if not overlap:
        print(
            "Suabrray file and full frame file do not overlap. Quitting.\n\n")
        return 0

    # Locate sources
    sub_fwhm = get_fwhm(subarray.meta.instrument.filter)
    sub_source_image = os.path.join(
        out_dir, '{}_subarray_source_map_countrate_compare.png'.format(
            os.path.basename(subarray_file)))
    #sub_sources = find_sources(subarray.data, threshold=subarray_threshold, fwhm=sub_fwhm, show_sources=True, save_sources=True, plot_name=sub_source_image)
    sub_sources = find_sources(subarray_rate.data,
                               threshold=subarray_threshold,
                               fwhm=sub_fwhm,
                               show_sources=True,
                               save_sources=True,
                               plot_name=sub_source_image)

    ff_fwhm = get_fwhm(fullframe.meta.instrument.filter)
    full_source_image = os.path.join(
        out_dir, '{}_fullframe_map_datamodels.png'.format(
            os.path.basename(fullframe_file)))
    #ff_sources = find_sources(fullframe.data, threshold=fullframe_threshold, fwhm=ff_fwhm, show_sources=True, save_sources=True, plot_name=full_source_image)
    ff_sources = find_sources(fullframe_rate.data,
                              threshold=fullframe_threshold,
                              fwhm=ff_fwhm,
                              show_sources=True,
                              save_sources=True,
                              plot_name=full_source_image)

    if sub_sources is None:
        print("No subarray sources to compare.")
        return 0

    if ff_sources is None:
        print("No full frame sources to compare")
        return 0

    # Put subarray sources in full frame detector coordinates
    # 1. Transform to RA, Dec
    # 2. Use WCS of full frame file to put coordinates in full frame coords

    # Transform subarray x,y -> RA, Dec -> full frame x, y
    ra, dec = sub_xy_to_radec(sub_sources['xcentroid'],
                              sub_sources['ycentroid'])
    ffx, ffy = ff_radec_to_xy(ra, dec)

    # Add RA, Dec and fullframe equivalent x,y to the subarray source catalog
    sub_sources['RA'] = ra
    sub_sources['Dec'] = dec
    sub_sources['fullframe_x'] = ffx
    sub_sources['fullframe_y'] = ffy

    # Find RA, Dec of sources in full frame catalog
    ffra, ffdec = ff_xy_to_radec(ff_sources['xcentroid'],
                                 ff_sources['ycentroid'])
    ff_sources['RA'] = ffra
    ff_sources['Dec'] = ffdec

    # Match catalogs
    ff_cat = SkyCoord(ra=ffra * u.degree, dec=ffdec * u.degree)
    sub_cat = SkyCoord(ra=ra * u.degree, dec=dec * u.degree)
    idx, d2d, d3d = sub_cat.match_to_catalog_3d(ff_cat)

    # Remove bad matches
    pscale = subarray.meta.wcsinfo.cdelt1
    if pscale is None:
        pscale = np.abs(subarray.meta.wcsinfo.cd1_1)
    pix_scale = pscale * 3600.
    max_sep = max_separation * pix_scale * u.arcsec  # Matches must be within a pixel
    sep_constraint = d2d < max_sep
    sub_catalog_matches = sub_sources[sep_constraint]
    ff_catalog_matches = ff_sources[idx[sep_constraint]]
    num_matched = len(ff_catalog_matches)
    print('Found {} matching sources in the two input files.'.format(
        num_matched))
    if num_matched == 0:
        print("No matching sources found. Quitting.\n\n\n")
        return 0, 0

    # What if we just do photometry on the sources in the subarray and their
    # calculated positions in the full frame?
    sub_pos = []
    ff_pos = []
    good_indexes = []
    non_zero_sub_dq = []
    non_zero_full_dq = []
    for i, line in enumerate(sub_catalog_matches):
        if ((line['fullframe_x'] > 5) & (line['fullframe_x'] < 2039) & \
            (line['fullframe_y'] > 5) & (line['fullframe_y'] < 2039)):
            sub_pos.append((line['xcentroid'], line['ycentroid']))
            #ff_pos.append((line['fullframe_x'], line['fullframe_y']))
            ff_pos.append((ff_catalog_matches[i]['xcentroid'],
                           ff_catalog_matches[i]['ycentroid']))
            good_indexes.append(i)

            # Make a note if there are any non-zero DQ flags within the apertures
            # During development, found some cases with NO_LIN_CORR and NO_FLAT_FIELD
            # that were screwing up photometry
            yc_sub = int(np.round(line['ycentroid']))
            xc_sub = int(np.round(line['xcentroid']))
            sub_dq = subarray.dq[yc_sub - 3:yc_sub + 4, xc_sub - 3:xc_sub + 4]
            if np.sum(sub_dq) > 0:
                non_zero_sub_dq.append(True)
            else:
                non_zero_sub_dq.append(False)

            yc = int(np.round(line['fullframe_x']))
            xc = int(np.round(line['fullframe_y']))
            sub_dq = fullframe.dq[yc - 1:yc + 2, xc - 1:xc + 2]
            if np.sum(sub_dq) > 0:
                non_zero_full_dq.append(True)
            else:
                non_zero_full_dq.append(False)

    print('Performing photometry on a total of {} sources.'.format(
        len(sub_pos)))

    # Now perform aperture photometry on the sources in the subarray
    # and full frame data. Keep the aperture small since the difference
    # in exposure time and SNR will be large
    #sub_pos = [(m['xcentroid'], m['ycentroid']) for m in sub_catalog_matches]
    #ff_pos = [(m['xcentroid'], m['ycentroid']) for m in ff_catalog_matches]

    sub_aperture = CircularAperture(sub_pos, r=aperture_radius)
    ff_aperture = CircularAperture(ff_pos, r=aperture_radius)
    sub_annulus = CircularAnnulus(sub_pos,
                                  r_in=bkgd_in_radius,
                                  r_out=bkgd_out_radius)
    full_annulus = CircularAnnulus(ff_pos,
                                   r_in=bkgd_in_radius,
                                   r_out=bkgd_out_radius)

    # Photometry
    #sub_phot_table = aperture_photometry(subarray.data, sub_aperture)
    #ff_phot_table = aperture_photometry(fullframe.data, ff_aperture)
    sub_phot_table = aperture_photometry(subarray_rate.data, sub_aperture)
    ff_phot_table = aperture_photometry(fullframe_rate.data, ff_aperture)

    sub_annulus_masks = sub_annulus.to_mask(method='center')
    full_annulus_masks = full_annulus.to_mask(method='center')

    #sub_bkg_median = median_background(sub_annulus_masks, subarray.data)
    sub_bkg_median = median_background(sub_annulus_masks, subarray_rate.data)

    sub_phot_table['annulus_median'] = sub_bkg_median
    sub_phot_table['aper_bkg'] = sub_bkg_median * sub_aperture.area
    sub_phot_table['aper_sum_bkgsub'] = sub_phot_table[
        'aperture_sum'] - sub_phot_table['aper_bkg']

    #full_bkg_median = median_background(full_annulus_masks, fullframe.data)
    full_bkg_median = median_background(full_annulus_masks,
                                        fullframe_rate.data)
    ff_phot_table['annulus_median'] = full_bkg_median
    ff_phot_table['aper_bkg'] = full_bkg_median * ff_aperture.area
    ff_phot_table['aper_sum_bkgsub'] = ff_phot_table[
        'aperture_sum'] - ff_phot_table['aper_bkg']

    # Compare photometry results
    delta_phot = ff_phot_table['aper_sum_bkgsub'].data - sub_phot_table[
        'aper_sum_bkgsub'].data
    delta_phot_perc = delta_phot / ff_phot_table['aper_sum_bkgsub'].data * 100.
    sub_phot_table['delta_from_fullframe'] = delta_phot
    sub_phot_table['delta_from_fullframe_percent'] = delta_phot_perc

    # Keep track of whether there are bad pixels in the apertures
    #sub_dq = np.zeros(len(sub_sources), dtype=bool)
    #sub_dq[good_indexes] = non_zero_sub_dq
    #sub_sources['sub_dq'] = sub_dq
    #full_dq = np.zeros(len(sub_sources), dtype=bool)
    #full_dq[good_indexes] = non_zero_full_dq
    #sub_sources['full_dq'] = full_dq

    sub_dq = np.zeros(len(sub_catalog_matches), dtype=bool)
    sub_dq[good_indexes] = non_zero_sub_dq
    sub_catalog_matches['sub_dq'] = sub_dq
    full_dq = np.zeros(len(sub_catalog_matches), dtype=bool)
    full_dq[good_indexes] = non_zero_full_dq
    sub_catalog_matches['full_dq'] = full_dq

    # Add photometry to the table
    #sub_phot_data = np.zeros(len(sub_sources))
    #sub_phot_data[good_indexes] = sub_phot_table['aper_sum_bkgsub'].data
    #ff_phot_data = np.zeros(len(sub_sources))
    #ff_phot_data[good_indexes] = ff_phot_table['aper_sum_bkgsub'].data
    #delta_phot_col = np.zeros(len(sub_sources))
    #delta_phot_col[good_indexes] = delta_phot
    #delta_phot_perc_col = np.zeros(len(sub_sources))
    #delta_phot_perc_col[good_indexes] = delta_phot_perc

    sub_phot_data = np.zeros(len(sub_catalog_matches))
    sub_phot_data[good_indexes] = sub_phot_table['aper_sum_bkgsub'].data
    ff_phot_data = np.zeros(len(sub_catalog_matches))
    ff_phot_data[good_indexes] = ff_phot_table['aper_sum_bkgsub'].data
    delta_phot_col = np.zeros(len(sub_catalog_matches))
    delta_phot_col[good_indexes] = delta_phot
    delta_phot_perc_col = np.zeros(len(sub_catalog_matches))
    delta_phot_perc_col[good_indexes] = delta_phot_perc

    #sub_sources['sub_phot'] = sub_phot_data
    #sub_sources['ff_phot'] = ff_phot_data
    #sub_sources['d_phot'] = delta_phot_col
    #sub_sources['d_phot_p'] = delta_phot_perc_col

    #sub_sources['xcentroid'].info.format = '7.3f'
    #sub_sources['ycentroid'].info.format = '7.3f'
    #sub_sources['fullframe_x'].info.format = '7.3f'
    #sub_sources['fullframe_y'].info.format = '7.3f'
    #sub_sources['sub_phot'].info.format = '7.3f'
    #sub_sources['ff_phot'].info.format = '7.3f'
    #sub_sources['d_phot'].info.format = '7.3f'
    #sub_sources['d_phot_p'].info.format = '7.3f'
    #print(sub_sources['xcentroid', 'ycentroid', 'fullframe_x', 'fullframe_y', 'sub_phot', 'ff_phot', 'd_phot_p', 'sub_dq', 'full_dq'])

    sub_catalog_matches['sub_phot'] = sub_phot_data
    sub_catalog_matches['ff_phot'] = ff_phot_data
    sub_catalog_matches['d_phot'] = delta_phot_col
    sub_catalog_matches['d_phot_p'] = delta_phot_perc_col

    sub_catalog_matches['xcentroid'].info.format = '7.3f'
    sub_catalog_matches['ycentroid'].info.format = '7.3f'
    sub_catalog_matches['fullframe_x'].info.format = '7.3f'
    sub_catalog_matches['fullframe_y'].info.format = '7.3f'
    sub_catalog_matches['sub_phot'].info.format = '7.3f'
    sub_catalog_matches['ff_phot'].info.format = '7.3f'
    sub_catalog_matches['d_phot'].info.format = '7.3f'
    sub_catalog_matches['d_phot_p'].info.format = '7.3f'

    final_sub_cat = sub_catalog_matches[good_indexes]

    print(final_sub_cat['xcentroid', 'ycentroid', 'fullframe_x', 'fullframe_y',
                        'sub_phot', 'ff_phot', 'd_phot_p', 'sub_dq',
                        'full_dq'])
    print('')

    # Save the complete table
    sub_base = os.path.basename(subarray_file).replace('.fits', '')
    full_base = os.path.basename(fullframe_file).replace('.fits', '')
    table_name = os.path.join(
        out_dir, 'photometry_comparison_{}_{}.txt'.format(sub_base, full_base))
    ascii.write(final_sub_cat, table_name, overwrite=True)
    print('Photometry results saved to: {}'.format(table_name))

    # Try filtering out sources where there is a pixel flagged in the full
    # frame or subarray DQ mask at the source location
    clean = []
    for row in final_sub_cat:
        clean.append(row['sub_dq'] == False and row['full_dq'] == False)
    if np.sum(clean) > 0:
        clean_table = final_sub_cat[clean]
        print('Excluding sources with a pixel flagged in the DQ array:')
        med_clean_diff = np.around(np.median(clean_table['d_phot_p']), 1)
        print(
            'Median photometry difference between subarray and full frame sources is: {}%\n\n\n'
            .format(med_clean_diff))
    else:
        clean_table = None
        print('No sources without a flagged pixel in the DQ arrays.')
    return final_sub_cat, clean_table
        mag_ann = np.zeros(N_star)
        merr_ann = np.zeros(N_star)

        # aperture sum
        apert_sum = APPHOT(img_uint16, DAOapert,
                           method='exact')['aperture_sum']
        ap_area = DAOapert.area()
        #print(apert_sum)

        apert_result = 'ID, Msky, sky_std, Sky count Pixel_N, Sky reject Pixel_N, mag_ann, merr_ann\n'

        for star_ID in range(0, N_stars)[10:12]:

            # since our `DAOannul` has many elements :
            mask_annul = (DAOannul.to_mask(method='center'))[star_ID]
            mask_apert = (DAOapert.to_mask(method='center'))[star_ID]
            # CAUTION!! YOU MUST USE 'center', NOT 'exact'!!!

            cutimg = mask_annul.cutout(img)
            #cutimg.tofile('{0!s}_DAOstarfinder_Star_Flux_pixel_value_starID_{1:04}.csv'.format(f_name[:-4], star_ID), sep=',')
            df_cutimg = pd.DataFrame(cutimg * 65536.0, dtype=np.uint16)
            df_cutimg.to_csv(
                '{0!s}_DAOstarfinder_Star_Flux_pixel_value_starID_{1:04}.csv'.
                format(f_name[:-4], star_ID))

            cut_apert = mask_apert.cutout(img)
            #cutimg.tofile('{0!s}_DAOstarfinder_Star_Flux_pixel_value_starID_{1:04}.csv'.format(f_name[:-4], star_ID), sep=',')
            df_cut_apert = pd.DataFrame(cut_apert * 65536.0, dtype=np.uint16)
            df_cut_apert.to_csv(
                '{0!s}_DAOstarfinder_Star_apertruer_Flux_pixel_value_starID_{1:04}.csv'
예제 #20
0
def runPhotUtils(drcInfo, radius=4, suffix='_pu.dat', date=dateDef_):
    """
    Input:
    drcInfo: a text file with the information from the headers
    of the fits files -- TARGNAME, FILTER1, FILTER2, EXPTIME,
    ORIENTAT, RA, DEC, and JDAN; these can be easily extracted
    using astropy.io.fits; one could also open the fits files
    in this code, but that seems more memory intensive than
    just opening the fits files once and outputting to a file.

    Variables to change: filternames, EEBand, ZPT, r_in,
    and r_out. Can replace _rad with _r(integer radius) if it
    helps you keep track of things.

    When running this in a loop on multiple targets, sometimes
    one could run out of memory space. This causes the computer
    to kill the program. When this happens, just remove the
    targets that have already been run from the drcInfo and
    run again.
    """

    info = np.loadtxt(drcInfo, dtype=str)
    infoN = np.genfromtxt(drcInfo, dtype=str, names=True)
    nameCols = np.array(infoN.dtype.names)

    jdan = np.int(np.where(nameCols == 'JDAN')[0])
    filt1 = np.int(np.where(nameCols == 'FILTER1')[0])
    filt2 = np.int(np.where(nameCols == 'FILTER2')[0])
    targN = np.int(np.where(nameCols == 'TARGNAME')[0])

    fileNames = info[:, jdan]

    for ff in range(len(info)):

        image = drcDir + fileNames[ff] + ".fits"
        print(image)

        hdu = fits.open(image)
        sci = hdu[1].data
        hdu.close()

        data = sci.copy()

        f1 = info[:, filt1][ff]
        f2 = info[:, filt2][ff]

        targname = info[:, targN][ff]
        print(targname)

        # Would need to change the below numbers if
        # the data is in different filters. It could
        # be handy to have a table to call.
        if (f1 == 'F606W') or (f2 == 'F606W'):
            filt = 'F606W'
            if abs(radius - 4) <= 1e-3:
                EEband = 0.839  # 4 pixel
            elif abs(radius - 3) <= 1e-3:
                EEband = 0.795
            ZPT = 26.667

        elif (f1 == 'F814W') or (f2 == 'F814W'):
            filt = 'F814W'
            if abs(radius - 4) <= 1e-3:
                EEband = 0.830  # 4 pixels
            elif abs(radius - 3) <= 1e-3:
                EEband = 0.77
            ZPT = 26.779

        mean, median, std = sigma_clipped_stats(data, sigma=3.0, maxiters=10)

        daofind = DAOStarFinder(fwhm=2.5, threshold=5. * std)
        sources = daofind(data - median)

        loc = np.array([sources['xcentroid'], sources['ycentroid']])
        positions = np.transpose(loc)

        apertures_rad = CircularAperture(positions, r=radius)
        rawflux_rad = aperture_photometry(data, apertures_rad)

        #  Added this on Nov 2
        rawflux_rad['roundness1'] = sources['roundness1']
        rawflux_rad['roundness2'] = sources['roundness2']
        rawflux_rad['sharpness'] = sources['sharpness']

        annulus_apertures = CircularAnnulus(positions, r_in=9., r_out=12.)

        annulus_masks = annulus_apertures.to_mask(method='center')

        bkg_median = []
        for mask in annulus_masks:

            annulus_data = mask.multiply(data)
            annulus_data_1d = annulus_data[mask.data > 0]
            _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
            bkg_median.append(median_sigclip)

        bkg_median = np.array(bkg_median)

        rawflux_rad['annulus_median'] = bkg_median
        rawflux_rad['aper_bkg'] = bkg_median * apertures_rad.area

        rawflux_rad['final_phot'] = rawflux_rad['aperture_sum'] \
            - rawflux_rad['aper_bkg']

        mask_negative = (rawflux_rad['final_phot'] > 0)
        rawflux_pos_rad = rawflux_rad[mask_negative]

        final_phot = -2.5 * np.log10(rawflux_pos_rad['final_phot']/EEband) \
            + ZPT

        rawflux_pos_rad['magr'] = final_phot

        rawflux_pos_rad['id'] = np.arange(0, len(rawflux_pos_rad), 1)

        s0 = ' '
        header = s0.join(rawflux_pos_rad.dtype.names)

        saveDir = f2mag_dirs(targname, date=date, workDir='../')

        outName = saveDir + fileNames[ff] + '_' + filt + suffix

        np.savetxt(outName, rawflux_pos_rad, header=header)

        # An attempt to free up memory; Didn't seem to work?
        rawflux_pos_rad = None
        data = None
        sci = None

        print('Moving On.')

    return None
def find_zero_point(stack,
                    fil,
                    ext,
                    catalog,
                    data_path=None,
                    show_fwhm=False,
                    show_bkg=False,
                    show_contours=False,
                    r_annulus_in=3.5,
                    r_annulus_out=4.5,
                    log=None):
    '''

    Function used to find the FWHM, zero point, and zero point error for a specific stack and filter.

    Initially finds all sources in specified catalog within the image and matches them to a
    10+ sigma source in the image.
    Then makes a series of cuts to determine the standard stars to use when finding the zero point:

        * Only considers sources with a center count (in adu / pix / sec) between 100 and 300
            - Plots radial profiles of these sources
            - Finds the median FWHM of the image from these sources (returns this value)
        * Removes sources too close to the edge
        * Removes sources that are too close to each other
        * Removes oblong sources (most likely galaxies or two very close stars)

    Checks isophotes of remaining sources to determine if the stack is aligned correctly.
        * Shows the user a sample cutout of a source that reflects the isophotes of the stack as a whole
    If not, then allows the user the option of checking them individual images and possibly remaking the stack
    with `revisit_stack` before continuing.

    If ``show_bkg`` is True, shows the remaining sources to be used when finding the zero point, color coded by
    the median background level.

    Finds the instrumental magnitudes of the remaining sources and compares to the magnitudes from the catalog
    and determines the zero point and zero point error. Returns these values along with the FWHM of the image.

    Parameters
    ----------
    :param stack: str
        Name of the stack to use (include path to the stack).

    :param fil: str
        Name of the filter of the stack.

    :param ext: int
        Extension to use (only used if `revisit_stack` is called).

    :param catalog: str
        Name of catalog to query for magnitudes.

    :param data_path: str, optional
        Path to the original data (only used if `revisit_stack` is called).
        Default is ``None``. If ``None`` and `revisit_stack` is called, error will occur.

    :param show_fwhm: boolean, optional
        Set to ``True`` to view the radial profiles when finding the FWHM of the image.
        Default is ``False``.

    :param show_bkg: boolean, optional
        Set to ``True`` to view the final sources before finding the zero point,
        color coded by the median background level.
        Default is ``False``.

    :param show_contours: boolean, optional
        Set to ``True`` to view the contours when examining the isophotes. Also shows isophotes in `revisit_stack`.
        Default is ``False``.

    :param r_annulus_in: int, optional
        Radius for the inner annulus when performing photometry on the final sources to determine their
        instrumental magnitude.
        Default is ``3.5``.

    :param r_annulus_out: int, optional
        Radius for the oter annulus when performing photometry on the final sources to determine their
        instrumental magnitude.
        Default is ``4.5``.

    :param log:
        In-depth log.
        If no log is inputted, information is printed out instead of being written to ``log``.
        Default is ``None``.

    Returns
    -------

    :return: float, float, float
        First float is the calculated zero point of the image.
        Second float is the calculated zero point error of the image.
        Third float is the calculated FWHM of the image.

    '''

    if log is not None:
        log.info("Computing FWHM of sources in the stack %s" % stack)
    else:
        print("Computing FWHM of sources in the stack %s" % stack)

    with fits.open(stack) as hdr:
        header, data = hdr[0].header, hdr[0].data
    w = wcs.WCS(header)

    # Center in pixel coordinates, change to real coordinates
    real_coords = w.wcs_pix2world(
        np.array([[header['NAXIS1'] / 2, header['NAXIS2'] / 2]], np.float), 1)
    coords = SkyCoord(real_coords[0][0] * u.deg,
                      real_coords[0][1] * u.deg,
                      frame='fk5')

    Vizier.ROW_LIMIT = -1  # So that you get all the rows, not just the first 50

    # Information about the sources from catalog found in the extension (full area, not radially)
    cat_ID, cat_ra, cat_dec, cat_mag, cat_err = find_catalog(catalog, fil)
    cat = Vizier.query_region(coords, width=13.65 * u.arcmin, catalog=cat_ID)
    if len(cat) == 0:
        log.info('No catalog sources at input coordinates!  Exiting...')
        sys.exit()
    else:
        cat = cat[0]
        mask = (~cat[cat_ra].mask) & (~cat[cat_dec].mask) &\
            (~cat[cat_mag].mask) & (~cat[cat_err].mask)
        cat = cat[mask]

    # Find the sources above 10 std in the image
    _, median, std = sigma_clipped_stats(data, sigma=3.0)
    daofind = DAOStarFinder(
        fwhm=7, threshold=10. *
        std)  # Looking for best sources, so raise threshold way up
    dao_sources = daofind(
        np.asarray(data))  # DAOStarFinder sources == dao_sources
    if log is not None:
        log.info(
            "{0} objects found in {1} and {2} sources above 10 std found in the stack"
            .format(len(cat), catalog, len(dao_sources)))
        log.info(
            "DAOStarFinder found {0} sources that are 10 std above the background"
            .format(len(dao_sources)))
    else:
        print(
            "{0} objects found in {1} and {2} sources above 10 std found in the stack"
            .format(len(cat), catalog, len(dao_sources)))
        print(
            "DAOStarFinder found {0} sources that are 10 std above the background"
            .format(len(dao_sources)))

    ra_dao = [
    ]  # Change pixel coordinates to real coordinates for DAOStarFinder sources
    dec_dao = []
    for i, j in enumerate(dao_sources):
        pixel_coords = np.array([[j['xcentroid'], j['ycentroid']]], np.float)
        dao_coords = w.wcs_pix2world(pixel_coords, 1)
        ra_dao.append(dao_coords[0][0])
        dec_dao.append(dao_coords[0][1])

    # Coordinates need to be in real, not pixel
    coords_dao = SkyCoord(ra_dao * u.deg, dec_dao * u.deg,
                          frame='fk5')  # Coordinates from sources
    coords_cat = SkyCoord(cat[cat_ra], cat[cat_dec],
                          frame='fk5')  # Coordinates from catalog

    # Match each catalog source to the closest dao_source
    idx, d2, d3 = coords_cat.match_to_catalog_sky(
        coords_dao)  # Match smaller to larger
    if log is not None:
        log.info("{0} objects have been matched".format(len(idx)))
    else:
        print("{0} objects have been matched".format(len(idx)))

    # Next step: find the radial profiles from intermediate-brightness sources and then find the fwhm of the field
    step_size = 1
    fwhm_orig = 4

    coords = [
        w.wcs_world2pix(np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0]
        for j in idx
    ]  # Positions of DAO

    radii = np.arange(step_size, 2.5 * fwhm_orig,
                      step_size)  # Radii of apertures
    x_data = np.arange(
        0, 2.5 * fwhm_orig,
        step_size)  # Steps (starting at zero, to 2.5 * fwhm, by step_size)

    apers_area = [np.pi * (step_size**2)]  # Circle area = pi*(r**2)
    for r in radii:
        apers_area.append(np.pi *
                          ((r + step_size)**2 -
                           r**2))  # Annulus area = pi*(r_out**2 - r_in**2)

    apertures = [CircularAperture(coords, r=step_size)
                 ]  # For circle aperture around center
    for r in radii:
        apertures.append(CircularAnnulus(coords, r_in=r, r_out=r +
                                         step_size))  # Annuli apertures

    phot_table = aperture_photometry(
        data, apertures
    )  # Each row is a source, with len(x_data) photometry measurements

    if show_fwhm:
        fig, ax = plt.subplots()
    sigmas = []
    new_idx = []
    for i, source in enumerate(
            phot_table
    ):  # Find the sums per area (adu/pix/sec) for each of the sources
        sums = [source[i] / apers_area[i - 3] for i in range(3, len(source))]
        # Only consider moderately bright sources; too bright are saturated and too dim can have larger error values
        if 100 <= sums[0] <= 30000 and sums[len(sums) - 1] < 30000:
            try:  # Try unless it takes too much time...if that happens, it's probably a bad fit and we don't want it
                w_fit = x_data
                f_fit = sums
                g, _ = curve_fit(fix_x0, w_fit, f_fit)
                if show_fwhm:  # Add two plots: radial profile and Gaussian fit
                    ax.plot(x_data, sums, 'o-', alpha=0.5, lw=2,
                            markersize=4)  # Plot the radial profiles
                    ax.plot(x_data,
                            fix_x0(x_data, *g),
                            '^-',
                            alpha=0.5,
                            lw=2,
                            markersize=4)
                sigmas.append(
                    np.abs(g[1])
                )  # Sigma can be either positive or negative; fix_x0 allows both
                new_idx.append(
                    idx[i]
                )  # Append to a list that will be used later for future sources
            except RuntimeError:
                pass

    fwhm_values = [i * 2.35482 for i in sigmas]
    sigmas_post_clipping, _, _ = sigmaclip(sigmas, low=3,
                                           high=3)  # Clip outlier sigma values
    med = np.median(sigmas_post_clipping)  # Median sigma value
    fwhm = med * 2.35482  # FWHM to be used to find the zero point and make cuts
    if log is not None:
        log.info(
            "Median sigma: {0} Median FWHM: {1} Number of sources counted/total found in field: {2}/{3}"
            .format('%5.3f' % med, '%2.3f' % fwhm, len(sigmas_post_clipping),
                    len(dao_sources)))
    else:
        print(
            "Median sigma: {0} Median FWHM: {1} Number of sources counted/total found in field: {2}/{3}"
            .format('%5.3f' % med, '%2.3f' % fwhm, len(sigmas_post_clipping),
                    len(dao_sources)))

    clipped_fwhm_values = [i * 2.35482 for i in sigmas_post_clipping]

    if show_fwhm:
        ax.set_title("Radial profiles of sources 10 std above background"
                     )  # Plot of radial profiles and Gaussian fits
        ax.set_xlabel("Radial distance from centroid of source (pixels)")
        ax.set_ylabel("Count (adu per second per pixel)")
        plt.show()

        fig, ax = plt.subplots(
        )  # Histogram of fwhm_values after sigma clipping
        plt.hist(clipped_fwhm_values, bins=30)
        ax.set_title("FWHM values for Gaussian-fit radial profiles")
        ax.set_xlabel("FWHM value for best-fit Gaussian (pixels)")
        plt.show()

    # Calculate the zero point
    if log is not None:
        log.info(
            "Computing the zero point of the stack {0} with filter {1} and FWHM of {2}"
            .format(stack, fil, '%2.3f' % fwhm))
    else:
        print(
            "Computing the zero point of the stack {0} with filter {1} and FWHM of {2}"
            .format(stack, fil, '%2.3f' % fwhm))

    orig_coords = [
        w.wcs_world2pix(np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0]
        for j in new_idx
    ]  # DAO pos

    # First cut: removing sources near the edge
    edge_error = 100
    first_cut_coords, first_cut_idx, first_fwhm_values = [], [], []
    for i, j in enumerate(orig_coords):
        if not (j[0] < edge_error or data.shape[0] - j[0] < edge_error
                or j[1] < edge_error or data.shape[0] - j[1] < edge_error):
            first_cut_coords.append(orig_coords[i])
            first_cut_idx.append(new_idx[i])
            first_fwhm_values.append(fwhm_values[i])

    # Second cut: removing sources that are not isolated
    # 1: Make sure no two sources are within (r_annulus_out + r_annulus_in) * fwhm of each other
    # 2: If a second source is present in the annulus, the sigma clipping for the mask will cut out the additional
    # counts from these pixels so that the second source will be cut out and not interfere with the zero point
    # If the sources are close enough together, they will either be cut out here or will look oblong and be cut out next
    overlap = []
    for i, j in enumerate(first_cut_coords):
        for k in range(i + 1, len(first_cut_coords)):
            # (r_ann_out + r_ann_in)*fwhm so that the annulus of one doesn't overlap the circular aperture of the other
            if np.sqrt((j[0] - first_cut_coords[k][0])**2 + (j[1] - first_cut_coords[k][1])**2) < \
                    (r_annulus_out + r_annulus_in) * fwhm:
                overlap.append(i)
                overlap.append(k)

    second_cut_coords, second_cut_idx, second_fwhm_values = [], [], []
    for i, j in enumerate(first_cut_coords):
        if i not in overlap:
            second_cut_coords.append(j)
            second_cut_idx.append(first_cut_idx[i])
            second_fwhm_values.append(first_fwhm_values[i])

    # Third cut: removing sources that are not circular
    # Part 1: remove sources with a fwhm 1.2 * median fwhm of the field (1.2 determined by considering 5 stacks)
    third_cut_coords, third_cut_idx, third_fwhm_values = [], [], []
    for i, j in enumerate(second_fwhm_values):
        if not (j > 1.2 * fwhm):
            third_cut_coords.append(second_cut_coords[i])
            third_cut_idx.append(second_cut_idx[i])
            third_fwhm_values.append(j)

    if log is not None:
        log.info(
            "Checking if the stack was performed correctly with {0} sources".
            format(len(third_cut_coords)))  # Final check
    else:
        print("Checking if the stack was performed correctly with {0} sources".
              format(len(third_cut_coords)))  # Final check

    d_x_y = 40
    field_sigmas = cutouts_2d_gaussian(stack,
                                       third_cut_coords,
                                       d_x_y,
                                       fwhm,
                                       show_cutouts=show_contours)
    field_ratio = np.median(field_sigmas)
    if log is not None:
        log.info("Median field ratio (y_sigma/x_sigma): {0}".format(
            '%2.3f' % field_ratio))
    else:
        print("Median field ratio (y_sigma/x_sigma): {0}".format('%2.3f' %
                                                                 field_ratio))

    if not ((1 / 1.2) > field_ratio or 1.2 < field_ratio):
        if log is not None:
            log.info(
                "Good to go! Now calculating the zero point with {0} sources".
                format(len(third_cut_coords)))
        else:
            print(
                "Good to go! Now calculating the zero point with {0} sources".
                format(len(third_cut_coords)))

    else:  # Stack is slightly off. Check to see what the issue is
        if log is not None:
            log.info("Stack seems to be off. Sample cutout shown")
        else:
            print("Stack seems to be off. Sample cutout shown")

        if show_contours:
            for i, j in enumerate(third_cut_coords):
                d = data[int(j[1]) - d_x_y:int(j[1]) + d_x_y,
                         int(j[0]) - d_x_y:int(j[0]) +
                         d_x_y]  # Cutout of source
                x, y = np.meshgrid(
                    np.linspace(0,
                                np.shape(d)[1] - 1,
                                np.shape(d)[1]),
                    np.linspace(0,
                                np.shape(d)[0] - 1,
                                np.shape(d)[0]))

                initial_guess = (100, d_x_y, d_x_y, fwhm / 2.35482,
                                 fwhm / 2.35482, 0, 0
                                 )  # Initial guess is IMPORTANT
                popt, pcov = curve_fit(twoD_Gaussian, (x, y),
                                       d.ravel(),
                                       p0=initial_guess)  # Fit of 2D Gaussian

                if np.abs((popt[4] / popt[3]) - field_ratio) < 0.01:
                    if log is not None:
                        log.info(
                            'x sigma = {0} y sigma = {1} ratio = {2}'.format(
                                '%7.3f' % popt[3], '%7.3f' % popt[4],
                                '%7.3f' % (popt[4] / popt[3])))
                    else:
                        print('x sigma = {0} y sigma = {1} ratio = {2}'.format(
                            '%7.3f' % popt[3], '%7.3f' % popt[4],
                            '%7.3f' % (popt[4] / popt[3])))

                    fitted_data = twoD_Gaussian((x, y), *popt)
                    contours = np.arange(np.min(d), np.max(d), 30.)
                    norm = simple_norm(data, 'sqrt', percent=99)
                    plt.imshow(d, norm=norm)
                    plt.colorbar()
                    plt.contour(x,
                                y,
                                fitted_data.reshape(np.shape(d)),
                                contours,
                                colors='w')
                    plt.show()
                    break  # Breaks out of loop

        if input(
                "Would you like to revisit the stack and look at isophotes within it? Type 'yes' or 'no': "
        ) == "yes":
            try:
                zp, zp_err, fwhm = revisit_stack(stack, fil, ext,
                                                 third_cut_coords, fwhm,
                                                 show_fwhm, show_bkg,
                                                 show_contours, data_path, log)
                return zp, zp_err, fwhm
            except TypeError:
                pass
        elif log is not None:
            log.info(
                "User decided not to remake the stack even though isophotes are not circular"
            )

    final_coords, final_idx = third_cut_coords, third_cut_idx  # Final coordinates and idx to use

    # Annuli of DAO sources
    annulus_apertures = CircularAnnulus(final_coords,
                                        r_in=fwhm * r_annulus_in,
                                        r_out=fwhm * r_annulus_out)
    annulus_masks = annulus_apertures.to_mask(
        method='center')  # Create masks to highlight pixels in annuli

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        _, median_sigclip, stddev = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)
    bkg_median = np.array(bkg_median)

    if show_bkg:  # Finds the sources for each upper median background limit and plots them over the image
        groupings = [[], [], [], [], [], [], [], []]
        for i, j in enumerate(bkg_median):
            if j < 0:
                groupings[0].append(i)
            elif j < 0.1:
                groupings[1].append(i)
            elif j < 0.25:
                groupings[2].append(i)
            elif j < 0.5:
                groupings[3].append(i)
            elif j < 0.75:
                groupings[4].append(i)
            elif j < 1:
                groupings[5].append(i)
            elif j < 2.5:
                groupings[6].append(i)
            else:
                groupings[7].append(i)

        positions = [[], [], [], [], [], [], [], []]
        for i, j in enumerate(final_idx):
            if i in groupings[0]:  # Positions of DAO
                positions[0].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[1]:
                positions[1].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[2]:
                positions[2].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[3]:
                positions[3].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[4]:
                positions[4].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[5]:
                positions[5].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[6]:
                positions[6].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
            elif i in groupings[7]:
                positions[7].append(
                    w.wcs_world2pix(
                        np.array([[ra_dao[j], dec_dao[j]]], np.float), 1)[0])
        colors = [
            'w', 'r', 'orange', 'lime', 'deepskyblue', 'b', 'purple', 'k'
        ]
        bkg_med_list = []
        for i, j in enumerate(positions):
            if len(positions[i]) != 0:
                apertures = CircularAperture(
                    positions[i], 2.5 * fwhm)  # Aperture to perform photometry
                # Annuli of DAO sources
                annulus_apertures = CircularAnnulus(positions[i],
                                                    r_in=fwhm * r_annulus_in,
                                                    r_out=fwhm * r_annulus_out)
                annulus_masks = annulus_apertures.to_mask(
                    method='center'
                )  # Create masks to highlight pixels in annuli

                bkg_median_1 = []
                for mask in annulus_masks:
                    annulus_data = mask.multiply(data)
                    annulus_data_1d = annulus_data[mask.data > 0]
                    _, median_sigclip, stddev = sigma_clipped_stats(
                        annulus_data_1d)
                    bkg_median_1.append(median_sigclip)
                bkg_median_1 = np.array(bkg_median_1)
                bkg_med_list.append(bkg_median_1)
                apertures.plot(color='white', lw=2)
                annulus_apertures.plot(color=colors[i], lw=2)

        if log is not None:
            log.info("Plotting the sources by median background value")
            log.info(
                "Number of sources per list (<0, 0.1, 0.25, 0.5, 0.75, 1, 2.5, >2.5)"
            )
        else:
            print("Plotting the sources by median background value")
            print(
                "Number of sources per list (<0, 0.1, 0.25, 0.5, 0.75, 1, 2.5, >2.5)"
            )
        for i in range(len(bkg_med_list)):
            if log is not None:
                log.info(len(bkg_med_list[i]))
            else:
                print(len(bkg_med_list[i]))

        norm = simple_norm(data, 'sqrt', percent=99)
        plt.suptitle(
            'w < 0 | r < 0.1 | o < 0.25 | g < 0.5 | lb < 0.75 | db < 1 | p < 2.5 | b > 2.5'
        )
        plt.imshow(data, norm=norm)
        plt.colorbar()
        plt.show(
        )  # Plot of matched sources with annuli, color coded by median background value in annulus

    mag_cat = []  # Moving past show_bkg
    magerr_cat = []
    for i, j in enumerate(final_idx):
        mag_cat.append(
            cat[i][cat_mag])  # Reference magnitudes of catalog objects
        magerr_cat.append(cat[i][cat_err])

    final_apertures = CircularAperture(final_coords, 2.5 *
                                       fwhm)  # Aperture to perform photometry
    # Annuli of DAO sources
    final_ann_aps = CircularAnnulus(final_coords,
                                    r_in=fwhm * r_annulus_in,
                                    r_out=fwhm * r_annulus_out)
    final_ann_masks = final_ann_aps.to_mask(
        method='center')  # Create masks to highlight pixels in annuli

    final_bkg_median = []
    for mask in final_ann_masks:  # For each masked annulus, find the median background value and add to list
        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        _, median_sigclip, stddev = sigma_clipped_stats(annulus_data_1d)
        final_bkg_median.append(median_sigclip)
    final_bkg_median = np.array(final_bkg_median)

    sigma_clip = SigmaClip(
        sigma=3)  # This section is to find the error on the background
    bkg_estimator = MedianBackground()
    bkg = Background2D(data, (120, 120),
                       filter_size=(3, 3),
                       sigma_clip=sigma_clip,
                       bkg_estimator=bkg_estimator)
    error = calc_total_error(data, bkg.background_rms, 1)

    phot_table = aperture_photometry(
        data, final_apertures, error=error)  # Photometry, error accounted for
    bkg_aper = final_bkg_median * final_apertures.area

    flux = np.asarray(phot_table['aperture_sum']) - bkg_aper
    fluxerr = np.sqrt(np.asarray(phot_table['aperture_sum_err'])**2 + bkg_aper)

    ap = absphot.absphot()
    zpt, zpterr = ap.zpt_iteration(flux, fluxerr, mag_cat, magerr_cat)
    print(zpt, zpterr)

    mag_inst = -2.5 * np.log10(
        (np.asarray(phot_table['aperture_sum']) -
         bkg_aper))  # Instrumental magnitudes (and error)

    # Find zero point
    if log is not None:
        log.info(
            'Calculating zero point from {0} stars with sigma clipping and a maximum std error of 0.1.'
            .format(len(mag_inst)))
    else:
        print(
            'Calculating zero point from {0} stars with sigma clipping and a maximum std error of 0.1.'
            .format(len(mag_inst)))

    for i in np.flip(np.linspace(1, 3, num=10)):
        _, zp, zp_err = sigma_clipped_stats(mag_cat - mag_inst, sigma=i)
        if zp_err < 0.1:
            break

    if log is not None:
        log.info('zp = {0} +/- {1}'.format('%2.3f' % zp, '%2.3f' % zp_err))
    else:
        print('zp = {0} +/- {1}'.format('%2.3f' % zp, '%2.3f' % zp_err))

    # Online zp (vega): https://www.ukirt.hawaii.edu/instruments/wfcam/user_guide/performance.html
    return zp, zp_err, fwhm
예제 #22
0
g_iraf, ge_iraf, gf_iraf, gsky_iraf = np.loadtxt('phot_test_g.txdump', usecols=(1,2,3,4), unpack=True)
i_iraf, ie_iraf, if_iraf, isky_iraf = np.loadtxt('phot_test_i.txdump', usecols=(1,2,3,4), unpack=True)

# now try python
x, y = np.loadtxt(coords_file, usecols=(0,1), unpack=True)
positions = np.array(zip(x,y))

hdu_g = fits.open(fits_g)
hdu_i = fits.open(fits_i)

apertures = CircularAperture(positions, r=8.)
annulus_apertures = CircularAnnulus(positions, r_in=10., r_out=14.)
print apertures.area()
ap_mask = apertures.to_mask(method='subpixel', subpixels=7)
dummy = np.ones_like(hdu_g[0].data)
ann_mask = annulus_apertures.to_mask(method='center')
ap_g = [m.apply(hdu_g[0].data) for i,m in enumerate(ap_mask)]
ap_i = [m.apply(hdu_i[0].data) for i,m in enumerate(ap_mask)]
area_g = [np.sum(m.apply(dummy)) for i,m in enumerate(ap_mask)]
area_i = [np.sum(m.apply(dummy)) for i,m in enumerate(ap_mask)]

print area_g, area_i
# plt.imshow(ap_g[0], interpolation='nearest')
# plt.show()
ann_g = [m.apply(hdu_g[0].data, fill_value=-999.) for i,m in enumerate(ann_mask)]
ann_i = [m.apply(hdu_i[0].data, fill_value=-999.) for i,m in enumerate(ann_mask)]

flux_g = np.array([np.sum(a) for j,a in enumerate(ap_g)])
flux_i = np.array([np.sum(a) for j,a in enumerate(ap_i)])

bkg_med_g = np.array([sigma_clipped_stats(a, iters=0, mask_value=0.)[1] for j,a in enumerate(ann_g)])
예제 #23
0
 sigma_clip = SigmaClip(sigma=3)
 bkg_estimator = MedianBackground()
 bkg = Background2D(sci_med, (60, 60),
                    filter_size=(3, 3),
                    sigma_clip=sigma_clip,
                    bkg_estimator=bkg_estimator,
                    mask=masked.mask,
                    exclude_percentile=80)
 error = calc_total_error(sci_med, bkg.background_rms, 1)
 fwhm = np.float(input('FWHM of stars? '))
 log.info('Using FWHM of ' + str(fwhm))
 apertures = CircularAperture(positions, r=fwhm * 2.5)
 annulus_apertures = CircularAnnulus(positions,
                                     r_in=fwhm * 3,
                                     r_out=fwhm * 4)
 annulus_masks = annulus_apertures.to_mask(method='center')
 bkg_median = []
 for mask in annulus_masks:
     annulus_data = mask.multiply(sci_med)
     annulus_data_1d = annulus_data[mask.data > 0]
     _, median_sigclip, stddev = sigma_clipped_stats(annulus_data_1d)
     bkg_median.append(median_sigclip)
 bkg_median = np.array(bkg_median)
 phot = aperture_photometry(sci_med, apertures, error=error)
 bkg_aper = bkg_median * apertures.area
 mag_inst = -2.5 * np.log10(
     (np.asarray(phot['aperture_sum']) - bkg_aper))
 mag_inst_err = 2.5 / np.log(10.) * np.asarray(
     phot['aperture_sum_err']) / np.asarray(phot['aperture_sum'])
 log.info('Calculating magnitudes for ' + sci)
 for i, m in enumerate(mag_inst):
예제 #24
0
파일: photometry.py 프로젝트: fschmnn/pnlf
def growth_curve(data, x, y, model, rmax=30, alpha=None, plot=False, **kwargs):
    '''do a growth curve analysis on the given star
    
    measure the amount of light as a function of radius and tries
    to fit a Gaussian to the measured profile. Returns the FWHM
    of the Gaussian.

    Parameters
    ----------

    model : str
        shape of the PSF. Must be either `gaussian` or `moffat`.
    
    rmax : float
        maximum radius for the growth curve
    '''

    # -----------------------------------------------------------------
    # determine background (we use same bkg_median for all apertures)
    # -----------------------------------------------------------------

    r_in = 0.7 * rmax
    r_out = rmax
    annulus_aperture = CircularAnnulus((x, y), r_in=r_in, r_out=r_out)
    mask = annulus_aperture.to_mask(method='center')
    annulus_data = mask.multiply(data)
    annulus_data_1d = annulus_data[mask.data > 0]
    _, bkg_median, _ = sigma_clipped_stats(
        annulus_data_1d[~np.isnan(annulus_data_1d)], maxiters=None)

    if np.isnan(bkg_median):
        logger.warning('background contains NaN')
        if model == 'moffat':
            return np.array([np.inf, np.inf]), np.atleast_1d(np.inf)
        else:
            return np.array([np.inf]), np.atleast_1d(np.inf)

    # -----------------------------------------------------------------
    # measure flux for different aperture radii
    # -----------------------------------------------------------------

    radius = []
    flux = []

    r = 0.5
    while True:
        if r > rmax:
            logger.debug(f'no convergence within a radius of {rmax:.2f}')
            break

        aperture = CircularAperture((x, y), r=r)
        phot = aperture_photometry(data, aperture)
        flux.append(phot['aperture_sum'][0] - aperture.area * bkg_median)
        radius.append(r)

        #if test_convergence(flux,**kwargs):
        #    pass
        #    break

        r += 0.5

    radius = np.array(radius)
    flux = np.array(flux)
    flux = flux / flux[-1]

    if np.any(np.isnan(flux)):
        if model == 'moffat':
            return np.array([np.inf, np.inf]), np.atleast_1d(np.inf)
        else:
            return np.array([np.inf]), np.atleast_1d(np.inf)

    # -----------------------------------------------------------------
    # fit moffat or gaussian
    # -----------------------------------------------------------------

    if model == 'moffat':
        '''
        guess = np.array([2,2])
        func = light_in_moffat
        fit,sig = optimization.curve_fit(func, radius,flux , guess)
        alpha, gamma = fit[0], fit[1]
        fwhm = 2*gamma * np.sqrt(2**(1/alpha)-1)
        #print(f'alpha={alpha:.2f}, gamma={gamma:.2f}, fwhm={fwhm:.2f}')
        '''
        func = light_in_moffat

        if alpha:
            model = _moffat_model(alpha=alpha)
            model.alpha.fixed = True
        else:
            model = _moffat_model()

        fitter = fitting.LevMarLSQFitter()
        fitted_line = fitter(model, radius, flux)
        alpha, gamma = fitted_line.parameters
        fit = [alpha, gamma]
        fwhm = 2 * gamma * np.sqrt(2**(1 / alpha) - 1)
        print(f'alpha={alpha:.2f}, gamma={gamma:.2f}, fwhm={fwhm:.2f}')

    elif model == 'gaussian':
        '''
        guess =5
        func = light_in_gaussian
        fit,sig = optimization.curve_fit(func, radius,flux , guess)
        fwhm = fit[0]
        '''
        func = light_in_gaussian

        model = _gaussian_model()
        fitter = fitting.LevMarLSQFitter()
        fitted_line = fitter(model, radius, flux)
        fwhm = fitted_line.parameters
        fit = [fwhm]
    else:
        raise TypeError('model must be `moffat` or `gaussian`')

    if plot:
        from astropy.visualization import simple_norm

        fig = plt.figure(figsize=(6.9, 6.9 / 2))
        ax1 = fig.add_subplot(121)
        ax2 = fig.add_subplot(122)

        norm = simple_norm(data, percent=99, clip=False)  #, percent=99.)
        yslice = slice(int(x - rmax / 2), int(x + rmax / 2))
        xslice = slice(int(y - rmax / 2), int(y + rmax / 2))
        im1 = ax1.imshow(data[xslice, yslice],
                         norm=norm,
                         origin='lower',
                         cmap='Greens')

        p = ax2.plot(radius, flux, label='observed')
        ax2.plot(radius,
                 func(radius, *fit),
                 label='fit',
                 ls='--',
                 color=p[0].get_color())

        plt.xlabel('radius in px')
        plt.ylabel('light in aperture')
        plt.legend()
        plt.grid()

    return fit
예제 #25
0
파일: lib.py 프로젝트: epascale/pyCIRSF
def photom_av(ima, pos, radius, r_in=False, r_out=False, mode='median'):
    '''
    Aperture photometry in an aperture located at pixel coordinates 
    pos = ( (x0, y0), (x1, y1), ... ) with a radius=radius.
    When r_in and r_out are given, background is estimated in CircularAnnulus and subtracted.
    
    mode refers to how the background is estimated within the circlar annulus.
    Can be 'median' or 'mean'
    
    Photometry is calculating by median averaging the pixels within the aperture and 
    multiplying by the number of pixels in the aperture (including fractions of pixels).

    '''
    # Setting up the mask 
    if hasattr(ima, 'mask'):
      if ima.mask.size == 1:
	mask = np.zeros(ima.shape, dtype=np.bool) | ima.mask
      else:
        mask = ima.mask.copy()
    else:
        mask = np.zeros(ima.shape, dtype=np.bool)
        
        
    ### Performing the actual photometry - identical for each method
    # Median averaging of flux in aperture
    # Setting up the aperture 
    apertures = CircularAperture(pos, r = radius) 
    ap_mask = apertures.to_mask(method='center')
    # Setting up arrays to store data
    nflx = len(ap_mask)
    flx = np.zeros(nflx, dtype=np.float)
    flux_max = np.zeros(nflx, dtype=np.float)
    flux_min = np.zeros(nflx, dtype=np.float)
    # Median averaging of flux
    for i, am in enumerate(ap_mask):
      fluxmask = ~mask & am.to_image(shape=mask.shape).astype(np.bool)
      flx[i] = np.median(ima[fluxmask])
      flux_max[i] = np.max(ima[fluxmask])
      flux_min[i] = np.min(ima[fluxmask])
      
      
      
      
    # Aperture photometry on mask to see how many masked pixels are in the 
    # aperture
    apm       = aperture_photometry(mask.astype(int), apertures)
    # Number of unmasked pixels in aperture
    ap_area   = Column(name = 'area_aper',
		       data=apertures.area() - apm['aperture_sum'].data)
    
    # Flux in aperture using median av flux and fractional no. pixels in aperture
    flux_init = flx*ap_area
    

    ### Two different modes for analysing the background
    if ( r_in and r_out and mode in ('mean', 'median') ):
      
      ### This stuff is the same regardless of method
      # Setting up the annulus
      anulus_apertures = CircularAnnulus(pos, r_in=r_in, r_out=r_out)
      # Performing annulus photometry on the mask
      bkgm = aperture_photometry(mask.astype(int), anulus_apertures)
      # Number of masked pixels in bkg
      mbkg_area = Column(name = 'bpix_bkg',
			 data=bkgm['aperture_sum'])  
      # Number of non-masked pixels in aperture and bkg        
      bkg_area  = Column(name = 'area_bkg',
			 data=anulus_apertures.area() - bkgm['aperture_sum'])
      
      
      ### This stuff is specific to the mean
      if mode == 'mean':
	# Perform the annulus photometry on the image
	bkg  = aperture_photometry(ima, anulus_apertures, mask=mask)
        # Average bkg where this divides by only number of NONMASKED pixels
        # as the aperture photometry ignores the masked pixels
        bkga = Column(name='background',
		      data=bkg['aperture_sum']/bkg_area)
        # Bkg subtracted flux
        flux = flux_init - bkga*ap_area
        # Adding that data
        ap.add_column(bkga)
        
        
      elif mode == 'median':
	# Number of pixels in the annulus, a different method
	aperture_mask = anulus_apertures.to_mask(method='center')
	nbkg = len(aperture_mask)
	
	# Background mask
	bkgm = np.zeros(nbkg, dtype=np.float)
	
	# Median averaging
	for i, am in enumerate(aperture_mask):
	  bmask = ~mask & am.to_image(shape=mask.shape).astype(np.bool)
	  bkgm[i] = np.median(ima[bmask])
		
	flux = flux_init - bkgm*ap_area
	bkgm = Column(name = 'background', data = bkgm)

        
    return flux, apm, flx, ap_area, flux_max, flux_min #flux, no.masked pixels in ap, median av flux
예제 #26
0
파일: photometry.py 프로젝트: fschmnn/pnlf
def measure_flux(LineMaps,
                 peak_tbl,
                 alpha,
                 Rv,
                 Ebv,
                 lines=None,
                 aperture_size=1.5,
                 background='local',
                 extinction='MW'):
    '''measure flux for all lines in lines

    for each position in peak_tbl, the flux inside an aperture of 
    `aperture_size` is measured for each line `lines` (if `LineMaps`
    has an extinsion). The background is estimated from an annulus
    with a sigma clipped median (both median and mean are reported).
    The [OIII] fluxes are Milky Way extinction corrected and the 
    internal extinction is estimated if the Halpha and Hbeta lines
    are present.
    
    Parameters
    ----------
    
    LineMaps : Galaxy
       Galaxy object with detected sources
    
    peak_tbl : astropy table
        Table with columns `x` and `y` (position of the sources)
    
    alpha : float
        power index of the moffat

    lines : list
       list of lines that are measured
    
    aperture_size : float
       size of the aperture in multiples of the fwhm

    background : no longer used

    extinction : no longer used
    '''

    #del self.peaks_tbl['SkyCoord']

    # convertion factor from arcsec to pixel (used for the PSF)
    input_unit = 1e-20 * u.erg / u.cm**2 / u.s
    '''
    check the input parameters
    '''

    # self must be of type Galaxy
    if not isinstance(LineMaps, ReadLineMaps):
        logger.warning('input should be of type ReadLineMaps')

    if background not in ['global', 'local', None]:
        raise TypeError(f'unknown Background estimation: {background}')

    # if no line is specified, we measure the flux in all line maps
    if not lines:
        lines = LineMaps.lines
    else:
        # make sure lines is a list
        lines = [lines] if not isinstance(lines, list) else lines

        for line in lines:
            if not hasattr(LineMaps, line):
                raise AttributeError(
                    f'{LineMaps.name} has no attribute {line}')

    logger.info(
        f'measuring fluxes in {LineMaps.name} for {len(peak_tbl)} sources\naperture = {aperture_size} fwhm'
    )
    '''
    loop over all lines to measure the fluxes for all sources
    '''
    out = {}
    for line in lines:

        logger.info(f'measuring fluxes in {line} line map')

        # select data and error (copy in case we need to modify it)
        data = getattr(LineMaps, f'{line}').copy()
        error = getattr(LineMaps, f'{line}_err').copy()

        try:
            v_disp = getattr(LineMaps, f'{line}_SIGMA')
        except:
            logger.warning('no maps with velocity dispersion for ' + line)
            v_disp = np.zeros(data.shape)

        # the fwhm varies slightly with wavelength
        wavelength = int(re.findall(r'\d{4}', line)[0])
        PSF_correction = correct_PSF(wavelength)

        # calculate a global background map
        mask = np.isnan(data)
        '''
        bkg = Background2D(data,(10,10), 
                        #filter_size=(15,15),
                        sigma_clip= None,#SigmaClip(sigma=3.,maxiters=None), 
                        bkg_estimator=MedianBackground(),
                        mask=mask).background
        bkg[mask] = np.nan

        from astropy.convolution import convolve, Gaussian2DKernel, Box2DKernel

        kernel = Box2DKernel(10) #Gaussian2DKernel(10) 
        bkg_convolve = convolve(data,kernel,nan_treatment='interpolate',preserve_nan=True)
    
        # this is too slow and the masks ignore bright HA emitter etc.
        source_mask = np.zeros(self.shape,dtype=bool)

        for fwhm in np.unique(peak_tbl['fwhm']):
            source_part = peak_tbl[peak_tbl['fwhm']==fwhm]
            positions = np.transpose((source_part['x'], source_part['y']))
            r = 4 * (fwhm-PSF_correction) / 2 
            aperture = CircularAperture(positions, r=r)
            for m in aperture.to_mask(method='center'):
                source_mask |= m.to_image(self.shape).astype(bool)
        '''
        '''
        loop over the individual pointings (they have different fwhm)
        '''
        for fwhm in np.unique(peak_tbl['fwhm']):

            source_part = peak_tbl[peak_tbl['fwhm'] == fwhm]
            positions = np.transpose((source_part['x'], source_part['y']))

            gamma = (fwhm - PSF_correction) / (2 * np.sqrt(2**(1 / alpha) - 1))

            if aperture_size > 3:
                logger.warning('aperture > 3 FWHM')
            r = aperture_size * (fwhm - PSF_correction) / 2
            aperture = CircularAperture(positions, r=r)

            # measure the flux for each source
            phot = aperture_photometry(data, aperture, error=error)

            # the local background subtraction estimates the background for
            # each source individually (annulus with 5 times the area of aperture)
            r_in = 4 * (fwhm - PSF_correction) / 2
            r_out = np.sqrt(5 * r**2 + r_in**2)
            annulus_aperture = CircularAnnulus(positions,
                                               r_in=r_in,
                                               r_out=r_out)
            annulus_masks = annulus_aperture.to_mask(method='center')

            # background from annulus with sigma clipping
            bkg_median = []
            bgk_mean = []
            for mask in annulus_masks:
                # select the pixels inside the annulus and calulate sigma clipped median
                annulus_data = mask.multiply(data)
                annulus_data_1d = annulus_data[mask.data > 0]
                _, median_sigclip, _ = sigma_clipped_stats(
                    annulus_data_1d[~np.isnan(annulus_data_1d)],
                    sigma=3,
                    maxiters=10,
                    cenfunc='median')
                mean_sigclip, _, _ = sigma_clipped_stats(
                    annulus_data_1d[~np.isnan(annulus_data_1d)],
                    sigma=3,
                    maxiters=10,
                    cenfunc='mean')
                bkg_median.append(median_sigclip)
                bgk_mean.append(mean_sigclip)

            # save bkg_median in case we need it again and multiply background with size of the aperture
            phot['bkg_median'] = np.array(bkg_median) * aperture.area
            phot['bkg_mean'] = np.array(bgk_mean) * aperture.area
            '''
            # background from annulus with masked sources
            ones = np.ones(self.shape)
            # calculate flux in annulus where other sources are masked
            bkg_phot = aperture_photometry(data,annulus_aperture,mask=source_mask)
            # calculate area of the annulus (parts can be masked)
            bkg_area = aperture_photometry(ones,annulus_aperture,mask=source_mask)
            # save bkg_median in case we need it again
            phot['bkg_median'] = bkg_phot['aperture_sum'] / bkg_area['aperture_sum'] 
            # multiply background with size of the aperture
            phot['bkg_local'] = phot['bkg_median'] * aperture.area
            '''

            phot[f'{line}_flux'] = phot['aperture_sum'] - phot['bkg_median']
            phot[f'{line}_flux_raw'] = phot['aperture_sum']

            # we don't subtract the background from OIII because there is none
            #if line == 'OIII5006_DAP':
            #    phot[f'{line}_flux'] = phot['aperture_sum']

            # correct for flux that is lost outside of the aperture
            phot[f'{line}_flux'] /= light_in_moffat(r, alpha, gamma)
            phot[f'{line}_flux_raw'] /= light_in_moffat(r, alpha, gamma)
            phot[f'{line}_flux_err'] = phot[
                'aperture_sum_err'] / light_in_moffat(r, alpha, gamma)
            phot['bkg_median'] /= light_in_moffat(r, alpha, gamma)
            phot['bkg_mean'] /= light_in_moffat(r, alpha, gamma)
            #print(f'{fwhm}: {light_in_moffat(r,alpha,gamma):.2f}')

            # calculate the average of the velocity dispersion
            aperture = CircularAperture(positions, r=4)
            SIGMA = aperture_photometry(v_disp, aperture)
            phot['SIGMA'] = SIGMA['aperture_sum'] / aperture.area

            # calculate stellar mass (for mass specific PN number, not used)
            #aperture = CircularAperture(positions, r=2)
            #stellar_mass = aperture_photometry(LineMaps.stellar_mass,aperture)
            #phot['stellar_mass'] = stellar_mass['aperture_sum'] / aperture.area

            # save fwhm in an additional column
            phot['fwhm'] = fwhm

            # concatenate new sources with output table
            if 'flux' in locals():
                phot['id'] += np.amax(flux['id'], initial=0)
                flux = vstack([flux, phot])
            else:
                flux = phot

        # for consistent table output
        for col in flux.colnames:
            flux[col].info.format = '%.8g'
        flux['fwhm'].info.format = '%.3g'

        out[line] = flux

        # we need an empty table for the next line
        del flux

    # so far we have an individual table for each emission line
    for line, v in out.items():

        # find the wavelength for the extinction correction
        wavelength = re.findall(r'\d{4}', line)
        if len(wavelength) != 1:
            logger.error(
                'line name must contain wavelength as 4 digit number in angstrom'
            )
        wavelength = int(wavelength[0])

        # first we create the output table with
        if 'flux' not in locals():
            flux = v[['id', 'xcenter', 'ycenter', 'fwhm']]
            flux.rename_columns(['xcenter', 'ycenter'], ['x', 'y'])
            flux['x'] = flux[
                'x'].value  # we don't want them to be in pixel units
            flux['y'] = flux['y'].value  #

        flux[f'{line}_flux'] = v[f'{line}_flux']
        flux[f'{line}_flux_err'] = v[f'{line}_flux_err']
        flux[f'{line}_flux_raw'] = v[f'{line}_flux_raw']
        flux[f'{line}_bkg_median'] = v[f'bkg_median']
        flux[f'{line}_bkg_mean'] = v[f'bkg_mean']

        # linemaps are already MW extinction corrected (OIII sum is not)
        # the new [OIII] fluxes use the DAP [OIII] errors and hence are already extinction corrected
        if line == 'OIII5006':
            rc = pyneb.RedCorr(R_V=3.1, E_BV=Ebv, law='CCM89')
            flux['OIII5006_flux'] *= rc.getCorr(5006)
            flux['OIII5006_flux_raw'] *= rc.getCorr(5006)
            flux['OIII5006_bkg_median'] *= rc.getCorr(5006)
            flux['OIII5006_bkg_mean'] *= rc.getCorr(5006)
            logger.info(
                f'lambda{wavelength}: Av={-2.5*np.log10(1/rc.getCorr(5006)):.2f}'
            )

        # those columns are only needed for tests
        if False:
            flux[f'{line}_aperture_sum'] = v['aperture_sum']
            flux[f'{line}_bkg_local'] = v['bkg_local']
            flux[f'{line}_bkg_median'] = v['bkg_median']
            #flux[f'{k}_bkg_convole'] = v['bkg_convolve']
        flux[f'{line}_SIGMA'] = v['SIGMA']

    # the internal extinction correction based on the balmer decrement
    # we do not calculate an error of E(B-V) and hance also do not account for this in the corrected errors
    if 'HB4861' in lines and 'HA6562' in lines:
        logger.info('correction for internal extinction with balmer decrement')
        rc = pyneb.RedCorr(R_V=3.1, law='CCM89')
        rc.setCorr(obs_over_theo=flux['HA6562_flux'] / flux['HB4861_flux'] /
                   2.86,
                   wave1=6562.81,
                   wave2=4861.33)
        rc.E_BV[(rc.E_BV < 0) |
                (flux['HB4861_flux'] < 3 * flux['HB4861_flux_err']) |
                (flux['HA6562_flux'] < 3 * flux['HA6562_flux_err'])] = 0
        flux['EBV_balmer'] = rc.E_BV
        for line in lines:
            wavelength = int(re.findall(r'\d{4}', line)[0])
            flux[f'{line}_flux_corr'] = flux[f'{line}_flux'] * rc.getCorr(
                wavelength)
            flux[f'{line}_flux_corr_err'] = flux[
                f'{line}_flux_err'] * rc.getCorr(wavelength)
            flux[f'{line}_bkg_median_corr'] = flux[
                f'{line}_bkg_median'] * rc.getCorr(wavelength)
            flux[f'{line}_bkg_mean_corr'] = flux[
                f'{line}_bkg_mean'] * rc.getCorr(wavelength)

    logger.info('all flux measurements completed')

    return flux
예제 #27
0
파일: photometry.py 프로젝트: fschmnn/pnlf
def measure_single_flux(img,
                        positions,
                        aperture_size,
                        model='Moffat',
                        alpha=None,
                        gamma=None,
                        fwhm=None,
                        bkg=True,
                        plot=False):
    '''Measure the flux for a single object
    
    The Background is subtracted from an annulus. No error is reported

    Parameters
    ----------

    img : ndarray
        array with the image data

    position : tuple
        position (x,y) of the source

    aperture_size : float
        aperture size in units of FWHM
    
    alpha : float
        power index of the Moffat
    
    gamma : float
        other parameter for the Moffat

    bkg : bool
        determines if the background is subtracted
    '''

    if model == 'Moffat':
        fwhm = 2 * gamma * np.sqrt(2**(1 / alpha) - 1)

    r = aperture_size * (fwhm) / 2
    aperture = CircularAperture(positions, r=r)
    phot = aperture_photometry(img, aperture)

    r_in = 4 * fwhm / 2
    r_out = np.sqrt(3 * r**2 + r_in**2)
    annulus_aperture = CircularAnnulus(positions, r_in=r_in, r_out=r_out)
    mask = annulus_aperture.to_mask(method='center')

    annulus_data = mask.multiply(img)
    annulus_data_1d = annulus_data[mask.data > 0]
    _, median_sigclip, _ = sigma_clipped_stats(
        annulus_data_1d[~np.isnan(annulus_data_1d)], sigma=3, maxiters=1)

    phot['bkg_median'] = np.array(median_sigclip)
    phot['bkg_local'] = phot['bkg_median'] * aperture.area
    if bkg:
        phot['flux'] = phot['aperture_sum'] - phot['bkg_local']
    else:
        phot['flux'] = phot['aperture_sum']

    if model == 'Moffat':
        correction = light_in_moffat(r, alpha, gamma)
    elif model == 'Gaussian':
        correction = light_in_gaussian(r, fwhm)
    else:
        raise ValueError(f'unkown model {model}')
    phot['flux'] /= correction

    if plot:
        from astropy.visualization import simple_norm

        norm = simple_norm(img, 'sqrt', percent=99)
        plt.imshow(img, norm=norm)
        aperture.plot(color='orange', lw=2)
        annulus_aperture.plot(color='red', lw=2)
        plt.show()

    #return phot
    return phot['flux'][0]
예제 #28
0
    def find_fluxes(self, image_number, set):

        #get total shift from first image to this one
        x_shift, y_shift = self.get_total_shift(image_number, set)

        #add the shift onto the positions of the stars in the first image
        #to find their positions in this image
        x = self.catalogue['xcentroid'] + x_shift
        y = self.catalogue['ycentroid'] + y_shift

        positions = (x, y)

        # Shape and size of aperture object
        apertures = CircularAperture(positions, r=9)

        image_data = self.get_image(image_number, set)[0].data

        # Local background subtraction

        # Define size of background aperture
        annulus_apertures = CircularAnnulus(positions, r_in=10, r_out=15)
        apertures = CircularAperture(positions, r=9)
        apers = [apertures, annulus_apertures]

        # find counts in each aperture and annulus
        phot_table2 = aperture_photometry(image_data, apers)

        for col in phot_table2.colnames:
            phot_table2[
                col].info.format = '%.8g'  # for consistent table output

        # MEAN
        # Background sub using mean

        phot_table2['residual_aperture_sum_mean'] = phot_table2[
            'aperture_sum_1'] * 0 - 1
        phot_table2['mean'] = phot_table2['aperture_sum_1'] * 0 - 1

        for i in range(len(phot_table2)):

            x = positions[0][i]
            y = positions[1][i]

            #check that largest aperture does not exceed the boundaries of the image
            if Utilities.is_within_boundaries(x, y, len(image_data[0]),
                                              len(image_data), 15):

                # Calc the mean background in the second aperture ring
                bkg_mean = phot_table2['aperture_sum_1'][
                    i] / annulus_apertures.area
                phot_table2['mean'][i] = bkg_mean

                # Calc background level in each main aperture and subtract
                bkg_sum = bkg_mean * apertures.area
                final_sum = phot_table2['aperture_sum_0'][i] - bkg_sum
                phot_table2['residual_aperture_sum_mean'][i] = final_sum

        phot_table2[
            'residual_aperture_sum_mean'].info.format = '%.8g'  # for consistent table output

        # MEDIAN

        # Background sub using median
        phot_table2['median'] = phot_table2['aperture_sum_1'] * 0 - 1
        phot_table2['residual_aperture_sum_med'] = phot_table2[
            'aperture_sum_1'] * 0 - 1

        #for each source
        for q in range(0, len(phot_table2)):
            x = positions[0][q]
            y = positions[1][q]
            xypos = (x, y)

            #check that largest aperture does not exceed the boundaries of the image
            if Utilities.is_within_boundaries(x, y, len(image_data[0]),
                                              len(image_data), 15):
                annulus = CircularAnnulus(xypos,
                                          r_in=Constants.inner_radius,
                                          r_out=Constants.outer_radius)
                ann_mask = annulus.to_mask(method='center')
                weighted_data = ann_mask.multiply(image_data)
                phot_table2['median'][q] = np.median(
                    weighted_data[weighted_data != 0])

                # Calc the median background in the second aperture ring
                bkg_med = phot_table2['median'][q]

                # Calc background level in each main aperture and subtract
                bkg_sum = bkg_med * apertures.area
                final_sum = phot_table2['aperture_sum_0'][q] - bkg_sum

                phot_table2['residual_aperture_sum_med'][q] = final_sum

        phot_table2[
            'residual_aperture_sum_med'].info.format = '%.8g'  # for consistent table output

        return phot_table2
예제 #29
0
r2 = pyregion.open('regions_phys.reg')
full_mask = r2.get_mask(hdu=hdu_g[0])
# full_mask_i = full_mask_g #r2.get_mask(hdu=hdu_i[0])

# plt.imshow(full_mask_g)
# plt.show()

median_g = 191.9227
median_i = 623.8584

positions = [(5819.6140, 5535.8705)]
radii = [91., 136., 182., 227., 273., 318., 364., 409., 455., 500., 545., 591., 636., 682., 727., 772., 818.]
apertures = [CircularAperture(positions, r=r) for r in radii]
annulus = CircularAnnulus(positions, r_in=900., r_out=1000.)
ann_mask = annulus.to_mask(method='exact')
ann_g = ann_mask[0].apply(hdu_g[0].data) * ann_mask[0].apply(np.abs(full_mask-1))
ann_i = ann_mask[0].apply(hdu_i[0].data) * ann_mask[0].apply(np.abs(full_mask-1))
ann_keep = np.where(ann_g != 0)
bkg_mean_g = np.median(ann_g[ann_keep])
bkg_mean_i = np.median(ann_i[ann_keep])

print bkg_mean_g, bkg_mean_i

aper_mask = apertures[0].to_mask(method='exact')
dbl_mask_aper = aper_mask[0].apply(hdu_g[0].data-bkg_mean_g, fill_value=-999.) * aper_mask[0].apply(np.abs(full_mask-1), fill_value=-999.)
plt.imshow(dbl_mask_aper)
plt.show()

mean, median, std = sigma_clipped_stats(dbl_mask_aper, mask_value=-999.)
print mean, median, std
예제 #30
0
def ap_phot(positions, data, radius, r_in=None, r_out=None):
    try:

        from astropy.stats import sigma_clipped_stats
        from photutils import aperture_photometry
        from photutils import CircularAperture, CircularAnnulus
        import numpy as np
        import os, sys

        if r_in == None or r_out == None:
            print('Warning - ap_phot -  inner and outer annulus not set ')
            print('Setting to r-in = 10 r_out = 20')
            r_in = 10
            r_out = 20

        if not isinstance(positions, list):
            positions = list(positions)

        apertures = CircularAperture(positions, r=radius)
        annulus_apertures = CircularAnnulus(positions, r_in=r_in, r_out=r_out)
        annulus_masks = annulus_apertures.to_mask(method='center')

        #        print(positions)
        if r_out >= data.shape[0] or r_out > data.shape[1]:
            print('Error - Apphot - Annulus size greater than image size')

        bkg_median = []

        if not isinstance(annulus_masks, list):
            annulus_masks = list(annulus_masks)

        for mask in annulus_masks:

            annulus_data = mask.multiply(data)

            if annulus_data is None:

                if np.isnan(np.sum(annulus_data)):
                    print('Error - annulus data is nan')
                print('Error - Annulus == None setting to zero')
                annulus_data = np.zeros(
                    (int(2 * r_out + 1), int(2 * r_out + 1)))

            annulus_data_1d = annulus_data[mask.data > 0]

            annulus_data_1d_nonan = annulus_data_1d[~np.isnan(annulus_data_1d)]

            _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d_nonan,
                                                       cenfunc=np.nanmedian,
                                                       stdfunc=np.nanstd)
            bkg_median.append(median_sigclip)

        bkg_median = np.array(bkg_median)
        phot = aperture_photometry(data, apertures)
        phot = phot.to_pandas()

        phot['annulus_median'] = bkg_median
        phot['aper_bkg'] = bkg_median * np.pi * radius**2

        phot['aper_sum_bkgsub'] = phot['aperture_sum'] - phot['aper_bkg']

        aperture_sum = np.array(phot['aper_sum_bkgsub'])
        bkg_sum = np.array(phot['annulus_median'])

        aperture_sum[aperture_sum <= 0] = 0

    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        print(exc_type, fname, exc_tb.tb_lineno, e)

    return aperture_sum, bkg_sum
예제 #31
0
def photom(ima, pos, radius, r_in=None, r_out=None, method='median'):
    '''
    Aperture photometry in an aperture located at pixel coordinates 
    pos = ( (x0, y0), (x1, y1), ... ) with a radius=radius.
    When r_in and r_out are given, background is estimated in CircularAnnulus and subtracted.
    
    method refers to how the background is estimated within the circlar annulus.
    Can be 'median' or 'mean' or 'mode'

    '''
    
    ima_local = np.ma.asanyarray(ima.copy())
    ima_local.fill_value = np.nan
    mask_ = ima_local.mask
    ima_  = ima_local.filled()
    
    ### Do photometry - identical for each method
    apertures = CircularAperture(pos, r = radius)
    ap        = aperture_photometry(ima_, apertures, 
                                    mask=mask_, method='exact')
    # Aperture photometry on mask to estimate # of masked pixels in aperture
    apm       = aperture_photometry(mask_.astype(int), apertures,
                                    method='exact')
    ap.add_columns( [apertures.area()-apm['aperture_sum'], apm['aperture_sum']],
                   names=['aperture_area', 'aperture_badpix'])
    ap.add_column(ap['aperture_sum'], index=3, name='Flux')

    if ( r_in == None or r_out == None or not method in ('mean', 'median', 'mode') ): 
      # Quit here if background correction is not requested
      return ap

    annulus_apertures = CircularAnnulus(pos, r_in=r_in, r_out=r_out)
    annulus_masks = annulus_apertures.to_mask(method='center')
    bg_values = []
    for annulus_mask in annulus_masks:
      bg_ima = annulus_mask.cutout(ima_)
      bg_mask = annulus_mask.cutout(mask_.astype(np.int))
      bg_ima = np.ma.array(bg_ima, mask= bg_mask.astype(np.bool) | ~annulus_mask.data.astype(np.bool))
      
      if method == 'mean': bg_val = bg_ima.mean()
      elif method == 'median': bg_val = np.ma.median(bg_ima)
      elif method == 'mode': 
        kernel = gaussian_kde(bg_ima.data[~bg_ima.mask], bw_method='silverman')
        mode = bg_ima.mean()
        std  = bg_ima.std()
        
        mode = minimize_scalar(lambda x: -kernel(x), bounds=(mode-3*std, mode+3*std),
                               method='bounded')
        bg_val=mode.x[0]
        
        if False:
          median = np.ma.median(bg_ima)
          h, b = np.histogram(bg_ima.data[~bg_ima.mask], bins=15, normed=True)
          bc = 0.5*(b[1:]+ b[:-1])
          plt.figure(33); plt.clf(); plt.ioff()
          fig, (ax0,ax1) = plt.subplots(ncols=2, nrows=1, num=33)
          ax0.plot(bc, h, 'x')
          x = np.linspace(bc.min(), bc.max(), 100)
          ax0.plot(x, kernel(x))
          ax0.vlines(mode.x, ax0.get_ylim()[0], ax0.get_ylim()[1])
          ax0.vlines(median, ax0.get_ylim()[0], ax0.get_ylim()[1])
          ax1.imshow(bg_ima)
          plt.show()
        
        
      bg_values.append(bg_val)
    ap.add_column(Column(data=bg_values, name = 'background'))  
    ap['Flux'] = ap['Flux'] - ap['aperture_area']*ap['background']
    return ap, bg_ima
예제 #32
0
def runPhotUtils(targname,jdan,filt):

    if filt=='F606W':
        EEband = 0.839
        ZPT = 26.667
        fils = '_f606w/'

    elif filt=='F814W':
        EEband = 0.830
        ZPT = 26.779
        fils = '_f814w/'

    image = targname + fils + '/crClean/' + jdan + '_WJ2.fits'

    hdu = fits.open(image)
    sci = hdu[0].data
    hdr = hdu[0].header
    hdu.close()

    data = sci.copy()

    mean, median, std = sigma_clipped_stats(data, sigma=3.0, \
                                            maxiters=10)

    daofind = DAOStarFinder(fwhm=2.5, threshold=5.*std)
    sources = daofind(data - median)

    loc = np.array([sources['xcentroid'], sources['ycentroid']])
    positions = np.transpose(loc)

    apertures_r4 = CircularAperture(positions, r=4.)
    rawflux_r4 = aperture_photometry(data, apertures_r4)

    annulus_apertures = CircularAnnulus(positions, r_in=9., \
                                        r_out=12.)

    annulus_masks = annulus_apertures.to_mask(method='center')

    bkg_median = []
    for mask in annulus_masks:

        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)

    bkg_median = np.array(bkg_median)

    rawflux_r4['annulus_median'] = bkg_median
    rawflux_r4['aper_bkg'] = bkg_median*apertures_r4.area

    rawflux_r4['final_phot'] = rawflux_r4['aperture_sum'] \
    - rawflux_r4['aper_bkg']

    mask_negative = (rawflux_r4['final_phot'] > 0)
    rawflux_pos_r4 = rawflux_r4[mask_negative]

    final_phot = -2.5*np.log10(rawflux_pos_r4['final_phot']/EEband) \
        +2.5*np.log10(hdr['exptime']) + ZPT

    rawflux_pos_r4['magr'] = final_phot
    s0 = ' '
    header = s0.join(rawflux_pos_r4.dtype.names)

    saveDir = 'photUtils0820/hor1run0608/'
    outname = saveDir + jdan + '_' + filt + 'photU.dat'

    np.savetxt(outname,rawflux_pos_r4,header=header)


    return None
예제 #33
0
def do_aper_phot(data, catalog, extname, ivar_adu):
    # catalog should be the catalog with refined centroids
    # for **one CI camera**

    print('Attempting to do aperture photometry')
    positions = list(zip(catalog['xcentroid'], catalog['ycentroid']))

    radii = aper_rad_pix(extname)

    apertures = [CircularAperture(positions, r=r) for r in radii]
    annulus_apertures = CircularAnnulus(positions, r_in=60.0, r_out=65.0)
    annulus_masks = annulus_apertures.to_mask(method='center')

    par = common.ci_misc_params()

    b_over_a = (1.0 if (extname == 'CIC') else par['nominal_mer_cd'] /
                par['nominal_sag_cd'])

    # the long axis of elliptical aperture (in terms of pixels) needs to
    # be in the CI pixel Y direction
    apertures_ell = [
        EllipticalAperture(positions, a, a * b_over_a, theta=np.pi / 2)
        for a in radii
    ]

    # 107 um fiber diam, 9 um on a side for a pixel
    # fiber diam from Table 4.1 of https://arxiv.org/abs/1611.00037
    rad_fiber_pix_sag = (107.0 / 9.0) / 2.0
    deg_to_normal = 5.43  # [desi-commiss 522]
    if extname != 'CIC':
        rad_fiber_pix_mer = rad_fiber_pix_sag * np.sin(deg_to_normal /
                                                       (180.0 / np.pi))
    else:
        rad_fiber_pix_mer = rad_fiber_pix_sag

    aper_fib = EllipticalAperture(positions,
                                  rad_fiber_pix_sag,
                                  rad_fiber_pix_mer,
                                  theta=np.pi / 2)

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        # this sigma_clipped_stats call is actually the slow part !!
        _, median_sigclip, std_bg = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)

    bkg_median = np.array(bkg_median)
    phot = aperture_photometry(data,
                               apertures,
                               error=aper_phot_unc_map(ivar_adu))

    for i, aperture in enumerate(apertures):
        aper_bkg_tot = bkg_median * _get_area_from_ap(aperture)
        catalog['aper_sum_bkgsub_' +
                str(i)] = phot['aperture_sum_' + str(i)] - aper_bkg_tot

        catalog['aper_bkg_' + str(i)] = aper_bkg_tot
        catalog['aperture_sum_err_' + str(i)] = phot['aperture_sum_err_' +
                                                     str(i)]

    ###
    del phot
    phot = aperture_photometry(data,
                               apertures_ell,
                               error=aper_phot_unc_map(ivar_adu))
    for i, aperture in enumerate(apertures_ell):
        aper_bkg_tot = bkg_median * _get_area_from_ap(aperture)
        catalog['aper_ell_sum_bkgsub_' +
                str(i)] = phot['aperture_sum_' + str(i)] - aper_bkg_tot

        catalog['aper_ell_bkg_' + str(i)] = aper_bkg_tot
        catalog['aperture_ell_sum_err_' + str(i)] = phot['aperture_sum_err_' +
                                                         str(i)]
    ###

    ###
    del phot
    phot = aperture_photometry(data,
                               aper_fib,
                               error=aper_phot_unc_map(ivar_adu))

    aper_bkg_tot = bkg_median * _get_area_from_ap(aper_fib)
    catalog['aper_sum_bkgsub_fib'] = phot['aperture_sum'] - aper_bkg_tot

    catalog['aper_bkg_fib'] = aper_bkg_tot
    catalog['aperture_sum_err_fib'] = phot['aperture_sum_err']

    ####

    # is .area() result a vector or scalar ??
    catalog['sky_annulus_area_pix'] = _get_area_from_ap(annulus_apertures)
    catalog['sky_annulus_median'] = bkg_median
예제 #34
0
def runPhotUtils(drcInfo, radius=4, suffix='_pu.dat', date=dateDef_):
    """
    Input:
    drcInfo: a text file with the information from the headers
    of the fits files -- TARGNAME, FILTER1, FILTER2, EXPTIME,
    ORIENTAT, RA, DEC, and JDAN; these can be easily extracted
    using astropy.io.fits; one could also open the fits files
    in this code, but that seems more memory intensive than
    just opening the fits files once and outputting to a file.

    Variables to change: filternames, EEBand, ZPT, r_in,
    and r_out. Can replace _rad with _r(integer radius) if it
    helps you keep track of things.

    When running this in a loop on multiple targets, sometimes
    one could run out of memory space. This causes the computer
    to kill the program. When this happens, just remove the
    targets that have already been run from the drcInfo and
    run again.
    """

    info = np.loadtxt(drcInfo, dtype=str)
    infoN = np.genfromtxt(drcInfo, dtype=str, names=True)
    nameCols = np.array(infoN.dtype.names)

    jdan = np.int(np.where(nameCols == 'JDAN')[0])
    filt1 = np.int(np.where(nameCols == 'FILTER1')[0])
    filt2 = np.int(np.where(nameCols == 'FILTER2')[0])
    targN = np.int(np.where(nameCols == 'TARGNAME')[0])

    fileNames = info[:, jdan]

    for ff in range(len(info)):

        image = drcDir + fileNames[ff] + ".fits"
        print(image)

        hdu = fits.open(image)
        sci = hdu[1].data
        hdu.close()

        data = sci.copy()

        f1 = info[:, filt1][ff]
        f2 = info[:, filt2][ff]

        targname = info[:, targN][ff]
        print(targname)

        # Would need to change the below numbers if
        # the data is in different filters. It could
        # be handy to have a table to call.
        if (f1 == 'F606W') or (f2 == 'F606W'):
            filt = 'F606W'
            if abs(radius - 4) <= 1e-3:
                EEband = 0.839  # 4 pixel
            elif abs(radius - 3) <= 1e-3:
                EEband = 0.795
            ZPT = 26.667

        elif (f1 == 'F814W') or (f2 == 'F814W'):
            filt = 'F814W'
            if abs(radius - 4) <= 1e-3:
                EEband = 0.830  # 4 pixels
            elif abs(radius - 3) <= 1e-3:
                EEband = 0.77
            ZPT = 26.779

        saveDir = f2mag_dirs(targname, date=date, workDir='../')
        outName = saveDir + fileNames[ff] + '_' + filt

        mean, median, std = sigma_clipped_stats(data, sigma=3.0, maxiters=10)

        daofind = DAOStarFinder(fwhm=2., threshold=5. * std)
        sources = daofind(data - median)

        loc = np.array([sources['xcentroid'], sources['ycentroid']])
        positions = np.transpose(loc)

        apertures_rad = CircularAperture(positions, r=radius)
        rawflux_rad = aperture_photometry(data, apertures_rad)

        #  Added this on Nov 2
        rawflux_rad['roundness1'] = sources['roundness1']
        rawflux_rad['roundness2'] = sources['roundness2']
        rawflux_rad['sharpness'] = sources['sharpness']

        annulus_apertures = CircularAnnulus(positions, r_in=9., r_out=12.)

        annulus_masks = annulus_apertures.to_mask(method='center')

        bkg_median = []
        for mask in annulus_masks:

            annulus_data = mask.multiply(data)
            annulus_data_1d = annulus_data[mask.data > 0]
            _, median_sigclip, _ = sigma_clipped_stats(annulus_data_1d)
            bkg_median.append(median_sigclip)

        bkg_median = np.array(bkg_median)

        rawflux_rad['annulus_median'] = bkg_median
        rawflux_rad['aper_bkg'] = bkg_median * apertures_rad.area

        rawflux_rad['final_phot'] = rawflux_rad['aperture_sum'] \
            - rawflux_rad['aper_bkg']

        # Looking at the excess flux and trying to use it as a
        # star/galaxy indicator. Added Nov 10.
        apertures_r2 = CircularAperture(positions, r=radius + 2)
        rawflux_r2 = aperture_photometry(data, apertures_r2)

        rawflux_r2['aper_bkg'] = rawflux_rad['annulus_median'] \
            * apertures_r2.area
        rawflux_r2['final_phot'] = rawflux_r2['aperture_sum'] \
            - rawflux_r2['aper_bkg']

        mask_negative = (rawflux_rad['final_phot'] > 0) \
            & (rawflux_r2['final_phot'] > 0)

        rawflux_pos_rad = rawflux_rad[mask_negative]
        rawflux_pos_r2 = rawflux_r2[mask_negative]

        mag_rad = -2.5 * np.log10(rawflux_pos_rad['final_phot'])
        mag_r2 = -2.5 * np.log10(rawflux_pos_r2['final_phot'])

        delta_mag = mag_rad - mag_r2

        mask_1 = (delta_mag > 0) & (delta_mag < 0.1)
        mean, median, std = sigma_clipped_stats(delta_mag[mask_1],
                                                sigma=3.0,
                                                maxiters=5)
        apcor = median

        mask_r2 = (delta_mag > 0) & (delta_mag < apcor + 1.5 * std)

        # Diagnostic plot to see if the flag is viable
        fig, ax = plt.subplots()

        ax.scatter(mag_r2[mask_r2], delta_mag[mask_r2], c='k', s=10)
        ax.axhline(apcor, ls='-', c='r')
        ax.set_xlim(-18, -8)
        ax.set_ylim(-0.2, 0.3)
        ax.set_xlabel('Mag [r={0}]'.format(radius + 2))
        ax.set_ylabel(r'$\Delta$mag')

        plt.savefig(outName + '_64mask.png', dpi=600, bbox_inches='tight')
        plt.close()

        maskCol = np.zeros((len(mask_r2), 1))
        for ii in range(len(mask_r2)):
            if mask_r2[ii]:
                maskCol[ii] = int(1)
            else:
                maskCol[ii] = int(0)

        rawflux_pos_rad['six_4_flag'] = maskCol

        final_phot = -2.5 * np.log10(rawflux_pos_rad['final_phot']/EEband) \
            + ZPT

        rawflux_pos_rad['magr'] = final_phot

        rawflux_pos_rad['id'] = np.arange(0, len(rawflux_pos_rad), 1)

        s0 = ' '
        header = s0.join(rawflux_pos_rad.dtype.names)

        np.savetxt(outName + suffix, rawflux_pos_rad, header=header)

        # An attempt to free up memory; Didn't seem to work?
        sources = None
        rawflux_rad = None
        rawflux_pos_rad = None
        data = None
        sci = None

        print('Moving On.')

    return None