def create_aperture_from_ds9_region(region_file): ''' Loads a ds9 region ('reg') file saved in physical format and returns an astropy aperture instance. ''' # f = open(region_file,'rb') for i in range(3): f.readline() region_text = f.read() pdb.set_trace() regions = region_text.split('\n') src_apertures,bkg_apertures = [],[] for region in regions[:-1]: # This is a poor example of string handling, but it works. value_text = region.split('(')[1].split(')')[0] pos_x,pos_y,r_in,r_out = np.array(value_text.split(','),dtype = float) src_apertures.append(photutils.CircularAperture((pos_x,pos_y), r=r_in)) bkg_aperture.append(photutils.CircularAnnulus((pos_x,pos_y), r_in=r_in, r_out=r_out)) return src_apertures,bkg_apertures
def _aperture_phot(self, x, y, data, radsize=1, sky_inner=5, skywidth=5, method="subpixel", subpixels=4): """Perform sky subtracted aperture photometry, uses photutils functions, photutil must be installed Parameters ---------- radsize: int Size of the radius sky_inner: int Inner radius of the sky annulus skywidth: int Width of the sky annulus method: string Pixel sampling method to use subpixels: int How many subpixels to use Notes ----- background is taken from sky annulus pixels, check into masking bad pixels """ if not photutils_installed: print("Install photutils to enable") else: apertures = photutils.CircularAperture((x, y), radsize) rawflux_table = photutils.aperture_photometry( data, apertures, subpixels=1, method="center") outer = sky_inner + skywidth annulus_apertures = photutils.CircularAnnulus( (x, y), r_in=sky_inner, r_out=outer) bkgflux_table = photutils.aperture_photometry( data, annulus_apertures) # to calculate the mean local background, divide the circular annulus aperture sums # by the area fo the circular annuls. The bkg sum with the circular aperture is then # then mean local background tims the circular apreture area. aperture_area = apertures.area() annulus_area = annulus_apertures.area() bkg_sum = ( bkgflux_table['aperture_sum'] * aperture_area / annulus_area)[0] skysub_flux = rawflux_table['aperture_sum'][0] - bkg_sum return ( float(rawflux_table['aperture_sum'][0]), bkg_sum, skysub_flux)
def get_encircled_energy_and_rad_at_EE(data, x, y, radii, get_rad_at_EE=0.9, plot=False, ax=None): """ A function to calculate the encircled energy at a given position, summing up the flux in apertures of size *radii*, normalizing the EE to the last flux value. INPUT: data - a two dimensional np.array x - x centroid y - y centroid radii - an array of radii to calculate the EE get_rad_at_EE - the EE of which to calculate the radius value plot - plot a EE vs radii plot OUTPUT: df a dataframe with two columns: - radii - EE rad_at_EE - the radius when EE is a given input value NOTE: Assumes that the data is background subtracted EXAMPLE: radii = np.arange(1,450) df_ee, r_at_EE90 = phothelp.get_encircled_energy(fimg.data,fimg.xcenter,fimg.ycenter,radii,plot=True) """ apertures = [photutils.CircularAperture((x, y), r=r) for r in radii] phot_table = photutils.aperture_photometry(data, apertures) df = phot_table[phot_table.colnames[3:]].to_pandas() EE = df.loc[0] / df.loc[0][-1] df = pd.DataFrame(list(zip(radii, EE)), columns=["radii", "EE"]) #r_at_EE = df[df["EE"] > get_rad_at_EE]["radii"].values[0] r_at_EE = np.interp(get_rad_at_EE, df.EE.values, df.radii.values) if plot: if ax == None: fig, ax = plt.subplots() ax.plot(radii, EE.values) ax.set_xlabel("Radii") ax.set_ylabel("Encircled Energy") ax.set_title("EE" + gk.num2str(get_rad_at_EE * 100, 1) + "% at r=" + str(r_at_EE)) ax.vlines(r_at_EE, 0, get_rad_at_EE, color="green", linestyle="--") ax.hlines(get_rad_at_EE, 0, r_at_EE, color="green", linestyle="--") ax.minorticks_on() return df, r_at_EE
def subtract_background(self, subtract_min_value=True, plot_background=False, ax=None): """ A function to subtract the background for the FRD tests INPUT: subtract_min_value - subtracts the .min value (no negative numbers) plot_background - if True, plots the estimated background with the meshes it used. """ self.aper = photutils.CircularAperture(positions=(self.x, self.y), r=self.r) # get the mask from the aperture # hack mask = self.aper.to_mask()[0].to_image(self.data.shape) # use sigma clipping sigma_clip = SigmaClip(sigma=3., iters=10) bkg_estimator = photutils.MedianBackground() self.bkg = photutils.Background2D(self.data, self.box_size, filter_size=(3, 3), sigma_clip=sigma_clip, bkg_estimator=bkg_estimator, mask=mask, edge_method="crop") self.data_background = self.data - self.bkg.background if subtract_min_value: self.subtracted_value = self.data_background.min() print("Subtracted min value:", self.subtracted_value) self.data_background -= self.subtracted_value if plot_background: if ax == None: self.fig, self.ax = plt.subplots() else: self.ax = ax im = self.ax.imshow(self.bkg.background, origin='lower', cmap='Greys_r') self.ax.set_xlim(0, self.bkg.background.shape[1]) self.ax.set_ylim(0, self.bkg.background.shape[0]) self.ax.set_title("Estimated Background", y=1.02) self.ax.set_xlabel("X pixels") self.ax.set_ylabel("Y pixels") if ax == None: # Only do this if ax is not supplied # Don't know how to deal with this without passing the figure explicitly self.fig.colorbar(im) self.bkg.plot_meshes(outlines=True, color='#1f77b4', ax=self.ax) return self.data_background
def finish(self, survey, brickname, version_header, apradec=None, apertures=None): # apradec = (ra,dec): aperture photometry locations # apertures: RADII in PIXELS if apradec is not None: assert(apertures is not None) (ra,dec) = apradec ok,xx,yy = self.wcs.radec2pixelxy(ra, dec) assert(np.all(ok)) del ok apxy = np.vstack((xx - 1., yy - 1.)).T ap_iphots = [np.zeros((len(ra), len(apertures)), np.float32) for band in self.bands] ap_dphots = [np.zeros((len(ra), len(apertures)), np.float32) for band in self.bands] ap_rphots = [np.zeros((len(ra), len(apertures)), np.float32) for band in self.bands] coimgs = [] comods = [] for iband,band in enumerate(self.bands): coimg = self.co_images[band] comod = self.co_models[band] coiv = self.co_invvars[band] con = self.co_nobs[band] with np.errstate(divide='ignore', invalid='ignore'): coimg /= coiv comod /= coiv coimg[coiv == 0] = 0. comod[coiv == 0] = 0. coimgs.append(coimg) comods.append(comod) hdr = copy_header_with_wcs(version_header, self.wcs) self.add_to_header(hdr, band) self.write_coadds(survey, brickname, hdr, band, coimg, comod, coiv, con) if apradec is not None: import photutils mask = (coiv == 0) with np.errstate(divide='ignore'): imsigma = 1.0/np.sqrt(coiv) imsigma[mask] = 0. for irad,rad in enumerate(apertures): aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(coimg, aper, error=imsigma, mask=mask) ap_iphots[iband][:,irad] = p.field('aperture_sum') ap_dphots[iband][:,irad] = p.field('aperture_sum_err') p = photutils.aperture_photometry(coimg - comod, aper, mask=mask) ap_rphots[iband][:,irad] = p.field('aperture_sum') self.write_color_image(survey, brickname, coimgs, comods) if apradec is not None: return ap_iphots, ap_dphots, ap_rphots
def psf_norm_2d(array, fwhm, size, threshold, mask_core, full_output, verbose): """ 2d case """ if size is not None: if size < array.shape[0]: psfs = frame_crop(array, size, force=True, verbose=False) else: psfs = array.copy() else: psfs = array.copy() # we check if the psf is centered and fix it if needed cy, cx = frame_center(psfs, verbose=False) xcom, ycom = photutils.centroid_com(psfs) if not (np.allclose(cy, ycom, atol=1e-2) or np.allclose(cx, xcom, atol=1e-2)): # first we find the centroid and put it in the center of the array centry, centrx = fit_2d(psfs) shiftx, shifty = centrx - cx, centry - cy psfs = frame_shift(array, -shifty, -shiftx, imlib=imlib, interpolation=interpolation) if size is not None: psfs = frame_crop(psfs, size, force=True, verbose=False) for _ in range(2): centry, centrx = fit_2d(psfs) cy, cx = frame_center(psfs, verbose=False) shiftx, shifty = centrx - cx, centry - cy psfs = frame_shift(psfs, -shifty, -shiftx, imlib=imlib, interpolation=interpolation) # we check whether the flux is normalized and fix it if needed fwhm_aper = photutils.CircularAperture((frame_center(psfs)), fwhm/2) fwhm_aper_phot = photutils.aperture_photometry(psfs, fwhm_aper, method='exact') fwhm_flux = np.array(fwhm_aper_phot['aperture_sum']) if fwhm_flux > 1.1 or fwhm_flux < 0.9: psf_norm_array = psfs / np.array(fwhm_aper_phot['aperture_sum']) else: psf_norm_array = psfs if threshold is not None: psf_norm_array[np.where(psf_norm_array < threshold)] = 0 if mask_core is not None: psf_norm_array = get_circle(psf_norm_array, radius=mask_core) if verbose: print("Flux in 1xFWHM aperture: {:.3f}".format(fwhm_flux[0])) if full_output: return psf_norm_array, fwhm_flux, fwhm else: return psf_norm_array
def _apphot_one((irad, band, rad, img, sigma, isimage, apxy)): import photutils result = [irad, band, isimage] aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(img, aper, error=sigma) result.append(p.field('aperture_sum')) if sigma is not None: result.append(p.field('aperture_sum_err')) else: result.append(None) return result
def perform_aperture_photometry(xcen, ycen, filename, aperture_radii = np.arange(2, 15), bkg_r_in=16., bkg_r_out=20): ofile = fits.open(filename) apertures = [pu.CircularAperture((xcen, ycen), r=r) for r in aperture_radii] annulus_apertures = pu.CircularAnnulus((xcen, ycen), r_in=bkg_r_in, r_out=bkg_r_out) apertures.append(annulus_apertures) phot_table = pu.aperture_photometry(ofile[1].data, apertures ) bkg_colname = phot_table.colnames[-1] bkg_mean = phot_table[bkg_colname] / annulus_apertures.area() for aper, icol in zip(apertures, phot_table.colnames[3:]): bkg_sum = bkg_mean*aper.area() phot_table.add_column(phot_table[icol]-bkg_sum, name='{}_bkg_sub'.format(icol)) return phot_table, apertures
def aperture_flux(array, yc, xc, fwhm, ap_factor=1, mean=False, verbose=False): """ Returns the sum of pixel values in a circular aperture centered on the input coordinates. The radius of the aperture is set as (ap_factor*fwhm)/2. Parameters ---------- array : array_like Input frame. yc, xc : list or 1d arrays List of y and x coordinates of sources. fwhm : float FWHM in pixels. ap_factor : int, optional Diameter of aperture in terms of the FWHM. Returns ------- flux : list of floats List of fluxes. Note ---- From Photutils documentation, the aperture photometry defines the aperture using one of 3 methods: 'center': A pixel is considered to be entirely in or out of the aperture depending on whether its center is in or out of the aperture. 'subpixel': A pixel is divided into subpixels and the center of each subpixel is tested (as above). 'exact': (default) The exact overlap between the aperture and each pixel is calculated. """ n_obj = len(yc) flux = np.zeros((n_obj)) for i, (y, x) in enumerate(zip(yc, xc)): if mean: ind = circle(y, x, (ap_factor * fwhm) / 2) values = array[ind] obj_flux = np.mean(values) else: aper = photutils.CircularAperture((x, y), (ap_factor * fwhm) / 2) obj_flux = photutils.aperture_photometry(array, aper, method='exact') obj_flux = np.array(obj_flux['aperture_sum']) flux[i] = obj_flux if verbose: print('Coordinates of object {} : ({},{})'.format(i, y, x)) print('Object Flux = {:.2f}'.format(flux[i])) return flux
def test_aper_phot(capsys): """Check that apertures are as expected from photutils""" radius = 5 apertures = photutils.CircularAperture((50, 50), radius) aperture_area = apertures.area() assert_equal(aperture_area, np.pi * radius**2) rawflux_table = photutils.aperture_photometry(test_data, apertures, subpixels=1, method="center") total_flux = float(rawflux_table['aperture_sum'][0]) # Verify the expected circular area sum assert_equal(total_flux, 207.0)
def doAperturePhotometry(locs, data, fitsfilename, params, bkg_method='2d'): aplocs = [] for i, pair in enumerate(locs): aplocs.append(pair[::-1]) apertures = pt.CircularAperture(aplocs, r=params['ap_phot_rad']) uncert_table = pt.aperture_photometry(data, apertures, method='subpixel', subpixels=10) bkg = getBackground(data, fitsfilename, bkg_method=bkg_method, locs=locs, params=params) if bkg_method == '2d': phot_table = pt.aperture_photometry(data, apertures, method='subpixel', subpixels=10) bkg_table = pt.aperture_photometry(bkg.background, apertures, method='subpixel', subpixels=10) phot_table['residual_aperture_sum'] = phot_table[ 'aperture_sum'] - bkg_table['aperture_sum'] elif bkg_method == 'aperture': phot_table = pt.aperture_photometry(data, apertures, method='subpixel', subpixels=10) phot_table['residual_aperture_sum'] = phot_table[ 'aperture_sum'] - bkg.background * apertures.area() elif bkg_method == 'median': phot_table = pt.aperture_photometry(data, apertures, method='subpixel', subpixels=10) phot_table['residual_aperture_sum'] = phot_table[ 'aperture_sum'] - bkg.background * apertures.area() elif bkg_method == 'None': phot_table = pt.aperture_photometry(data, apertures, method='subpixel', subpixels=10) phot_table['residual_aperture_sum'] = phot_table['aperture_sum'] uncert_final = np.sqrt(uncert_table['aperture_sum']) return phot_table, bkg, uncert_final
def gaussian_kernel(size, size_y=None): """ Gaussian kernel. """ size = int(size) if not size_y: size_y = size else: size_y = int(size_y) x, y = np.mgrid[-size:size + 1, -size_y:size_y + 1] g = np.exp(-(x**2 / float(size) + y**2 / float(size_y))) fwhm = size fwhm_aper = photutils.CircularAperture((frame_center(g)), fwhm / 2.) fwhm_aper_phot = photutils.aperture_photometry(g, fwhm_aper) g_norm = g / np.array(fwhm_aper_phot['aperture_sum']) return g_norm / g_norm.max()
def starphot(imdata, position, radius, r_in, r_out): """ sources: http://photutils.readthedocs.io/en/stable/ photutils/aperture.html ARGS: imdata: Numpy array containing the star. position: [x,y] coordinates where x corresponds to the second index of imdata radius: Radius of the circular aperture used to compute the flux of the star. r_in: Inner radius of the annulus used to compute the background mean. r_out: Outer radius of the annulus used to compute the background mean. Returns [flux, background variance, background mean] """ try: statmask = photutils.make_source_mask(imdata, snr=5, npixels=5, dilate_size=10) except TypeError: return None bkg_annulus = photutils.CircularAnnulus(position, r_in, r_out) bkg_phot_table = photutils.aperture_photometry(imdata, bkg_annulus, method='subpixel', mask=statmask) bkg_mean_per_pixel = bkg_phot_table['aperture_sum'] / bkg_annulus.area() src_aperture = photutils.CircularAperture(position, radius) src_phot_table = photutils.aperture_photometry(imdata, src_aperture, method='subpixel') signal = src_phot_table['aperture_sum'] - bkg_mean_per_pixel*\ src_aperture.area() #noise_squared = signal + bkg_mean_per_pixel*src_aperture.area() mean, median, std = sigma_clipped_stats(imdata, sigma=3.0, iters=5, mask=statmask) noise_squared = std**2 return float(str(signal.data[0])), noise_squared,\ float(str(bkg_mean_per_pixel.data[0]))
def plot(image, radius, mul): """Use photutils DAO starfinder to get the locations of any objects in the image (above) with the threshold being one of the input values. Then use astropy's statistics to subtract the median from the image. Makes an array of two arrays which are our data points locations, then transpose those points to get the actual locations on our .fits image. Uses photutils CircularAperture to put an aperture around each potential star in our positions array. """ mean, median, std = astropy.stats.sigma_clipped_stats(image.data) DAO = photutils.detection.DAOStarFinder(threshold=mul * std, fwhm=3.0) sources = DAO(image.data - median) source_points = np.array([sources['xcentroid'], sources['ycentroid']]) positions = np.transpose(source_points) apertures = photutils.CircularAperture(positions, r=radius) fig, ax = plt.subplots(figsize=(15, 15)) ax.imshow(image.data, cmap='Greys', origin='lower', vmin=100, vmax=5000) ax.set_title('Interactive Starfinder') apertures.plot(color='c', lw=1, alpha=1)
def mk_sky_phot(instr, filt): img = fitsio.getdata(orig_names[instr][filt]["img"]) pixscale = proj_plane_pixel_scales( WCS(fitsio.getheader(orig_names[instr][filt]["img"])))[0] * 3600. apersizes = useful.apersizes / pixscale aperradii = apersizes / 2. sky_apers = fitsio.getdata( '/data/highzgal/PUBLICACCESS/SPLASH/PROCESS/errors/sky_apers_%s_%s.fits' % (instr, filt)) orig_wcs = WCS( fitsio.getheader( '/data/highzgal/PUBLICACCESS/SPLASH/PROCESS/data/orig/mosaic_%s_%s.img.fits' % (instr, filt))) new_wcs = WCS(fitsio.getheader(orig_names[instr][filt]["img"])) pos = np.array(zip(sky_apers['IMGX'], sky_apers['IMGY'])) pos = orig_wcs.all_pix2world(pos, 1) pos = new_wcs.all_world2pix(pos, 1) # cond = (aperradii[-1] < pos[:,0]) & (pos[:,0] < img.shape[1]-aperradii[-1]) & \ # (aperradii[-1] < pos[:,1]) & (pos[:,1] < img.shape[0]-aperradii[-1]) for i, radius in enumerate(aperradii): sys.stdout.write( "\rMeasuring Photometry on sky apertures for %s %s - aperture#%i (%.2f px) ..." % (instr, filt, i + 1, radius)) sys.stdout.flush() apertures = photutils.CircularAperture(pos, r=radius) sky_apers['FLUX_APER'][:, i] = photutils.aperture_photometry( img, apertures)['aperture_sum'] sys.stdout.write("done!\n") sys.stdout.flush() fitsio.writeto('sky_apers_orig_%s_%s.fits' % (instr, filt), sky_apers, overwrite=True)
def petrosian_radiu(data, r_step=5., petrosian_ratio=0.2): """ measure petrosian radiu in units of pixel from data. Note: Centroid position is determined from data Args: data (2d np array) r_step=5. (float): aperture radius steps in units of pixel petrosian_ratio=0.2: the surface brightness at r_petrosian is petrosian_ratio times the surface brightness within r_petrosian. """ ny, nx = data.shape xc, yc = pu.centroid_1dg(data) rs = np.arange(r_step, ny // 2, r_step) apts = [pu.CircularAperture(positions=(xc, yc), r=r) for r in rs] tab = at.Table(pu.aperture_photometry(data, apts)) if len(rs) == 1: cols = ['aperture_sum'] else: cols = ['aperture_sum_' + str(i) for i in range(len(rs))] aperture_sum = np.array(list(tab[cols][0])) annulus_sum = np.zeros_like(rs) annulus_sum[0] = aperture_sum[0] for i in range(1, len(rs)): annulus_sum[i] = aperture_sum[i] - aperture_sum[i - 1] aperture_areas = np.pi * rs**2 annulus_areas = np.append(aperture_areas[0], np.diff(aperture_areas)) annulus_avg = annulus_sum / annulus_areas aperture_avg = aperture_sum / aperture_areas diff = annulus_avg - aperture_avg * petrosian_ratio r_petrosian = _find_a_root(x=rs, y=diff) return r_petrosian
def get_sex_whts(catalog, instr, filt): radius = 3. / 2. / useful.pix_scale orig_wcs = WCS( fitsio.getheader( '/data/highzgal/PUBLICACCESS/SPLASH/PROCESS/data/orig/mosaic_%s_%s.img.fits' % (instr, filt))) new_wcs = WCS(fitsio.getheader(orig_names[instr][filt]["img"])) pos = zip(catalog['X_IMAGE'], catalog['Y_IMAGE']) pos = orig_wcs.all_pix2world(pos, 1) pos = new_wcs.all_world2pix(pos, 1) wht = fitsio.getdata( '/data/highzgal/PUBLICACCESS/SPLASH/PROCESS/data/orig/mosaic_%s_%s.wht.fits' % (instr, filt)) aperture = photutils.CircularAperture(pos, r=radius) avg_wht = photutils.aperture_photometry( wht, aperture)['aperture_sum'] / (np.pi * radius**2) return avg_wht
def BuildEPSF(filter='Ks'): """Builds the effective PSF used for the photometry. Currently uses the SCAO PSF from SimCADO. """ src = sim.source.star(mag=19, filter_name=filter, spec_type='M0V') image = img.MakeImage(src, exposure=1800, NDIT=1, view='wide', chip='centre', filter=filter, ao_mode='scao') # PSF_AnisoCADO_SCAO_FVPSF_4mas_EsoMedian_20190328.fits peaks_tbl = phu.find_peaks(image, threshold=135000., box_size=11) peaks_tbl['peak_value'].info.format = '%.8g' # for consistent table output # make sure the positions are correct (use the exact ones) peaks_tbl['x_peak'] = src.x_pix peaks_tbl['y_peak'] = src.y_pix positions = (peaks_tbl['x_peak'], peaks_tbl['y_peak']) apertures = phu.CircularAperture(positions, r=5.) # extract cutouts of the stars using the extract_stars() function stars_tbl = apta.Table() stars_tbl['x'] = peaks_tbl['x_peak'] stars_tbl['y'] = peaks_tbl['y_peak'] mean_val, median_val, std_val = apy.stats.sigma_clipped_stats(img_data, sigma=2.) img_data -= median_val # subtract background nddata = apy.nddata.NDData(data=img_data) stars = phu.psf.extract_stars(nddata, stars_tbl, size=170) # build the epsf epsf_builder = phu.EPSFBuilder(oversampling=4, maxiters=5, progress_bar=False) epsf, fitted_stars = epsf_builder(stars) # save the epsf with open(os.path.join('objects', 'epsf-scao-fv.pkl'), 'wb') as output: pickle.dump(epsf, output, -1)
def _apphot_one(args): (irad, band, rad, img, sigma, mask, isimage, apxy) = args import photutils result = [irad, band, isimage] aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(img, aper, error=sigma, mask=mask) result.append(p.field('aperture_sum')) if sigma is not None: result.append(p.field('aperture_sum_err')) else: result.append(None) # If a mask is passed, also photometer it! if mask is not None: p = photutils.aperture_photometry(mask, aper) maskedpix = p.field('aperture_sum') # normalize by number of pixels (pi * rad**2) maskedpix /= (np.pi * rad**2) result.append(maskedpix) else: result.append(None) return result
def run_forced_phot(cat, tim, ceres=True, derivs=False, agn=False, do_forced=True, do_apphot=True, get_model=False, ps=None, timing=False, fixed_also=False, ceres_threads=1): ''' fixed_also: if derivs=True, also run without derivatives and report that flux too? ''' if timing: tlast = Time() if ps is not None: import pylab as plt opti = None forced_kwargs = {} if ceres: from tractor.ceres_optimizer import CeresOptimizer B = 8 try: opti = CeresOptimizer(BW=B, BH=B, threads=ceres_threads) except: if ceres_threads > 1: raise RuntimeError( 'ceres_threads requested but not supported by tractor.ceres version' ) opti = CeresOptimizer(BW=B, BH=B) #forced_kwargs.update(verbose=True) # nsize = 0 for src in cat: # Limit sizes of huge models # from tractor.galaxy import ProfileGalaxy # if isinstance(src, ProfileGalaxy): # px,py = tim.wcs.positionToPixel(src.getPosition()) # h = src._getUnitFluxPatchSize(tim, px, py, tim.modelMinval) # MAXHALF = 128 # if h > MAXHALF: # #print('halfsize', h,'for',src,'-> setting to',MAXHALF) # nsize += 1 # src.halfsize = MAXHALF src.freezeAllBut('brightness') src.getBrightness().freezeAllBut(tim.band) #print('Limited the size of', nsize, 'large galaxy models') if derivs: realsrcs = [] derivsrcs = [] Iderivs = [] for i, src in enumerate(cat): from tractor import PointSource realsrcs.append(src) if not isinstance(src, PointSource): continue Iderivs.append(i) brightness_dra = src.getBrightness().copy() brightness_ddec = src.getBrightness().copy() brightness_dra.setParams(np.zeros(brightness_dra.numberOfParams())) brightness_ddec.setParams( np.zeros(brightness_ddec.numberOfParams())) brightness_dra.freezeAllBut(tim.band) brightness_ddec.freezeAllBut(tim.band) dsrc = SourceDerivatives(src, [brightness_dra, brightness_ddec], tim, ps) derivsrcs.append(dsrc) Iderivs = np.array(Iderivs) if fixed_also: pass else: # For convenience, put all the real sources at the front of # the list, so we can pull the IVs off the front of the list. cat = realsrcs + derivsrcs if agn: from tractor.galaxy import ExpGalaxy, DevGalaxy, FixedCompositeGalaxy from tractor import PointSource from legacypipe.survey import SimpleGalaxy, RexGalaxy realsrcs = [] agnsrcs = [] iagn = [] for i, src in enumerate(cat): realsrcs.append(src) ## ?? if isinstance(src, (SimpleGalaxy, RexGalaxy)): #print('Skipping SIMP or REX:', src) continue if isinstance(src, (ExpGalaxy, DevGalaxy, FixedCompositeGalaxy)): iagn.append(i) bright = src.getBrightness().copy() bright.setParams(np.zeros(bright.numberOfParams())) bright.freezeAllBut(tim.band) agn = PointSource(src.pos, bright) agn.freezeAllBut('brightness') #print('Adding "agn"', agn, 'to', src) #print('agn params:', agn.getParamNames()) agnsrcs.append(src) iagn = np.array(iagn) cat = realsrcs + agnsrcs print('Added AGN to', len(iagn), 'galaxies') tr = Tractor([tim], cat, optimizer=opti) tr.freezeParam('images') disable_galaxy_cache() F = fits_table() if do_forced: if timing and (derivs or agn): t = Time() print('Setting up:', t - tlast) tlast = t if derivs: if fixed_also: print('Forced photom with fixed positions:') R = tr.optimize_forced_photometry(variance=True, fitstats=False, shared_params=False, priors=False, **forced_kwargs) F.flux_fixed = np.array([ src.getBrightness().getFlux(tim.band) for src in cat ]).astype(np.float32) N = len(cat) F.flux_fixed_ivar = R.IV[:N].astype(np.float32) if timing: t = Time() print('Forced photom with fixed positions finished:', t - tlast) tlast = t cat = realsrcs + derivsrcs tr.setCatalog(Catalog(*cat)) print('Forced photom with position derivatives:') if ps is None and not get_model: forced_kwargs.update(wantims=False) R = tr.optimize_forced_photometry(variance=True, fitstats=True, shared_params=False, priors=False, **forced_kwargs) if ps is not None or get_model: (data, mod, ie, chi, roi) = R.ims1[0] if ps is not None: ima = dict(vmin=-2. * tim.sig1, vmax=5. * tim.sig1, interpolation='nearest', origin='lower', cmap='gray') imchi = dict(interpolation='nearest', origin='lower', vmin=-5, vmax=5, cmap='RdBu') plt.clf() plt.imshow(data, **ima) plt.title('Data: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(mod, **ima) plt.title('Model: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(chi, **imchi) plt.title('Chi: %s' % tim.name) ps.savefig() if derivs: trx = Tractor([tim], realsrcs) trx.freezeParam('images') modx = trx.getModelImage(0) chix = (data - modx) * tim.getInvError() plt.clf() plt.imshow(modx, **ima) plt.title('Model without derivatives: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(chix, **imchi) plt.title('Chi without derivatives: %s' % tim.name) ps.savefig() if derivs or agn: cat = realsrcs N = len(cat) F.flux = np.array([ src.getBrightness().getFlux(tim.band) for src in cat ]).astype(np.float32) F.flux_ivar = R.IV[:N].astype(np.float32) F.fracflux = R.fitstats.profracflux[:N].astype(np.float32) F.rchisq = R.fitstats.prochi2[:N].astype(np.float32) try: F.fracmasked = R.fitstats.promasked[:N].astype(np.float32) except: print( 'No "fracmasked" available (only in recent Tractor versions)') if derivs: F.flux_dra = np.zeros(len(F), np.float32) F.flux_ddec = np.zeros(len(F), np.float32) F.flux_dra[Iderivs] = np.array( [src.getParams()[0] for src in derivsrcs]).astype(np.float32) F.flux_ddec[Iderivs] = np.array( [src.getParams()[1] for src in derivsrcs]).astype(np.float32) F.flux_dra_ivar = np.zeros(len(F), np.float32) F.flux_ddec_ivar = np.zeros(len(F), np.float32) F.flux_dra_ivar[Iderivs] = R.IV[N::2].astype(np.float32) F.flux_ddec_ivar[Iderivs] = R.IV[N + 1::2].astype(np.float32) if agn: F.flux_agn = np.zeros(len(F), np.float32) F.flux_agn_ivar = np.zeros(len(F), np.float32) F.flux_agn[iagn] = np.array( [src.getParams()[0] for src in agnsrcs]) F.flux_agn_ivar[iagn] = R.IV[N:].astype(np.float32) if timing: t = Time() print('Forced photom:', t - tlast) tlast = t if do_apphot: import photutils img = tim.getImage() ie = tim.getInvError() with np.errstate(divide='ignore'): imsigma = 1. / ie imsigma[ie == 0] = 0. apimg = [] apimgerr = [] # Aperture photometry locations xxyy = np.vstack( [tim.wcs.positionToPixel(src.getPosition()) for src in cat]).T apxy = xxyy - 1. apertures = apertures_arcsec / tim.wcs.pixel_scale() #print('Apertures:', apertures, 'pixels') #print('apxy shape', apxy.shape) # --> (2,N) # The aperture photometry routine doesn't like pixel positions outside the image H, W = img.shape Iap = np.flatnonzero((apxy[0, :] >= 0) * (apxy[1, :] >= 0) * (apxy[0, :] <= W - 1) * (apxy[1, :] <= H - 1)) print('Aperture photometry for', len(Iap), 'of', len(apxy[0, :]), 'sources within image bounds') for rad in apertures: aper = photutils.CircularAperture(apxy[:, Iap], rad) p = photutils.aperture_photometry(img, aper, error=imsigma) apimg.append(p.field('aperture_sum')) apimgerr.append(p.field('aperture_sum_err')) ap = np.vstack(apimg).T ap[np.logical_not(np.isfinite(ap))] = 0. F.apflux = np.zeros((len(F), len(apertures)), np.float32) F.apflux[Iap, :] = ap.astype(np.float32) apimgerr = np.vstack(apimgerr).T apiv = np.zeros(apimgerr.shape, np.float32) apiv[apimgerr != 0] = 1. / apimgerr[apimgerr != 0]**2 F.apflux_ivar = np.zeros((len(F), len(apertures)), np.float32) F.apflux_ivar[Iap, :] = apiv if timing: print('Aperture photom:', Time() - tlast) if get_model: return F, mod return F
def photometry(ax, image, pos, ap_size=10, r_in=20, r_out=30, back_photo=True): ''' Create apertures around specified list of stars Parameters: ----------- ax : axis class axis of a subplot. image : ndarray Image for aperture photometry to be performed on. pos : 2darray A list of positions in x,y pixel coordinates for each star. Needs to be as [[0,0],[1,1]...] ap_size : int The radius of a circular aperture in pixel size r_in : int The inner radius of background aperture in pixel size (Ignore if back_photo=False) r_out : int The outer radius of background aperture in pixel size (Ignore if back_photo=False) back_photo : bool Set to True if want to return an array of background values, False to ignore anything to do with background Returns ------- photometry of each star : array average background pixel value around each star : array (If back_photo=True) plots image with the aperture and centroids located for each star ''' nstars = np.arange(0, np.shape(pos)[0], 1) name_stars = [] for i in nstars: name_stars.append('Star {}'.format(i)) #print name_stars aperture = photutils.CircularAperture(pos, r=ap_size) if back_photo == True: back_aperture = photutils.CircularAnnulus(pos, r_in, r_out) pos = np.array(pos) #plt.imshow(image,origin='lower') for i in range(len(name_stars)): circle1 = plt.Circle((pos[i, 0], pos[i, 1]), ap_size, color='black', fill=False, zorder=100) ax.add_artist(circle1) plt.axhline(pos[i, 1], xmin=pos[i, 0] / 100. - .01, xmax=pos[i, 0] / 100. + .02, color='black') plt.axvline(pos[i, 0], ymin=pos[i, 1] / 100. - .01, ymax=pos[i, 1] / 100. + .02, color='black') plt.text(pos[i, 0] + ap_size + 1, pos[i, 1], name_stars[i], zorder=11) #print pos[i,1] #print pos[i,0] if back_photo == True: circle2 = plt.Circle((pos[i, 0], pos[i, 1]), r_in, color='cyan', fill=False, zorder=10) circle3 = plt.Circle((pos[i, 0], pos[i, 1]), r_out, color='cyan', fill=False, zorder=10) ax.add_artist(circle2) ax.add_artist(circle3) #aperture.plot(origin=(0,0),indices=None,ax=ax,fill=False) #plt.ion() phot_table = photutils.aperture_photometry(image, aperture) flux_values = phot_table[ 'aperture_sum'].data #gets a list of the total flux in specified aperture for each star if back_photo == True: back_table = photutils.aperture_photometry(image, back_aperture) area = np.pi * (r_out**2 - r_in**2) a = np.ones((np.shape(image)[0], np.shape(image)[1])) area = photutils.aperture_photometry(a, back_aperture) back_values = back_table['aperture_sum'].data / area[ 'aperture_sum'].data return flux_values, back_values * np.pi * ap_size**2 else: return flux_values
ph, pw = psfimg.shape print('psf img shape', psfimg.shape) apertures = [1., 3., 3.5, 3.75, 4., 6., 8., 10.] #apxy = np.array([[pw/2., ph/2.]]) #for ioff,apxy in enumerate([np.array([[pw/2., ph/2.]]), # np.array([[pw/2, ph/2]])]): for ioff, apxy in enumerate([np.array([[pw / 2, ph / 2]])]): print('apxy', apxy) ap = [] for rad in apertures: aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(psfimg, aper) ap.append(p.field('aperture_sum')) ap = np.vstack(ap).T ap = ap[0, :] print('Aperture fluxes:', ap) #plt.plot(apertures, ap, '.-', color='br'[ioff], label='%i,%i + %.1f,%.1f' % (x, y, apxy[0,0], apxy[0,1])) plt.plot(apertures, ap, '.-', label='%i,%i' % (x, y)) plt.legend(loc='lower right') plt.xlabel('Aperture') plt.ylabel('Flux of PSF model') plt.ylim(0.8, 1.2) plt.xlim(2, 6) plt.axhline(1.0, color='k', alpha=0.25)
def aperture_photometry(calibratable, ra, dec, apply_calibration=False, assume_background_subtracted=False, use_cutout=False, direct_load=None): import photutils from astropy.coordinates import SkyCoord from astropy.io import fits from astropy.table import vstack from astropy.wcs import WCS ra = np.atleast_1d(ra) dec = np.atleast_1d(dec) coord = SkyCoord(ra, dec, unit='deg') if not use_cutout: wcs = calibratable.wcs apertures = photutils.SkyCircularAperture(coord, r=APERTURE_RADIUS) # something that is photometerable implements mask, background, and wcs if not assume_background_subtracted: pixels_bkgsub = calibratable.background_subtracted_image.data else: pixels_bkgsub = calibratable.data bkgrms = calibratable.rms_image.data mask = calibratable.mask_image.data phot_table = photutils.aperture_photometry(pixels_bkgsub, apertures, error=bkgrms, wcs=wcs) phot_table['zp'] = calibratable.header['MAGZP'] + calibratable.header[ 'APCOR4'] phot_table['obsjd'] = calibratable.header['OBSJD'] phot_table['filtercode'] = 'z' + calibratable.header['FILTER'][-1] pixap = apertures.to_pixel(wcs) annulus_masks = pixap.to_mask(method='center') maskpix = [ annulus_mask.cutout(mask.data) for annulus_mask in annulus_masks ] else: phot_table = [] maskpix = [] for s in coord: if direct_load is not None and 'sci' in direct_load: sci_path = direct_load['sci'] else: if assume_background_subtracted: sci_path = calibratable.local_path else: sci_path = calibratable.background_subtracted_image.local_path if direct_load is not None and 'mask' in direct_load: mask_path = direct_load['mask'] else: mask_path = calibratable.mask_image.local_path if direct_load is not None and 'rms' in direct_load: rms_path = direct_load['rms'] else: rms_path = calibratable.rms_image.local_path with fits.open(sci_path, memmap=True) as f: wcs = WCS(f[0].header) pixcoord = wcs.all_world2pix([[s.ra.deg, s.dec.deg]], 0)[0] pixx, pixy = pixcoord nx = calibratable.header['NAXIS1'] ny = calibratable.header['NAXIS2'] xmin = max(0, pixx - 1.5 * APERTURE_RADIUS.value) xmax = min(nx, pixx + 1.5 * APERTURE_RADIUS.value) ymin = max(0, pixy - 1.5 * APERTURE_RADIUS.value) ymax = min(ny, pixy + 1.5 * APERTURE_RADIUS.value) ixmin = int(np.floor(xmin)) ixmax = int(np.ceil(xmax)) iymin = int(np.floor(ymin)) iymax = int(np.ceil(ymax)) ap = photutils.CircularAperture([pixx - ixmin, pixy - iymin], APERTURE_RADIUS.value) # something that is photometerable implements mask, background, and wcs with fits.open(sci_path, memmap=True) as f: pixels_bkgsub = f[0].data[iymin:iymax, ixmin:ixmax] with fits.open(rms_path, memmap=True) as f: bkgrms = f[0].data[iymin:iymax, ixmin:ixmax] with fits.open(mask_path, memmap=True) as f: mask = f[0].data[iymin:iymax, ixmin:ixmax] pt = photutils.aperture_photometry(pixels_bkgsub, ap, error=bkgrms) annulus_mask = ap.to_mask(method='center') mp = annulus_mask.cutout(mask.data) maskpix.append(mp) phot_table.append(pt) phot_table = vstack(phot_table) if apply_calibration: magzp = calibratable.header['MAGZP'] apcor = calibratable.header[APER_KEY] phot_table['mag'] = -2.5 * np.log10( phot_table['aperture_sum']) + magzp + apcor phot_table['magerr'] = 1.0826 * phot_table[ 'aperture_sum_err'] / phot_table['aperture_sum'] # check for invalid photometry on masked pixels phot_table['flags'] = [ int(np.bitwise_or.reduce(m, axis=(0, 1))) for m in maskpix ] # rename some columns phot_table.rename_column('aperture_sum', 'flux') phot_table.rename_column('aperture_sum_err', 'fluxerr') return phot_table
def psf_norm(array, fwhm=4, size=None, threshold=None, mask_core=None, full_output=False, verbose=False): """ Scales a PSF, so the 1*FWHM aperture flux equals 1. Parameters ---------- array: array_like The psf 2d array. fwhm: float, optional The the Full Width Half Maximum in pixels. size : int or None, optional If int it will correspond to the size of the squared subimage to be cropped form the psf array. threshold : None of float, optional Sets to zero small values, trying to leave only the core of the PSF. mask_core : None of float, optional Sets the radius of a circular aperture for the core of the PSF, everything else will be set to zero. Returns ------- psf_norm: array_like The normalized psf. """ if size is not None: psfs = frame_crop(array, min(int(size), array.shape[0]), verbose=False) else: psfs = array.copy() # If frame size is even we drop last row and last column if psfs.shape[0] % 2 == 0: psfs = psfs[:-1, :] if psfs.shape[1] % 2 == 0: psfs = psfs[:, :-1] # we check if the psf is centered and fix it if needed cy, cx = frame_center(psfs, verbose=False) if cy != np.where(psfs == psfs.max())[0] or cx != np.where( psfs == psfs.max())[1]: # first we find the centroid and put it in the center of the array centroidy, centroidx = fit_2dgaussian(psfs, fwhmx=fwhm, fwhmy=fwhm) shiftx, shifty = centroidx - cx, centroidy - cy psfs = frame_shift(psfs, -shifty, -shiftx) for _ in range(2): centroidy, centroidx = fit_2dgaussian(psfs, fwhmx=fwhm, fwhmy=fwhm) cy, cx = frame_center(psfs, verbose=False) shiftx, shifty = centroidx - cx, centroidy - cy psfs = frame_shift(psfs, -shifty, -shiftx) # we check whether the flux is normalized and fix it if needed fwhm_aper = photutils.CircularAperture((frame_center(psfs)), fwhm / 2.) fwhm_aper_phot = photutils.aperture_photometry(psfs, fwhm_aper, method='exact') fwhm_flux = np.array(fwhm_aper_phot['aperture_sum']) if verbose: print "Flux in 1xFWHM aperture: {}".format(fwhm_flux) if fwhm_flux > 1.1 or fwhm_flux < 0.9: psf_norm_array = psfs / np.array(fwhm_aper_phot['aperture_sum']) else: psf_norm_array = psfs if threshold is not None: psf_norm_array[np.where(psf_norm_array < threshold)] = 0 if mask_core is not None: psf_norm_array = get_circle(psf_norm_array, radius=mask_core) if full_output: return psf_norm_array, fwhm_flux else: return psf_norm_array
def stage_4(resid=None, sky=None, ps=None, tim=None, mask=None, sourcepix=None, filters=None, radii_arcsec=None, orig_catalog=None, plots=False, lsbcat='lsb.fits', expnum=None, extname=None, pixscale=None, **kwa): #ok,x,y = tim.subwcs.radec2pixelxy(188.7543, 13.3847) #print 'X,Y', x,y fstack = np.dstack(filters) amax = np.argmax(fstack, axis=2) fmax = np.max(fstack, axis=2) if plots: sna = dict(interpolation='nearest', origin='lower', vmin=-2., vmax=32., cmap='gray') plt.clf() plt.imshow(fmax, **sna) plt.title('Filter max') ps.savefig() from matplotlib.ticker import FixedFormatter radformat = FixedFormatter(['%i' % r for r in radii_arcsec]) plt.clf() plt.imshow(amax, interpolation='nearest', origin='lower', cmap='jet') plt.title('Filter argmax') plt.colorbar(ticks=np.arange(amax.max() + 1), format=radformat) ps.savefig() peak_amax = np.zeros_like(amax) hot = (fmax > 10.) blobs, nblobs = label(hot) print 'Nblobs', nblobs #print 'blobs max', blobs.max() peaks = [] for blob in range(1, nblobs + 1): I = (blobs == blob) imax = np.argmax(fmax * I) py, px = np.unravel_index(imax, fmax.shape) peaks.append((px, py)) #print 'blob imax', imax print 'px,py', px, py print 'blob max', fmax.flat[imax] ifilt = amax.flat[imax] print 'blob argmax', ifilt #print ' =', amax[py,px] if plots: peak_amax[I] = ifilt px = [x for x, y in peaks] py = [y for x, y in peaks] if plots: plt.clf() plt.imshow(amax * hot, interpolation='nearest', origin='lower', cmap='jet') ax = plt.axis() plt.plot(px, py, '+', color='w') plt.axis(ax) plt.title('Filter argmax') plt.colorbar(ticks=np.arange((amax * hot).max() + 1), format=radformat) ps.savefig() # Total flux estimate # fmax is S/N in the amax filter. Back out... fluxes = [] for x, y in zip(px, py): print 'peak', x, y ifilt = amax[y, x] print 'ifilt', ifilt sn = fmax[y, x] print 'S/N', sn r = radii_arcsec[ifilt] sigma = r / pixscale knorm = 1. / (2. * np.sqrt(np.pi) * sigma) blurred = sn * (knorm * tim.sig1) fluxest = blurred * 2. * np.pi * (2. * sigma**2) fluxes.append(fluxest) fluxes = np.array(fluxes) mags = 22.5 - 2.5 * np.log10(fluxes) if plots: for bg in [1, 2]: plt.clf() if bg == 1: plt.imshow(peak_amax, interpolation='nearest', origin='lower', cmap='jet') else: plt.imshow(tim.getImage(), **tim.ima) ax = plt.axis() plt.plot(px, py, '+', color='w', ms=10, mew=2) # Extended sources in the catalog if len(orig_catalog): E = orig_catalog[np.maximum(orig_catalog.shapeexp_r, orig_catalog.shapedev_r) >= 3.] plt.plot(E.x, E.y, 'x', color='k', ms=10, mew=2) for x, y, m in zip(px, py, mags): ra, dec = tim.subwcs.pixelxy2radec(x + 1, y + 1) plt.text(x, y, '%.1f (%.2f,%.2f)' % (m, ra, dec), color='w', ha='left', fontsize=12) ''' evcc.fits: https://sites.google.com/site/extendedvcc/ wget "https://sites.google.com/site/extendedvcc/table/Table2_2014_8_7.txt?attredirects=0&d=1" -O table2.txt text2fits -S 35 -H "id_evcc id_vcc ngc ra dec ra_fiber dec_fiber delta cz_sdss cz_ned mem_evcc mem_vcc morph_pri morph_sec morph_index morph_vcc u u_err g g_err r r_err i i_err z z_err r_kron r_50" -f sssddddfffssssssffffffffffff table2.txt evcc.fits -n - vcc.fits: text2fits -S 27 -H "id_vcc ngc ra dec cz_sdss cz_ned mem morph_vcc u u_err g g_err r r_err i i_err z z_err r_kron r_50" -f ssddffssffffffffffff table3.txt vcc.fits -n - ''' mydir = os.path.dirname(__file__) fn = os.path.join(mydir, 'evcc.fits') T = fits_table(fn) ok, x, y = tim.subwcs.radec2pixelxy(T.ra, T.dec) x = x[ok] y = y[ok] plt.plot(x, y, 'o', mec=(0, 1, 0), mfc='none', ms=50, mew=5) fn = os.path.join(mydir, 'vcc.fits') T = fits_table(fn) ok, x, y = tim.subwcs.radec2pixelxy(T.ra, T.dec) x = x[ok] y = y[ok] plt.plot(x, y, 'o', mec=(0, 1, 1), mfc='none', ms=50, mew=5) plt.axis(ax) plt.title('Peaks') #plt.title('Filter peak argmax') #plt.colorbar(ticks=np.arange((peak_amax*hot).max()+1), format=radformat) ps.savefig() LSB = fits_table() LSB.filter = np.array([tim.band] * len(px)) LSB.expnum = np.array([expnum] * len(px)).astype(np.int32) LSB.extname = np.array([extname] * len(px)) LSB.x = np.array(px).astype(np.int16) LSB.y = np.array(py).astype(np.int16) ra, dec = tim.subwcs.pixelxy2radec(LSB.x, LSB.y) LSB.ra = ra LSB.dec = dec LSB.flux = fluxes.astype(np.float32) LSB.mag = mags.astype(np.float32) LSB.radius = np.array(radii_arcsec)[amax[py, px]].astype(np.float32) LSB.sn = fmax[py, px].astype(np.float32) # Apertures, radii in ARCSEC. apertures_arcsec = np.array([0.5, 0.75, 1., 1.5, 2., 3.5, 5., 7.]) apertures = apertures_arcsec / pixscale apxy = np.vstack((px, py)).T import photutils with np.errstate(divide='ignore'): imsigma = 1.0 / tim.getInvError() imsigma[tim.getInvError() == 0] = 0 for photimg, err, name, errname in [ (resid, imsigma, 'apflux', 'apflux_ivar'), (tim.getImage() - sky, None, 'apimgflux', None), ((tim.getImage() - sky) * mask, None, 'apmaskedimgflux', None), (1. - (mask * 1.), None, 'apgoodpix', None), (sourcepix, None, 'apsources', None), ]: apimg = [] apimgerr = [] for rad in apertures: aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(photimg, aper, error=err) apimg.append(p.field('aperture_sum')) if err is not None: apimgerr.append(p.field('aperture_sum_err')) ap = np.vstack(apimg).T ap[np.logical_not(np.isfinite(ap))] = 0. LSB.set(name, ap.astype(np.float32)) if err is not None: apiv = 1. / (np.vstack(apimgerr).T)**2 apiv[np.logical_not(np.isfinite(apiv))] = 0. LSB.set(errname, apiv.astype(np.float32)) LSB.cut(np.argsort(-LSB.sn)) LSB.writeto(lsbcat) return dict(LSB=LSB, filters=None, mod=None, sky=None)
def run(self, plot_suffix, plot_folder, get_rad_at_EE=0.96, MAXRAD_FACTOR=0.54, fwzm_z=20, use_azimuthal_averaging=True, force_max_radii_to_390=False, soft_bg_est=True): """ Run the analysis INPUT: plot_suffix = "f01" plot_folder = "results/" get_rad_at_EE = 0.96 FACTOR = 0.54 fwzm_z=20 soft_bg_est = True : If False, will not use software background estimator OUTPUT: Saves files to folders """ utils.make_dir(plot_folder) fig, ax = plt.subplots(ncols=3, nrows=2, figsize=(20, 15)) plt.title('FIND ME') ax_img = ax.flat[0] ax_img_bkg = ax.flat[1] ax_img_bkg_sub = ax.flat[2] ax_cut = ax.flat[3] ax_ee = ax.flat[4] ax_img_bkg_sub2 = ax.flat[5] # ------------- # Plot #1: Normal image x, y = self.fimg.get_centroid(plot_lines=True, ax=ax_img) self.fimg.ax.set_title("Hist stretch") #cbar_ax = fig.add_axes([0.38, 0.59, 0.015, 0.3],label="Counts") #fig.colorbar(self.fimg.im, cax=cbar_ax) # ------------- # Plot #2: Background estimation if soft_bg_est == True: #x,y = self.fimg.get_centroid() #print("Centroids",x,y) CBS = phothelp.CircularBackgroundSubractor(self.fimg.data, x=x, y=y, r=400) data_back_sub = CBS.subtract_background(plot_background=True, ax=ax_img_bkg) self.subtracted_value = CBS.subtracted_value # ------------- # Plot #3: Background subtracted image self.fimg.data = data_back_sub self.fimg.plot(ax=ax_img_bkg_sub, colorbar=False, title="Background subtracted (linear stretch)", stretch="linear") # add colorbar #cbar_ax = fig.add_axes([0.38, 0.09, 0.015, 0.3],label="Counts") #fig.colorbar(self.fimg.im, cax=cbar_ax) # Get centroid again after taking out the background x, y = self.fimg.get_centroid() else: print('Not subtracting backround') self.subtracted_value = 0 # ------------- # Plot #4: Cuts if use_azimuthal_averaging == True: #self.max_radii_for_EE = print("Using azimuthal averaging") _r, _mean, _r_hwzm = self.fimg.get_radial_profile( rmax=500, plot=True, z=fwzm_z, return_hwzm=True, ax=ax_cut, xcen=x, ycen=y, subtract_min=True) # 500 ~2/1024 self.max_radii_for_EE = (_r_hwzm * 2. * MAXRAD_FACTOR)[0] if force_max_radii_to_390 == True: print("FORCING MAX RADII TO 390") self.max_radii_for_EE = 390. else: print("Not using azimuthal averaging, Using mean(X+Y) cutouts") cut_x, self.fwhm_x = self.fimg.get_centroid_line_cut( line="X", return_FWHM=True, plot=True, ax=ax_cut, use_butter_filter=True, butter_cutoff_freq=0.03, fwzm_z=fwzm_z) cut_y, self.fwhm_y = self.fimg.get_centroid_line_cut( line="Y", return_FWHM=True, plot=True, ax=ax_cut, use_butter_filter=True, butter_cutoff_freq=0.03, fwzm_z=fwzm_z) #return cut_x, cut_y ax_cut.plot(butter.low_pass_butter(cut_x, cutoff_freq=0.03)) ax_cut.plot(butter.low_pass_butter(cut_y, cutoff_freq=0.03)) self.max_radii_for_EE = ( (self.fwhm_x + self.fwhm_y) / 2.) * MAXRAD_FACTOR ax_cut.set_ylim(0, 64000.) # ------------- # Plot #4: Encircled energy plot print('Max radii for EE = {}'.format(self.max_radii_for_EE)) radii = np.arange(1, self.max_radii_for_EE) df, self.r_ee = phothelp.get_encircled_energy_and_rad_at_EE( self.fimg.data, self.fimg.xcenter, self.fimg.ycenter, radii, plot=True, ax=ax_ee, get_rad_at_EE=get_rad_at_EE) ax_ee.set_xlim(0, 500) ax_ee.set_xlabel("Radii (pixels)") # Overplot aperture in figure $ with EE aper = photutils.CircularAperture((x, y), r=self.r_ee) aper.plot(ax=ax_img_bkg_sub, color="green") ax_img_bkg_sub.annotate("EE" + str(100 * get_rad_at_EE) + "%", xy=(x, y + self.r_ee), color="white", fontsize=8) # overplot max aperture aper = photutils.CircularAperture((x, y), r=self.max_radii_for_EE) aper.plot(ax=ax_img_bkg_sub, color="red") ax_img_bkg_sub.annotate("EE100% @ r = " + str(MAXRAD_FACTOR * 2) + " HWHM", xy=(x, y + self.max_radii_for_EE), color="white", fontsize=8) # Hist plot self.fimg.plot(ax=ax_img_bkg_sub2, colorbar=False, title="Background subtracted (Hist stretch)", stretch="hist") aper = photutils.CircularAperture((x, y), r=self.r_ee) aper.plot(ax=ax_img_bkg_sub2, color="green") ax_img_bkg_sub2.annotate("EE" + str(100 * get_rad_at_EE) + "%", xy=(x, y + self.r_ee), color="white", fontsize=8) # overplot max aperture aper = photutils.CircularAperture((x, y), r=self.max_radii_for_EE) aper.plot(ax=ax_img_bkg_sub2, color="red") ax_img_bkg_sub2.annotate("EE100% @ r = " + str(MAXRAD_FACTOR * 2) + " HWHM", xy=(x, y + self.max_radii_for_EE), color="white", fontsize=8) fig.tight_layout() fig.subplots_adjust(top=0.9) fig.suptitle("FRD Overview Plot, file: " + self.basename, fontsize=20) fig.savefig(plot_folder + self.basename + "_" + plot_suffix + ".png") print("Saved file:", plot_folder + self.basename + "_" + plot_suffix + ".png") df.to_csv(plot_folder + self.basename + "_" + plot_suffix + ".csv") print("Saved file:", plot_folder + self.basename + "_" + plot_suffix + ".csv")
def noise_per_annulus(array, separation, fwhm, init_rad=None, wedge=(0, 360), verbose=False, debug=False, mask=None): """ Measures the noise as the standard deviation of apertures defined in each annulus with a given separation. Parameters ---------- array : array_like Input frame. separation : float Separation in pixels of the centers of the annuli measured from the center of the frame. fwhm : float FWHM in pixels. init_rad : float Initial radial distance to be used. If None then the init_rad = FWHM. wedge : tuple of floats, optional Initial and Final angles for using a wedge. For example (-90,90) only considers the right side of an image. Be careful when using small wedges, this leads to computing a standard deviation of very small samples (<10 values). verbose : bool, optional If True prints information. debug : bool, optional If True plots the positioning of the apertures. Returns ------- noise : array_like Vector with the noise value per annulus. vector_radd : array_like Vector with the radial distances values. """ # dprint(fwhm) def find_coords(rad, sep, init_angle, fin_angle): angular_range = fin_angle - init_angle npoints = (np.deg2rad(angular_range) * rad) / sep #(2*np.pi*rad)/sep ang_step = angular_range / npoints #360/npoints x = [] y = [] for i in range(int(npoints)): newx = rad * np.cos(np.deg2rad(ang_step * i + init_angle)) newy = rad * np.sin(np.deg2rad(ang_step * i + init_angle)) x.append(newx) y.append(newy) return np.array(y), np.array(x) ### if array.ndim != 2: raise TypeError('Input array is not a frame or 2d array') if not isinstance(wedge, tuple): raise TypeError('Wedge must be a tuple with the initial and final ' 'angles') init_angle, fin_angle = wedge centery, centerx = frame_center(array) n_annuli = int(np.floor((centery) / separation)) - 1 noise = [] vector_radd = [] if verbose: print('{} annuli'.format(n_annuli)) if init_rad is None: init_rad = fwhm if debug: _, ax = plt.subplots(figsize=(6, 6)) ax.imshow(array, origin='lower', interpolation='nearest', alpha=0.5, cmap='gray') for i in range(n_annuli): y = centery + init_rad + separation * i rad = dist(centery, centerx, y, centerx) yy, xx = find_coords(rad, fwhm, init_angle, fin_angle) yy += centery xx += centerx apertures = photutils.CircularAperture((xx, yy), fwhm / 2) mask = np.bool_(mask) # quick2D(mask) if mask: fluxes = photutils.aperture_photometry(array, apertures, mask=mask) else: fluxes = photutils.aperture_photometry(array, apertures) fluxes = np.array(fluxes['aperture_sum']) noise_ann = np.std(fluxes) noise.append(noise_ann) vector_radd.append(rad) # if debug: # if i == 1: # plt.plot(fluxes) # plt.figure() # plt.hist(fluxes, bins=50) if debug: for j in range(xx.shape[0]): # Circle takes coordinates as (X,Y) aper = plt.Circle((xx[j], yy[j]), radius=fwhm / 2, color='r', fill=False, alpha=0.8) ax.add_patch(aper) cent = plt.Circle((xx[j], yy[j]), radius=0.8, color='r', fill=True, alpha=0.5) ax.add_patch(cent) if verbose: print('Radius(px) = {}, Noise = {:.3f} '.format(rad, noise_ann)) return np.array(noise), np.array(vector_radd)
def aper_phot(self, x, y): """Perform aperture photometry, uses photutils functions, photutils must be available """ if not photutils_installed: print("Install photutil to enable") else: sigma = 0. # no centering amp = 0. # no centering if self.aperphot_pars["center"][0]: center = True delta = 10 popt = self.gauss_center(x, y, delta) if 5 > popt.count(0) > 1: # an error occurred in centering warnings.warn( "Problem fitting center, using original coordinates") else: amp, x, y, sigma, offset = popt radius = int(self.aperphot_pars["radius"][0]) width = int(self.aperphot_pars["width"][0]) inner = int(self.aperphot_pars["skyrad"][0]) subsky = bool(self.aperphot_pars["subsky"][0]) aper_flux = photutils.aperture_photometry( self._data, x, y, apertures=photutils.CircularAperture(radius), subpixels=1, method="center") aperture_area = np.pi * (radius)**2 if subsky: outer = inner + width annulus_sky = photutils.annulus_circular( self._data, x, y, inner, outer) annulus_area = np.pi * (outer**2 - inner**2) total_flux = aper_flux - annulus_sky * (aperture_area / annulus_area) else: total_flux = aper_flux # compute the magnitude of the sky corrected flux magzero = float(self.aperphot_pars["zmag"][0]) mag = magzero - 2.5 * (np.log10(total_flux)) pheader = ("x\ty\tradius\tflux\tmag(zpt={0:0.2f})\tsky\t".format( magzero)).expandtabs(15) if center: pheader += ("fwhm") pstr = "\n{0:.2f}\t{1:0.2f}\t{2:d}\t{3:0.2f}\t{4:0.2f}\t{5:0.2f}\t{6:0.2f}".format( x + 1, y + 1, radius, total_flux, mag, annulus_sky / annulus_area, math_helper.gfwhm(sigma)).expandtabs(15) else: pstr = "\n{0:0.2f}\t{1:0.2f}\t{2:d}\t{3:0.2f}\t{4:0.2f}\t{5:0.2f}".format( x + 1, y + 1, radius, total_flux, mag, annulus_sky / annulus_area, ).expandtabs(15) print(pheader + pstr) logging.info(pheader + pstr)
def frame_quick_report(array, fwhm, source_xy=None, verbose=True): """ Gets information from given frame: Integrated flux in aperture, SNR of central pixel (max or given coordinates), mean SNR in aperture. Parameters ---------- array : array_like 2d array or input frame. fwhm : float Size of the FWHM in pixels. source_xy : tuple of floats X and Y coordinates of the source center. verbose : {True, False}, bool optional If True prints to stdout the frame info. Returns ------- obj_flux : float Integrated flux in aperture. snr_pixels : array_like SNR of pixels in 1FWHM aperture. fy, fx : float Fitted coordinates. """ if not array.ndim == 2: raise TypeError('Array is not 2d.') if source_xy is not None: x, y = source_xy if verbose: print print('Coordinates of chosen px X,Y = {:},{:}'.format(x,y)) else: y, x = np.where(array == array.max()) y = y[0] x = x[0] if verbose: print print('Coordinates of Max px X,Y = {:},{:}'.format(x,y)) # we get integrated flux on aperture with diameter=1FWHM aper = photutils.CircularAperture((x, y), fwhm/2.) obj_flux = photutils.aperture_photometry(array, aper, method='exact') obj_flux = obj_flux['aperture_sum'][0] # we get the mean and stddev of SNRs on aperture yy, xx = draw.circle(y, x, fwhm/2.) snr_pixels = [snr_ss(array, (x_,y_), fwhm, plot=False, verbose=False) for \ y_, x_ in zip(yy, xx)] meansnr = np.mean(snr_pixels) stdsnr = np.std(snr_pixels) if verbose: print('Central pixel SNR: ') snr_ss(array, (x,y), fwhm, plot=False, verbose=True) print('-----------------------------------------') print('In 1*FWHM circular aperture:') print('Integrated flux = {:.3f}'.format(obj_flux)) print('Mean SNR = {:.3f}'.format(meansnr)) print('Max SNR = {:.3f}, stddev SNRs = {:.3f}'.format(np.max(snr_pixels), stdsnr)) print('-----------------------------------------') return obj_flux, snr_pixels
def sky_fibers_for_brick( survey, brickname, bands=['g', 'r', 'z'], apertures_arcsec=[0.5, 0.75, 1., 1.5, 2., 3.5, 5., 7.]): ''' Produces a table of possible DESI sky fiber locations for a given "brickname" (eg, "0001p000") read from the given LegacySurveyData object *survey*. ''' import fitsio from astrometry.util.fits import fits_table from astrometry.util.util import Tan import photutils from legacypipe.utils import find_unique_pixels fn = survey.find_file('blobmap', brick=brickname) blobs = fitsio.read(fn) print('Blobs:', blobs.min(), blobs.max()) header = fitsio.read_header(fn) wcs = Tan(header) goodpix = (blobs == -1) for band in bands: fn = survey.find_file('nexp', brick=brickname, band=band) if not os.path.exists(fn): # Skip continue nexp = fitsio.read(fn) goodpix[nexp == 0] = False # Cut to unique brick area... required since the blob map drops # blobs that are completely outside the brick's unique area, thus # those locations are not masked. brick = survey.get_brick_by_name(brickname) H, W = wcs.shape U = find_unique_pixels(wcs, W, H, None, brick.ra1, brick.ra2, brick.dec1, brick.dec2) goodpix[U == 0] = False del U x, y, blobdist = sky_fiber_locations(goodpix) skyfibers = fits_table() skyfibers.brickid = np.zeros(len(x), np.int32) + brick.brickid skyfibers.brickname = np.array([brickname] * len(x)) skyfibers.x = x.astype(np.int16) skyfibers.y = y.astype(np.int16) skyfibers.blobdist = blobdist skyfibers.ra, skyfibers.dec = wcs.pixelxy2radec(x + 1, y + 1) pixscale = wcs.pixel_scale() apertures = np.array(apertures_arcsec) / pixscale # Now, do aperture photometry at these points in the coadd images for band in bands: imfn = survey.find_file('image', brick=brickname, band=band) ivfn = survey.find_file('invvar', brick=brickname, band=band) if not (os.path.exists(imfn) and os.path.exists(ivfn)): continue coimg = fitsio.read(imfn) coiv = fitsio.read(ivfn) apflux = np.zeros((len(skyfibers), len(apertures)), np.float32) apiv = np.zeros((len(skyfibers), len(apertures)), np.float32) skyfibers.set('apflux_%s' % band, apflux) skyfibers.set('apflux_ivar_%s' % band, apiv) with np.errstate(divide='ignore', invalid='ignore'): imsigma = 1. / np.sqrt(coiv) imsigma[coiv == 0] = 0 apxy = np.vstack((skyfibers.x, skyfibers.y)).T for irad, rad in enumerate(apertures): aper = photutils.CircularAperture(apxy, rad) p = photutils.aperture_photometry(coimg, aper, error=imsigma) apflux[:, irad] = p.field('aperture_sum') err = p.field('aperture_sum_err') apiv[:, irad] = 1. / err**2 header = fitsio.FITSHDR() for i, ap in enumerate(apertures_arcsec): header.add_record( dict(name='AP%i' % i, value=ap, comment='Aperture radius (arcsec)')) skyfibers._header = header return skyfibers