def compute_photutils(settings, image_data): # Taken from photuils example http://photutils.readthedocs.io/en/stable/psf.html # See also http://photutils.readthedocs.io/en/stable/api/photutils.psf.DAOPhotPSFPhotometry.html#photutils.psf.DAOPhotPSFPhotometry sigma_psf = settings.sigma_psf crit_separation = settings.crit_separation threshold = settings.threshold box_size = settings.box_size niters = settings.iters bkgrms = MADStdBackgroundRMS(SigmaClip(sigma=3.)) std = bkgrms(image_data) logger.info('Using sigma=%f, threshold=%f, separation=%f, box_size=%d, niters=%d, std=%f' % \ (sigma_psf, threshold, crit_separation, box_size, niters, std)) fitter = LevMarLSQFitter() # See findpars args http://stsdas.stsci.edu/cgi-bin/gethelp.cgi?findpars photargs = { 'crit_separation': crit_separation * sigma_psf * gaussian_sigma_to_fwhm, #'crit_separation': crit_separation, 'threshold': threshold * std, 'fwhm': sigma_psf * gaussian_sigma_to_fwhm, 'sigma_radius': sigma_psf * gaussian_sigma_to_fwhm, #'sigma': 3.0, 'fitter': fitter, 'niters': niters, 'fitshape': (box_size, box_size), 'sharplo': 0.2, 'sharphi': 2.0, 'roundlo': -1.0, 'roundhi': 1.0, 'psf_model': IntegratedGaussianPRF(sigma=sigma_psf), 'aperture_radius': sigma_psf * gaussian_sigma_to_fwhm, } # starfinder takes 'exclude border' # photargs['psf_model'].sigma.fixed = False photometry = DAOPhotPSFPhotometry(**photargs) # Column names: # 'flux_0', 'x_fit', 'x_0', 'y_fit', 'y_0', 'flux_fit', 'id', 'group_id', # 'flux_unc', 'x_0_unc', 'y_0_unc', 'iter_detected' result_tab = photometry(image=image_data) # Only use from final iteration # result_tab = result_tab[result_tab['iter_detected'] == niters] logger.info('Fit info: %s' % fitter.fit_info['message']) # Filter out negative flux #result_tab = result_tab[result_tab['flux_fit'] >= 0] # Formula: https://en.wikipedia.org/wiki/Instrumental_magnitude result_tab['mag'] = -2.5 * np.log10(result_tab['flux_fit']) result_tab['mag_unc'] = np.abs(-2.5 * np.log10(result_tab['flux_fit'] + result_tab['flux_unc']) - \ -2.5 * np.log10(result_tab['flux_fit'] - result_tab['flux_unc'])) / 2.0 # http://www.ucolick.org/~bolte/AY257/s_n.pdf #result_tab['snr'] = 1.0875 / result_tab['mag_unc'] result_tab['snr'] = 1.0 / (np.power(10, (result_tab['mag_unc'] / 2.5)) - 1) residual_image = photometry.get_residual_image() return result_tab, residual_image, std
psf.y_0.fixed = True pos = Table(names=['x_0', 'y_0'], data=[catalog['X_IMAGE_DBL'], catalog['Y_IMAGE_DBL']]) # Using the initial positions from photutils.psf import DAOPhotPSFPhotometry photometry = DAOPhotPSFPhotometry( crit_separation= 19, # The higher the crit_separation, the higher the computational cost threshold=1.0, fwhm=3.0, psf_model=psf, fitshape=9, sigma=3.0, ratio=1.0, theta=0.0, sigma_radius=1.5, sharplo=0.2, sharphi=1.0, roundlo=-1.0, roundhi=1.0, fitter=LevMarLSQFitter(), niters=3, aperture_radius=5) import timeit tic = timeit.default_timer() phot_results = photometry(image_data, init_guesses=pos) residual = photometry.get_residual_image()
def daophot(cnam, ccd, xlo, xhi, ylo, yhi, niters, method, fwhm, beta, gfac, thresh, rejthresh): """ Perform iterative PSF photometry and star finding on region of CCD """ print(xlo, ylo, xhi, yhi) # first check we are within a single window wnam1 = ccd.inside(xlo, ylo, 2) wnam2 = ccd.inside(xhi, yhi, 2) if wnam1 != wnam2: raise hcam.HipercamError( 'PSF photometry cannot currently be run across seperate windows') wnam = wnam1 print(wnam) # background stats from whole windpw # estimate background RMS wind = ccd[wnam] rms_func = MADStdBackgroundRMS(sigma_clip=SigmaClip(sigma=rejthresh)) bkg_rms = rms_func(wind.data) bkg_func = MMMBackground(sigma_clip=SigmaClip(sigma=rejthresh)) bkg = bkg_func(wind.data) print(' Background estimate = {}, BKG RMS = {}'.format(bkg, bkg_rms)) # crop window to ROI wind = ccd[wnam].window(xlo, xhi, ylo, yhi) # correct FWHM for binning fwhm /= wind.xbin if method == 'm': psf_model = MoffatPSF(fwhm, beta) print(' FWHM = {:.1f}, BETA={:.1f}'.format(fwhm, beta)) else: psf_model = IntegratedGaussianPRF(sigma=fwhm * gaussian_fwhm_to_sigma) print(' FWHM = {:.1f}'.format(fwhm)) # region to extract around positions for fits fitshape = int(5 * fwhm) # ensure odd if fitshape % 2 == 0: fitshape += 1 photometry_task = DAOPhotPSFPhotometry(gfac * fwhm, thresh * bkg_rms, fwhm, psf_model, fitshape, niters=niters, sigma=rejthresh) with warnings.catch_warnings(): warnings.simplefilter('ignore') results = photometry_task(wind.data - bkg) # filter out junk fits tiny = 1e-30 bad_errs = (results['flux_unc'] < tiny) | (results['x_0_unc'] < tiny) | ( results['y_0_unc'] < tiny) results = results[~bad_errs] results.write('table_{}.fits'.format(cnam)) print(' found {} stars'.format(len(results))) xlocs, ylocs = results['x_fit'], results['y_fit'] # convert to device coordinates xlocs = wind.x(xlocs) ylocs = wind.y(ylocs) return xlocs, ylocs
def run_daophot(image, threshold, star_tbl, niters=1, snr_lim=5, duet=None, diag=False): ''' Given an image and a PSF, go run DAOPhot PSF-fitting algorithm ''' from photutils.psf import DAOPhotPSFPhotometry, IntegratedGaussianPRF if duet is None: duet = Telescope() fwhm = (duet.psf_fwhm / duet.pixel).to('').value # Fix star table columns star_tbl['x_0'] = star_tbl['x'] star_tbl['y_0'] = star_tbl['y'] # Define a fittable PSF model sigma = fwhm / (2. * np.sqrt(2 * np.log(2))) # Simple Gaussian model to fit #psf_model = IntegratedGaussianPRF(sigma=sigma) #flux_norm = 1 # Use DUET-like PSF #oversample = 2 # Needs to be oversampled but only minimally #duet_psf_os = duet.psf_model(pixel_size=duet.pixel/oversample, x_size=12, y_size=12) # Even numbers work better #psf_model = EPSFModel(duet_psf_os.array,oversampling=oversample) #flux_norm = 1/oversample**2 # A quirk of constructing an oversampled ePSF using photutils psf_model = duet.epsf_model # Temporarily turn off Astropy warnings import warnings from astropy.utils.exceptions import AstropyWarning warnings.simplefilter('ignore', category=AstropyWarning) ## FROM HERE ON NO UNITS ########### # Initialise a Photometry object # This object loops find, fit and subtract threshold = threshold.to(image.unit) photometry = DAOPhotPSFPhotometry(fwhm, threshold.value, fwhm, psf_model, (5, 5), niters=niters, sigma_radius=5, aperture_radius=fwhm) # Problem with _recursive_lookup while fitting (needs latest version of astropy fix to modeling/utils.py) result = photometry(image=image.value, init_guesses=star_tbl) residual_image = photometry.get_residual_image() # Filter results to only keep those with S/N greater than snr_lim (default is 5) result_sig = result[np.abs(result['flux_fit'] / result['flux_unc']) >= snr_lim] if diag: print("PSF-fitting complete") # Turn warnings back on again warnings.simplefilter('default') ## FROM HERE ON YES UNITS ########### result_sig['flux_fit'] = result_sig['flux_fit'] * image.unit result_sig['flux_unc'] = result_sig['flux_unc'] * image.unit return result_sig, residual_image * image.unit
def get_photometry(self, aperture_radius=3, fwhm=None, sky_radius_inner=None, sky_radius_outer=None, mag_zero_point=0, mode="basic"): if sky_radius_outer is None: sky_radius_outer = np.min(self.image.shape) // 2 if sky_radius_inner is None: sky_radius_inner = sky_radius_outer - 3 x, y = np.array(self.image.shape) // 2 r = aperture_radius ro = sky_radius_outer dw = sky_radius_outer - sky_radius_inner bg = np.copy(self.image[y - ro:y + ro, x - ro:x + ro]) bg[dw:-dw, dw:-dw] = 0 bg_median = np.median(bg[bg != 0]) bg_std = np.std(bg[bg != 0]) noise = bg_std * np.sqrt(np.sum(bg != 0)) im = np.copy(self.image[y - r:y + r, x - r:x + r]) if "circ" in mode.lower(): pass elif "basic" in mode.lower(): flux = np.sum(im - bg_median) self.results = None elif "psf" in mode.lower(): if fwhm is None: warnings.warn("``fwhm`` must be given for PSF photometry") flux = 0 try: x, y = self._find_center(self.image, fwhm) flux = self._basic_psf_flux(self.image, fwhm, x=x, y=y) ##### Really bad form!!! Keep this in mind =) self.x, self.y = x, y except: flux = 0 elif "dao" in mode.lower(): if fwhm is None: warnings.warn("``fwhm`` must be given for PSF photometry") flux = 0 sigma = fwhm / 2.35 prf = IntegratedGaussianPRF(sigma) fitshape = im.shape[0] if im.shape[0] % 2 == 1 else im.shape[0] - 1 daophot = DAOPhotPSFPhotometry(crit_separation=sigma, threshold=np.median(im), fwhm=fwhm, psf_model=prf, fitshape=fitshape) #try: results = daophot(im) width, height = im.shape dist_from_centre = np.sqrt((results["x_fit"] - width / 2)**2 + \ (results["y_fit"] - height / 2)**2) i = np.argmin(dist_from_centre) x, y = results["x_fit"][i], results["y_fit"][i], flux = results["flux_fit"][i] ##### Really bad form!!! Keep this in mind =) self.x, self.y = x, y self.results = results #except: # self.results = None # flux = 0 snr = flux / noise mag = -2.5 * np.log10(flux) + mag_zero_point return mag, snr, flux, noise, bg_std, bg_median
daogroup = DAOGroup(2.0*fwhm) mmm_bkg = MMMBackground() fitter = LevMarLSQFitter() psf_model = IntegratedGaussianPRF(sigma=(fwhm/gaussian_sigma_to_fwhm)) #fitshape must be odd fitshape = 2*int(fwhm)+1 print 'Performing photometry' #photometry = IterativelySubtractedPSFPhotometry(finder=iraffind, group_maker=daogroup, bkg_estimator=mmm_bkg, psf_model=psf_model,fitter=LevMarLSQFitter(), niters=None, fitshape=(fitshape, fitshape), aperture_radius=fwhm) #niters = None means continue until all stars subtracted - might recur infinitely from photutils.psf import DAOPhotPSFPhotometry photometry = DAOPhotPSFPhotometry(crit_separation=3*fwhm, threshold=3.5*std, fwhm=fwhm, psf_model=psf_model, fitshape=(fitshape, fitshape), sharplo=0.0, sharphi=2.0, roundlo=-5.0, roundhi=5.0, fitter=iraffind, niters=100, aperture_radius=1.2*fwhm) raw_input('Done') print 'Results table' result_tab = photometry(image=image) raw_input( 'Creating residual image') residual_image = photometry.get_residual_image() print 'Plotting' plt.subplot(1, 2, 1) plt.imshow(image, cmap='viridis', aspect=1, interpolation='nearest', origin='lower') plt.colorbar(orientation='horizontal', fraction=0.046, pad=0.04) plt.subplot(1 ,2, 2) plt.imshow(residual_image, cmap='viridis', aspect=1, interpolation='nearest', origin='lower') plt.title('Residual Image')