def threshold(self, nsig=1.5, box_size=(50, 50), filter_size=(3, 3)): """ Generate threshold image self.thresh_img is set in place Args: nsig (float, optional): Primary threshold parameter box_size (tuple): Primary Background2D parameter filter_size (tuple): Primary Background2D parameter Returns: """ if self.hdu is None: self.load_image() # Background bkg_estimator = photutils.MedianBackground() self.bkg = photutils.Background2D(self.hdu.data, box_size, filter_size=filter_size, bkg_estimator=bkg_estimator, exclude_percentile=self.exc_per) # Threshold self.thresh_img = self.bkg.background + (nsig * self.bkg.background_rms)
def source_morphology(in_image, ext_name, **kwargs): fo = fits.open(in_image, "append") hdu = fo[ext_name] segm_obj = SegmentationImage(fo["DEBLEND"].data) errmap = fo["WEIGHT_MAP"].data seg_props = fo["DEBLEND_PROPS"].data im = hdu.data bkg_estimator = photutils.MedianBackground() bkg = photutils.Background2D(hdu.data, (50, 50), bkg_estimator=bkg_estimator) im -= bkg.background npix = im.shape[0] center_slice = segm_obj.data[int(npix / 2) - 2:int(npix / 2) + 2, int(npix / 2) - 2:int(npix / 2) + 2] central_index = np.where(seg_props["id"] == center_slice[0, 0])[0][0] fo.flush() fo.close() source_morph = statmorph.SourceMorphology(im, segm_obj, central_index, weightmap=errmap, **kwargs) return source_morph
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 sub_background(image: CCDData, filter_size: int = 27, box_size: int = 150): """ Perform background subtraction using photutils' median background estimator over a 2D mesh. """ binning = image.header['BINNING'] filter_size = int(filter_size / binning) box_size = int(box_size / binning) bkg_estimator = photutils.MedianBackground() bkg = photutils.Background2D(image, (box_size, box_size), filter_size=(filter_size, filter_size), bkg_estimator=bkg_estimator) sub = image.data - bkg.background return sub
def _perform(self): """ Returns an Argument() with the parameters that depend on this operation. """ self.log.info(f"Running {self.__class__.__name__} action") box_size = self.cfg['Extract'].getint('background_box_size', 128) self.log.debug(f" Using box size = {box_size} pixels") bkg = photutils.Background2D(self.action.args.ccddata, box_size=box_size, mask=self.action.args.source_mask, sigma_clip=stats.SigmaClip()) self.action.args.background = bkg return self.action.args
def _perform(self): """ Returns an Argument() with the parameters that depends on this operation. """ self.log.info(f"Running {self.__class__.__name__} action") box_size = self.cfg['Extract'].getint('background_box_size', 128) self.log.debug(f" Using box size = {box_size} pixels") self.action.args.background = [None] * len( self.action.args.kd.pixeldata) for i, pd in enumerate(self.action.args.kd.pixeldata): bkg = photutils.Background2D(pd, box_size=box_size, mask=self.action.args.source_mask[i], sigma_clip=stats.SigmaClip()) self.action.args.background[i] = bkg # self.action.args.kd.pixeldata[i].data -= bkg.background return self.action.args
def detect_sources(in_image, ext_name, filt_wheel, **kwargs): fo = fits.open(in_image, "append") hdu = fo[ext_name] # build kernel for pre-filtering. How big? # don't assume redshift knowledge here typical_kpc_per_arcsec = 8.0 kernel_kpc_fwhm = 5.0 kernel_arcsec_fwhm = kernel_kpc_fwhm / typical_kpc_per_arcsec kernel_pixel_fwhm = kernel_arcsec_fwhm / hdu.header["PIXSIZE"] sigma = kernel_pixel_fwhm * gaussian_fwhm_to_sigma nsize = int(5 * kernel_pixel_fwhm) kernel = Gaussian2DKernel(sigma, x_size=nsize, y_size=nsize) bkg_estimator = photutils.MedianBackground() bkg = photutils.Background2D(hdu.data, (50, 50), bkg_estimator=bkg_estimator) thresh = bkg.background + (5.0 * bkg.background_rms) segmap_obj = photutils.detect_sources(hdu.data, thresh, npixels=5, filter_kernel=kernel, **kwargs) if segmap_obj is None: nhdu = fits.ImageHDU() nhdu.header["EXTNAME"] = "SEGMAP" fo.append(nhdu) thdu = fits.BinTableHDU() thdu.header["EXTNAME"] = "SEGMAP_PROPS" fo.append(thdu) fo.flush() fo.close() return None, None, None else: # Error image can be computed with photutils plus a GAIN keyword -- ratio of flux units to counts gain = filt_wheel[hdu.header["FILTER"]][2] errmap = calc_total_error(hdu.data, bkg.background_rms, effective_gain=gain) segmap = segmap_obj.data props = photutils.source_properties(hdu.data, segmap, errmap) props_table = astropy.table.Table(props.to_table()) # these give problems given their format/NoneType objects props_table.remove_columns([ "sky_centroid", "sky_centroid_icrs", "source_sum_err", "background_sum", "background_mean", "background_at_centroid", ]) nhdu = fits.ImageHDU(segmap) # save segmap and info nhdu.header["EXTNAME"] = "SEGMAP" fo.append(nhdu) thdu = fits.BinTableHDU(props_table) thdu.header["EXTNAME"] = "SEGMAP_PROPS" fo.append(thdu) fo.flush() nhdu = fits.ImageHDU(errmap) # save errmap nhdu.header["EXTNAME"] = "WEIGHT_MAP" fo.append(nhdu) fo.flush() fo.close() return segmap_obj, kernel, errmap
def make_background(data, sigma=3., snr=3., npixels=4, boxsize=(10, 10), filter_size=(5, 5), mask_sources=True, inmask=None): """ Use photutils to create a background model from the input data. data : 2D `~numpy.ndarray` Data from which to extract background model sigma : float (default: 2.0) Number of sigma to use for sigma clipping snr : float (default: 2.0) SNR to use when masking sources npixels : int (default: 7) Number of connected pixels to use when masking sources boxsize : tuple or int (default: (7, 7)) Size of box used to create the gridded background map filter_size : tuple or int (default: (3, 3)) Window size of the 2D median filter to apply to the low-res background map mask_sources : bool (default: True) If true, then use `~photutils.make_source_mask` to mask sources before creating background """ sigma_clip = stats.SigmaClip(sigma=sigma) bkg_estimator = photutils.SExtractorBackground() if inmask is not None: cov_mask = np.zeros_like(inmask, dtype=bool) cov_mask[inmask != np.nan] = False cov_mask[inmask == np.nan] = True else: cov_mask = np.zeros_like(data, dtype=bool) if mask_sources: src_mask = photutils.make_source_mask(data, nsigma=snr, npixels=npixels, mask=cov_mask) mask = (cov_mask | src_mask) bkg = photutils.Background2D(data, boxsize, filter_size=filter_size, sigma_clip=sigma_clip, bkg_estimator=bkg_estimator, mask=mask) else: bkg = photutils.Background2D(data, boxsize, filter_size=filter_size, sigma_clip=sigma_clip, bkg_estimator=bkg_estimator, mask=cov_mask) return bkg
nx, ny = im.shape print("Dimensions", im.shape) # Make new "image" or "exposure" class that has: # -flux image # -noise/variance image # -mask image # -background image # -FITS header # -psf # -wcs? # Get the background print("Computing background image") # THIS TAKES WAY TOO LONG bkg = photutils.Background2D(im, (nx/10, ny/10), filter_size=1,method='median') subim = im-bkg.background # Create the mask # mask=1 bad # mask=0 good #try: # saturate = head['saturate'] #except: # saturate = 60000.0 #mask = im > 0.9*saturate mask = create_mask(im,head) # Create the noise mask noise = create_noise(im,head,mask) #try:
for ntile in [4]: data = np.tile(rawdata, (ntile, ntile)) line = "| {0:4d}^2 image background |".format(data.shape[0]) t0 = time.time() bkg = sep.Background(data) t1 = time.time() t_sep = (t1 - t0) * 1.e3 line += " {0:7.2f} ms |".format(t_sep) if HAVE_PHOTUTILS: t0 = time.time() try: bkg = photutils.Background(data, (64, 64)) # estimate background except AttributeError: bkg = photutils.Background2D(data, (64, 64)) # estimate background t1 = time.time() t_pu = (t1 - t0) * 1.e3 line += " {0:7.2f} ms | {1:6.2f} |".format(t_pu, t_pu / t_sep) print(line) #------------------------------------------------------------------------------ # Circular aperture photometry benchmarks if not CONDENSED: print(blankline) line = "| **aperture photometry** | |" if HAVE_PHOTUTILS: line += " | |" print(line)
def forced_phot(image, wcs, zp, catalog_alt, catalog_az, catalog_mag, catalog_id, nside=8, phot_params=None, do_background=False, return_table=False, mjd=0.): """ Generate a map of the extinction on the sky Parameters ---------- image : array The image to find the extinction map from wcs : wcs-like object WCS describing the image. Note WCS RA,Dec will be interpreted as Az,Alt. zp : float The zeropoint of the image catalog_alt : array Altitude of stars expected to be in the image (degrees) catalog_az : array Azimuth of stars expected in the image (degrees) catalog_mag : array Magnitudes of stars expected in the image nside : int Healpixel nside to set resoltion of output map phot_params : dict (None) Dictionary holding common photometry kwargs. Loads defaults from default_phot_params if None. do_background : bool (False) Do a 2D background model subtraction. Skipped by default because astropy is really slow. return_table : bool (False) If True, return the astropy table with the photometry, otherwise, return the extinction map. Output ------ extinction : array A healpixel array (where latitude and longitude are altitude and azimuth). Pixels with no stars are filled with the healpixel mask value. Non-masked pixels have the measured extinction in magnitudes. """ if phot_params is None: phot_params = default_phot_params() # Find the healpixel for each catalog star lat = np.radians(90. - catalog_alt) catalog_hp = hp.ang2pix(nside, lat, np.radians(catalog_az)) order = np.argsort(catalog_hp) catalog_alt = catalog_alt[order] catalog_az = catalog_az[order] catalog_mag = catalog_mag[order] catalog_id = catalog_id[order] catalog_hp = catalog_hp[order] catalog_x, catalog_y = wcs.all_world2pix(catalog_az, catalog_alt, 0) good_transform = ~np.isnan(catalog_x) # Find the x,y positions of helpix centers lat, lon = hp.pix2ang(nside, np.arange(hp.nside2npix(nside))) hp_alt = np.degrees(np.pi / 2. - lat) hp_az = np.degrees(lon) hp_x, hp_y = wcs.all_world2pix(hp_az, hp_alt, 0) # Run the photometry at the expected catalog positions if do_background: sigma_clip = phu.SigmaClip(sigma=phot_params['bk_clip_sigma'], iters=phot_params['bk_iter']) bkg_estimator = phu.MedianBackground() bkg = phu.Background2D( image, (phot_params['background_size'], phot_params['background_size']), filter_size=(phot_params['bk_filter_size'], phot_params['bk_filter_size']), sigma_clip=sigma_clip, bkg_estimator=bkg_estimator) bk_img = image - bkg.background else: bk_img = image positions = list(zip(catalog_x[good_transform], catalog_y[good_transform])) apertures = phu.CircularAperture(positions, r=phot_params['apper_r']) annulus_apertures = phu.CircularAnnulus(positions, r_in=phot_params['ann_r_in'], r_out=phot_params['ann_r_out']) apers = [apertures, annulus_apertures] phot_table = phu.aperture_photometry(bk_img, 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 phot_table['catalog_id'] = catalog_id[good_transform] phot_table['residual_aperture_mag'] = -2.5 * np.log10(final_sum) + zp detected = np.where(final_sum > 0) mag_difference = phot_table['residual_aperture_mag'][ detected] - catalog_mag[good_transform][detected] phot_table['mag_difference'] = np.zeros( phot_table['residual_aperture_mag'].data.size, dtype=float) phot_table['mjd'] = np.zeros(phot_table['residual_aperture_mag'].data.size, dtype=float) phot_table['mag_difference'][detected] = mag_difference phot_table['mjd'] = mjd bins = np.arange(hp.nside2npix(nside) + 1) - 0.5 result, be, bn = binned_statistic(catalog_hp[good_transform][detected], mag_difference, bins=bins) if return_table: return phot_table, result else: return result
def getBackground(data, fitsfilename, bkg_method='2d', box_size=128, params=None, locs=None): if bkg_method == '2d': bkg_estimator = pt.MedianBackground() data_mask = (data < 1.1 * np.median(data)) bkg_maskarr = np.ma.array(data, mask=~data_mask) bkg = pt.Background2D(bkg_maskarr, (box_size, box_size), filter_size=(3, 3), sigma_clip=None, bkg_estimator=bkg_estimator) bkg_median = bkg.background_median plt.imshow(bkg.background, origin='lower', vmin=bkg_median - 100, vmax=bkg_median + 100, cmap='Greys') plt.colorbar(orientation='horizontal') if not os.path.exists( os.path.join(os.path.dirname(fitsfilename), 'background')): os.makedirs( os.path.join(os.path.dirname(fitsfilename), 'background')) savepath = os.path.join( os.path.join(os.path.dirname(fitsfilename), 'background'), 'bkg_' + os.path.split(fitsfilename[:-5])[-1] + '.png') plt.savefig(savepath) plt.clf() plt.close() elif bkg_method == 'aperture': bkg_arr = data.copy() bkg = [] for loc in locs: bkg_arr[loc[0] - params['sky_rad_in']:loc[0] + params['sky_rad_in'], loc[1] - params['sky_rad_in']:loc[1] + params['sky_rad_in']] = np.nan bkg.append( np.nanmedian(bkg_arr[loc[0] - params['sky_rad_out']:loc[0] + params['sky_rad_out'], loc[1] - params['sky_rad_out']:loc[1] + params['sky_rad_out']].flatten())) plt.imshow(bkg_arr, origin='lower', vmin=np.nanmedian(bkg_arr) - 100, vmax=np.nanmedian(bkg_arr) + 100) plt.colorbar() for loc in locs: plt.gca().add_patch( patch.Rectangle((loc[1] - params['sky_rad_out'], loc[0] - params['sky_rad_out']), 2 * params['sky_rad_out'], 2 * params['sky_rad_out'], color='k', alpha=0.5)) if not os.path.exists( os.path.join(os.path.dirname(fitsfilename), 'background')): os.makedirs( os.path.join(os.path.dirname(fitsfilename), 'background')) savepath = os.path.join( os.path.join(os.path.dirname(fitsfilename), 'background'), 'bkg_' + os.path.split(fitsfilename[:-5])[-1] + '.png') plt.savefig(savepath) plt.clf() plt.close() bkg = np.array(bkg) bkg = BkgWrapper(bkg) elif bkg_method == 'median': return BkgWrapper(np.median(data.flatten())) elif bkg_method == 'None': return None else: raise ValueError('Method %s not supported.' % bkg_method) return bkg