def do_phot(image,position,radius = 5, r_in=15., r_out=20.): aperture = CircularAperture(position,r=radius) bkg_aperture = CircularAnnulus(position,r_in=r_in,r_out=r_out) # perform the photometry; the default method is 'exact' phot = aperture_photometry(image, aperture) bkg = aperture_photometry(image, bkg_aperture) # calculate the mean background level (per pixel) in the annuli bkg_mean = bkg['aperture_sum'] / bkg_aperture.area() bkg_sum = bkg_mean * aperture.area() #look at ipython notebook; his may need editing; 'phot' in second line below may need brackets with 'flux_sum' inside #phot['bkg_sum'] = bkg_sum #phot['flux_sum'] = phot['flux'] - bkg_sum #these two lines from ipython notebook flux_bkgsub = phot['aperture_sum'] - bkg_sum phot['aperture_sum_bkgsub'] = flux_bkgsub return phot
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
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
def find_backgrounds(image,sources,inner_radius=10.,outer_radius=15.): ''' Return the backgrounds of multiple sources determine by centering an annulus on each source and taking a mean. Parameters: image : nd array 2D stellar image sources : nd array Array of (x,y) coordinates inner_radius : float Radial distance (pixels) of inner annulus outer_radius : Radial distance (pixels) of outer annulus Returns: backgrounds : nd array Measured background for each star in sources. ''' annulus_apertures = CircularAnnulus(sources, r_in=inner_radius, r_out=outer_radius) annulus_phot = aperture_photometry(image, annulus_apertures) bkg_mean = annulus_phot['aperture_sum'] / annulus_apertures.area() backgrounds = bkg_mean.data return backgrounds
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 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()
def autocorr(self, ell_mask_scale=2, aperture_radius=5, annulus_width=4): # Compute 2D autocorrelation function try: masked_image = self.masked_image.copy() if ell_mask_scale > 0: masked_image.mask |= ~self.elliptical_mask(ell_mask_scale) masked_image = masked_image.filled(0.0) fft_imgae = np.fft.fft2(masked_image) acorr_image = np.fft.ifft2(fft_imgae * np.conjugate(fft_imgae)).real acorr_image = np.fft.ifftshift(acorr_image) ny, nx = masked_image.shape yy, xx = np.mgrid[:ny, :nx] circ = CircularAperture([nx // 2, ny // 2], r=aperture_radius) ann = CircularAnnulus([nx // 2, ny // 2], r_in=aperture_radius, r_out=aperture_radius + annulus_width) ann_mean = aperture_photometry( acorr_image, ann)['aperture_sum'][0] / ann.area() circ_mean = aperture_photometry( acorr_image, circ)['aperture_sum'][0] / circ.area() except: acorr_image = np.nan circ_mean = np.nan ann_mean = np.nan return acorr_image, circ_mean, ann_mean
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
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
def snr_astropy(image,sx,sy,r,r_in,r_out): """ Returns signal to noise ratio, signal value, noise value using Astropy's Photutils module Args: image (2d float array): Image array extracted from fits file sx,sy (float): x and y pixel locations for the center of the source r (float): radius for aperture r_in,r_out (float): inner and outer radii for annulus Return: snr (float): signal-to-noise ratio signal (float): sum of all pixel values within the specified aperture minus sky background noise (float): noise in the signal calculated as std deviation of pixels in the sky annulus times sqrt of the area of the signal aperture poisson_noise (float): snr determined from the poisson noise in the source signal. Poisson noise = sqrt(counts) [because variance of a poisson distribution is the parameter itself]. Poisson snr = Signal/sqrt(signal) = sqrt(signal) Written by: Logan Pearce, 2018 """ import warnings warnings.filterwarnings('ignore') from photutils import CircularAperture, CircularAnnulus positions = (cx-1,cy-1) ap = CircularAperture(positions,r=r) skyan = CircularAnnulus(positions,r_in=11,r_out=14) apsum = ap.do_photometry(image)[0] skysum = skyan.do_photometry(image)[0] averagesky = skysum/skyan.area() signal = (apsum - ap.area()*averagesky)[0] n = ap.area() ap_an = aperture_annulus(sx,sy,r,r_in,r_out) skyan = ap_an[1] poisson_noise = np.sqrt(signal) noise = noise = np.std(image[skyan[1],skyan[0]]) noise = noise*np.sqrt(n) snr = signal/noise return snr,signal,noise,poisson_noise
def ap_phot(sources, data, source_r=6., sky_in=15, sky_out=20, plot=False): global fig centroids = (sources['xcentroid'], sources['ycentroid']) source_aperture = CircularAperture(centroids, r=source_r) source_area = source_aperture.area() source_table = aperture_photometry(data, source_aperture) sky_aperture = CircularAnnulus(centroids, r_in=sky_in, r_out=sky_out) sky_area = sky_aperture.area() sky_table = aperture_photometry(data, sky_aperture) sky_subtracted_source = deepcopy(source_table) for i in range(np.shape(centroids)[1]): sky_value = sky_table[i][3] sky_per_pix = sky_value / sky_area sky_behind_source = sky_per_pix * source_area sky_subtracted_source[i][3] -= sky_behind_source if plot: fig = plt.figure(figsize=(17, 17)) plt.imshow(data, cmap='gray', origin='lower', vmin=0, vmax=1500) for i in range(np.shape(centroids)[1]): plt.annotate(str(source_table[i][0]), xy=(np.float64(source_table[i][1]) + 15., np.float64(source_table[i][2]) + 15.), color="white") source_aperture.plot(color='blue', lw=1.5, alpha=0.5) sky_aperture.plot(color="orange", lw=0.5, alpha=0.5) plt.tight_layout() plt.show() return sky_subtracted_source
def get_background(frames, centroids, bckg_method): print 'Calculating background levels' frames = np.asarray(frames) backgrounds = [] pbar = progressbar.ProgressBar() for i in pbar(range(len(frames))): if bckg_method == 'annulus': annulus_apertures = CircularAnnulus(centroids[i], r_in=10., r_out=15.) bkgflux = aperture_photometry(frames[i], annulus_apertures) bkg_mean = bkgflux['aperture_sum'] / annulus_apertures.area() backgrounds.append(bkg_mean[0]) if bckg_method == 'corners': #calculate background by summing flux in corners of image, fit gaussian, get mean background = [] N = 10 background += list(np.reshape(frames[i, :N, :N], (N**2))) background += list(np.reshape(frames[i, -N:, :N], (N**2))) background += list(np.reshape(frames[i, :N, -N:], (N**2))) background += list(np.reshape(frames[i, -N:, -N:], (N**2))) mu, sigma = norm.fit(background) backgrounds.append(mu) print 'Backgrounds done calculating' print ' ' return backgrounds
def plot_apertures(self, ax, offset=9, wcs=None): if wcs: cpix = self._ref_centroids_sky.to_pixel(wcs) xlim = ax.get_xlim() ylim = ax.get_ylim() if self._apertures_obj is not None: if wcs: apertures_obj = [ CircularAperture(cpix, r) for r in self.aperture_radii ] else: apertures_obj = self._apertures_obj [apt.plot(ax=ax, alpha=0.25) for apt in apertures_obj] for istar, (x, y) in enumerate(apertures_obj[0].positions): if (xlim[0] <= x <= xlim[1]) and (ylim[0] <= y <= ylim[1]): yoffset = offset if y < ylim[1] - 10 else -offset ax.text(x + offset, y + yoffset, istar) if self._apertures_sky is not None: if wcs: apertures_sky = CircularAnnulus(cpix, self.aperture_radii[-1], self.aperture_radii[-1] + 15) else: apertures_sky = self._apertures_sky apertures_sky.plot(ax=ax, alpha=0.25)
def GetAP(cut, theta, reso=0.5): """ Performs aperture photometry. Calculates the average value of the cutout within a circle of radius theta minus the average in the outer ring [theta, sqrt(2)*theta] Params ------ - cut: the cutout (numpy array) - theta: radius (float, in arcmin) - reso: pixel resolution (float, in arcmin/pix) """ theta_R_pix = theta / reso positions = [(cut.shape[1] / 2., cut.shape[0] / 2.)] apertures = CircularAperture(positions, r=theta_R_pix) annulus_apertures = CircularAnnulus(positions, r_in=theta_R_pix, r_out=theta_R_pix * np.sqrt(2)) apers = [apertures, annulus_apertures] phot_table = aperture_photometry(cut, apers) bkg_mean = phot_table['aperture_sum_1'] / annulus_apertures.area() bkg_sum = bkg_mean * apertures.area() final_sum = phot_table['aperture_sum_0'] - bkg_sum phot_table['residual_aperture_sum'] = final_sum return phot_table['residual_aperture_sum'][0] / apertures.area()
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)
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
def __init__(self, data, coord, r_in, r_out, method, subpixels=None): from photutils import CircularAnnulus self.ap = CircularAnnulus(coord, r_in=r_in, r_out=r_out) self.sec_data = SecImData(self.ap, data, method=method, subpixels=subpixels)
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
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
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
def annulus_background(self, positions, radius=3.): #Obtain the sky local background annulus_apertures = CircularAnnulus(positions, r_in=radius + 2., r_out=radius + 4.) bkg_table = aperture_photometry(self.data, annulus_apertures) bkg_counts = float(bkg_table['aperture_sum']) bkg_mean = bkg_counts / annulus_apertures.area() bkg = np.random.poisson(bkg_mean, self.data.shape) return bkg, bkg_counts, bkg_mean
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))
def ref2image(data,positions,aper,fig_name): apertures = CircularAperture(positions, r=aper[0]) annulus_apertures = CircularAnnulus(positions, r_in=aper[1], r_out=aper[2]) fig = plt.figure(figsize=(20,20));fig.add_subplot(111) apertures.plot(color='blue',lw=2,alpha=1) annulus_apertures.plot(color='red',lw=2,alpha=0.5) norm = ImageNormalize(stretch=HistEqStretch(data)) plt.imshow(data, cmap='Greys', origin='lower', norm=norm) for i in range(len(positions)): plt.text(positions[i][0]+10,positions[i][1]+10,str(i+1),fontdict={'size':'50','color':'blue'}) plt.savefig(fig_name,dpi=150) plt.show()
def imageshow(data,positions,aper=[8,12,20],rim_size=50): min_x=int(np.min(positions.T[0]))-rim_size;max_x=int(np.max(positions.T[0]))+rim_size min_y=int(np.min(positions.T[1]))-rim_size;max_y=int(np.max(positions.T[1]))+rim_size data=data[min_y:max_y,min_x:max_x] positions=positions-np.array([min_x,min_y]) apertures = CircularAperture(positions, r=aper[0]) annulus_apertures = CircularAnnulus(positions, r_in=aper[1], r_out=aper[2]) fig = plt.figure(figsize=(20,20));fig.add_subplot(111) apertures.plot(color='blue',lw=2,alpha=1) annulus_apertures.plot(color='red',lw=2,alpha=0.5) norm = ImageNormalize(stretch=HistEqStretch(data)) plt.imshow(data, cmap='Greys', origin='lower', norm=norm) plt.show()
def ap_phot(im, cen, r1, r2, r3): """ Simple aperture photometry, using a circular source aperture and sky annulus. """ apertures = CircularAperture(cen, r1) annulus_apertures = CircularAnnulus(cen, r2, r3) apers = [apertures, annulus_apertures] phot_table = aperture_photometry(im, apers) bkg_mean = phot_table['aperture_sum_1'] / annulus_apertures.area() bkg_sum = bkg_mean * apertures.area() final_sum = phot_table['aperture_sum_0'] - bkg_sum return float(final_sum)
def remove_concentric(data, x_mean=512, y_mean=512, x_stddev=3, y_stddev=3, Nsig=10, dp=0.1): """ This function first fits a gaussian PSF to the data to find the centroid. Then, it subtracts concentric apertures from the data rather than just subtracting the gaussian model. """ data = data.copy() # Don't overwrite the data nx, ny = data.shape gauss, psf, centroid = fit_gaussian_psf(data, x_mean=x_mean, y_mean=y_mean, x_stddev=x_stddev, y_stddev=y_stddev) std = (gauss.x_stddev_0.value + gauss.y_stddev_0.value)/2.0 x0, y0 = centroid radii = np.arange(dp, Nsig*std, dp)[::-1] # Get extents using the photutils function pos = np.atleast_2d(centroid) extents = np.zeros((len(pos), 4), dtype=int) extents[:, 0] = pos[:, 0] - max(radii) + 0.5 extents[:, 1] = pos[:, 0] + max(radii) + 1.5 extents[:, 2] = pos[:, 1] - max(radii) + 0.5 extents[:, 3] = pos[:, 1] + max(radii) + 1.5 _, extent, phot_extent = get_phot_extents(data, pos, extents) x_min, x_max, y_min, y_max = extent x_pmin, x_pmax, y_pmin, y_pmax = phot_extent args = [x_pmin[0], x_pmax[0], y_pmin[0], y_pmax[0], x_max[0]-x_min[0], y_max[0]-y_min[0]] # Fit apertures psf = np.zeros(data.shape) for radius in radii: Rin = radius - dp Rout = radius if Rin > dp/10.0: ap = CircularAnnulus(centroid, r_in=Rin, r_out=Rout) else: ap = CircularAperture(centroid, r=Rout) phot = aperture_photometry(data, ap) avg_flux = phot['aperture_sum'].item()/ap.area() outer = circular_overlap_grid(*args, r=Rout, use_exact=1, subpixels=5) if Rout-dp > dp/10.0: inner = circular_overlap_grid(*args, r=Rin, use_exact=1, subpixels=5) else: inner = 0.0 annulus_overlap = outer - inner psf[y_min[0]:y_max[0], x_min[0]:x_max[0]] += annulus_overlap * avg_flux return data - psf, psf, np.array(centroid)
def aperture_photometry(self, image, positions, r, r_int, r_ext): # Define apertures and anulluses at all found sources: object_aperture = CircularAperture(positions, r) background_annulus = CircularAnnulus(positions, r_int, r_ext) # Calculate the flux in each aperture (star, and background, and subtract) backgound_solution = aperture_photometry(image, background_annulus) object_solution = aperture_photometry(image, object_aperture) background_per_pixel = backgound_solution[ "aperture_sum"] / background_annulus.area() object_solution[ "aperture_sum"] -= background_per_pixel * object_aperture.area() return np.array(object_solution["aperture_sum"]), background_per_pixel
def do_phot(fname, datadir, phot_dir_name, apsize, annsize): hdul = fits.open(fname) if '.fz' in fname: data = hdul[1].data header = hdul[1].header hdul.close() else: data = hdul[0].data header = hdul[0].header hdul.close() gain = header.get('GAIN') if gain == None: a = 1 else: data = data.clip(min=0) rdnoise = header['RDNOISE'] error = np.sqrt(data * gain) + rdnoise mean, median, std = sigma_clipped_stats(data, sigma=3.0, iters=5) daofind = DAOStarFinder(fwhm=5.0, threshold=5. * std) sources = daofind(data - median) positions = (sources['xcentroid'], sources['ycentroid']) apertures_pix = CircularAperture(positions, r=apsize) annulus_pix = CircularAnnulus(positions, r_in=annsize[0], r_out=annsize[1]) apertures_sky = apertures_pix.to_sky(wcs.WCS(header)) annulus_sky = annulus_pix.to_sky(wcs.WCS(header)) apers = [apertures_sky, annulus_sky] # plt.imshow(data)#, cmap='Greys', origin='lower', norm=norm) # apertures_pix.plot(color='white', lw=1.5, alpha=0.5) # annulus_pix.plot(color='white', lw=1.5, alpha=0.5) # plt.show() # pdb.set_trace() phot_table = aperture_photometry(data, apers, wcs=wcs.WCS(header), error=error) bkg_mean = phot_table['aperture_sum_1'] / annulus_pix.area() bkg_sum = bkg_mean * apertures_pix.area() apertures_pix.area() final_sum = phot_table['aperture_sum_0'] - bkg_sum final_error = np.sqrt(phot_table['aperture_sum_err_0']**2 + phot_table['aperture_sum_err_1']**2) phot_table['residual_aperture_sum'] = final_sum phot_table['residual_err'] = final_error fname = datadir + phot_dir_name + "phot_table_{0:.7f}_{1}s.txt".format( header['MJD-OBS'], header['EXPTIME']) phot_table.write(fname, format='ascii') replace_comma(fname)
def Photometrie(self): q = [] for elem in self.data: phot = [] for x, y in zip(self.Tx[self.index], self.Ty[self.index]): radii = range(3, 25) positions = [(x, y)] apertures = [CircularAperture(positions, r=r) for r in radii] annulus_apertures = CircularAnnulus(positions, r_in=70., r_out=80.) phot_table = aperture_photometry(self.data[self.index], apertures) phot_background = aperture_photometry(self.data[self.index], annulus_apertures) bkg_mean = (phot_background['aperture_sum'] / annulus_apertures.area()) Int = [] Area = [] Int_bkg1 = [] for num in range(3, 25): Int.append(phot_table[0][num]) Area.append(apertures[num - 3].area()) Int_bkg1.append(phot_table[0][num] - apertures[num - 3].area() * bkg_mean) phot.append(Int_bkg1) filename = self.files[self.index] print('Processing file: ' + str(filename)) phot = numpy.array(phot) qq = (phot[0] - phot[1]) / (phot[0] + phot[1]) with open(filename + '.txt', 'w') as f: for elem3 in qq: for elem2 in elem3: f.write( str(self.JD[self.index]) + '\t' + str(self.retarder[self.index]) + '\t' + str(elem2) + '\n') q.append(qq.flatten()) self.nextframe(1) q = numpy.array(q) with open('q', 'w') as f: for elem in q: for elem2 in elem: f.write(str(elem2) + '\n')
def add_star(self, y, x, mag=None, name=None): print("Adding star ({},{})".format(x, y)) self.make_current() if mag is not None: isVar = False else: isVar = True st = Star(self, x, y, isVar, mag=mag, name=name) self.stars.append(st) r = np.mean([s.r for s in self.stars]) d_d = np.mean([s.d_d for s in self.stars]) d_a = np.mean([s.d_a for s in self.stars]) print("Added star", st) for s in self.stars: s.apertures[self] = CircularAperture((s.x[self], s.y[self]), r) s.annuli[self] = CircularAnnulus( (s.x[self], s.y[self]), r + d_d, r + d_d + d_a ) s.r = r s.d_d = d_d s.d_a = d_a print("\tUpdated aperture radii of star" "({},{}) to ({}, {}~{})".format( np.around(s.x[self], decimals=2), np.around(s.y[self], decimals=2), np.around(r, decimals=2), np.around(r + d_d, decimals=2), np.around(r + d_d + d_a, decimals=2) ) )
def measure_source_fwhm(detection, data, rmax=10): """ TO USE CAREFULLY Function used to estimate the FWHM of a source. It performs aperture photometry with inscreasing radius from the source position, and then, tries to find where half of the maximum of flux is reached. Parameters ---------- detection : :class:`~pandas:pandas.Series` Row of a DataFrame containing detected sources. Should have columns ``xcentroid`` and ``ycentroid``, in classic convention (not astropy) data : 2D :class:`~numpy:numpy.ndarray` Flux map used for the photometry """ x, y = np.array(detection[['xcentroid', 'ycentroid']]) photo_flux = np.zeros(rmax) for r in range(rmax): if r == 0: aper = CircularAperture((x, y), 1.) else: aper = CircularAnnulus((x, y), r, r + 1) photo_flux[r] = aper.do_photometry(data)[0] / aper.area() def get_fwhm(flux): #We assume max is on 0. If not, source is probably contaminated flux = flux - flux.min() spline = UnivariateSpline(np.arange(rmax), flux - flux[0] / 2., s=0) if spline.roots().shape != (1, ): return np.nan return spline.roots()[0] return (get_fwhm(photo_flux))
def skyvalue(data, y0, x0, r_in, r_out, masking): masking = masking.astype(bool) ann = CircularAnnulus([x0, y0], r_in=r_in, r_out=r_out) phot = aperture_photometry(data, ann, mask=masking) phot1 = aperture_photometry(masking * 1, ann) pixel_count = phot1['aperture_sum'][0] sky = phot['aperture_sum'][0] / (ann.area - pixel_count) masked_image = np.ma.masked_array(data, masking) # Determine sky std y_in = int(y0 - r_out) y_out = int(y0 + r_out) x_in = int(x0 - r_out) x_out = int(x0 + r_out) if y_in < 0: y_in = 0 if y_out < 0: y_out = 0 if x_in < 0: x_in = 0 if x_out < 0: x_out = 0 masked_image = masked_image[y_in:y_out, x_in:x_out] new_mask = np.zeros(np.shape(masked_image)) + 1 for yi in range(len(masked_image)): for xi in range(len(masked_image[0])): position = (xi - r_out)**2 + (yi - r_out)**2 if position < (r_out)**2 and position > r_in**2: new_mask[yi, xi] = 0 new_mask = new_mask.astype(bool) Sky_region = np.ma.masked_array(masked_image, new_mask) std = np.ma.std(Sky_region) return (sky, std, ann.area - pixel_count)
def phot(self, image, objpos, aper): """ Aperture photometry using Astropy's photutils. Parameters ---------- image : numpy array 2D image array objpos : list of tuple Object poistions as list of tuples aper : float Aperture radius in pixels Returns ------- phot_table : astropy table Output table with stellar photometry """ try: from astropy.table import hstack from photutils import aperture_photometry, CircularAnnulus, CircularAperture except ImportError: pass apertures = CircularAperture(objpos, r = aper) annulus_apertures = CircularAnnulus(objpos, r_in = self.inner_radius, r_out = self.outer_radius) rawflux_table = aperture_photometry(image, apertures = apertures, method = self.method) bkgflux_table = aperture_photometry(image, apertures = annulus_apertures, method = self.method) phot_table = hstack([rawflux_table, bkgflux_table], table_names = ["raw", "bkg"]) bkg = phot_table["aperture_sum_bkg"] / annulus_apertures.area() phot_table["msky"] = bkg phot_table["area"] = apertures.area() phot_table["nsky"] = annulus_apertures.area() bkg_sum = bkg * apertures.area() final_sum = phot_table["aperture_sum_raw"] - bkg_sum phot_table["flux"] = final_sum return phot_table
# print mean_i, median_i, std_i 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.)
def align_norm(fnlist, tolerance=5, thresh=3.5): """Aligns a set of images to each other, as well as normalizing the images to the same average brightness. Both the alignment and normalization are accomplished through stellar photometry using the IRAF routine 'daophot'. The centroids of a handful of stars are found and used to run the IRAF routine 'imalign'. The instrumental magnitudes of the stars are used to determine by how much each image must be scaled for the photometry to match across images. The images are simply updated with their rescaled, shifted selves. This overwrites the previous images and adds the header keyword 'fpphot' to the images. A handful of temporary files are created during this process, which should all be deleted by the routine at the end. But if it is interrupted, they might not be. If the uncertainty images exist, this routine also shifts them by the same amounts as the intensity images, as well as updating the uncertainty values for both the new normalization and the uncertainties in normalizing the images. Inputs: fnlist -> List of strings, each the path to a fits image. tolerance -> How close two objects can be and still be considered the same object. Default is 3 pixels. thresh -> Optional. Level above sky background variation to look for objs. Default is 3.5 (times SkySigma). Decrease if center positions aren't being found accurately. Increase for crowded fields to decrease computation time. """ # Get image FWHMs fwhm = np.empty(len(fnlist)) firstimage = FPImage(fnlist[0]) toggle = firstimage.fwhm axcen = firstimage.axcen aycen = firstimage.aycen arad = firstimage.arad firstimage.close() if axcen is None: print "Error! Images have not yet been aperture-masked! Do this first!" crash() if toggle is None: print "Warning! FWHMs have not been measured!" print "Assuming 5 pixel FWHM for all images." for i in range(len(fnlist)): fwhm[i] = 5 else: for i in range(len(fnlist)): image = FPImage(fnlist[i]) fwhm[i] = image.fwhm image.close() # Get sky background levels skyavg = np.empty(len(fnlist)) skysig = np.empty(len(fnlist)) for i in range(len(fnlist)): image = FPImage(fnlist[i]) skyavg[i], skysig[i], _skyvar = image.skybackground() image.close() # Identify the stars in each image xlists = [] ylists = [] print "Identifying stars in each image..." for i in range(len(fnlist)): xlists.append([]) ylists.append([]) image = FPImage(fnlist[i]) axcen = image.axcen aycen = image.aycen arad = image.arad sources = daofind(image.inty-skyavg[i], fwhm=fwhm[i], threshold=thresh*skysig[i]).as_array() for j in range(len(sources)): # If the source is not near the center or edge centermask = ((sources[j][1]-axcen)**2 + (sources[j][2]-aycen)**2 > (0.05*arad)**2) edgemask = ((sources[j][1]-axcen)**2 + (sources[j][2]-aycen)**2 < (0.95*arad)**2) if np.logical_and(centermask, edgemask): xlists[i].append(sources[j][1]) ylists[i].append(sources[j][2]) image.close() # Match objects between fields print "Matching objects between images..." xcoo = [] ycoo = [] for i in range(len(xlists[0])): # For each object in the first image accept = True for j in range(1, len(fnlist)): # For each other image dist2 = ((np.array(xlists[j])-xlists[0][i])**2 + (np.array(ylists[j])-ylists[0][i])**2) if (min(dist2) > tolerance**2): accept = False break if accept: # We found an object at that position in every image xcoo.append(xlists[0][i]) ycoo.append(ylists[0][i]) # Create coordinate arrays for the photometry and shifting x = np.zeros((len(fnlist), len(xcoo))) y = np.zeros_like(x) for i in range(len(xcoo)): # For every object found in the first image for j in range(len(fnlist)): # Find that object in every image dist2 = ((np.array(xlists[j])-xcoo[i])**2 + (np.array(ylists[j])-ycoo[i])**2) index = np.argmin(dist2) x[j, i] = xlists[j][index] y[j, i] = ylists[j][index] # Do aperture photometry on the matched objects print "Performing photometry on matched stars..." counts = np.zeros_like(x) dcounts = np.zeros_like(x) for i in range(len(fnlist)): image = FPImage(fnlist[i]) apertures = CircularAperture((x[i], y[i]), r=2*fwhm[i]) annuli = CircularAnnulus((x[i], y[i]), r_in=3*fwhm[i], r_out=4*fwhm[i]) phot_table = aperture_photometry(image.inty, apertures, error=np.sqrt(image.vari)) sky_phot_table = aperture_photometry(image.inty, annuli, error=np.sqrt(image.vari)) counts[i] = phot_table["aperture_sum"] / apertures.area() counts[i] -= sky_phot_table["aperture_sum"] / annuli.area() counts[i] *= apertures.area() dcounts[i] = phot_table["aperture_sum_err"] / apertures.area() image.close() # Calculate the shifts and normalizations norm, dnorm = calc_norm(counts, dcounts) for i in range(x.shape[1]): x[:, i] = -(x[:, i] - x[0, i]) y[:, i] = -(y[:, i] - y[0, i]) xshifts = np.average(x, axis=1) yshifts = np.average(y, axis=1) # Normalize the images and put shifts in the image headers for i in range(len(fnlist)): image = FPImage(fnlist[i], update=True) image.phottog = "True" image.dnorm = dnorm[i] image.inty /= norm[i] image.vari = image.vari/norm[i]**2 image.xshift = xshifts[i] image.yshift = yshifts[i] image.close() return
def tso_aperture_photometry(datamodel, xcenter, ycenter, radius, radius_inner, radius_outer): """ Create a photometric catalog for NIRCam TSO imaging observations. Parameters ---------- datamodel : `CubeModel` The input `CubeModel` of a NIRCam TSO imaging observation. xcenter, ycenter : float The ``x`` and ``y`` center of the aperture. radius : float The radius (in pixels) of the circular aperture. radius_inner, radius_outer : float The inner and outer radii (in pixels) of the circular-annulus aperture, used for local background estimation. Returns ------- catalog : `~astropy.table.QTable` An astropy QTable (Quantity Table) containing the source photometry. """ if not isinstance(datamodel, CubeModel): raise ValueError('The input data model must be a CubeModel.') # For the SUB64P subarray with the WLP8 pupil, the circular aperture # extends beyond the image and the circular annulus does not have any # overlap with the image. In that case, we simply sum all values # in the array and skip the background subtraction. sub64p_wlp8 = False if (datamodel.meta.instrument.pupil == 'WLP8' and datamodel.meta.subarray.name == 'SUB64P'): sub64p_wlp8 = True if not sub64p_wlp8: phot_aper = CircularAperture((xcenter, ycenter), r=radius) bkg_aper = CircularAnnulus((xcenter, ycenter), r_in=radius_inner, r_out=radius_outer) aperture_sum = [] aperture_sum_err = [] annulus_sum = [] annulus_sum_err = [] nimg = datamodel.data.shape[0] if sub64p_wlp8: info = ('Photometry measured as the sum of all values in the ' 'subarray. No background subtraction was performed.') for i in np.arange(nimg): aperture_sum.append(np.sum(datamodel.data[i, :, :])) aperture_sum_err.append( np.sqrt(np.sum(datamodel.err[i, :, :]**2))) else: info = ('Photometry measured in a circular aperture of r={0} ' 'pixels. Background calculated as the mean in a ' 'circular annulus with r_inner={1} pixels and ' 'r_outer={2} pixels.'.format(radius, radius_inner, radius_outer)) for i in np.arange(nimg): aper_sum, aper_sum_err = phot_aper.do_photometry( datamodel.data[i, :, :], error=datamodel.err[i, :, :]) ann_sum, ann_sum_err = bkg_aper.do_photometry( datamodel.data[i, :, :], error=datamodel.err[i, :, :]) aperture_sum.append(aper_sum[0]) aperture_sum_err.append(aper_sum_err[0]) annulus_sum.append(ann_sum[0]) annulus_sum_err.append(ann_sum_err[0]) aperture_sum = np.array(aperture_sum) aperture_sum_err = np.array(aperture_sum_err) annulus_sum = np.array(annulus_sum) annulus_sum_err = np.array(annulus_sum_err) # construct metadata for output table meta = OrderedDict() meta['instrument'] = datamodel.meta.instrument.name meta['detector'] = datamodel.meta.instrument.detector meta['channel'] = datamodel.meta.instrument.channel meta['subarray'] = datamodel.meta.subarray.name meta['filter'] = datamodel.meta.instrument.filter meta['pupil'] = datamodel.meta.instrument.pupil meta['target_name'] = datamodel.meta.target.catalog_name meta['xcenter'] = xcenter meta['ycenter'] = ycenter ra_icrs, dec_icrs = datamodel.meta.wcs(xcenter, ycenter) meta['ra_icrs'] = ra_icrs meta['dec_icrs'] = dec_icrs meta['apertures'] = info # initialize the output table tbl = QTable(meta=meta) if hasattr(datamodel, 'int_times') and datamodel.int_times is not None: nrows = len(datamodel.int_times) else: nrows = 0 if nrows == 0: log.warning("There is no INT_TIMES table in the input file.") if nrows > 0: shape = datamodel.data.shape if len(shape) == 2: num_integ = 1 else: # len(shape) == 3 num_integ = shape[0] int_start = datamodel.meta.exposure.integration_start if int_start is None: int_start = 1 log.warning("INTSTART not found; assuming a value of %d", int_start) # Columns of integration numbers & times of integration from the # INT_TIMES table. int_num = datamodel.int_times['integration_number'] mid_utc = datamodel.int_times['int_mid_MJD_UTC'] offset = int_start - int_num[0] # both are one-indexed if offset < 0: log.warning("Range of integration numbers in science data extends " "outside the range in INT_TIMES table.") log.warning("Can't use INT_TIMES table.") del int_num, mid_utc nrows = 0 # flag as bad else: log.debug("Times are from the INT_TIMES table.") time_arr = mid_utc[offset: offset + num_integ] int_times = Time(time_arr, format='mjd', scale='utc') else: log.debug("Times were computed from EXPSTART and TGROUP.") dt = (datamodel.meta.exposure.group_time * (datamodel.meta.exposure.ngroups + 1)) dt_arr = (np.arange(1, 1 + datamodel.meta.exposure.nints) * dt - (dt / 2.)) int_dt = TimeDelta(dt_arr, format='sec') int_times = (Time(datamodel.meta.exposure.start_time, format='mjd') + int_dt) tbl['MJD'] = int_times.mjd tbl['aperture_sum'] = aperture_sum tbl['aperture_sum_err'] = aperture_sum_err if not sub64p_wlp8: tbl['annulus_sum'] = annulus_sum tbl['annulus_sum_err'] = annulus_sum_err annulus_mean = annulus_sum / bkg_aper.area() annulus_mean_err = annulus_sum_err / bkg_aper.area() tbl['annulus_mean'] = annulus_mean tbl['annulus_mean_err'] = annulus_mean_err aperture_bkg = annulus_mean * phot_aper.area() aperture_bkg_err = annulus_mean_err * phot_aper.area() tbl['aperture_bkg'] = aperture_bkg tbl['aperture_bkg_err'] = aperture_bkg_err net_aperture_sum = aperture_sum - aperture_bkg net_aperture_sum_err = np.sqrt(aperture_sum_err ** 2 + aperture_bkg_err ** 2) tbl['net_aperture_sum'] = net_aperture_sum tbl['net_aperture_sum_err'] = net_aperture_sum_err else: colnames = ['annulus_sum', 'annulus_sum_err', 'annulus_mean', 'annulus_mean_err', 'aperture_bkg', 'aperture_bkg_err'] for col in colnames: tbl[col] = np.full(nimg, np.nan) tbl['net_aperture_sum'] = aperture_sum tbl['net_aperture_sum_err'] = aperture_sum_err return tbl
xcenterOffset = center[0] - margins #xcenterOffset = xcenterInt - margins ycenterInt = int(center[1]) ycenterOffset = center[1] - margins #ycenterOffset = ycenterInt - margins if (ycenterOffset<0) or (xcenterOffset<0): continue zoomImageData = window.data[ycenterInt-margins:ycenterInt+margins, xcenterInt-margins:xcenterInt+margins] (xcen, ycen) = photutils.morphology.centroid_2dg(zoomImageData, error=None, mask=None) xcen+= xcenterOffset ycen+= ycenterOffset s.setLatestPosition(trueFrameNumber, (xcen, ycen)) apertures = CircularAperture((xcen, ycen), r=apertureRadius) annulus_apertures = CircularAnnulus((xcen, ycen), r_in=innerSkyRadius, r_out=outerSkyRadius) # Draw the re-positioned apertures xll = window.xll/window.xbin - xmin yll = window.yll/window.ybin - ymin if arg.preview: ppgplot.pgslct(bitmapView) plotx= xcen + xll ploty= ycen + yll #print xll, yll, center, xcen, ycen ppgplot.pgcirc(plotx, ploty, apertureRadius) ppgplot.pgcirc(plotx, ploty, innerSkyRadius) ppgplot.pgcirc(plotx, ploty, outerSkyRadius) ppgplot.pgptxt(plotx-10, ploty-10, 0, 0, str(index))
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
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
lf, rt = xStar - 10, xStar + 10 bt, tp = yStar - 10, yStar + 10 patch = stokesI.arr[lf:rt, bt:tp] saturatedStars.append(np.sum(np.logical_or(patch > 9e3, patch < -100)) > 1) # Check for stars near the edge of the image ny, nx = stokesI.arr.shape edgeStars = np.logical_or( np.logical_or(xStars < 40, xStars > (nx - 40)), np.logical_or(yStars < 40, yStars > (ny - 40))) # Now let's do aperture photometry on the remaining sources in the image # 1. Setup the apertures sourcePos = [xStars, yStars] apertures = CircularAperture(sourcePos, r = 6.0) annulus_apertures = CircularAnnulus(sourcePos, r_in=12., r_out=14.) # 2. Perform the basic photometry rawflux_table = aperture_photometry(stokesI.arr, apertures) bkgflux_table = aperture_photometry(stokesI.arr, annulus_apertures) phot_table = hstack([rawflux_table, bkgflux_table], table_names=['raw', 'bkg']) # 3. Compute background contribution and subtract from raw photometry bkg_mean = phot_table['aperture_sum_bkg'] / annulus_apertures.area() bkg_sum = bkg_mean * apertures.area() final_sum = phot_table['aperture_sum_raw'] - bkg_sum phot_table['residual_aperture_sum'] = final_sum # Compute the signal-to-noise ratio and find the stars with SNR < 3.0 SNR = final_sum/bkg_sum bkgDominated = SNR < 1.0
def extract_ifu(input_model, source_type, extract_params): """This function does the extraction. Parameters ---------- input_model : IFUCubeModel The input model. source_type : string "point" or "extended" extract_params : dict The extraction parameters for aperture photometry. Returns ------- ra, dec : float ra and dec are the right ascension and declination respectively at the nominal center of the image. wavelength : ndarray, 1-D The wavelength in micrometers at each pixel. net : ndarray, 1-D The count rate (or flux) minus the background at each pixel. background : ndarray, 1-D The background count rate that was subtracted from the total source count rate to get `net`. npixels : ndarray, 1-D, float64 For each slice, this is the number of pixels that were added together to get `net`. dq : ndarray, 1-D, uint32 The data quality array. """ data = input_model.data shape = data.shape if len(shape) != 3: log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape)) raise RuntimeError("The IFU cube should be 3-D.") # We need to allocate net, background, npixels, and dq arrays # no matter what. We may need to divide by npixels, so the default # is 1 rather than 0. net = np.zeros(shape[0], dtype=np.float64) background = np.zeros(shape[0], dtype=np.float64) npixels = np.ones(shape[0], dtype=np.float64) dq = np.zeros(shape[0], dtype=np.uint32) x_center = extract_params['x_center'] y_center = extract_params['y_center'] if x_center is None: x_center = float(shape[2]) / 2. else: x_center = float(x_center) if y_center is None: y_center = float(shape[1]) / 2. else: y_center = float(y_center) method = extract_params['method'] # subpixels is only needed if method = 'subpixel'. subpixels = extract_params['subpixels'] subtract_background = extract_params['subtract_background'] smaller_axis = float(min(shape[1], shape[2])) # for defaults radius = None inner_bkg = None outer_bkg = None if source_type == 'point': radius = extract_params['radius'] if radius is None: radius = smaller_axis / 4. if subtract_background: inner_bkg = extract_params['inner_bkg'] if inner_bkg is None: inner_bkg = radius outer_bkg = extract_params['outer_bkg'] if outer_bkg is None: outer_bkg = min(inner_bkg * math.sqrt(2.), smaller_axis / 2. - 1.) if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg: log.debug("Turning background subtraction off, due to " "the values of inner_bkg and outer_bkg.") subtract_background = False width = None height = None theta = None else: width = extract_params['width'] if width is None: width = smaller_axis / 2. height = extract_params['height'] if height is None: height = smaller_axis / 2. theta = extract_params['theta'] * math.pi / 180. subtract_background = False log.debug("IFU 1-D extraction parameters:") log.debug(" x_center = %s", str(x_center)) log.debug(" y_center = %s", str(y_center)) if source_type == 'point': log.debug(" radius = %s", str(radius)) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" inner_bkg = %s", str(inner_bkg)) log.debug(" outer_bkg = %s", str(outer_bkg)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) else: log.debug(" width = %s", str(width)) log.debug(" height = %s", str(height)) log.debug(" theta = %s degrees", str(extract_params['theta'])) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) # Check for out of bounds. # The problem with having the background aperture extend beyond the # image is that the normalization would not account for the resulting # decrease in the area of the annulus, so the background subtraction # would be systematically low. outside = False f_nx = float(shape[2]) f_ny = float(shape[1]) if x_center < 0. or x_center >= f_nx - 1. or \ y_center < 0. or y_center >= f_ny - 1.: outside = True log.error("Target location is outside the image.") if subtract_background and \ (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5): outside = True log.error("Background region extends outside the image.") if outside: (ra, dec) = (0., 0.) wavelength = np.zeros(shape[0], dtype=np.float64) dq[:] = dqflags.pixel['DO_NOT_USE'] return (ra, dec, wavelength, net, background, npixels, dq) # all bad x0 = float(shape[2]) / 2. y0 = float(shape[1]) / 2. (ra, dec, wavelength) = get_coordinates(input_model, x0, y0) position = (x_center, y_center) if source_type == 'point': aperture = CircularAperture(position, r=radius) if subtract_background: annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg) normalization = aperture.area() / annulus.area() else: aperture = RectangularAperture(position, width, height, theta) # No background is computed for an extended source. npixels[:] = aperture.area() for k in range(shape[0]): phot_table = aperture_photometry(data[k, :, :], aperture, method=method, subpixels=subpixels) net[k] = float(phot_table['aperture_sum'][0]) if subtract_background: bkg_table = aperture_photometry(data[k, :, :], annulus, method=method, subpixels=subpixels) background[k] = float(bkg_table['aperture_sum'][0]) net[k] = net[k] - background[k] * normalization # Check for NaNs in the wavelength array, flag them in the dq array, # and truncate the arrays if NaNs are found at endpoints (unless the # entire array is NaN). (wavelength, net, background, npixels, dq) = \ nans_in_wavelength(wavelength, net, background, npixels, dq) return (ra, dec, wavelength, net, background, npixels, dq)
follow the algorithm outlined in http://photutils.readthedocs.org/en/latest/photutils/aperture.html#global-background-subtraction error in photometry is assumed as Poissonian -- sqrt(N) but smooth background error not included for now """ for eachFile in imFiles: print('Prcoessing %s ...' %(eachFile)) try: hdu = pyfits.open(eachFile) im = hdu[0].data except: print('File could not be opened: %s' %(eachFile)) # create aperture and annulus objects apertures = CircularAperture(coords, r=aper_size) annulus_apertures = CircularAnnulus(coords, r_in=annulus, r_out=dannulus) npix_src, npix_bkg = apertures.area(), annulus_apertures.area() # calculate the object and annulus flux data_error = np.sqrt(im) rawflux_table = aperture_photometry(im, apertures, error=data_error) bkgflux_table = aperture_photometry(im, annulus_apertures, error=data_error) # stack the two tables into one phot_table = hstack([rawflux_table, bkgflux_table], table_names=['raw', 'bkg']) # calculate bkg mean & normalize by area bkg_mean = phot_table['aperture_sum_bkg'] / npix_bkg # final photometric counts -- in ADUs Nsrc = phot_table['aperture_sum_raw'] - bkg_mean * npix_src
def do_detection(self): """Flag outlier pixels in DQ of input images.""" self.build_suffix(**self.outlierpars) self._convert_inputs() pars = self.outlierpars save_intermediate_results = pars['save_intermediate_results'] # Start by performing initial TSO Photometry on stack of DataModels # TODO: need information about the actual source position in # TSO imaging mode (for all subarrays). # Meanwhile, this is a placeholder representing the geometric # center of the image. nints, ny, nx = self.inputs.data.shape xcenter = (ny - 1) / 2. ycenter = (ny - 1) / 2. # all radii are in pixel units if self.inputs.meta.instrument.pupil == 'WLP8': radius = 50 radius_inner = 60 radius_outer = 70 else: radius = 3 radius_inner = 4 radius_outer = 5 apertures = CircularAperture((xcenter, ycenter), r=radius) aperture_mask = apertures.to_mask(method='center')[0] # This mask has 1 for mask region, 0 for outside of mask median_mask = aperture_mask.to_image((ny, nx)) inv_median_mask = np.abs(median_mask - 1) # Perform photometry catalog = tso_aperture_photometry(self.inputs, xcenter, ycenter, radius, radius_inner, radius_outer) # Extract net photometry for the source # This will be the value used for scaling the median image within # the aperture region phot_values = catalog['net_aperture_sum'] # Convert CubeModel into ModelContainer of 2-D DataModels for image in self.input_models: image.wht = resample_utils.build_driz_weight( image, weight_type='exptime', good_bits=pars['good_bits'] ) # Initialize intermediate products used in the outlier detection input_shape = self.input_models[0].data.shape median_model = datamodels.ImageModel(init=input_shape) median_model.meta = deepcopy(self.input_models[0].meta) base_filename = self.inputs.meta.filename median_model.meta.filename = self.make_output_path( basepath=base_filename, suffix='median' ) # Perform median combination on set of drizzled mosaics median_model.data = self.create_median(self.input_models) aper2 = CircularAnnulus((xcenter, ycenter), r_in=radius_inner, r_out=radius_outer) tbl1 = aperture_photometry(median_model.data, apertures, error=median_model.data * 0.0 + 1.0) tbl2 = aperture_photometry(median_model.data, aper2, error=median_model.data * 0.0 + 1.0) aperture_sum = u.Quantity(tbl1['aperture_sum'][0]) annulus_sum = u.Quantity(tbl2['aperture_sum'][0]) annulus_mean = annulus_sum / aper2.area() aperture_bkg = annulus_mean * apertures.area() median_phot_value = aperture_sum - aperture_bkg if save_intermediate_results: log.info("Writing out MEDIAN image to: {}".format( median_model.meta.filename)) median_model.save(median_model.meta.filename) # Scale the median image by the initial photometry (only in aperture) # to create equivalent of 'blot' images # Area outside of aperture in median will remain unchanged blot_models = datamodels.ModelContainer() for i in range(nints): scale_factor = float(phot_values[i] / median_phot_value) scaled_image = datamodels.ImageModel(init=median_model.data.shape) scaled_image.meta = deepcopy(median_model.meta) scaled_data = (median_model.data * (scale_factor * median_mask) + ( median_model.data * inv_median_mask)) scaled_image.data = scaled_data blot_models.append(scaled_image) if save_intermediate_results: log.info("Writing out Scaled Median images...") def make_output_path(ignored, idx=None): output_path = self.make_output_path( basepath=base_filename, suffix='blot', idx=idx, component_format='_{asn_id}_{idx}' ) return output_path blot_models.save(make_output_path) # Perform outlier detection using statistical comparisons between # each original input image and its blotted version of the median image self.detect_outliers(blot_models) # clean-up (just to be explicit about being finished # with these results) del median_model, blot_models
def time_series(xcenter, ycenter, filenames, r = None, r_in = None, r_out = None, rs_in = None, rs_out = None, flat_name = False, w = None, h = None, w_in = None, w_out = None, h_out = None, ws_in = None, ws_out = None, hs_out = None, red = False, red2 = False, bg_xcen = None, bg_ycen = None, mode = "astropy", src_shape = "Circ", bkg_shape = "Circ", average = "med"): flux_table = Table(names = ('raw_flux', 'bkg_flux', 'res_flux', 'time')) for i, hdu in enumerate(filenames): test_im = test_image(filename = hdu, r = red, r2 = red2, f_name = flat_name) image2d, time, header, mask = test_im[0], test_im[1], test_im[2], test_im[3] ap_phot = photometry(image2d, xcenter, ycenter, mask, index = i, shape = src_shape, rad = r, r_in = rs_in, r_out = rs_out, ht = h, wid = w, w_in = ws_in, w_out = ws_out, h_out = hs_out) raw_flux = ap_phot[0] source_ap = ap_phot[1] if mode == "astropy": if bkg_shape == "Circ": bkg_ap = CircularAnnulus((xcenter[i], ycenter[i]), r_in = r_in, r_out = r_out) bkg = aperture_photometry(image2d, bkg_ap, mask = mask) bkg_mean = bkg['aperture_sum']/bkg_ap.area() elif bkg_shape == "Rect": bkg_ap = photometry(image2d, bg_xcen, bg_ycen, mask, index = i, shape = bkg_shape, ht = h, wid = w)[1] bkg = aperture_photometry(image2d, bkg_ap, mask = mask) bkg_mean = bkg['aperture_sum']/bkg_ap.area() elif bkg_shape == "RectAnn": bkg_ap = RectangularAnnulus((xcenter[i], ycenter[i]), w_in = w_in, w_out = w_out, h_out = h_out, theta = 0.0) bkg = aperture_photometry(image2d, bkg_ap, mask = mask) bkg_mean = bkg['aperture_sum']/bkg_ap.area() else: warnings.warn("Not a recognized astropy shape") bkg_flux = bkg_mean*source_ap.area() res_flux = raw_flux - bkg_flux elif mode == "shapes": y, x = np.mgrid[:image2d.shape[0], :image2d.shape[1]] if bkg_shape == "Circ": bkg_pts = ((((x - xcenter[i])**2 + (y - ycenter[i])**2) > (r_in)**2) & (((x - xcenter[i])**2 + (y -ycenter[i])**2) < (r_out)**2)) elif bkg_shape == "CIS": bkg_pts = ((((x - xcenter[i])**2 + (y - ycenter[i])**2) > (r_in)**2) & ((np.abs(x - xcenter[i]) < r_out) & (np.abs(y -ycenter[i]) < r_out))) else: warnings.warn("Not a recognized shape") if average == "med": bkg_med = np.nanmedian(image2d[bkg_pts]) elif average == "avg": bkg_med = np.nanmean(image2d[bkg_pts]) elif average =="mad": ad = np.abs(image2d[bkg_pts]-np.nanmedian(image2d[bkg_pts])) mad = np.nanmedian(ad) keep_pts = (np.abs(image2d-np.nanmedian(image2d[bkg_pts]))<(5*mad)) & bkg_pts bkg_med = np.nanmean(image2d[keep_pts]) else: warnings.warn("Not a recognized average") bkg_flux = bkg_med*(np.pi*(r**2)) res_flux = raw_flux - bkg_flux elif mode == "col_col": new_im = col_col(image2d, mask, xcenter[i], ycenter[i], r, box = 150) bkg_flux = 0 res_flux = photometry(new_im, xcenter, ycenter, mask, index = i, shape = 'Circ', rad = r)[0] elif mode == "row_row": new_im = row_row(image2d, mask, xcenter[i], ycenter[i], r, box = 150) bkg_flux = 0 res_flux = photometry(new_im, xcenter, ycenter, mask, index = i, shape = 'Circ', rad = r)[0] else: raise Warning("Not a recognized mode") flux_table.add_row([raw_flux, bkg_flux, res_flux, time]) return flux_table
txdump_out = open('phot_test_g.txdump','w+') iraf.ptools.txdump(textfiles='mag_test_g.dat', fields="id,mag,merr,sum,msky,stdev,rapert,xcen,ycen,ifilter,xairmass,image", expr='yes', headers='no', Stdout=txdump_out) txdump_out.close() 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)]
def extract_ifu(input_model, source_type, extract_params): """This function does the extraction. Parameters ---------- input_model: IFUCubeModel The input model. source_type: string "point" or "extended" extract_params: dict The extraction parameters for aperture photometry. Returns ------- (wavelength, net, background, dq) """ data = input_model.data shape = data.shape if len(shape) != 3: log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape)) raise RuntimeError("The IFU cube should be 3-D.") # We need to allocate net, background, and dq arrays no matter what. net = np.zeros(shape[0], dtype=np.float64) background = np.zeros(shape[0], dtype=np.float64) dq = np.zeros(shape[0], dtype=np.int32) x_center = extract_params['x_center'] y_center = extract_params['y_center'] if x_center is None: x_center = float(shape[2]) / 2. else: x_center = float(x_center) if y_center is None: y_center = float(shape[1]) / 2. else: y_center = float(y_center) method = extract_params['method'] # subpixels is only needed if method = 'subpixel'. subpixels = extract_params['subpixels'] subtract_background = extract_params['subtract_background'] smaller_axis = float(min(shape[1], shape[2])) # for defaults if source_type == 'point': radius = extract_params['radius'] if radius is None: radius = smaller_axis / 4. if subtract_background: inner_bkg = extract_params['inner_bkg'] if inner_bkg is None: inner_bkg = radius outer_bkg = extract_params['outer_bkg'] if outer_bkg is None: outer_bkg = min(inner_bkg * math.sqrt(2.), smaller_axis / 2. - 1.) width = None height = None theta = None else: width = extract_params['width'] if width is None: width = smaller_axis / 2. height = extract_params['height'] if height is None: height = smaller_axis / 2. theta = extract_params['theta'] * math.pi / 180. radius = None inner_bkg = None outer_bkg = None if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg: subtract_background = False log.debug("IFU 1-D extraction parameters:") log.debug(" x_center = %s", str(x_center)) log.debug(" y_center = %s", str(y_center)) if source_type == 'point': log.debug(" radius = %s", str(radius)) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" inner_bkg = %s", str(inner_bkg)) log.debug(" outer_bkg = %s", str(outer_bkg)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) else: log.debug(" width = %s", str(width)) log.debug(" height = %s", str(height)) log.debug(" theta = %s degrees", str(extract_params['theta'])) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) # Check for out of bounds. # The problem with having the background aperture extend beyond the # image is that the normalization would not account for the resulting # decrease in the area of the annulus, so the background subtraction # would be systematically low. outside = False f_nx = float(shape[2]) f_ny = float(shape[1]) if x_center < 0. or x_center >= f_nx - 1. or \ y_center < 0. or y_center >= f_ny - 1.: outside = True log.error("Target location is outside the image.") if subtract_background and \ (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5): outside = True log.error("Background region extends outside the image.") if outside: wavelength = np.zeros(shape[0], dtype=np.float64) dq[:] = dqflags.pixel['DO_NOT_USE'] return (wavelength, net, background, dq) # all bad wcs = input_model.meta.wcs x_array = np.empty(shape[0], dtype=np.float64) x_array.fill(float(shape[2]) / 2.) y_array = np.empty(shape[0], dtype=np.float64) y_array.fill(float(shape[1]) / 2.) z_array = np.arange(shape[0], dtype=np.float64) # for wavelengths _, _, wavelength = wcs(x_array, y_array, z_array) position = (x_center, y_center) if source_type == 'point': aperture = CircularAperture(position, r=radius) if subtract_background: annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg) normalization = aperture.area() / annulus.area() else: aperture = RectangularAperture(position, width, height, theta) # No background is computed for an extended source. for k in range(shape[0]): phot_table = aperture_photometry(data[k, :, :], aperture, method=method, subpixels=subpixels) net[k] = float(phot_table['aperture_sum'][0]) if subtract_background: bkg_table = aperture_photometry(data[k, :, :], annulus, method=method, subpixels=subpixels) background[k] = float(bkg_table['aperture_sum'][0]) net[k] = net[k] - background[k] * normalization return (wavelength, net, background, dq)
def extract_ifu(input_model, source_type, extract_params): """This function does the extraction. Parameters ---------- input_model : IFUCubeModel The input model. source_type : string "point" or "extended" extract_params : dict The extraction parameters for aperture photometry. Returns ------- ra, dec : float ra and dec are the right ascension and declination respectively at the nominal center of the image. wavelength : ndarray, 1-D The wavelength in micrometers at each pixel. net : ndarray, 1-D The count rate (counts / s) minus the background at each pixel. background : ndarray, 1-D The background count rate that was subtracted from the total source count rate to get `net`. dq : ndarray, 1-D, int32 The data quality array. """ data = input_model.data shape = data.shape if len(shape) != 3: log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape)) raise RuntimeError("The IFU cube should be 3-D.") # We need to allocate net, background, and dq arrays no matter what. net = np.zeros(shape[0], dtype=np.float64) background = np.zeros(shape[0], dtype=np.float64) dq = np.zeros(shape[0], dtype=np.int32) x_center = extract_params['x_center'] y_center = extract_params['y_center'] if x_center is None: x_center = float(shape[2]) / 2. else: x_center = float(x_center) if y_center is None: y_center = float(shape[1]) / 2. else: y_center = float(y_center) method = extract_params['method'] # subpixels is only needed if method = 'subpixel'. subpixels = extract_params['subpixels'] subtract_background = extract_params['subtract_background'] smaller_axis = float(min(shape[1], shape[2])) # for defaults if source_type == 'point': radius = extract_params['radius'] if radius is None: radius = smaller_axis / 4. if subtract_background: inner_bkg = extract_params['inner_bkg'] if inner_bkg is None: inner_bkg = radius outer_bkg = extract_params['outer_bkg'] if outer_bkg is None: outer_bkg = min(inner_bkg * math.sqrt(2.), smaller_axis / 2. - 1.) if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg: log.debug("Turning background subtraction off, due to " "the values of inner_bkg and outer_bkg.") subtract_background = False width = None height = None theta = None else: width = extract_params['width'] if width is None: width = smaller_axis / 2. height = extract_params['height'] if height is None: height = smaller_axis / 2. theta = extract_params['theta'] * math.pi / 180. radius = None subtract_background = False inner_bkg = None outer_bkg = None log.debug("IFU 1-D extraction parameters:") log.debug(" x_center = %s", str(x_center)) log.debug(" y_center = %s", str(y_center)) if source_type == 'point': log.debug(" radius = %s", str(radius)) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" inner_bkg = %s", str(inner_bkg)) log.debug(" outer_bkg = %s", str(outer_bkg)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) else: log.debug(" width = %s", str(width)) log.debug(" height = %s", str(height)) log.debug(" theta = %s degrees", str(extract_params['theta'])) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) # Check for out of bounds. # The problem with having the background aperture extend beyond the # image is that the normalization would not account for the resulting # decrease in the area of the annulus, so the background subtraction # would be systematically low. outside = False f_nx = float(shape[2]) f_ny = float(shape[1]) if x_center < 0. or x_center >= f_nx - 1. or \ y_center < 0. or y_center >= f_ny - 1.: outside = True log.error("Target location is outside the image.") if subtract_background and \ (x_center - outer_bkg < -0.5 or x_center + outer_bkg > f_nx - 0.5 or y_center - outer_bkg < -0.5 or y_center + outer_bkg > f_ny - 0.5): outside = True log.error("Background region extends outside the image.") if outside: (ra, dec) = (0., 0.) wavelength = np.zeros(shape[0], dtype=np.float64) dq[:] = dqflags.pixel['DO_NOT_USE'] return (ra, dec, wavelength, net, background, dq) # all bad if hasattr(input_model.meta, 'wcs'): wcs = input_model.meta.wcs else: log.warning("WCS function not found in input.") wcs = None if wcs is not None: x_array = np.empty(shape[0], dtype=np.float64) x_array.fill(float(shape[2]) / 2.) y_array = np.empty(shape[0], dtype=np.float64) y_array.fill(float(shape[1]) / 2.) z_array = np.arange(shape[0], dtype=np.float64) # for wavelengths ra, dec, wavelength = wcs(x_array, y_array, z_array) nelem = len(wavelength) ra = ra[nelem // 2] dec = dec[nelem // 2] else: (ra, dec) = (0., 0.) wavelength = np.arange(1, shape[0] + 1, dtype=np.float64) position = (x_center, y_center) if source_type == 'point': aperture = CircularAperture(position, r=radius) if subtract_background: annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg) normalization = aperture.area() / annulus.area() else: aperture = RectangularAperture(position, width, height, theta) # No background is computed for an extended source. for k in range(shape[0]): phot_table = aperture_photometry(data[k, :, :], aperture, method=method, subpixels=subpixels) net[k] = float(phot_table['aperture_sum'][0]) if subtract_background: bkg_table = aperture_photometry(data[k, :, :], annulus, method=method, subpixels=subpixels) background[k] = float(bkg_table['aperture_sum'][0]) net[k] = net[k] - background[k] * normalization # Check for NaNs in the wavelength array, flag them in the dq array, # and truncate the arrays if NaNs are found at endpoints (unless the # entire array is NaN). nan_mask = np.isnan(wavelength) n_nan = nan_mask.sum(dtype=np.intp) if n_nan > 0: log.warning("%d NaNs in wavelength array.", n_nan) dq[nan_mask] = np.bitwise_or(dq[nan_mask], dqflags.pixel['DO_NOT_USE']) not_nan = np.logical_not(nan_mask) flag = np.where(not_nan) if len(flag[0]) > 0: n_trimmed = flag[0][0] + nelem - (flag[0][-1] + 1) if n_trimmed > 0: log.info("Output arrays have been trimmed by %d elements", n_trimmed) slc = slice(flag[0][0], flag[0][-1] + 1) wavelength = wavelength[slc] net = net[slc] background = background[slc] dq = dq[slc] else: dq |= dqflags.pixel['DO_NOT_USE'] return (ra, dec, wavelength, net, background, dq)
def tso_aperture_photometry(datamodel, xcenter, ycenter, radius, radius_inner, radius_outer): """ Create a photometric catalog for NIRCam TSO imaging observations. Parameters ---------- datamodel : `CubeModel` The input `CubeModel` of a NIRCam TSO imaging observation. xcenter, ycenter : float The ``x`` and ``y`` center of the aperture. radius : float The radius (in pixels) of the circular aperture. radius_inner, radius_outer : float The inner and outer radii (in pixels) of the circular-annulus aperture, used for local background estimation. Returns ------- catalog : `~astropy.table.QTable` An astropy QTable (Quantity Table) containing the source photometry. """ if not isinstance(datamodel, CubeModel): raise ValueError('The input data model must be a CubeModel.') aper1 = CircularAperture((xcenter, ycenter), r=radius) aper2 = CircularAnnulus((xcenter, ycenter), r_in=radius_inner, r_out=radius_outer) nimg = datamodel.data.shape[0] aperture_sum = [] aperture_sum_err = [] annulus_sum = [] annulus_sum_err = [] for i in np.arange(nimg): tbl1 = aperture_photometry(datamodel.data[i, :, :], aper1, error=datamodel.err[i, :, :]) tbl2 = aperture_photometry(datamodel.data[i, :, :], aper2, error=datamodel.err[i, :, :]) aperture_sum.append(tbl1['aperture_sum'][0]) aperture_sum_err.append(tbl1['aperture_sum_err'][0]) annulus_sum.append(tbl2['aperture_sum'][0]) annulus_sum_err.append(tbl2['aperture_sum_err'][0]) # convert array of Quantities to Quantity arrays aperture_sum = u.Quantity(aperture_sum) aperture_sum_err = u.Quantity(aperture_sum_err) annulus_sum = u.Quantity(annulus_sum) annulus_sum_err = u.Quantity(annulus_sum_err) # construct metadata for output table meta = OrderedDict() meta['instrument'] = datamodel.meta.instrument.name meta['detector'] = datamodel.meta.instrument.detector meta['channel'] = datamodel.meta.instrument.channel meta['subarray'] = datamodel.meta.subarray.name meta['filter'] = datamodel.meta.instrument.filter meta['pupil'] = datamodel.meta.instrument.pupil meta['target_name'] = datamodel.meta.target.catalog_name meta['xcenter'] = xcenter meta['ycenter'] = ycenter ra_icrs, dec_icrs = datamodel.meta.wcs(xcenter, ycenter) meta['ra_icrs'] = ra_icrs meta['dec_icrs'] = dec_icrs info = ('Photometry measured in a circular aperture of r={0} pixels. ' 'Background calculated as the mean in a circular annulus with ' 'r_inner={1} pixels and r_outer={2} pixels.' .format(radius, radius_inner, radius_outer)) meta['apertures'] = info tbl = QTable(meta=meta) dt = (datamodel.meta.exposure.group_time * (datamodel.meta.exposure.ngroups + 1)) dt_arr = (np.arange(1, 1 + datamodel.meta.exposure.nints) * dt - (dt / 2.)) int_dt = TimeDelta(dt_arr, format='sec') int_times = (Time(datamodel.meta.exposure.start_time, format='mjd') + int_dt) tbl['MJD'] = int_times.mjd tbl['aperture_sum'] = aperture_sum tbl['aperture_sum_err'] = aperture_sum_err tbl['annulus_sum'] = annulus_sum tbl['annulus_sum_err'] = annulus_sum_err annulus_mean = annulus_sum / aper2.area() annulus_mean_err = annulus_sum_err / aper2.area() tbl['annulus_mean'] = annulus_mean tbl['annulus_mean_err'] = annulus_mean_err aperture_bkg = annulus_mean * aper1.area() aperture_bkg_err = annulus_mean_err * aper1.area() tbl['aperture_bkg'] = aperture_bkg tbl['aperture_bkg_err'] = aperture_bkg_err net_aperture_sum = aperture_sum - aperture_bkg net_aperture_sum_err = np.sqrt(aperture_sum_err ** 2 + aperture_bkg_err ** 2) tbl['net_aperture_sum'] = net_aperture_sum tbl['net_aperture_sum_err'] = net_aperture_sum_err return tbl
plt.figure(56) plt.plot(info['jd']-np.median(info['jd']),info['flux'][:,0],'o-') sys.exit() tp.lightcurve(info) position = (xnew,ynew) aperture = CircularAperture(position, r=radius) bkg_aperture = CircularAnnulus(position, r_in=15., r_out=20.) # perform the photometry; the default method is 'exact' phot = aperture_photometry(image, aperture) bkg = aperture_photometry(image, bkg_aperture) # calculate the mean background level (per pixel) in the annuli bkg_mean = bkg['aperture_sum'] / bkg_aperture.area() bkg_mean bkg_sum = bkg_mean * aperture.area() # plot the apertures plt.imshow(scale_image(image, scale='sqrt', percent=98.), origin='lower',cmap='gray') aperture.plot(color='blue') bkg_aperture.plot(color='cyan', hatch='//', alpha=0.8)
def azimuthal_avg_radial_intensity(wave, rtout, plotname, dstar, annulus_width=10, rrange=[10,200], group=8, obs=None, other_obs=None): """ The 'obs' option only works for Herschel PACS/SPIRE image. The 'obs' option now accept """ import numpy as np import matplotlib as mpl # to avoid X server error mpl.use('Agg') from astropy.io import ascii, fits import matplotlib.pyplot as plt from photutils import aperture_photometry as ap from photutils import CircularAperture, CircularAnnulus from astropy import units as u from astropy.coordinates import SkyCoord from astropy import wcs from hyperion.model import ModelOutput import astropy.constants as const import os pc = const.pc.cgs.value AU = const.au.cgs.value # radial grid in arcsec # make the annulus center on r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.5 # r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.55 # source_center = '12 01 36.3 -65 08 53.0' def ExtractIntensityObs(rrange, annulus_width, obs): import numpy as np from astropy.io import fits from astropy.coordinates import SkyCoord from astropy import wcs from photutils import aperture_photometry as ap from photutils import CircularAperture, CircularAnnulus r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.5 # r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.55 imgpath = obs['imgpath'] source_center = obs['source_center'] # Read in data and set up coversions im_hdu = fits.open(imgpath) im = im_hdu[1].data wave = im_hdu[0].header['WAVELNTH'] # error if (wave < 200.0) & (wave > 70.0): im_err = im_hdu[5].data elif (wave > 200.0) & (wave < 670.0): im_err = im_hdu[2].data else: im_err_exten = raw_input('The extension that includes the image error: ') im_err = im_hdu[int(im_err_exten)].data w = wcs.WCS(im_hdu[1].header) coord = SkyCoord(source_center, unit=(u.hourangle, u.deg)) pixcoord = w.wcs_world2pix(coord.ra.degree, coord.dec.degree, 1) pix2arcsec = abs(im_hdu[1].header['CDELT1'])*3600. # determine whether need to convert the unit factor = 1 print 'Image unit is ', im_hdu[1].header['BUNIT'] if im_hdu[1].header['BUNIT'] != 'Jy/pixel': print 'Image unit is ', im_hdu[1].header['BUNIT'] if im_hdu[1].header['BUNIT'] == 'MJy/sr': # convert intensity unit from MJy/sr to Jy/pixel factor = 1e6/4.25e10*abs(im_hdu[1].header['CDELT1']*im_hdu[1].header['CDELT2'])*3600**2 else: factor = raw_input('What is the conversion factor to Jy/pixel?') I = np.empty_like(r[:-1]) I_low = np.empty_like(r[:-1]) I_hi = np.empty_like(r[:-1]) I_err = np.empty_like(r[:-1]) # for calculating the uncertainty from the variation within each annulus # construct the x- and y-matrix grid_x, grid_y = np.meshgrid(np.linspace(0,len(im[0,:])-1,len(im[0,:])), np.linspace(0,len(im[:,0])-1,len(im[:,0]))) grid_dist = ((grid_x-pixcoord[0])**2+(grid_y-pixcoord[1])**2)**0.5 # iteration for ir in range(len(r)-1): aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec, r_out=r[ir+1]/pix2arcsec) phot = ap(im, aperture, error=im_err) I[ir] = phot['aperture_sum'].data * factor / aperture.area() # uncertainty im_dum = np.where((grid_dist < r[ir+1]/pix2arcsec) & (grid_dist >= r[ir]/pix2arcsec), im, np.nan) # estimate the uncertainty by offsetting the annulus by +/- 1 pixel offset = -1 if r[ir]/pix2arcsec + offset < 0: offset = -r[ir]/pix2arcsec aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(im, aperture, error=im_err) I_low[ir] = phot['aperture_sum'].data * factor / aperture.area() offset = 1 aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(im, aperture, error=im_err) I_hi[ir] = phot['aperture_sum'].data * factor / aperture.area() I_err = (abs(I_low - I) + abs(I_hi - I))/2. return r, I, I_err if obs != None: I_obs = [] for o in obs: if 'label' not in o.keys(): label_dum = r'$\rm{observation}$' color_dum = 'g' linestyle_dum = '-' rrange_dum = rrange annulus_width_dum = annulus_width else: label_dum = o['label'] color_dum = o['plot_color'] linestyle_dum = o['plot_linestyle'] rrange_dum = o['rrange'] annulus_width_dum = o['annulus_width'] r_dum, I_dum, I_err_dum = ExtractIntensityObs(rrange_dum, annulus_width_dum, o) # determine the label I_obs.append({'imgpath':o['imgpath'], 'r':r_dum, 'I':I_dum, 'I_err':I_err_dum, 'label': label_dum, 'plot_color':color_dum, 'plot_linestyle':linestyle_dum}) # The first image should be the one to be compared primarily, and written out I = I_obs[0]['I'] I_err = I_obs[0]['I_err'] imgpath = I_obs[0]['imgpath'] # # read in from RTout rtout = ModelOutput(rtout) im = rtout.get_image(group=group, inclination=0, distance=dstar*pc, units='Jy', uncertainties=True) factor = 1 # Find the closest wavelength iwav = np.argmin(np.abs(wave - im.wav)) # avoid zero when log, and flip the image val = im.val[::-1, :, iwav] unc = im.unc[::-1, :, iwav] w = np.degrees(max(rtout.get_quantities().r_wall) / im.distance) * 3600 npix = len(val[:,0]) pix2arcsec = 2*w/npix I_sim = np.empty_like(r[:-1]) I_sim_hi = np.empty_like(r[:-1]) I_sim_low = np.empty_like(r[:-1]) I_sim_err = np.empty_like(r[:-1]) # for calculating the uncertainty from the variation within each annulus # construct the x- and y-matrix grid_x, grid_y = np.meshgrid(np.linspace(0,npix-1,npix), np.linspace(0,npix-1,npix)) dist_x = abs(grid_x - ((npix-1)/2.)) dist_y = abs(grid_y - ((npix-1)/2.)) grid_dist = (dist_x**2+dist_y**2)**0.5 # iteration for ir in range(len(r)-1): aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5), r_in=r[ir]/pix2arcsec, r_out=r[ir+1]/pix2arcsec) phot = ap(val, aperture, error=unc) I_sim[ir] = phot['aperture_sum'].data / aperture.area() # uncertainty im_dum = np.where((grid_dist < r[ir+1]/pix2arcsec) & (grid_dist >= r[ir]/pix2arcsec), val, np.nan) # I_sim_err[ir] = phot['aperture_sum_err'].data / aperture.area() # I_sim_err[ir] = (np.nanstd(im_dum)**2+phot['aperture_sum_err'].data**2)**0.5 * factor / aperture.area() offset = -1 aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(val, aperture, error=unc) I_sim_low[ir] = phot['aperture_sum'].data * factor / aperture.area() offset = 1 aperture = CircularAnnulus((npix/2.+0.5, npix/2.+0.5), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(val, aperture, error=unc) I_sim_hi[ir] = phot['aperture_sum'].data * factor / aperture.area() I_sim_err = (abs(I_sim_low - I_sim)+ abs(I_sim_hi - I_sim))/2. if obs != None: # write the numbers into file foo = open(plotname+'_radial_profile_'+str(wave)+'um.txt', 'w') # print some header info foo.write('# wavelength '+str(wave)+' um \n') foo.write('# image file '+os.path.basename(imgpath)+' \n') foo.write('# annulus width '+str(annulus_width)+' arcsec \n') # write profiles foo.write('r_in \t I \t I_err \t I_sim \t I_sim_err \n') foo.write('# [arcsec] \t [Jy/pixel] \t [Jy/pixel] \t [Jy/pixel] \t [Jy/pixel] \n') for i in range(len(I)): foo.write('%f \t %e \t %e \t %e \t %e \n' % (r[i]+annulus_width/2., I[i], I_err[i], I_sim[i], I_sim_err[i])) foo.close() else: # write the numbers into file foo = open(plotname+'_radial_profile_'+str(wave)+'um.txt', 'w') # print some header info foo.write('# wavelength '+str(wave)+' um \n') foo.write('# annulus width '+str(annulus_width)+' arcsec \n') # write profiles foo.write('r_in \t I_sim \t I_sim_err \n') foo.write('# [arcsec] \t [Jy/pixel] \t [Jy/pixel] \n') for i in range(len(I_sim)): foo.write('%f \t %e \t %e \n' % (r[i]+annulus_width/2., I_sim[i], I_sim_err[i])) foo.close() # plot fig = plt.figure(figsize=(8,6)) ax = fig.add_subplot(111) I_sim_hi = np.log10((I_sim+I_sim_err)/I_sim.max())-np.log10(I_sim/I_sim.max()) I_sim_low = np.log10(I_sim/I_sim.max())-np.log10((I_sim-I_sim_err)/I_sim.max()) i_sim = ax.errorbar(np.log10(r[:-1]*dstar), np.log10(I_sim/I_sim.max()), color='b', yerr=(I_sim_low, I_sim_hi), marker='o', linestyle='-', mec='None', markersize=5, ecolor='b', elinewidth=1.5, capthick=1.5, barsabove=True) if obs != None: plot_profile = [] plot_label = [] for o in I_obs: I_hi = np.log10((o['I']+o['I_err'])/o['I'].max())-np.log10(o['I']/o['I'].max()) I_low = np.log10(o['I']/o['I'].max())-np.log10((o['I']-o['I_err'])/o['I'].max()) i = ax.errorbar(np.log10(o['r'][:-1]*dstar), np.log10(o['I']/o['I'].max()), color=o['plot_color'], yerr=(I_low, I_hi), marker='o', linestyle=o['plot_linestyle'], mec='None', markersize=5, ecolor=o['plot_color'], elinewidth=1.5, capthick=1.5, barsabove=True) plot_profile.append(i) plot_label.append(o['label']) plot_profile.append(i_sim) plot_label.append(r'$\rm{simulation}$') ax.legend(plot_profile, plot_label, fontsize=16, numpoints=1, loc='best') else: ax.legend([i_sim], [r'$\rm{simulation}$'], fontsize=16, numpoints=1, loc='best') # limit radius ax.axvline([np.log10(100*dstar)], color='k', linestyle='--', linewidth=1) # [ax.spines[axis].set_linewidth(1.5) for axis in ['top','bottom','left','right']] ax.minorticks_on() ax.tick_params('both',labelsize=18,width=1.5,which='major',pad=10,length=5) ax.tick_params('both',labelsize=18,width=1.5,which='minor',pad=10,length=2.5) ax.set_xlabel(r'$\rm{log(\it{b})\,[\rm{AU}]}$', fontsize=18) ax.set_ylabel(r'$\rm{log(I\,/\,I_{max})}$', fontsize=18) # fix the tick label font ticks_font = mpl.font_manager.FontProperties(family='STIXGeneral',size=18) for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font) for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font) fig.savefig(plotname+'_radial_profile_'+str(wave)+'um.pdf', format='pdf', dpi=300, bbox_inches='tight') fig.clf()
def ExtractIntensityObs(rrange, annulus_width, obs): import numpy as np from astropy.io import fits from astropy.coordinates import SkyCoord from astropy import wcs from photutils import aperture_photometry as ap from photutils import CircularAperture, CircularAnnulus r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.5 # r = np.arange(rrange[0], rrange[1], annulus_width, dtype=float) - annulus_width*0.55 imgpath = obs['imgpath'] source_center = obs['source_center'] # Read in data and set up coversions im_hdu = fits.open(imgpath) im = im_hdu[1].data wave = im_hdu[0].header['WAVELNTH'] # error if (wave < 200.0) & (wave > 70.0): im_err = im_hdu[5].data elif (wave > 200.0) & (wave < 670.0): im_err = im_hdu[2].data else: im_err_exten = raw_input('The extension that includes the image error: ') im_err = im_hdu[int(im_err_exten)].data w = wcs.WCS(im_hdu[1].header) coord = SkyCoord(source_center, unit=(u.hourangle, u.deg)) pixcoord = w.wcs_world2pix(coord.ra.degree, coord.dec.degree, 1) pix2arcsec = abs(im_hdu[1].header['CDELT1'])*3600. # determine whether need to convert the unit factor = 1 print 'Image unit is ', im_hdu[1].header['BUNIT'] if im_hdu[1].header['BUNIT'] != 'Jy/pixel': print 'Image unit is ', im_hdu[1].header['BUNIT'] if im_hdu[1].header['BUNIT'] == 'MJy/sr': # convert intensity unit from MJy/sr to Jy/pixel factor = 1e6/4.25e10*abs(im_hdu[1].header['CDELT1']*im_hdu[1].header['CDELT2'])*3600**2 else: factor = raw_input('What is the conversion factor to Jy/pixel?') I = np.empty_like(r[:-1]) I_low = np.empty_like(r[:-1]) I_hi = np.empty_like(r[:-1]) I_err = np.empty_like(r[:-1]) # for calculating the uncertainty from the variation within each annulus # construct the x- and y-matrix grid_x, grid_y = np.meshgrid(np.linspace(0,len(im[0,:])-1,len(im[0,:])), np.linspace(0,len(im[:,0])-1,len(im[:,0]))) grid_dist = ((grid_x-pixcoord[0])**2+(grid_y-pixcoord[1])**2)**0.5 # iteration for ir in range(len(r)-1): aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec, r_out=r[ir+1]/pix2arcsec) phot = ap(im, aperture, error=im_err) I[ir] = phot['aperture_sum'].data * factor / aperture.area() # uncertainty im_dum = np.where((grid_dist < r[ir+1]/pix2arcsec) & (grid_dist >= r[ir]/pix2arcsec), im, np.nan) # estimate the uncertainty by offsetting the annulus by +/- 1 pixel offset = -1 if r[ir]/pix2arcsec + offset < 0: offset = -r[ir]/pix2arcsec aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(im, aperture, error=im_err) I_low[ir] = phot['aperture_sum'].data * factor / aperture.area() offset = 1 aperture = CircularAnnulus((pixcoord[0],pixcoord[1]), r_in=r[ir]/pix2arcsec + offset, r_out=r[ir+1]/pix2arcsec + offset) phot = ap(im, aperture, error=im_err) I_hi[ir] = phot['aperture_sum'].data * factor / aperture.area() I_err = (abs(I_low - I) + abs(I_hi - I))/2. return r, I, I_err
def photometry(star_positions, aperture_radii, centroid_stamp_half_width, psf_stddev_init, aperture_annulus_radius, output_path): """ Parameters ---------- master_dark_path : str Path to master dark frame master_flat_path :str Path to master flat field target_centroid : `~numpy.ndarray` position of centroid, with shape (2, 1) comparison_flux_threshold : float Minimum fraction of the target star flux required to accept for a comparison star to be included aperture_radii : `~numpy.ndarray` Range of aperture radii to use centroid_stamp_half_width : int Centroiding is done within image stamps centered on the stars. This parameter sets the half-width of the image stamps. psf_stddev_init : float Initial guess for the width of the PSF stddev parameter, used for fitting 2D Gaussian kernels to the target star's PSF. aperture_annulus_radius : int For each aperture in ``aperture_radii``, measure the background in an annulus ``aperture_annulus_radius`` pixels bigger than the aperture radius output_path : str Path to where outputs will be saved. """ master_dark = fits.getdata(master_dark_path) master_flat = fits.getdata(master_flat_path) master_flat[master_flat < 0.1] = 1.0 # tmp #star_positions = init_centroids(image_paths[0:3], master_flat, master_dark, # target_centroid, plots=True, # min_flux=comparison_flux_threshold).T # Initialize some empty arrays to fill with data: times = np.zeros(len(image_paths)) fluxes = np.zeros((len(image_paths), len(star_positions), len(aperture_radii))) errors = np.zeros((len(image_paths), len(star_positions), len(aperture_radii))) xcentroids = np.zeros((len(image_paths), len(star_positions))) ycentroids = np.zeros((len(image_paths), len(star_positions))) airmass = np.zeros(len(image_paths)) airpress = np.zeros(len(image_paths)) humidity = np.zeros(len(image_paths)) telfocus = np.zeros(len(image_paths)) psf_stddev = np.zeros(len(image_paths)) medians = np.zeros(len(image_paths)) with ProgressBar(len(image_paths)) as bar: for i in range(len(image_paths)): bar.update() # Subtract image by the dark frame, normalize by flat field imagedata = (fits.getdata(image_paths[i]) - master_dark) / master_flat # Collect information from the header imageheader = fits.getheader(image_paths[i]) exposure_duration = imageheader['EXPTIME'] times[i] = Time(imageheader['DATE-OBS'], format='isot', scale=imageheader['TIMESYS'].lower()).jd medians[i] = np.median(imagedata) airmass[i] = imageheader['AIRMASS'] airpress[i] = imageheader['AIRPRESS'] humidity[i] = imageheader['HUMIDITY'] telfocus[i] = imageheader['TELFOCUS'] # Initial guess for each stellar centroid informed by previous centroid for j in range(len(star_positions)): if i == 0: init_x = star_positions[j][0] init_y = star_positions[j][1] else: init_x = ycentroids[i-1][j] init_y = xcentroids[i-1][j] # Cut out a stamp of the full image centered on the star image_stamp = imagedata[int(init_y) - centroid_stamp_half_width: int(init_y) + centroid_stamp_half_width, int(init_x) - centroid_stamp_half_width: int(init_x) + centroid_stamp_half_width] # Measure stellar centroid with 2D gaussian fit x_stamp_centroid, y_stamp_centroid = centroid_com(image_stamp) y_centroid = x_stamp_centroid + init_x - centroid_stamp_half_width x_centroid = y_stamp_centroid + init_y - centroid_stamp_half_width xcentroids[i, j] = x_centroid ycentroids[i, j] = y_centroid # For the target star, measure PSF: if j == 0: psf_model_init = models.Gaussian2D(amplitude=np.max(image_stamp), x_mean=centroid_stamp_half_width, y_mean=centroid_stamp_half_width, x_stddev=psf_stddev_init, y_stddev=psf_stddev_init) fit_p = fitting.LevMarLSQFitter() y, x = np.mgrid[:image_stamp.shape[0], :image_stamp.shape[1]] best_psf_model = fit_p(psf_model_init, x, y, image_stamp - np.median(image_stamp)) psf_stddev[i] = 0.5*(best_psf_model.x_stddev.value + best_psf_model.y_stddev.value) positions = np.vstack([ycentroids[i, :], xcentroids[i, :]]) for k, aperture_radius in enumerate(aperture_radii): target_apertures = CircularAperture(positions, aperture_radius) background_annuli = CircularAnnulus(positions, r_in=aperture_radius + aperture_annulus_radius, r_out=aperture_radius + 2 * aperture_annulus_radius) flux_in_annuli = aperture_photometry(imagedata, background_annuli)['aperture_sum'].data background = flux_in_annuli/background_annuli.area() flux = aperture_photometry(imagedata, target_apertures)['aperture_sum'].data background_subtracted_flux = (flux - background * target_apertures.area()) fluxes[i, :, k] = background_subtracted_flux/exposure_duration errors[i, :, k] = np.sqrt(flux) ## Save some values results = PhotometryResults(times, fluxes, errors, xcentroids, ycentroids, airmass, airpress, humidity, medians, psf_stddev, aperture_radii) results.save(output_path) return results
def extract_ifu(input_model, source_type, extract_params): """This function does the extraction. Parameters ---------- input_model : IFUCubeModel The input model. source_type : string "point" or "extended" extract_params : dict The extraction parameters for aperture photometry. Returns ------- ra, dec : float ra and dec are the right ascension and declination respectively at the nominal center of the image. wavelength : ndarray, 1-D The wavelength in micrometers at each pixel. net : ndarray, 1-D The count rate (or flux) minus the background at each pixel. background : ndarray, 1-D The background count rate that was subtracted from the total source count rate to get `net`. npixels : ndarray, 1-D, float64 For each slice, this is the number of pixels that were added together to get `net`. dq : ndarray, 1-D, uint32 The data quality array. """ data = input_model.data shape = data.shape if len(shape) != 3: log.error("Expected a 3-D IFU cube; dimension is %d.", len(shape)) raise RuntimeError("The IFU cube should be 3-D.") # We need to allocate net, background, npixels, and dq arrays # no matter what. We may need to divide by npixels, so the default # is 1 rather than 0. net = np.zeros(shape[0], dtype=np.float64) background = np.zeros(shape[0], dtype=np.float64) npixels = np.ones(shape[0], dtype=np.float64) dq = np.zeros(shape[0], dtype=np.uint32) # For an extended target, the entire aperture will be extracted, so # it makes no sense to shift the extraction location. if source_type.lower() != "extended": ra_targ = input_model.meta.target.ra dec_targ = input_model.meta.target.dec locn = locn_from_wcs(input_model, ra_targ, dec_targ) if locn is None or np.isnan(locn[0]): log.warning("Couldn't determine pixel location from WCS, so " "nod/dither correction will not be applied.") x_center = extract_params['x_center'] y_center = extract_params['y_center'] if x_center is None: x_center = float(shape[-1]) / 2. else: x_center = float(x_center) if y_center is None: y_center = float(shape[-2]) / 2. else: y_center = float(y_center) else: (x_center, y_center) = locn log.info("Using x_center = %g, y_center = %g, based on " "TARG_RA and TARG_DEC.", x_center, y_center) method = extract_params['method'] # subpixels is only needed if method = 'subpixel'. subpixels = extract_params['subpixels'] subtract_background = extract_params['subtract_background'] smaller_axis = float(min(shape[-2], shape[-1])) # for defaults radius = None inner_bkg = None outer_bkg = None if source_type == 'extended': # Ignore any input parameters, and extract the whole image. width = float(shape[-1]) height = float(shape[-2]) x_center = width / 2. - 0.5 y_center = height / 2. - 0.5 theta = 0. subtract_background = False else: radius = extract_params['radius'] if radius is None: radius = smaller_axis / 4. if subtract_background: inner_bkg = extract_params['inner_bkg'] if inner_bkg is None: inner_bkg = radius outer_bkg = extract_params['outer_bkg'] if outer_bkg is None: outer_bkg = min(inner_bkg * math.sqrt(2.), smaller_axis / 2. - 1.) if inner_bkg <= 0. or outer_bkg <= 0. or inner_bkg >= outer_bkg: log.debug("Turning background subtraction off, due to " "the values of inner_bkg and outer_bkg.") subtract_background = False width = None height = None theta = None log.debug("IFU 1-D extraction parameters:") log.debug(" x_center = %s", str(x_center)) log.debug(" y_center = %s", str(y_center)) if source_type == 'point': log.debug(" radius = %s", str(radius)) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" inner_bkg = %s", str(inner_bkg)) log.debug(" outer_bkg = %s", str(outer_bkg)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) else: log.debug(" width = %s", str(width)) log.debug(" height = %s", str(height)) log.debug(" theta = %s degrees", str(theta)) log.debug(" subtract_background = %s", str(subtract_background)) log.debug(" method = %s", method) if method == "subpixel": log.debug(" subpixels = %s", str(subpixels)) x0 = float(shape[2]) / 2. y0 = float(shape[1]) / 2. (ra, dec, wavelength) = get_coordinates(input_model, x0, y0) position = (x_center, y_center) if source_type == 'point': aperture = CircularAperture(position, r=radius) else: aperture = RectangularAperture(position, width, height, theta) if subtract_background and inner_bkg is not None and outer_bkg is not None: annulus = CircularAnnulus(position, r_in=inner_bkg, r_out=outer_bkg) else: annulus = None # Compute the area of the aperture and possibly also of the annulus. normalization = 1. temp = np.ones(shape[-2:], dtype=np.float64) phot_table = aperture_photometry(temp, aperture, method=method, subpixels=subpixels) aperture_area = float(phot_table['aperture_sum'][0]) log.debug("aperture.area() = %g; aperture_area = %g", aperture.area(), aperture_area) if subtract_background and annulus is not None: # Compute the area of the annulus. phot_table = aperture_photometry(temp, annulus, method=method, subpixels=subpixels) annulus_area = float(phot_table['aperture_sum'][0]) log.debug("annulus.area() = %g; annulus_area = %g", annulus.area(), annulus_area) if annulus_area > 0.: normalization = aperture_area / annulus_area else: log.warning("Background annulus has no area, so background " "subtraction will be turned off.") subtract_background = False del temp npixels[:] = aperture_area for k in range(shape[0]): phot_table = aperture_photometry(data[k, :, :], aperture, method=method, subpixels=subpixels) net[k] = float(phot_table['aperture_sum'][0]) if subtract_background: bkg_table = aperture_photometry(data[k, :, :], annulus, method=method, subpixels=subpixels) background[k] = float(bkg_table['aperture_sum'][0]) net[k] = net[k] - background[k] * normalization # Check for NaNs in the wavelength array, flag them in the dq array, # and truncate the arrays if NaNs are found at endpoints (unless the # entire array is NaN). (wavelength, net, background, npixels, dq) = \ nans_in_wavelength(wavelength, net, background, npixels, dq) return (ra, dec, wavelength, net, background, npixels, dq)
def addzb(fitsname, redo=False, fau_dir=None): telescopes = ['1','2','3','4'] # night = (fitsname.split('/'))[3] night = (fitsname.split('/'))[-2] print 'beginning ' + fitsname # 2D spectrum h = pyfits.open(fitsname,mode='update') if 'BARYSRC4' in h[0].header.keys() and not redo: print fitsname + " already done" h.flush() h.close() return specstart = datetime.datetime.strptime(h[0].header['DATE-OBS'],"%Y-%m-%dT%H:%M:%S.%f") specmid = specstart + datetime.timedelta(seconds = h[0].header['EXPTIME']/2.0) specend = specstart + datetime.timedelta(seconds = h[0].header['EXPTIME']) t0 = datetime.datetime(2000,1,1) t0jd = 2451544.5 aperture_radius = 3.398 # fiber radius in pixels annulus_inner = 2.0*aperture_radius annulus_outer = 3.0*aperture_radius for telescope in telescopes: print 'beginning telescope ' + telescope + ' on ' + fitsname # get the barycentric redshift for each time ra = h[0].header['TARGRA' + telescope] dec = h[0].header['TARGDEC' + telescope] try: pmra = h[0].header['PMRA' + telescope] except: pmra = 0.0 try: pmdec = h[0].header['PMDEC' + telescope] except: pmdec = 0.0 try: parallax = h[0].header['PARLAX' + telescope] except: parallax = 0.0 try: rv = h[0].header['RV' + telescope] except: rv = 0.0 objname = h[0].header['OBJECT' + telescope] if fau_dir is None: faupath = '/Data/t' + telescope + '/' + night + '/' + night + '.T' + telescope + '.FAU.' + objname + '.????.fits' else: faupath = fau_dir + '/t' + telescope + '/' + night + '/' + night + '.T' + telescope + '.FAU.' + objname + '.????.fits' guideimages = glob.glob(faupath) # if telescope == '2' and "HD62613" in fitsname: ipdb.set_trace() times = [] fluxes = np.array([]) for guideimage in guideimages: try: fauimage = pyfits.open(guideimage) except: print "corrupt file for " + guideimage continue # midtime of the guide image (UTC) midtime = datetime.datetime.strptime(fauimage[0].header['DATE-OBS'],"%Y-%m-%dT%H:%M:%S") +\ datetime.timedelta(seconds=fauimage[0].header['EXPTIME']/2.0) # convert to Julian date midjd = t0jd + (midtime-t0).total_seconds()/86400.0 # only look at images during the spectrum if midtime < specstart or midtime > specend: continue # find the fiber position try: fiber_x = fauimage[0].header['XFIBER' + telescope] fiber_y = fauimage[0].header['YFIBER' + telescope] except: print "keywords missing for " + guideimage continue # do aperture photometry positions = [(fiber_x,fiber_y)] apertures = CircularAperture(positions,r=aperture_radius) annulus_apertures = CircularAnnulus(positions, r_in=annulus_inner, r_out=annulus_outer) # calculate the background-subtracted flux at the fiber position rawflux_table = aperture_photometry(fauimage[0].data, apertures) bkgflux_table = aperture_photometry(fauimage[0].data, annulus_apertures) bkg_mean = bkgflux_table['aperture_sum'].sum() / annulus_apertures.area() bkg_sum = bkg_mean * apertures.area() flux = rawflux_table['aperture_sum'].sum() - bkg_sum # append to the time and flux arrays times.append(midjd) fluxes = np.append(fluxes,flux) if len(times) == 0: print "No guider images for " + fitsname + " on Telescope " + telescope + "; assuming mid time" if 'daytimeSky' in fitsname: h[0].header['BARYCOR' + telescope] = ('UNKNOWN','Barycentric redshift') h[0].header['BARYSRC' + telescope] = ('UNKNOWN','Source for the barycentric redshift') h[0].header['FLUXMID' + telescope] = (midjd,'Flux-weighted mid exposure time (JD_UTC)') continue # convert specmid to Julian date midjd = t0jd + (specmid-t0).total_seconds()/86400.0 print midjd zb = barycorr(midjd, ra, dec, pmra=pmra, pmdec=pmdec, parallax=parallax, rv=rv)/2.99792458e8 h[0].header['BARYCOR' + telescope] = (zb,'Barycentric redshift') h[0].header['BARYSRC' + telescope] = ('MIDTIME','Source for the barycentric redshift') h[0].header['FLUXMID' + telescope] = (midjd,'Flux-weighted mid exposure time (JD_UTC)') continue zb = np.asarray(barycorr(times, ra, dec, pmra=pmra, pmdec=pmdec, parallax=parallax, rv=rv))/2.99792458e8 # weight the barycentric correction by the flux #*******************!*!*!*!*!*!* #this assumes guider images were taken ~uniformly throughout the spectroscopic exposure! #****************!*!*!*!*!**!*!*!*!**!*!*!*!* wzb = np.sum(zb*fluxes)/np.sum(fluxes) wmidjd = np.sum(times*fluxes)/np.sum(fluxes) # update the header to include aperture photometry and barycentric redshift h[0].header['BARYCOR' + telescope] = (wzb,'Barycentric redshift') h[0].header['BARYSRC' + telescope] = ('FAU Flux Weighted','Source for the barycentric redshift') h[0].header['FLUXMID' + telescope] = (wmidjd,'Flux-weighted mid exposure time (JD_UTC)') hdu = pyfits.PrimaryHDU(zip(times,fluxes)) hdu.header['TELESCOP'] = ('T' + telescope,'Telescope') h.append(hdu) # write updates to the disk h.flush() h.close()