def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Islands") opts = img.opts minsize = opts.minpix_isl if minsize is None: minsize = int(img.pixel_beamarea() / 3.0) # 1/3 of beam area in pixels if minsize < 6: minsize = 6 # Need at least 6 pixels to obtain good fits mylogger.userinfo(mylog, "Minimum number of pixels per island", '%i' % minsize) img.minpix_isl = minsize if opts.detection_image != '': # Use a different image for island detection. The detection # image and the measurement image must have the same shape # and be registered. Otherwise, one could reproject the # detection image using, e.g., the Kapteyn package. # # First, set up up an Image object and run a limited # op_chain. from . import _run_op_list mylogger.userinfo(mylog, "\nDetermining islands from detection image") det_chain, det_opts = self.setpara_bdsm(img, opts.detection_image) det_img = Image(det_opts) det_img.log = 'Detection image' success = _run_op_list(det_img, det_chain) if not success: return # Check that the ch0 images are the same size ch0_map = img.ch0_arr det_ch0_map = det_img.ch0_arr det_shape = det_ch0_map.shape ch0_shape = ch0_map.shape if det_shape != ch0_shape: raise RuntimeError( "Detection image shape does not match that of input image." ) # Run through islands and correct the image and rms, mean and max values img.island_labels = det_img.island_labels corr_islands = [] mean_map = img.mean_arr rms_map = img.rms_arr for i, isl in enumerate(det_img.islands): islcp = isl.copy(img.pixel_beamarea(), image=ch0_map[isl.bbox], mean=mean_map[isl.bbox], rms=rms_map[isl.bbox]) islcp.island_id = i corr_islands.append(islcp) img.islands = corr_islands img.nisl = len(img.islands) img.pyrank = det_img.pyrank img.minpix_isl = det_img.minpix_isl mylogger.userinfo(mylog, "\nContinuing processing using primary image") else: if opts.src_ra_dec is not None: mylogger.userinfo( mylog, "Constructing islands at user-supplied source locations") img.islands = self.coords_to_isl(img, opts) else: img.islands = self.ndimage_alg(img, opts) img.nisl = len(img.islands) mylogger.userinfo(mylog, "Number of islands found", '%i' % len(img.islands)) ch0_map = img.ch0_arr ch0_shape = ch0_map.shape pyrank = N.zeros(ch0_shape, dtype=N.int32) for i, isl in enumerate(img.islands): isl.island_id = i pyrank[isl.bbox] += N.invert(isl.mask_active) * (i + 1) pyrank -= 1 # align pyrank values with island ids and set regions outside of islands to -1 if opts.output_all: write_islands(img) if opts.savefits_rankim: func.write_image_to_file(img.use_io, img.imagename + '_pyrank.fits', pyrank, img) img.pyrank = pyrank img.completed_Ops.append('islands') return img
def __call__(self, img): import functions as func from copy import deepcopy as cp import os mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "ResidImage") mylog.info( "Calculating residual image after subtracting reconstructed gaussians" ) shape = img.ch0_arr.shape thresh = img.opts.fittedimage_clip resid_gaus = cp(img.ch0_arr) model_gaus = N.zeros(shape, dtype=N.float32) for g in img.gaussians: C1, C2 = g.centre_pix if hasattr(g, 'wisland_id') and img.waveletimage: isl = img.islands[g.wisland_id] else: isl = img.islands[g.island_id] b = self.find_bbox(thresh * isl.rms, g) bbox = N.s_[max(0, int(C1 - b)):min(shape[0], int(C1 + b + 1)), max(0, int(C2 - b)):min(shape[1], int(C2 + b + 1))] x_ax, y_ax = N.mgrid[bbox] ffimg = func.gaussian_fcn(g, x_ax, y_ax) resid_gaus[bbox] = resid_gaus[bbox] - ffimg model_gaus[bbox] = model_gaus[bbox] + ffimg # Apply mask to model and resid images if hasattr(img, 'rms_mask'): mask = img.rms_mask else: mask = img.mask_arr if isinstance(img.mask_arr, N.ndarray): pix_masked = N.where(img.mask_arr == True) model_gaus[pix_masked] = N.nan resid_gaus[pix_masked] = N.nan img.model_gaus_arr = model_gaus img.resid_gaus_arr = resid_gaus if img.opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/residual/' moddir = img.basedir + '/wavelet/model/' else: resdir = img.basedir + '/residual/' moddir = img.basedir + '/model/' if not os.path.exists(resdir): os.makedirs(resdir) if not os.path.exists(moddir): os.makedirs(moddir) func.write_image_to_file(img.use_io, img.imagename + '.resid_gaus.fits', resid_gaus, img, resdir) mylog.info( '%s %s' % ('Writing', resdir + img.imagename + '.resid_gaus.fits')) func.write_image_to_file(img.use_io, img.imagename + '.model.fits', (img.ch0_arr - resid_gaus), img, moddir) mylog.info( '%s %s' % ('Writing', moddir + img.imagename + '.model_gaus.fits')) ### residual rms and mean per island for isl in img.islands: resid = resid_gaus[isl.bbox] self.calc_resid_mean_rms(isl, resid, type='gaus') # Calculate some statistics for the Gaussian residual image non_masked = N.where(~N.isnan(img.ch0_arr)) mean = N.mean(resid_gaus[non_masked], axis=None) std_dev = N.std(resid_gaus[non_masked], axis=None) skew = stats.skew(resid_gaus[non_masked], axis=None) kurt = stats.kurtosis(resid_gaus[non_masked], axis=None) stat_msg = "Statistics of the Gaussian residual image:\n" stat_msg += " mean: %.3e (Jy/beam)\n" % mean stat_msg += " std. dev: %.3e (Jy/beam)\n" % std_dev stat_msg += " skew: %.3f\n" % skew stat_msg += " kurtosis: %.3f" % kurt mylog.info(stat_msg) # Now residual image for shapelets if img.opts.shapelet_do: mylog.info( "Calculating residual image after subtracting reconstructed shapelets" ) shape = img.ch0_arr.shape fimg = N.zeros(shape, dtype=N.float32) for isl in img.islands: if isl.shapelet_beta > 0: # make sure shapelet has nonzero scale for this island mask = isl.mask_active cen = isl.shapelet_centre - N.array(isl.origin) basis, beta, nmax, cf = isl.shapelet_basis, isl.shapelet_beta, \ isl.shapelet_nmax, isl.shapelet_cf image_recons = reconstruct_shapelets( isl.shape, mask, basis, beta, cen, nmax, cf) fimg[isl.bbox] += image_recons model_shap = fimg resid_shap = img.ch0_arr - fimg # Apply mask to model and resid images if hasattr(img, 'rms_mask'): mask = img.rms_mask else: mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) model_shap[pix_masked] = N.nan resid_shap[pix_masked] = N.nan img.model_shap_arr = model_shap img.resid_shap_arr = resid_shap if img.opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.resid_shap.fits', resid_shap, img, resdir) mylog.info( '%s %s' % ('Writing ', resdir + img.imagename + '.resid_shap.fits')) ### shapelet residual rms and mean per island for isl in img.islands: resid = resid_shap[isl.bbox] self.calc_resid_mean_rms(isl, resid, type='shap') # Calculate some statistics for the Shapelet residual image non_masked = N.where(~N.isnan(img.ch0_arr)) mean = N.mean(resid_shap[non_masked], axis=None) std_dev = N.std(resid_shap[non_masked], axis=None) skew = stats.skew(resid_shap[non_masked], axis=None) kurt = stats.kurtosis(resid_shap[non_masked], axis=None) mylog.info("Statistics of the Shapelet residual image:") mylog.info(" mean: %.3e (Jy/beam)" % mean) mylog.info(" std. dev: %.3e (Jy/beam)" % std_dev) mylog.info(" skew: %.3f" % skew) mylog.info(" kurtosis: %.3f" % kurt) img.completed_Ops.append('make_residimage') return img
def __call__(self, img): if img.opts.psf_vary_do: mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Psf_Vary") mylogger.userinfo(mylog, '\nEstimating PSF variations') opts = img.opts dir = img.basedir + '/misc/' plot = False # debug figures image = img.ch0_arr try: from astropy.io import fits as pyfits old_pyfits = False except ImportError, err: from distutils.version import StrictVersion import pyfits if StrictVersion(pyfits.__version__) < StrictVersion('2.2'): old_pyfits = True else: old_pyfits = False if old_pyfits: mylog.warning('PyFITS version is too old: psf_vary module skipped') return over = 2 generators = opts.psf_generators; nsig = opts.psf_nsig; kappa2 = opts.psf_kappa2 snrtop = opts.psf_snrtop; snrbot = opts.psf_snrbot; snrcutstack = opts.psf_snrcutstack gencode = opts.psf_gencode; primarygen = opts.psf_primarygen; itess_method = opts.psf_itess_method tess_sc = opts.psf_tess_sc; tess_fuzzy= opts.psf_tess_fuzzy bright_snr_cut = opts.psf_high_snr s_only = opts.psf_stype_only if opts.psf_snrcut < 5.0: mylogger.userinfo(mylog, "Value of psf_snrcut too low; increasing to 5") snrcut = 5.0 else: snrcut = opts.psf_snrcut img.psf_snrcut = snrcut if opts.psf_high_snr != None: if opts.psf_high_snr < 10.0: mylogger.userinfo(mylog, "Value of psf_high_snr too low; increasing to 10") high_snrcut = 10.0 else: high_snrcut = opts.psf_high_snr else: high_snrcut = opts.psf_high_snr img.psf_high_snr = high_snrcut wtfns=['unity', 'roundness', 'log10', 'sqrtlog10'] if 0 <= itess_method < 4: tess_method=wtfns[itess_method] else: tess_method='unity' ### now put all relevant gaussian parameters into a list ngaus = img.ngaus nsrc = img.nsrc num = N.zeros(nsrc, dtype=N.int32) peak = N.zeros(nsrc) xc = N.zeros(nsrc) yc = N.zeros(nsrc) bmaj = N.zeros(nsrc) bmin = N.zeros(nsrc) bpa = N.zeros(nsrc) code = N.array(['']*nsrc); rms = N.zeros(nsrc) src_id_list = [] for i, src in enumerate(img.sources): src_max = 0.0 for gmax in src.gaussians: # Take only brightest Gaussian per source if gmax.peak_flux > src_max: src_max = gmax.peak_flux g = gmax num[i] = i peak[i] = g.peak_flux xc[i] = g.centre_pix[0] yc[i] = g.centre_pix[1] bmaj[i] = g.size_pix[0] bmin[i] = g.size_pix[1] bpa[i] = g.size_pix[2] code[i] = img.sources[g.source_id].code rms[i] = img.islands[g.island_id].rms gauls = (num, peak, xc, yc, bmaj, bmin, bpa, code, rms) tr_gauls = self.trans_gaul(gauls) # takes gaussians with code=S and snr > snrcut. if s_only: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut and n[7] == 'S'] else: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut] g_gauls = self.trans_gaul(tr) # computes statistics of fitted sizes. Same as psfvary_fullstat.f in fBDSM. bmaj_a, bmaj_r, bmaj_ca, bmaj_cr, ni = _cbdsm.bstat(bmaj, None, nsig) bmin_a, bmin_r, bmin_ca, bmin_cr, ni = _cbdsm.bstat(bmin, None, nsig) bpa_a, bpa_r, bpa_ca, bpa_cr, ni = _cbdsm.bstat(bpa, None, nsig) # get subset of sources deemed to be unresolved. Same as size_ksclip_wenss.f in fBDSM. flag_unresolved = self.get_unresolved(g_gauls, img.beam, nsig, kappa2, over, img.psf_high_snr, plot) # see how much the SNR-weighted sizes of unresolved sources differ from the synthesized beam. wtsize_beam_snr = self.av_psf(g_gauls, img.beam, flag_unresolved) # filter out resolved sources tr_gaul = self.trans_gaul(g_gauls) tr = [n for i, n in enumerate(tr_gaul) if flag_unresolved[i]] g_gauls = self.trans_gaul(tr) mylogger.userinfo(mylog, 'Number of unresolved sources', str(len(g_gauls[0]))) # get a list of voronoi generators. vorogenS has values (and not None) if generators='field'. vorogenP, vorogenS = self.get_voronoi_generators(g_gauls, generators, gencode, snrcut, snrtop, snrbot, snrcutstack) mylogger.userinfo(mylog, 'Number of generators for PSF variation', str(len(vorogenP[0]))) if len(vorogenP[0]) < 3: mylog.warning('Insufficient number of generators') return mylogger.userinfo(mylog, 'Tesselating image') # group generators into tiles tile_prop = self.edit_vorogenlist(vorogenP, frac=0.9) # tesselate the image volrank, vorowts = self.tesselate(vorogenP, vorogenS, tile_prop, tess_method, tess_sc, tess_fuzzy, \ generators, gencode, image.shape) if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.volrank.fits', volrank, img, dir) tile_list, tile_coord, tile_snr = tile_prop ntile = len(tile_list) bar = statusbar.StatusBar('Determining PSF variation ............... : ', 0, ntile) mylogger.userinfo(mylog, 'Number of tiles for PSF variation', str(ntile)) # For each tile, calculate the weighted averaged psf image. Also for all the sources in the image. cdelt = list(img.wcs_obj.acdelt[0:2]) factor=3. psfimages, psfcoords, totpsfimage, psfratio, psfratio_aper = self.psf_in_tile(image, img.beam, g_gauls, \ cdelt, factor, snrcutstack, volrank, tile_prop, plot, img) npsf = len(psfimages) if opts.psf_use_shap: # use totpsfimage to get beta, centre and nmax for shapelet decomposition. Use nmax=5 or 6 mask=N.zeros(totpsfimage.shape, dtype=bool) (m1, m2, m3)=func.moment(totpsfimage, mask) betainit=sqrt(m3[0]*m3[1])*2.0 * 1.4 tshape = totpsfimage.shape cen = N.array(N.unravel_index(N.argmax(totpsfimage), tshape))+[1,1] cen = tuple(cen) nmax = 12 basis = 'cartesian' betarange = [0.5,sqrt(betainit*max(tshape))] beta, error = sh.shape_varybeta(totpsfimage, mask, basis, betainit, cen, nmax, betarange, plot) if error == 1: print ' Unable to find minimum in beta' # decompose all the psf images using the beta from above nmax=12; psf_cf=[] for i in range(npsf): psfim = psfimages[i] cf = sh.decompose_shapelets(psfim, mask, basis, beta, cen, nmax, mode='') psf_cf.append(cf) if img.opts.quiet == False: bar.increment() bar.stop() # transpose the psf image list xt, yt = N.transpose(tile_coord) tr_psf_cf = N.transpose(N.array(psf_cf)) # interpolate the coefficients across the image. Ok, interpolate in scipy for # irregular grids is crap. doesnt even pass through some of the points. # for now, fit polynomial. compress = 100.0 x, y = N.transpose(psfcoords) if len(x) < 3: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return psf_coeff_interp, xgrid, ygrid = self.interp_shapcoefs(nmax, tr_psf_cf, psfcoords, image.shape, \ compress, plot) psfshape = psfimages[0].shape skip = 5 aa = self.create_psf_grid(psf_coeff_interp, image.shape, xgrid, ygrid, skip, nmax, psfshape, \ basis, beta, cen, totpsfimage, plot) img.psf_images = aa else: if ntile < 4: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return else: # Fit stacked PSFs with Gaussians and measure aperture fluxes bm_pix = N.array([img.pixel_beam()[0]*fwsig, img.pixel_beam()[1]*fwsig, img.pixel_beam()[2]]) psf_maj = N.zeros(npsf) psf_min = N.zeros(npsf) psf_pa = N.zeros(npsf) if img.opts.quiet == False: bar.start() for i in range(ntile): psfim = psfimages[i] mask = N.zeros(psfim.shape, dtype=bool) x_ax, y_ax = N.indices(psfim.shape) maxv = N.max(psfim) p_ini = [maxv, (psfim.shape[0]-1)/2.0*1.1, (psfim.shape[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, bm_pix[1]/fwsig*1.1, bm_pix[2]*2] para, ierr = func.fit_gaus2d(psfim, p_ini, x_ax, y_ax, mask) ### first extent is major if para[3] < para[4]: para[3:5] = para[4:2:-1] para[5] += 90 ### clip position angle para[5] = divmod(para[5], 180)[1] psf_maj[i] = para[3] psf_min[i] = para[4] posang = para[5] while posang >= 180.0: posang -= 180.0 psf_pa[i] = posang if img.opts.quiet == False: bar.increment() bar.stop() # Interpolate Gaussian parameters if img.aperture == None: psf_maps = [psf_maj, psf_min, psf_pa, psfratio] else: psf_maps = [psf_maj, psf_min, psf_pa, psfratio, psfratio_aper] nimgs = len(psf_maps) bar = statusbar.StatusBar('Interpolating PSF images ................ : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.interp_prop), psf_maps, itertools.repeat(psfcoords), itertools.repeat(image.shape)), numcores=opts.ncores, bar=bar) if img.aperture == None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Smooth if desired if img.opts.psf_smooth != None: sm_scale = img.opts.psf_smooth / img.pix2beam([1.0, 1.0, 0.0])[0] / 3600.0 # pixels if img.opts.aperture == None: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int] else: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int] nimgs = len(psf_maps) bar = statusbar.StatusBar('Smoothing PSF images .................... : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.blur_image), psf_maps, itertools.repeat(sm_scale)), numcores=opts.ncores, bar=bar) if img.aperture == None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Make sure all smoothed, interpolated images are ndarrays psf_maj_int = N.array(psf_maj_int) psf_min_int = N.array(psf_min_int) psf_pa_int = N.array(psf_pa_int) psf_ratio_int = N.array(psf_ratio_int) if img.aperture == None: psf_ratio_aper_int = N.zeros(psf_maj_int.shape, dtype=N.float32) else: psf_ratio_aper_int = N.array(psf_ratio_aper_int, dtype=N.float32) # Blank with NaNs if needed mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) psf_maj_int[pix_masked] = N.nan psf_min_int[pix_masked] = N.nan psf_pa_int[pix_masked] = N.nan psf_ratio_int[pix_masked] = N.nan psf_ratio_aper_int[pix_masked] = N.nan # Store interpolated images. The major and minor axis images are # the sigma in units of arcsec, the PA image in units of degrees east of # north, the ratio images in units of 1/beam. img.psf_vary_maj_arr = psf_maj_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_min_arr = psf_min_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_pa_arr = psf_pa_int img.psf_vary_ratio_arr = psf_ratio_int # in 1/beam img.psf_vary_ratio_aper_arr = psf_ratio_aper_int # in 1/beam if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_maj.fits', img.psf_vary_maj_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_min.fits', img.psf_vary_min_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_pa.fits', img.psf_vary_pa_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio.fits', img.psf_vary_ratio_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio_aper.fits', img.psf_vary_ratio_aper_arr, img, dir) # Loop through source and Gaussian lists and deconvolve the sizes using appropriate beam bar2 = statusbar.StatusBar('Correcting deconvolved source sizes ..... : ', 0, img.nsrc) if img.opts.quiet == False: bar2.start() for src in img.sources: src_pos = img.sky2pix(src.posn_sky_centroid) src_pos_int = (int(src_pos[0]), int(src_pos[1])) gaus_c = img.gaus2pix(src.size_sky, src.posn_sky_centroid) gaus_bm = [psf_maj_int[src_pos_int]*fwsig, psf_min_int[src_pos_int]*fwsig, psf_pa_int[src_pos_int]*fwsig] gaus_dc, err = func.deconv2(gaus_bm, gaus_c) src.deconv_size_sky = img.pix2gaus(gaus_dc, src_pos) src.deconv_size_skyE = [0.0, 0.0, 0.0] for g in src.gaussians: gaus_c = img.gaus2pix(g.size_sky, src.posn_sky_centroid) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) g.deconv_size_sky = img.pix2gaus(gaus_dc, g.centre_pix) g.deconv_size_skyE = [0.0, 0.0, 0.0] if img.opts.quiet == False: bar2.spin() if img.opts.quiet == False: bar2.increment() bar2.stop() img.completed_Ops.append('psf_vary')
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Wavelet") if img.opts.atrous_do: if img.nisl == 0: mylog.warning( "No islands found. Skipping wavelet decomposition.") img.completed_Ops.append('wavelet_atrous') return mylog.info( "Decomposing gaussian residual image into a-trous wavelets") bdir = img.basedir + '/wavelet/' if img.opts.output_all: if not os.path.isdir(bdir): os.makedirs(bdir) if not os.path.isdir(bdir + '/residual/'): os.makedirs(bdir + '/residual/') if not os.path.isdir(bdir + '/model/'): os.makedirs(bdir + '/model/') dobdsm = img.opts.atrous_bdsm_do filter = { 'tr': { 'size': 3, 'vec': [1. / 4, 1. / 2, 1. / 4], 'name': 'Triangle' }, 'b3': { 'size': 5, 'vec': [1. / 16, 1. / 4, 3. / 8, 1. / 4, 1. / 16], 'name': 'B3 spline' } } if dobdsm: wchain, wopts = self.setpara_bdsm(img) n, m = img.ch0_arr.shape # Calculate residual image that results from normal (non-wavelet) Gaussian fitting Op_make_residimage()(img) resid = img.resid_gaus_arr lpf = img.opts.atrous_lpf if lpf not in ['b3', 'tr']: lpf = 'b3' jmax = img.opts.atrous_jmax l = len(filter[lpf]['vec'] ) # 1st 3 is arbit and 2nd 3 is whats expected for a-trous if jmax < 1 or jmax > 15: # determine jmax # Check if largest island size is # smaller than 1/3 of image size. If so, use it to determine jmax. min_size = min(resid.shape) max_isl_shape = (0, 0) for isl in img.islands: if isl.image.shape[0] * isl.image.shape[1] > max_isl_shape[ 0] * max_isl_shape[1]: max_isl_shape = isl.image.shape if max_isl_shape != ( 0, 0) and min(max_isl_shape) < min(resid.shape) / 3.0: min_size = min(max_isl_shape) * 4.0 else: min_size = min(resid.shape) jmax = int( floor( log((min_size / 3.0 * 3.0 - l) / (l - 1) + 1) / log(2.0) + 1.0)) + 1 if min_size * 0.55 <= (l + (l - 1) * (2**(jmax) - 1)): jmax = jmax - 1 img.wavelet_lpf = lpf img.wavelet_jmax = jmax mylog.info("Using " + filter[lpf]['name'] + ' filter with J_max = ' + str(jmax)) img.atrous_islands = [] img.atrous_gaussians = [] img.atrous_sources = [] img.atrous_opts = [] img.resid_wavelets_arr = cp(img.resid_gaus_arr) im_old = img.resid_wavelets_arr total_flux = 0.0 ntot_wvgaus = 0 stop_wav = False pix_masked = N.where(N.isnan(resid) == True) jmin = 1 if img.opts.ncores is None: numcores = 1 else: numcores = img.opts.ncores for j in range(jmin, jmax + 1): # extra +1 is so we can do bdsm on cJ as well mylogger.userinfo(mylog, "\nWavelet scale #" + str(j)) im_new = self.atrous(im_old, filter[lpf]['vec'], lpf, j, numcores=numcores, use_scipy_fft=img.opts.use_scipy_fft) im_new[ pix_masked] = N.nan # since fftconvolve wont work with blanked pixels if img.opts.atrous_sum: w = im_new else: w = im_old - im_new im_old = im_new suffix = 'w' + ` j ` filename = img.imagename + '.atrous.' + suffix + '.fits' if img.opts.output_all: func.write_image_to_file('fits', filename, w, img, bdir) mylog.info('%s %s' % ('Wrote ', img.imagename + '.atrous.' + suffix + '.fits')) # now do bdsm on each wavelet image. if dobdsm: wopts['filename'] = filename wopts['basedir'] = bdir box = img.rms_box[0] y1 = (l + (l - 1) * (2**(j - 1) - 1)) bs = max(5 * y1, box) # changed from 10 to 5 if bs > min(n, m) / 2: wopts['rms_map'] = False wopts['mean_map'] = 'const' wopts['rms_box'] = None else: wopts['rms_box'] = (bs, bs / 3) if hasattr(img, '_adapt_rms_isl_pos'): bs_bright = max(5 * y1, img.rms_box_bright[0]) if bs_bright < bs / 1.5: wopts['adaptive_rms_box'] = True wopts['rms_box_bright'] = (bs_bright, bs_bright / 3) else: wopts['adaptive_rms_box'] = False if j <= 3: wopts['ini_gausfit'] = 'default' else: wopts['ini_gausfit'] = 'nobeam' wid = (l + (l - 1) * (2**(j - 1) - 1)) # / 3.0 b1, b2 = img.pixel_beam()[0:2] b1 = b1 * fwsig b2 = b2 * fwsig cdelt = img.wcs_obj.acdelt[:2] wimg = Image(wopts) wimg.beam = (sqrt(wid * wid + b1 * b1) * cdelt[0] * 2.0, sqrt(wid * wid + b2 * b2) * cdelt[1] * 2.0, 0.0) wimg.orig_beam = img.beam wimg.pixel_beam = img.pixel_beam wimg.pixel_beamarea = img.pixel_beamarea wimg.log = 'Wavelet.' wimg.basedir = img.basedir wimg.extraparams['bbsprefix'] = suffix wimg.extraparams['bbsname'] = img.imagename + '.wavelet' wimg.extraparams['bbsappend'] = True wimg.bbspatchnum = img.bbspatchnum wimg.waveletimage = True wimg.j = j if hasattr(img, '_adapt_rms_isl_pos'): wimg._adapt_rms_isl_pos = img._adapt_rms_isl_pos self.init_image_simple(wimg, img, w, '.atrous.' + suffix) for op in wchain: op(wimg) gc.collect() if isinstance(op, Op_islands) and img.opts.atrous_orig_isl: if wimg.nisl > 0: # Find islands that do not share any pixels with # islands in original ch0 image. good_isl = [] # Make original rank image boolean; rank counts from 0, with -1 being # outside any island orig_rankim_bool = N.array(img.pyrank + 1, dtype=bool) # Multiply rank images old_islands = orig_rankim_bool * (wimg.pyrank + 1) - 1 # Exclude islands that don't overlap with a ch0 island. valid_ids = set(old_islands.flatten()) for idx, wvisl in enumerate(wimg.islands): if idx in valid_ids: wvisl.valid = True good_isl.append(wvisl) else: wvisl.valid = False wimg.islands = good_isl wimg.nisl = len(good_isl) mylogger.userinfo(mylog, "Number of islands found", '%i' % wimg.nisl) # Renumber islands: for wvindx, wvisl in enumerate(wimg.islands): wvisl.island_id = wvindx if isinstance(op, Op_gausfit): # If opts.atrous_orig_isl then exclude Gaussians outside of # the original ch0 islands nwvgaus = 0 if img.opts.atrous_orig_isl: gaul = wimg.gaussians tot_flux = 0.0 if img.ngaus == 0: gaus_id = -1 else: gaus_id = img.gaussians[-1].gaus_num wvgaul = [] for g in gaul: if not hasattr(g, 'valid'): g.valid = False if not g.valid: try: isl_id = img.pyrank[ int(g.centre_pix[0] + 1), int(g.centre_pix[1] + 1)] except IndexError: isl_id = -1 if isl_id >= 0: isl = img.islands[isl_id] gcenter = (g.centre_pix[0] - isl.origin[0], g.centre_pix[1] - isl.origin[1]) if not isl.mask_active[gcenter]: gaus_id += 1 gcp = Gaussian( img, g.parameters[:], isl.island_id, gaus_id) gcp.gaus_num = gaus_id gcp.wisland_id = g.island_id gcp.jlevel = j g.valid = True isl.gaul.append(gcp) isl.ngaus += 1 img.gaussians.append(gcp) nwvgaus += 1 tot_flux += gcp.total_flux else: g.valid = False g.jlevel = 0 else: g.valid = False g.jlevel = 0 vg = [] for g in wimg.gaussians: if g.valid: vg.append(g) wimg.gaussians = vg mylogger.userinfo( mylog, "Number of valid wavelet Gaussians", str(nwvgaus)) else: # Keep all Gaussians and merge islands that overlap tot_flux = check_islands_for_overlap(img, wimg) # Now renumber the islands and adjust the rank image before going to next wavelet image renumber_islands(img) total_flux += tot_flux if img.opts.interactive and has_pl: dc = '\033[34;1m' nc = '\033[0m' print dc + '--> Displaying islands and rms image...' + nc if max(wimg.ch0_arr.shape) > 4096: print dc + '--> Image is large. Showing islands only.' + nc wimg.show_fit(rms_image=False, mean_image=False, ch0_image=False, ch0_islands=True, gresid_image=False, sresid_image=False, gmodel_image=False, smodel_image=False, pyramid_srcs=False) else: wimg.show_fit() prompt = dc + "Press enter to continue or 'q' stop fitting wavelet images : " + nc answ = raw_input_no_history(prompt) while answ != '': if answ == 'q': img.wavelet_jmax = j stop_wav = True break answ = raw_input_no_history(prompt) if len(wimg.gaussians) > 0: img.resid_wavelets_arr = self.subtract_wvgaus( img.opts, img.resid_wavelets_arr, wimg.gaussians, wimg.islands) if img.opts.atrous_sum: im_old = self.subtract_wvgaus( img.opts, im_old, wimg.gaussians, wimg.islands) if stop_wav == True: break pyrank = N.zeros(img.pyrank.shape, dtype=N.int32) for i, isl in enumerate(img.islands): isl.island_id = i for g in isl.gaul: g.island_id = i for dg in isl.dgaul: dg.island_id = i pyrank[isl.bbox] += N.invert(isl.mask_active) * (i + 1) pyrank -= 1 # align pyrank values with island ids and set regions outside of islands to -1 img.pyrank = pyrank pdir = img.basedir + '/misc/' img.ngaus += ntot_wvgaus img.total_flux_gaus += total_flux mylogger.userinfo(mylog, "Total flux density in model on all scales", '%.3f Jy' % img.total_flux_gaus) if img.opts.output_all: func.write_image_to_file('fits', img.imagename + '.atrous.cJ.fits', im_new, img, bdir) mylog.info('%s %s' % ('Wrote ', img.imagename + '.atrous.cJ.fits')) func.write_image_to_file( 'fits', img.imagename + '.resid_wavelets.fits', (img.ch0_arr - img.resid_gaus_arr + img.resid_wavelets_arr), img, bdir + '/residual/') mylog.info('%s %s' % ('Wrote ', img.imagename + '.resid_wavelets.fits')) func.write_image_to_file( 'fits', img.imagename + '.model_wavelets.fits', (img.resid_gaus_arr - img.resid_wavelets_arr), img, bdir + '/model/') mylog.info('%s %s' % ('Wrote ', img.imagename + '.model_wavelets.fits')) img.completed_Ops.append('wavelet_atrous')
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Islands") opts = img.opts minsize = opts.minpix_isl if minsize == None: minsize = int(img.pixel_beamarea()/3.0) # 1/3 of beam area in pixels if minsize < 6: minsize = 6 # Need at least 6 pixels to obtain good fits mylogger.userinfo(mylog, "Minimum number of pixels per island", '%i' % minsize) img.minpix_isl = minsize if opts.detection_image != '': # Use a different image for island detection. The detection # image and the measurement image must have the same shape # and be registered. Otherwise, one could reproject the # detection image using, e.g., the Kapteyn package. # # First, set up up an Image object and run a limited # op_chain. from . import _run_op_list mylogger.userinfo(mylog, "\nDetermining islands from detection image") det_chain, det_opts = self.setpara_bdsm(img, opts.detection_image) det_img = Image(det_opts) det_img.log = 'Detection image' success = _run_op_list(det_img, det_chain) if not success: return # Check that the ch0 images are the same size ch0_map = img.ch0_arr det_ch0_map = det_img.ch0_arr det_shape = det_ch0_map.shape ch0_shape = ch0_map.shape if det_shape != ch0_shape: raise RuntimeError("Detection image shape does not match that of input image.") # Run through islands and correct the image and rms, mean and max values img.island_labels = det_img.island_labels corr_islands = [] mean_map = img.mean_arr rms_map = img.rms_arr for i, isl in enumerate(det_img.islands): islcp = isl.copy(img.pixel_beamarea(), image=ch0_map[isl.bbox], mean=mean_map[isl.bbox], rms=rms_map[isl.bbox]) islcp.island_id = i corr_islands.append(islcp) img.islands = corr_islands img.nisl = len(img.islands) img.pyrank = det_img.pyrank img.minpix_isl = det_img.minpix_isl mylogger.userinfo(mylog, "\nContinuing processing using primary image") else: if opts.src_ra_dec != None: mylogger.userinfo(mylog, "Constructing islands at user-supplied source locations") img.islands = self.coords_to_isl(img, opts) else: img.islands = self.ndimage_alg(img, opts) img.nisl = len(img.islands) mylogger.userinfo(mylog, "Number of islands found", '%i' % len(img.islands)) ch0_map = img.ch0_arr ch0_shape = ch0_map.shape pyrank = N.zeros(ch0_shape, dtype=N.int32) - 1 for i, isl in enumerate(img.islands): isl.island_id = i if i == 0: pyrank[isl.bbox] = N.invert(isl.mask_active) - 1 else: pyrank[isl.bbox] = N.invert(isl.mask_active) * i - isl.mask_active if opts.output_all: write_islands(img) if opts.savefits_rankim: func.write_image_to_file(img.use_io, img.imagename + '_pyrank.fits', pyrank, img) img.pyrank = pyrank img.completed_Ops.append('islands') return img
def export_image(img, outfile=None, img_format='fits', pad_image = False, img_type='gaus_resid', mask_dilation=0, clobber=False): """Write an image to a file. Returns True if successful, False if not. outfile - name of resulting file; if None, file is named automatically. img_type - type of image to export; see below img_format - format of resulting file: 'fits' or 'casa' incl_wavelet - include wavelet Gaussians in model and residual images? clobber - overwrite existing file? The following images may be exported: 'ch0' - image used for source detection 'rms' - rms map image 'mean' - mean map image 'pi' - polarized intensity image 'gaus_resid' - Gaussian model residual image 'gaus_model' - Gaussian model image 'shap_resid' - Shapelet model residual image 'shap_model' - Shapelet model image 'psf_major' - PSF major axis FWHM image (FWHM in arcsec) 'psf_minor' - PSF minor axis FWHM image (FWHM in arcsec) 'psf_pa' - PSF position angle image (degrees east of north) 'psf_ratio' - PSF peak-to-total flux ratio (in units of 1/beam) 'psf_ratio_aper' - PSF peak-to-aperture flux ratio (in units of 1/beam) 'island_mask' - Island mask image (0 = outside island, 1 = inside island) """ import os import functions as func from const import fwsig import mylogger mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"ExportImage") # First some checking: if not 'gausfit' in img.completed_Ops and 'gaus' in img_type: print '\033[91mERROR\033[0m: Gaussians have not been fit. Please run process_image first.' return False elif not 'shapelets' in img.completed_Ops and 'shap' in img_type: print '\033[91mERROR\033[0m: Shapelets have not been fit. Please run process_image first.' return False elif not 'polarisation' in img.completed_Ops and 'pi' in img_type: print '\033[91mERROR\033[0m: Polarization properties have not been calculated. Please run process_image first.' return False elif not 'psf_vary' in img.completed_Ops and 'psf' in img_type: print '\033[91mERROR\033[0m: PSF variations have not been calculated. Please run process_image first.' return False elif not 'collapse' in img.completed_Ops and 'ch0' in img_type: print '\033[91mERROR\033[0m: ch0 image has not been calculated. Please run process_image first.' return False elif not 'rmsimage' in img.completed_Ops and ('rms' in img_type or 'mean' in img_type): print '\033[91mERROR\033[0m: Mean and rms maps have not been calculated. Please run process_image first.' return False elif not 'make_residimage' in img.completed_Ops and ('resid' in img_type or 'model' in img_type): print '\033[91mERROR\033[0m: Residual and model maps have not been calculated. Please run process_image first.' return False format = img_format.lower() if (format in ['fits', 'casa']) == False: print '\033[91mERROR\033[0m: img_format must be "fits" or "casa"' return False filename = outfile if filename is None or filename == '': filename = img.imagename + '_' + img_type + '.' + format if os.path.exists(filename) and clobber == False: print '\033[91mERROR\033[0m: File exists and clobber = False.' return False if format == 'fits': use_io = 'fits' if format == 'casa': use_io = 'rap' bdir = '' try: if img_type == 'ch0': func.write_image_to_file(use_io, filename, img.ch0_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'rms': func.write_image_to_file(use_io, filename, img.rms_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'mean': func.write_image_to_file(use_io, filename, img.mean_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'pi': func.write_image_to_file(use_io, filename, img.ch0_pi_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_major': func.write_image_to_file(use_io, filename, img.psf_vary_maj_arr*fwsig, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_minor': func.write_image_to_file(use_io, filename, img.psf_vary_min_arr*fwsig, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_pa': func.write_image_to_file(use_io, filename, img.psf_vary_pa_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_ratio': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_ratio_aper': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_aper_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'gaus_resid': im = img.resid_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, pad_image, clobber=clobber) elif img_type == 'gaus_model': im = img.model_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, pad_image, clobber=clobber) elif img_type == 'shap_resid': func.write_image_to_file(use_io, filename, img.resid_shap_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'shap_model': func.write_image_to_file(use_io, filename, img.model_shap_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'island_mask': import numpy as N import scipy.ndimage as nd island_mask_bool = img.pyrank + 1 > 0 if mask_dilation > 0: # Dilate the mask by specified number of iterations island_mask_bool = nd.binary_dilation(island_mask_bool, iterations=mask_dilation) # Perform a binary closing to remove small holes/gaps. The # structure array is chosen to be about the size of the # beam (assuming a normally sampled psf), so that holes/gaps # smaller than the beam are removed. pbeam = int(round(img.beam2pix(img.beam)[0] * 1.5)) island_mask_bool = nd.binary_closing(island_mask_bool, structure=N.ones((pbeam, pbeam))) # Check for telescope, needed for CASA clean masks if img._telescope is None: print '\033[91mWARNING\033[0m: Telescope is unknown. Mask may not work correctly in CASA.' island_mask = N.array(island_mask_bool, dtype=N.float32) func.write_image_to_file(use_io, filename, island_mask, img, bdir, pad_image, clobber=clobber, is_mask=True) else: print "\n\033[91mERROR\033[0m: img_type not recognized." return False if filename == 'SAMP': print '--> Image sent to SMAP hub' else: print '--> Wrote file ' + repr(filename) if use_io == 'rap': # remove the temporary fits file used as a pyrap template import os os.remove(filename+'.fits') return True except RuntimeError, err: # Catch and log error mylog.error(str(err)) # Re-throw error if the user is not in the interactive shell if img._is_interactive_shell: return False else: raise
def export_image(img, outfile=None, img_format='fits', pad_image=False, img_type='gaus_resid', mask_dilation=0, clobber=False): """Write an image to a file. Returns True if successful, False if not. outfile - name of resulting file; if None, file is named automatically. img_type - type of image to export; see below img_format - format of resulting file: 'fits' or 'casa' incl_wavelet - include wavelet Gaussians in model and residual images? clobber - overwrite existing file? The following images may be exported: 'ch0' - image used for source detection 'rms' - rms map image 'mean' - mean map image 'pi' - polarized intensity image 'gaus_resid' - Gaussian model residual image 'gaus_model' - Gaussian model image 'shap_resid' - Shapelet model residual image 'shap_model' - Shapelet model image 'psf_major' - PSF major axis FWHM image (FWHM in arcsec) 'psf_minor' - PSF minor axis FWHM image (FWHM in arcsec) 'psf_pa' - PSF position angle image (degrees east of north) 'psf_ratio' - PSF peak-to-total flux ratio (in units of 1/beam) 'psf_ratio_aper' - PSF peak-to-aperture flux ratio (in units of 1/beam) 'island_mask' - Island mask image (0 = outside island, 1 = inside island) """ import os import functions as func from const import fwsig import mylogger mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "ExportImage") # First some checking: if not 'gausfit' in img.completed_Ops and 'gaus' in img_type: print '\033[91mERROR\033[0m: Gaussians have not been fit. Please run process_image first.' return False elif not 'shapelets' in img.completed_Ops and 'shap' in img_type: print '\033[91mERROR\033[0m: Shapelets have not been fit. Please run process_image first.' return False elif not 'polarisation' in img.completed_Ops and 'pi' in img_type: print '\033[91mERROR\033[0m: Polarization properties have not been calculated. Please run process_image first.' return False elif not 'psf_vary' in img.completed_Ops and 'psf' in img_type: print '\033[91mERROR\033[0m: PSF variations have not been calculated. Please run process_image first.' return False elif not 'collapse' in img.completed_Ops and 'ch0' in img_type: print '\033[91mERROR\033[0m: ch0 image has not been calculated. Please run process_image first.' return False elif not 'rmsimage' in img.completed_Ops and ('rms' in img_type or 'mean' in img_type): print '\033[91mERROR\033[0m: Mean and rms maps have not been calculated. Please run process_image first.' return False elif not 'make_residimage' in img.completed_Ops and ('resid' in img_type or 'model' in img_type): print '\033[91mERROR\033[0m: Residual and model maps have not been calculated. Please run process_image first.' return False format = img_format.lower() if (format in ['fits', 'casa']) == False: print '\033[91mERROR\033[0m: img_format must be "fits" or "casa"' return False filename = outfile if filename is None or filename == '': filename = img.imagename + '_' + img_type + '.' + format if os.path.exists(filename) and clobber == False: print '\033[91mERROR\033[0m: File exists and clobber = False.' return False if format == 'fits': use_io = 'fits' if format == 'casa': use_io = 'rap' bdir = '' try: if img_type == 'ch0': func.write_image_to_file(use_io, filename, img.ch0_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'rms': func.write_image_to_file(use_io, filename, img.rms_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'mean': func.write_image_to_file(use_io, filename, img.mean_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'pi': func.write_image_to_file(use_io, filename, img.ch0_pi_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_major': func.write_image_to_file(use_io, filename, img.psf_vary_maj_arr * fwsig, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_minor': func.write_image_to_file(use_io, filename, img.psf_vary_min_arr * fwsig, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_pa': func.write_image_to_file(use_io, filename, img.psf_vary_pa_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_ratio': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'psf_ratio_aper': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_aper_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'gaus_resid': im = img.resid_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, pad_image, clobber=clobber) elif img_type == 'gaus_model': im = img.model_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, pad_image, clobber=clobber) elif img_type == 'shap_resid': func.write_image_to_file(use_io, filename, img.resid_shap_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'shap_model': func.write_image_to_file(use_io, filename, img.model_shap_arr, img, bdir, pad_image, clobber=clobber) elif img_type == 'island_mask': import numpy as N import scipy.ndimage as nd island_mask_bool = img.pyrank + 1 > 0 if mask_dilation > 0: # Dilate the mask by specified number of iterations island_mask_bool = nd.binary_dilation(island_mask_bool, iterations=mask_dilation) # Perform a binary closing to remove small holes/gaps. The # structure array is chosen to be about the size of the # beam (assuming a normally sampled psf), so that holes/gaps # smaller than the beam are removed. pbeam = int(round(img.beam2pix(img.beam)[0] * 1.5)) island_mask_bool = nd.binary_closing(island_mask_bool, structure=N.ones( (pbeam, pbeam))) # Check for telescope, needed for CASA clean masks if img._telescope is None: print '\033[91mWARNING\033[0m: Telescope is unknown. Mask may not work correctly in CASA.' island_mask = N.array(island_mask_bool, dtype=N.float32) func.write_image_to_file(use_io, filename, island_mask, img, bdir, pad_image, clobber=clobber, is_mask=True) else: print "\n\033[91mERROR\033[0m: img_type not recognized." return False if filename == 'SAMP': print '--> Image sent to SMAP hub' else: print '--> Wrote file ' + repr(filename) if use_io == 'rap': # remove the temporary fits file used as a pyrap template import os os.remove(filename + '.fits') return True except RuntimeError, err: # Catch and log error mylog.error(str(err)) # Re-throw error if the user is not in the interactive shell if img._is_interactive_shell: return False else: raise
def export_image(img, outfile=None, img_format='fits', img_type='gaus_resid', clobber=False): """Write an image to a file. Returns True if successful, False if not. outfile - name of resulting file; if None, file is named automatically. img_type - type of image to export; see below img_format - format of resulting file: 'fits' or 'casa' incl_wavelet - include wavelet Gaussians in model and residual images? clobber - overwrite existing file? The following images may be exported: 'ch0' - image used for source detection 'rms' - rms map image 'mean' - mean map image 'pi' - polarized intensity image 'gaus_resid' - Gaussian model residual image 'gaus_model' - Gaussian model image 'shap_resid' - Shapelet model residual image 'shap_model' - Shapelet model image 'psf_major' - PSF major axis FWHM image (FWHM in arcsec) 'psf_minor' - PSF minor axis FWHM image (FWHM in arcsec) 'psf_pa' - PSF position angle image (degrees east of north) 'psf_ratio' - PSF peak-to-total flux ratio (in units of 1/beam) 'psf_ratio_aper' - PSF peak-to-aperture flux ratio (in units of 1/beam) """ import os import functions as func from const import fwsig import mylogger mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"ExportImage") # First some checking: if not 'gausfit' in img.completed_Ops and 'gaus' in img_type: print '\033[91mERROR\033[0m: Gaussians have not been fit. Please run process_image first.' return False elif not 'shapelets' in img.completed_Ops and 'shap' in img_type: print '\033[91mERROR\033[0m: Shapelets have not been fit. Please run process_image first.' return False elif not 'polarisation' in img.completed_Ops and 'pi' in img_type: print '\033[91mERROR\033[0m: Polarization properties have not been calculated. Please run process_image first.' return False elif not 'psf_vary' in img.completed_Ops and 'psf' in img_type: print '\033[91mERROR\033[0m: PSF variations have not been calculated. Please run process_image first.' return False elif not 'collapse' in img.completed_Ops and 'ch0' in img_type: print '\033[91mERROR\033[0m: ch0 image has not been calculated. Please run process_image first.' return False elif not 'rmsimage' in img.completed_Ops and ('rms' in img_type or 'mean' in img_type): print '\033[91mERROR\033[0m: Mean and rms maps have not been calculated. Please run process_image first.' return False elif not 'make_residimage' in img.completed_Ops and ('resid' in img_type or 'model' in img_type): print '\033[91mERROR\033[0m: Residual and model maps have not been calculated. Please run process_image first.' return False format = img_format.lower() if (format in ['fits', 'casa']) == False: print '\033[91mERROR\033[0m: img_format must be "fits" or "casa"' return False if format == 'casa': print "\033[91mERROR\033[0m: Only img_format = 'fits' is supported at the moment" return False filename = outfile if filename == None or filename == '': filename = img.imagename + '_' + img_type + '.' + format if os.path.exists(filename) and clobber == False: print '\033[91mERROR\033[0m: File exists and clobber = False.' return False if format == 'fits': use_io = 'fits' if format == 'casa': use_io = 'rap' bdir = '' try: if img_type == 'ch0': func.write_image_to_file(use_io, filename, img.ch0_arr, img, bdir, clobber=clobber) elif img_type == 'rms': func.write_image_to_file(use_io, filename, img.rms_arr, img, bdir, clobber=clobber) elif img_type == 'mean': func.write_image_to_file(use_io, filename, img.mean_arr, img, bdir, clobber=clobber) elif img_type == 'pi': func.write_image_to_file(use_io, filename, img.ch0_pi_arr, img, bdir, clobber=clobber) elif img_type == 'psf_major': func.write_image_to_file(use_io, filename, img.psf_vary_maj_arr*fwsig, img, bdir, clobber=clobber) elif img_type == 'psf_minor': func.write_image_to_file(use_io, filename, img.psf_vary_min_arr*fwsig, img, bdir, clobber=clobber) elif img_type == 'psf_pa': func.write_image_to_file(use_io, filename, img.psf_vary_pa_arr, img, bdir, clobber=clobber) elif img_type == 'psf_ratio': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_arr, img, bdir, clobber=clobber) elif img_type == 'psf_ratio_aper': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_aper_arr, img, bdir, clobber=clobber) elif img_type == 'gaus_resid': im = img.resid_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, clobber=clobber) elif img_type == 'gaus_model': im = img.model_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, clobber=clobber) elif img_type == 'shap_resid': func.write_image_to_file(use_io, filename, img.resid_shap_arr, img, bdir, clobber=clobber) elif img_type == 'shap_model': func.write_image_to_file(use_io, filename, img.model_shap_arr, img, bdir, clobber=clobber) else: print "\n\033[91mERROR\033[0m: img_type not recognized." return False if filename == 'SAMP': print '--> Image sent to SMAP hub' else: print '--> Wrote file ' + repr(filename) return True except RuntimeError, err: # Catch and log error mylog.error(str(err)) # Re-throw error if the user is not in the interactive shell if img._is_interactive_shell: return False else: raise
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Collapse") if img.opts.polarisation_do: pols = ['I', 'Q', 'U', 'V'] # make sure I is done first else: pols = ['I'] # assume I is always present img.ch0_Q_arr = None img.ch0_U_arr = None img.ch0_V_arr = None if img.shape[1] > 1: c_mode = img.opts.collapse_mode chan0 = img.opts.collapse_ch0 c_list = img.opts.collapse_av c_wts = img.opts.collapse_wt if c_list == []: c_list = N.arange(img.shape[1]) if len(c_list) == 1: c_mode = 'single' chan0 = c_list[0] img.collapse_ch0 = chan0 ch0sh = img.image_arr.shape[2:] if img.opts.polarisation_do: ch0images = ['ch0_arr', 'ch0_Q_arr', 'ch0_U_arr', 'ch0_V_arr'] else: ch0images = ['ch0_arr'] # assume all Stokes images have the same blank pixels as I: blank = N.isnan(img.image_arr[0]) hasblanks = blank.any() if img.opts.kappa_clip is None: kappa = -img.pixel_beamarea() else: kappa = img.opts.kappa_clip mean, rms, cmean, crms = chan_stats(img, kappa) img.channel_mean = mean img.channel_rms = rms img.channel_clippedmean = cmean img.channel_clippedrms = crms for ipol, pol in enumerate(pols): if c_mode == 'single': if pol == 'I': ch0 = img.image_arr[0, chan0] img.ch0_arr = ch0 mylogger.userinfo(mylog, 'Source extraction will be ' \ 'done on channel', '%i (%.3f MHz)' % \ (chan0, img.frequency/1e6)) else: ch0[:] = img.image_arr[ipol, chan0][:] img.__setattr__(ch0images[ipol][:], ch0) if c_mode == 'average': if not hasblanks: if pol == 'I': ch0, wtarr = avspc_direct(c_list, img.image_arr[0], img.channel_clippedrms, c_wts) else: # use wtarr from the I image, which is always collapsed first ch0, wtarr = avspc_direct(c_list, img.image_arr[ipol], img.channel_clippedrms, c_wts, wtarr=wtarr) else: if pol == 'I': ch0, wtarr = avspc_blanks(c_list, img.image_arr[0], img.channel_clippedrms, c_wts) else: # use wtarr from the I image, which is always collapsed first ch0, wtarr = avspc_blanks(c_list, img.image_arr[ipol], img.channel_clippedrms, c_wts, wtarr=wtarr) img.__setattr__(ch0images[ipol][:], ch0) if pol == 'I': img.avspc_wtarr = wtarr init_freq_collapse(img, wtarr) if c_wts == 'unity': mylogger.userinfo(mylog, 'Channels averaged with '\ 'uniform weights') else: mylogger.userinfo(mylog, 'Channels averaged with '\ 'weights=(1/rms)^2') mylogger.userinfo(mylog, 'Source extraction will be '\ 'done on averaged ("ch0") image') mylogger.userinfo(mylog, 'Frequency of averaged '\ 'image', '%.3f MHz' % \ (img.frequency/1e6,)) str1 = " ".join(str(n) for n in c_list) mylog.debug('%s %s' % ('Channels averaged : ', str1)) str1 = " ".join(["%9.4e" % n for n in wtarr]) mylog.debug('%s %s %s' % ('Channel weights : ', str1, '; unity=zero if c_wts="rms"')) if img.opts.output_all: func.write_image_to_file( img.use_io, img.imagename + '.ch0_' + pol + '.fits', ch0, img) mylog.debug('%s %s ' % ('Writing file ', img.imagename + '.ch0_' + pol + '.fits')) else: # Only one channel in image image = img.image_arr img.ch0_arr = image[0, 0] mylogger.userinfo(mylog, 'Frequency of image', '%.3f MHz' % (img.frequency / 1e6, )) if img.opts.polarisation_do: for pol in pols[1:]: if pol == 'Q': img.ch0_Q_arr = image[1, 0][:] if pol == 'U': img.ch0_U_arr = image[2, 0][:] if pol == 'V': img.ch0_V_arr = image[3, 0][:] # create mask if needed (assume all pols have the same mask as I) image = img.ch0_arr mask = N.isnan(image) img.blankpix = N.sum(mask) frac_blank = round( float(img.blankpix) / float(image.shape[0] * image.shape[1]), 3) mylogger.userinfo( mylog, "Number of blank pixels", str(img.blankpix) + ' (' + str(frac_blank * 100.0) + '%)') # Check whether the input image might be an AWimage. If so, and there # are no blank pixels, tell the user that they might to set blank_limit. # Once the AWimager incorporates blanking, this check can be removed. if img.opts.blank_limit is None and ( img.blankpix == 0 and ('restored' in img.filename.lower() or 'corr' in img.filename.lower() or 'aw' in img.filename.lower())): check_low = True else: check_low = False if img.opts.blank_limit is not None or check_low: import scipy import sys if check_low: threshold = 1e-5 else: threshold = img.opts.blank_limit mylogger.userinfo( mylog, "Blanking pixels with values " "below %.1e Jy/beam" % (threshold, )) bad = (abs(image) < threshold) original_stdout = sys.stdout # keep a reference to STDOUT sys.stdout = func.NullDevice() # redirect the real STDOUT count = scipy.signal.convolve2d(bad, N.ones((3, 3)), mode='same') sys.stdout = original_stdout # turn STDOUT back on mask_low = (count >= 5) if check_low: nlow = len(N.where(mask_low)[0]) if nlow / float(image.shape[0] * image.shape[1]) > 0.2: mylog.warn( 'A significant area of the image has very low values. To blank\nthese regions (e.g., because they are outside the primary beam), set the\nblank_limit option (values of 1e-5 to 1e-4 Jy/beam usually work well).\n' ) else: image[N.where(mask_low)] = N.nan mask = N.isnan(image) img.blankpix = N.sum(mask) frac_blank = round( float(img.blankpix) / float(image.shape[0] * image.shape[1]), 3) mylogger.userinfo( mylog, "Total number of blanked pixels", str(img.blankpix) + ' (' + str(frac_blank * 100.0) + '%)') masked = mask.any() img.masked = masked if masked: img.mask_arr = mask else: img.mask_arr = None if img.blankpix == image.shape[0] * image.shape[1]: # ALL pixels are blanked! raise RuntimeError('All pixels in the image are blanked.') img.completed_Ops.append('collapse')
def __call__(self, img): if img.opts.psf_vary_do: mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Psf_Vary") mylogger.userinfo(mylog, '\nEstimating PSF variations') opts = img.opts dir = img.basedir + '/misc/' plot = False # debug figures image = img.ch0_arr try: from astropy.io import fits as pyfits old_pyfits = False except ImportError, err: from distutils.version import StrictVersion import pyfits if StrictVersion(pyfits.__version__) < StrictVersion('2.2'): old_pyfits = True else: old_pyfits = False if old_pyfits: mylog.warning('PyFITS version is too old: psf_vary module skipped') return if opts.psf_fwhm is not None: # User has specified a constant PSF to use, so skip PSF fitting/etc. psf_maj = opts.psf_fwhm[0] # FWHM in deg psf_min = opts.psf_fwhm[1] # FWHM in deg psf_pa = opts.psf_fwhm[2] # PA in deg mylogger.userinfo(mylog, 'Using constant PSF (major, minor, pos angle)', '(%.5e, %.5e, %s) degrees' % (psf_maj, psf_maj, round(psf_pa, 1))) else: # Use did not specify a constant PSF to use, so estimate it over = 2 generators = opts.psf_generators; nsig = opts.psf_nsig; kappa2 = opts.psf_kappa2 snrtop = opts.psf_snrtop; snrbot = opts.psf_snrbot; snrcutstack = opts.psf_snrcutstack gencode = opts.psf_gencode; primarygen = opts.psf_primarygen; itess_method = opts.psf_itess_method tess_sc = opts.psf_tess_sc; tess_fuzzy= opts.psf_tess_fuzzy bright_snr_cut = opts.psf_high_snr s_only = opts.psf_stype_only if opts.psf_snrcut < 5.0: mylogger.userinfo(mylog, "Value of psf_snrcut too low; increasing to 5") snrcut = 5.0 else: snrcut = opts.psf_snrcut img.psf_snrcut = snrcut if opts.psf_high_snr is not None: if opts.psf_high_snr < 10.0: mylogger.userinfo(mylog, "Value of psf_high_snr too low; increasing to 10") high_snrcut = 10.0 else: high_snrcut = opts.psf_high_snr else: high_snrcut = opts.psf_high_snr img.psf_high_snr = high_snrcut wtfns=['unity', 'roundness', 'log10', 'sqrtlog10'] if 0 <= itess_method < 4: tess_method=wtfns[itess_method] else: tess_method='unity' ### now put all relevant gaussian parameters into a list ngaus = img.ngaus nsrc = img.nsrc num = N.zeros(nsrc, dtype=N.int32) peak = N.zeros(nsrc) xc = N.zeros(nsrc) yc = N.zeros(nsrc) bmaj = N.zeros(nsrc) bmin = N.zeros(nsrc) bpa = N.zeros(nsrc) code = N.array(['']*nsrc); rms = N.zeros(nsrc) src_id_list = [] for i, src in enumerate(img.sources): src_max = 0.0 for gmax in src.gaussians: # Take only brightest Gaussian per source if gmax.peak_flux > src_max: src_max = gmax.peak_flux g = gmax num[i] = i peak[i] = g.peak_flux xc[i] = g.centre_pix[0] yc[i] = g.centre_pix[1] bmaj[i] = g.size_pix[0] bmin[i] = g.size_pix[1] bpa[i] = g.size_pix[2] code[i] = img.sources[g.source_id].code rms[i] = img.islands[g.island_id].rms gauls = (num, peak, xc, yc, bmaj, bmin, bpa, code, rms) tr_gauls = self.trans_gaul(gauls) # takes gaussians with code=S and snr > snrcut. if s_only: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut and n[7] == 'S'] else: tr = [n for n in tr_gauls if n[1]/n[8]>snrcut] g_gauls = self.trans_gaul(tr) # computes statistics of fitted sizes. Same as psfvary_fullstat.f in fBDSM. bmaj_a, bmaj_r, bmaj_ca, bmaj_cr, ni = _cbdsm.bstat(bmaj, None, nsig) bmin_a, bmin_r, bmin_ca, bmin_cr, ni = _cbdsm.bstat(bmin, None, nsig) bpa_a, bpa_r, bpa_ca, bpa_cr, ni = _cbdsm.bstat(bpa, None, nsig) # get subset of sources deemed to be unresolved. Same as size_ksclip_wenss.f in fBDSM. flag_unresolved = self.get_unresolved(g_gauls, img.beam, nsig, kappa2, over, img.psf_high_snr, plot) if len(flag_unresolved) == 0: mylog.warning('Insufficient number of sources to determine PSF variation.\nTry changing the PSF options or specify a (constant) PSF with the "psf_fwhm" option') return # see how much the SNR-weighted sizes of unresolved sources differ from the synthesized beam. wtsize_beam_snr = self.av_psf(g_gauls, img.beam, flag_unresolved) # filter out resolved sources tr_gaul = self.trans_gaul(g_gauls) tr = [n for i, n in enumerate(tr_gaul) if flag_unresolved[i]] g_gauls = self.trans_gaul(tr) mylogger.userinfo(mylog, 'Number of unresolved sources', str(len(g_gauls[0]))) # get a list of voronoi generators. vorogenS has values (and not None) if generators='field'. vorogenP, vorogenS = self.get_voronoi_generators(g_gauls, generators, gencode, snrcut, snrtop, snrbot, snrcutstack) mylogger.userinfo(mylog, 'Number of generators for PSF variation', str(len(vorogenP[0]))) if len(vorogenP[0]) < 3: mylog.warning('Insufficient number of generators') return mylogger.userinfo(mylog, 'Tesselating image') # group generators into tiles tile_prop = self.edit_vorogenlist(vorogenP, frac=0.9) # tesselate the image volrank, vorowts = self.tesselate(vorogenP, vorogenS, tile_prop, tess_method, tess_sc, tess_fuzzy, \ generators, gencode, image.shape) if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.volrank.fits', volrank, img, dir) tile_list, tile_coord, tile_snr = tile_prop ntile = len(tile_list) bar = statusbar.StatusBar('Determining PSF variation ............... : ', 0, ntile) mylogger.userinfo(mylog, 'Number of tiles for PSF variation', str(ntile)) # For each tile, calculate the weighted averaged psf image. Also for all the sources in the image. cdelt = list(img.wcs_obj.acdelt[0:2]) factor=3. psfimages, psfcoords, totpsfimage, psfratio, psfratio_aper = self.psf_in_tile(image, img.beam, g_gauls, \ cdelt, factor, snrcutstack, volrank, tile_prop, plot, img) npsf = len(psfimages) if opts.psf_use_shap: if opts.psf_fwhm is None: # use totpsfimage to get beta, centre and nmax for shapelet decomposition. Use nmax=5 or 6 mask=N.zeros(totpsfimage.shape, dtype=bool) (m1, m2, m3)=func.moment(totpsfimage, mask) betainit=sqrt(m3[0]*m3[1])*2.0 * 1.4 tshape = totpsfimage.shape cen = N.array(N.unravel_index(N.argmax(totpsfimage), tshape))+[1,1] cen = tuple(cen) nmax = 12 basis = 'cartesian' betarange = [0.5,sqrt(betainit*max(tshape))] beta, error = sh.shape_varybeta(totpsfimage, mask, basis, betainit, cen, nmax, betarange, plot) if error == 1: print ' Unable to find minimum in beta' # decompose all the psf images using the beta from above nmax=12; psf_cf=[] for i in range(npsf): psfim = psfimages[i] cf = sh.decompose_shapelets(psfim, mask, basis, beta, cen, nmax, mode='') psf_cf.append(cf) if img.opts.quiet == False: bar.increment() bar.stop() # transpose the psf image list xt, yt = N.transpose(tile_coord) tr_psf_cf = N.transpose(N.array(psf_cf)) # interpolate the coefficients across the image. Ok, interpolate in scipy for # irregular grids is crap. doesnt even pass through some of the points. # for now, fit polynomial. compress = 100.0 x, y = N.transpose(psfcoords) if len(x) < 3: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return psf_coeff_interp, xgrid, ygrid = self.interp_shapcoefs(nmax, tr_psf_cf, psfcoords, image.shape, \ compress, plot) psfshape = psfimages[0].shape skip = 5 aa = self.create_psf_grid(psf_coeff_interp, image.shape, xgrid, ygrid, skip, nmax, psfshape, \ basis, beta, cen, totpsfimage, plot) img.psf_images = aa else: if opts.psf_fwhm is None: if ntile < 4: mylog.warning('Insufficient number of tiles to do interpolation of PSF variation') return else: # Fit stacked PSFs with Gaussians and measure aperture fluxes bm_pix = N.array([img.pixel_beam()[0]*fwsig, img.pixel_beam()[1]*fwsig, img.pixel_beam()[2]]) psf_maj = N.zeros(npsf) psf_min = N.zeros(npsf) psf_pa = N.zeros(npsf) if img.opts.quiet == False: bar.start() for i in range(ntile): psfim = psfimages[i] mask = N.zeros(psfim.shape, dtype=bool) x_ax, y_ax = N.indices(psfim.shape) maxv = N.max(psfim) p_ini = [maxv, (psfim.shape[0]-1)/2.0*1.1, (psfim.shape[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, bm_pix[1]/fwsig*1.1, bm_pix[2]*2] para, ierr = func.fit_gaus2d(psfim, p_ini, x_ax, y_ax, mask) ### first extent is major if para[3] < para[4]: para[3:5] = para[4:2:-1] para[5] += 90 ### clip position angle para[5] = divmod(para[5], 180)[1] psf_maj[i] = para[3] psf_min[i] = para[4] posang = para[5] while posang >= 180.0: posang -= 180.0 psf_pa[i] = posang if img.opts.quiet == False: bar.increment() bar.stop() # Interpolate Gaussian parameters if img.aperture is None: psf_maps = [psf_maj, psf_min, psf_pa, psfratio] else: psf_maps = [psf_maj, psf_min, psf_pa, psfratio, psfratio_aper] nimgs = len(psf_maps) bar = statusbar.StatusBar('Interpolating PSF images ................ : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.interp_prop), psf_maps, itertools.repeat(psfcoords), itertools.repeat(image.shape)), numcores=opts.ncores, bar=bar) if img.aperture is None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Smooth if desired if img.opts.psf_smooth is not None: sm_scale = img.opts.psf_smooth / img.pix2beam([1.0, 1.0, 0.0])[0] / 3600.0 # pixels if img.opts.aperture is None: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int] else: psf_maps = [psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int] nimgs = len(psf_maps) bar = statusbar.StatusBar('Smoothing PSF images .................... : ', 0, nimgs) if img.opts.quiet == False: bar.start() map_list = mp.parallel_map(func.eval_func_tuple, itertools.izip(itertools.repeat(self.blur_image), psf_maps, itertools.repeat(sm_scale)), numcores=opts.ncores, bar=bar) if img.aperture is None: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int = map_list else: psf_maj_int, psf_min_int, psf_pa_int, psf_ratio_int, psf_ratio_aper_int = map_list # Make sure all smoothed, interpolated images are ndarrays psf_maj_int = N.array(psf_maj_int) psf_min_int = N.array(psf_min_int) psf_pa_int = N.array(psf_pa_int) psf_ratio_int = N.array(psf_ratio_int) if img.aperture is None: psf_ratio_aper_int = N.zeros(psf_maj_int.shape, dtype=N.float32) else: psf_ratio_aper_int = N.array(psf_ratio_aper_int, dtype=N.float32) # Blank with NaNs if needed mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) psf_maj_int[pix_masked] = N.nan psf_min_int[pix_masked] = N.nan psf_pa_int[pix_masked] = N.nan psf_ratio_int[pix_masked] = N.nan psf_ratio_aper_int[pix_masked] = N.nan # Store interpolated images. The major and minor axis images are # the sigma in units of arcsec, the PA image in units of degrees east of # north, the ratio images in units of 1/beam. img.psf_vary_maj_arr = psf_maj_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_min_arr = psf_min_int * img.pix2beam([1.0, 1.0, 0.0])[0] * 3600.0 # sigma in arcsec img.psf_vary_pa_arr = psf_pa_int img.psf_vary_ratio_arr = psf_ratio_int # in 1/beam img.psf_vary_ratio_aper_arr = psf_ratio_aper_int # in 1/beam if opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_maj.fits', img.psf_vary_maj_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_min.fits', img.psf_vary_min_arr*fwsig, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_pa.fits', img.psf_vary_pa_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio.fits', img.psf_vary_ratio_arr, img, dir) func.write_image_to_file(img.use_io, img.imagename + '.psf_vary_ratio_aper.fits', img.psf_vary_ratio_aper_arr, img, dir) # Loop through source and Gaussian lists and deconvolve the sizes using appropriate beam bar2 = statusbar.StatusBar('Correcting deconvolved source sizes ..... : ', 0, img.nsrc) if img.opts.quiet == False: bar2.start() for src in img.sources: src_pos = img.sky2pix(src.posn_sky_centroid) src_pos_int = (int(src_pos[0]), int(src_pos[1])) gaus_c = img.gaus2pix(src.size_sky, src.posn_sky_centroid) if opts.psf_fwhm is None: gaus_bm = [psf_maj_int[src_pos_int]*fwsig, psf_min_int[src_pos_int]*fwsig, psf_pa_int[src_pos_int]] else: # Use user-specified constant PSF instead gaus_bm = img.beam2pix(opts.psf_fwhm) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) src.deconv_size_sky = img.pix2gaus(gaus_dc, src_pos) src.deconv_size_skyE = [0.0, 0.0, 0.0] for g in src.gaussians: gaus_c = img.gaus2pix(g.size_sky, src.posn_sky_centroid) gaus_dc, err = func.deconv2(gaus_bm, gaus_c) g.deconv_size_sky = img.pix2gaus(gaus_dc, g.centre_pix) g.deconv_size_skyE = [0.0, 0.0, 0.0] if img.opts.quiet == False: bar2.spin() if img.opts.quiet == False: bar2.increment() bar2.stop() img.completed_Ops.append('psf_vary')
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "RMSimage") mylogger.userinfo(mylog, "Calculating background rms and mean images") if img.opts.polarisation_do: pols = ['I', 'Q', 'U', 'V'] ch0_images = [ img.ch0_arr, img.ch0_Q_arr, img.ch0_U_arr, img.ch0_V_arr ] cmeans = [img.clipped_mean] + img.clipped_mean_QUV crmss = [img.clipped_rms] + img.clipped_rms_QUV else: pols = ['I'] # assume I is always present ch0_images = [img.ch0_arr] cmeans = [img.clipped_mean] crmss = [img.clipped_rms] mask = img.mask_arr opts = img.opts cdelt = N.array(img.wcs_obj.acdelt[:2]) # Determine box size for rms/mean map calculations. # If user specifies rms_box, use it. Otherwise, use either an # adaptive binning scheme that shrinks the box near # the brightest sources or estimate rms_box from bright sources. # # The adaptive scheme calculates the rms/mean map # at two different scales: # 1) using a large rms_box, set by size of largest source # 2) using a small rms_box, set by size of largest bright source # Then, the rms and mean values at a given point are determined # by a weighted average of the values in the maps at the two # scales. fwsig = const.fwsig min_adapt_threshold = 10.0 if opts.adaptive_thresh is None: adapt_thresh = 50.0 start_thresh = 500.0 else: adapt_thresh = opts.adaptive_thresh if adapt_thresh < min_adapt_threshold: adapt_thresh = min_adapt_threshold opts.adaptive_thresh = min_adapt_threshold start_thresh = adapt_thresh brightsize = None isl_pos = [] do_adapt = img.opts.adaptive_rms_box img.use_rms_map = None img.mean_map_type = None # 'size' of brightest source kappa1 = 3.0 try: brightsize = int( round(2. * img.beam[0] / cdelt[0] / fwsig * sqrt(2. * log(img.max_value / (kappa1 * crms))))) except: brightsize = int(round(2. * img.beam[0] / cdelt[0] / fwsig)) mylog.info('Estimated size of brightest source (pixels) = ' + str(brightsize)) # Using clipped mean and rms and a starting threshold of 500 sigma, # search for bright sources. If fewer than 5 are found, reduce # threshold until limit set by adapt_thresh is hit. cmean = cmeans[0] crms = crmss[0] image = ch0_images[0] shape = image.shape isl_size_bright = [] isl_area_highthresh = [] isl_peak = [] max_isl_brightsize = 0.0 threshold = start_thresh if do_adapt: mylogger.userinfo(mylog, "Using adaptive scaling of rms_box") while len(isl_size_bright) < 5 and threshold >= adapt_thresh: isl_size_bright = [] isl_maxposn = [] act_pixels = (image - cmean) / threshold >= crms threshold *= 0.8 if isinstance(mask, N.ndarray): act_pixels[mask] = False rank = len(image.shape) connectivity = nd.generate_binary_structure(rank, rank) labels, count = nd.label(act_pixels, connectivity) slices = nd.find_objects(labels) for idx, s in enumerate(slices): isl_size_bright.append( max([s[0].stop - s[0].start, s[1].stop - s[1].start])) size_area = (labels[s] == idx + 1).sum() / img.pixel_beamarea() * 2.0 isl_area_highthresh.append(size_area) isl_maxposn.append(tuple(N.array(N.unravel_index(N.argmax(image[s]), image[s].shape))+\ N.array((s[0].start, s[1].start)))) isl_peak.append(nd.maximum(image[s], labels[s], idx + 1)) # Check islands found above at thresh_isl threshold to determine if # the bright source is embedded inside a large island or not. If it is, # exclude it from the bright-island list. Also find the size of the # largest island at this threshold to set the large-scale rms_box bright_threshold = threshold threshold = 10.0 act_pixels = (image - cmean) / threshold >= crms if isinstance(mask, N.ndarray): act_pixels[mask] = False rank = len(image.shape) connectivity = nd.generate_binary_structure(rank, rank) labels, count = nd.label(act_pixels, connectivity) slices = nd.find_objects(labels) isl_size = [] isl_size_highthresh = [] isl_size_lowthresh = [] isl_snr = [] thratio = threshold / bright_threshold for idx, s in enumerate(slices): isl_area_lowthresh = (labels[s] == idx + 1).sum() / img.pixel_beamarea() * 2.0 isl_maxposn_lowthresh = tuple( N.array(N.unravel_index(N.argmax(image[s]), image[s].shape)) + N.array((s[0].start, s[1].start))) isl_size += [s[0].stop - s[0].start, s[1].stop - s[1].start] if do_adapt and isl_maxposn_lowthresh in isl_maxposn: bright_indx = isl_maxposn.index(isl_maxposn_lowthresh) if isl_area_lowthresh < 25.0 or isl_area_lowthresh / isl_area_highthresh[ bright_indx] < 8.0: isl_pos.append(isl_maxposn_lowthresh) isl_size_lowthresh.append( max([s[0].stop - s[0].start, s[1].stop - s[1].start])) isl_size_highthresh.append(isl_size_bright[bright_indx]) isl_snr.append(isl_peak[bright_indx] / crms) if len(isl_size) == 0: max_isl_size = 0.0 else: max_isl_size = max(isl_size) mylog.info( 'Maximum extent of largest 10-sigma island using clipped rms (pixels) = ' + str(max_isl_size)) if len(isl_size_highthresh) == 0: max_isl_size_highthresh = 0.0 max_isl_size_lowthresh = 0.0 else: max_isl_size_highthresh = max(isl_size_highthresh) max_isl_size_lowthresh = max(isl_size_lowthresh) avg_max_isl_size = (max_isl_size_highthresh + max_isl_size_lowthresh) / 2.0 if hasattr(img, '_adapt_rms_isl_pos'): isl_pos = img._adapt_rms_isl_pos # set isl_pos to existing value (for wavelet analysis) if len(isl_pos) == 0: # No bright sources found do_adapt = False else: img._adapt_rms_isl_pos = isl_pos min_size_allowed = int(img.pixel_beam()[0] * 9.0) if opts.rms_box is None or (opts.rms_box_bright is None and do_adapt): if do_adapt: bsize = int( max(brightsize, min_size_allowed, max_isl_size_highthresh * 2.0)) else: bsize = int( max(brightsize, min_size_allowed, max_isl_size * 2.0)) bsize2 = int(max(min(image.shape) / 10.0, max_isl_size * 5.0)) if bsize < min_size_allowed: bsize = min_size_allowed if bsize % 10 == 0: bsize += 1 if bsize2 < min_size_allowed: bsize2 = min_size_allowed if bsize2 % 10 == 0: bsize2 += 1 bstep = int(round(min(bsize / 3., min(shape) / 10.))) bstep2 = int(round(min(bsize2 / 3., min(shape) / 10.))) if opts.rms_box_bright is None: img.rms_box_bright = (bsize, bstep) else: img.rms_box_bright = opts.rms_box_bright if opts.rms_box is None: img.rms_box = (bsize2, bstep2) else: img.rms_box = opts.rms_box else: if do_adapt: img.rms_box_bright = opts.rms_box_bright img.rms_box = opts.rms_box else: img.rms_box_bright = opts.rms_box img.rms_box = opts.rms_box if opts.kappa_clip is None: kappa = -img.pixel_beamarea() else: kappa = img.opts.kappa_clip if do_adapt: map_opts = (kappa, img.rms_box_bright, opts.spline_rank) else: map_opts = (kappa, img.rms_box, opts.spline_rank) for ipol, pol in enumerate(pols): data = ch0_images[ipol] mean = N.zeros(data.shape, dtype=N.float32) rms = N.zeros(data.shape, dtype=N.float32) if len(pols) > 1: pol_txt = ' (' + pol + ')' else: pol_txt = '' ## calculate rms/mean maps if needed if ((opts.rms_map is not False) or (opts.mean_map not in ['zero', 'const']) ) and img.rms_box[0] > min(image.shape) / 4.0: # rms box is too large - just use constant rms and mean self.output_rmsbox_size(img) mylogger.userinfo( mylog, 'Size of rms_box larger than 1/4 of image size') mylogger.userinfo(mylog, 'Using constant background rms and mean') img.use_rms_map = False img.mean_map_type = 'const' else: if (opts.rms_map is not False) or (opts.mean_map not in ['zero', 'const']): if len(data.shape) == 2: ## 2d case mean, rms = self.calculate_maps( img, data, mean, rms, mask, map_opts, do_adapt=do_adapt, bright_pt_coords=isl_pos, rms_box2=img.rms_box, logname="PyBDSM." + img.log, ncores=img.opts.ncores) elif len(data.shape) == 3: ## 3d case if not isinstance(mask, N.ndarray): mask = N.zeros(data.shape[0], dtype=bool) for i in range(data.shape[0]): ## iterate each plane mean, rms = self.calculate_maps( img, data[i], mean[i], rms[i], mask[i], map_opts, do_adapt=do_adapt, bright_pt_coords=isl_pos, rms_box2=img.rms_box, logname="PyBDSM." + img.log, ncores=img.opts.ncores) else: mylog.critical('Image shape not handleable' + pol_txt) raise RuntimeError("Can't handle array of this shape" + pol_txt) self.output_rmsbox_size(img) if do_adapt: mylogger.userinfo( mylog, 'Number of sources using small scale', str(len(isl_pos))) mylog.info('Background rms and mean images computed' + pol_txt) ## check if variation of rms/mean maps is significant enough: # check_rmsmap() sets img.use_rms_map # check_meanmap() sets img.mean_map_type if pol == 'I': if opts.rms_map is None and img.use_rms_map is None: if do_adapt and len(isl_pos) > 0: # Always use 2d map if there is at least one bright # source and adaptive scaling is desired img.use_rms_map = True else: self.check_rmsmap(img, rms) elif opts.rms_map is not None: img.use_rms_map = opts.rms_map if img.use_rms_map is False: mylogger.userinfo(mylog, 'Using constant background rms') else: mylogger.userinfo(mylog, 'Using 2D map for background rms') if opts.mean_map == 'default' and img.mean_map_type is None: self.check_meanmap(img, rms) elif opts.mean_map != 'default': img.mean_map_type = opts.mean_map if img.mean_map_type != 'map': mylogger.userinfo(mylog, 'Using constant background mean') else: mylogger.userinfo(mylog, 'Using 2D map for background mean') ## if rms map is insignificant, or rms_map==False use const value if img.use_rms_map is False: if opts.rms_value is None: rms[:] = crmss[ipol] else: rms[:] = opts.rms_value mylogger.userinfo(mylog, 'Value of background rms' + pol_txt, '%.5f Jy/beam' % rms[0][0]) else: rms_min = N.nanmin(rms) rms_max = N.nanmax(rms) mylogger.userinfo( mylog, 'Min/max values of background rms map' + pol_txt, '(%.5f, %.5f) Jy/beam' % (rms_min, rms_max)) if img.mean_map_type != 'map': if opts.mean_map == 'zero': val = 0.0 else: val = img.clipped_mean mean[:] = val mylogger.userinfo(mylog, 'Value of background mean' + pol_txt, str(round(val, 5)) + ' Jy/beam') else: mean_min = N.nanmin(mean) mean_max = N.nanmax(mean) mylogger.userinfo( mylog, 'Min/max values of background mean map' + pol_txt, '(%.5f, %.5f) Jy/beam' % (mean_min, mean_max)) if pol == 'I': # Apply mask to mean_map and rms_map by setting masked values to NaN if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) mean[pix_masked] = N.nan rms[pix_masked] = N.nan img.mean_arr = mean img.rms_arr = rms if opts.savefits_rmsim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) func.write_image_to_file(img.use_io, img.imagename + '.rmsd_I.fits', rms, img, resdir) mylog.info( '%s %s' % ('Writing ', resdir + img.imagename + '.rmsd_I.fits')) if opts.savefits_meanim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) func.write_image_to_file(img.use_io, img.imagename + '.mean_I.fits', mean, img, resdir) mylog.info( '%s %s' % ('Writing ', resdir + img.imagename + '.mean_I.fits')) if opts.savefits_normim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) zero_pixels = N.where(rms <= 0.0) rms_nonzero = rms.copy() rms_nonzero[zero_pixels] = N.NaN func.write_image_to_file(img.use_io, img.imagename + '.norm_I.fits', (image - mean) / rms_nonzero, img, resdir) mylog.info( '%s %s' % ('Writing ', resdir + img.imagename + '.norm_I.fits')) else: img.__setattr__('mean_' + pol + '_arr', mean) img.__setattr__('rms_' + pol + '_arr', rms) img.completed_Ops.append('rmsimage') return img
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"RMSimage") mylogger.userinfo(mylog, "Calculating background rms and mean images") if img.opts.polarisation_do: pols = ['I', 'Q', 'U', 'V'] ch0_images = [img.ch0_arr, img.ch0_Q_arr, img.ch0_U_arr, img.ch0_V_arr] cmeans = [img.clipped_mean] + img.clipped_mean_QUV crmss = [img.clipped_rms] + img.clipped_rms_QUV else: pols = ['I'] # assume I is always present ch0_images = [img.ch0_arr] cmeans = [img.clipped_mean] crmss = [img.clipped_rms] mask = img.mask_arr opts = img.opts cdelt = N.array(img.wcs_obj.acdelt[:2]) # Determine box size for rms/mean map calculations. # If user specifies rms_box, use it. Otherwise, use either an # adaptive binning scheme that shrinks the box near # the brightest sources or estimate rms_box from bright sources. # # The adaptive scheme calculates the rms/mean map # at two different scales: # 1) using a large rms_box, set by size of largest source # 2) using a small rms_box, set by size of largest bright source # Then, the rms and mean values at a given point are determined # by a weighted average of the values in the maps at the two # scales. fwsig = const.fwsig min_adapt_threshold = 10.0 if opts.adaptive_thresh == None: adapt_thresh = 50.0 start_thresh = 500.0 else: adapt_thresh = opts.adaptive_thresh if adapt_thresh < min_adapt_threshold: adapt_thresh = min_adapt_threshold opts.adaptive_thresh = min_adapt_threshold start_thresh = adapt_thresh brightsize = None isl_pos = [] do_adapt = img.opts.adaptive_rms_box img.use_rms_map = None img.mean_map_type = None # 'size' of brightest source kappa1 = 3.0 try: brightsize = int(round(2.*img.beam[0]/cdelt[0]/fwsig* sqrt(2.*log(img.max_value/(kappa1*crms))))) except: brightsize = int(round(2.*img.beam[0]/cdelt[0]/fwsig)) mylog.info('Estimated size of brightest source (pixels) = '+str(brightsize)) # Using clipped mean and rms and a starting threshold of 500 sigma, # search for bright sources. If fewer than 5 are found, reduce # threshold until limit set by adapt_thresh is hit. cmean = cmeans[0] crms = crmss[0] image = ch0_images[0] shape = image.shape isl_size_bright = [] isl_area_highthresh = [] isl_peak = [] max_isl_brightsize = 0.0 threshold = start_thresh if do_adapt: mylogger.userinfo(mylog, "Using adaptive scaling of rms_box") while len(isl_size_bright) < 5 and threshold >= adapt_thresh: isl_size_bright=[] isl_maxposn = [] act_pixels = (image-cmean)/threshold >= crms threshold *= 0.8 if isinstance(mask, N.ndarray): act_pixels[mask] = False rank = len(image.shape) connectivity = nd.generate_binary_structure(rank, rank) labels, count = nd.label(act_pixels, connectivity) slices = nd.find_objects(labels) for idx, s in enumerate(slices): isl_size_bright.append(max([s[0].stop-s[0].start, s[1].stop-s[1].start])) size_area = (labels[s] == idx+1).sum()/img.pixel_beamarea()*2.0 isl_area_highthresh.append(size_area) isl_maxposn.append(tuple(N.array(N.unravel_index(N.argmax(image[s]), image[s].shape))+\ N.array((s[0].start, s[1].start)))) isl_peak.append(nd.maximum(image[s], labels[s], idx+1)) # Check islands found above at thresh_isl threshold to determine if # the bright source is embedded inside a large island or not. If it is, # exclude it from the bright-island list. Also find the size of the # largest island at this threshold to set the large-scale rms_box bright_threshold = threshold threshold = 10.0 act_pixels = (image-cmean)/threshold >= crms if isinstance(mask, N.ndarray): act_pixels[mask] = False rank = len(image.shape) connectivity = nd.generate_binary_structure(rank, rank) labels, count = nd.label(act_pixels, connectivity) slices = nd.find_objects(labels) isl_size = [] isl_size_highthresh = [] isl_size_lowthresh = [] isl_snr = [] thratio = threshold/bright_threshold for idx, s in enumerate(slices): isl_area_lowthresh = (labels[s] == idx+1).sum()/img.pixel_beamarea()*2.0 isl_maxposn_lowthresh = tuple(N.array(N.unravel_index(N.argmax(image[s]), image[s].shape))+ N.array((s[0].start, s[1].start))) isl_size += [s[0].stop-s[0].start, s[1].stop-s[1].start] if do_adapt and isl_maxposn_lowthresh in isl_maxposn: bright_indx = isl_maxposn.index(isl_maxposn_lowthresh) if isl_area_lowthresh < 25.0 or isl_area_lowthresh/isl_area_highthresh[bright_indx] < 8.0: isl_pos.append(isl_maxposn_lowthresh) isl_size_lowthresh.append(max([s[0].stop-s[0].start, s[1].stop-s[1].start])) isl_size_highthresh.append(isl_size_bright[bright_indx]) isl_snr.append(isl_peak[bright_indx]/crms) if len(isl_size) == 0: max_isl_size = 0.0 else: max_isl_size = max(isl_size) mylog.info('Maximum extent of largest 10-sigma island using clipped rms (pixels) = '+str(max_isl_size)) if len(isl_size_highthresh) == 0: max_isl_size_highthresh = 0.0 max_isl_size_lowthresh = 0.0 else: max_isl_size_highthresh = max(isl_size_highthresh) max_isl_size_lowthresh = max(isl_size_lowthresh) avg_max_isl_size = (max_isl_size_highthresh + max_isl_size_lowthresh) / 2.0 if hasattr(img, '_adapt_rms_isl_pos'): isl_pos = img._adapt_rms_isl_pos # set isl_pos to existing value (for wavelet analysis) if len(isl_pos) == 0: # No bright sources found do_adapt = False else: img._adapt_rms_isl_pos = isl_pos min_size_allowed = int(img.pixel_beam()[0]*9.0) if opts.rms_box is None or (opts.rms_box_bright is None and do_adapt): if do_adapt: bsize = int(max(brightsize, min_size_allowed, max_isl_size_highthresh*2.0)) else: bsize = int(max(brightsize, min_size_allowed, max_isl_size*2.0)) bsize2 = int(max(min(image.shape)/10.0, max_isl_size*5.0)) if bsize < min_size_allowed: bsize = min_size_allowed if bsize % 10 == 0: bsize += 1 if bsize2 < min_size_allowed: bsize2 = min_size_allowed if bsize2 % 10 == 0: bsize2 += 1 bstep = int(round(min(bsize/3., min(shape)/10.))) bstep2 = int(round(min(bsize2/3., min(shape)/10.))) if opts.rms_box_bright is None: img.rms_box_bright = (bsize, bstep) else: img.rms_box_bright = opts.rms_box_bright if opts.rms_box is None: img.rms_box = (bsize2, bstep2) else: img.rms_box = opts.rms_box else: if do_adapt: img.rms_box_bright = opts.rms_box_bright img.rms_box = opts.rms_box else: img.rms_box_bright = opts.rms_box img.rms_box = opts.rms_box if opts.kappa_clip is None: kappa = -img.pixel_beamarea() else: kappa = img.opts.kappa_clip if do_adapt: map_opts = (kappa, img.rms_box_bright, opts.spline_rank) else: map_opts = (kappa, img.rms_box, opts.spline_rank) for ipol, pol in enumerate(pols): data = ch0_images[ipol] mean = N.zeros(data.shape, dtype=N.float32) rms = N.zeros(data.shape, dtype=N.float32) if len(pols) > 1: pol_txt = ' (' + pol + ')' else: pol_txt = '' ## calculate rms/mean maps if needed if ((opts.rms_map is not False) or (opts.mean_map not in ['zero', 'const'])) and img.rms_box[0] > min(image.shape)/4.0: # rms box is too large - just use constant rms and mean self.output_rmsbox_size(img) mylogger.userinfo(mylog, 'Size of rms_box larger than 1/4 of image size') mylogger.userinfo(mylog, 'Using constant background rms and mean') img.use_rms_map = False img.mean_map_type = 'const' else: if (opts.rms_map is not False) or (opts.mean_map not in ['zero', 'const']): if len(data.shape) == 2: ## 2d case mean, rms = self.calculate_maps(img, data, mean, rms, mask, map_opts, do_adapt=do_adapt, bright_pt_coords=isl_pos, rms_box2=img.rms_box, logname="PyBDSM."+img.log, ncores=img.opts.ncores) elif len(data.shape) == 3: ## 3d case if not isinstance(mask, N.ndarray): mask = N.zeros(data.shape[0], dtype=bool) for i in range(data.shape[0]): ## iterate each plane mean, rms = self.calculate_maps(img, data[i], mean[i], rms[i], mask[i], map_opts, do_adapt=do_adapt, bright_pt_coords=isl_pos, rms_box2=img.rms_box, logname="PyBDSM."+img.log, ncores=img.opts.ncores) else: mylog.critical('Image shape not handleable' + pol_txt) raise RuntimeError("Can't handle array of this shape" + pol_txt) self.output_rmsbox_size(img) if do_adapt: mylogger.userinfo(mylog, 'Number of sources using small scale', str(len(isl_pos))) mylog.info('Background rms and mean images computed' + pol_txt) ## check if variation of rms/mean maps is significant enough: # check_rmsmap() sets img.use_rms_map # check_meanmap() sets img.mean_map_type if pol == 'I': if opts.rms_map is None and img.use_rms_map is None: if do_adapt and len(isl_pos) > 0: # Always use 2d map if there is at least one bright # source and adaptive scaling is desired img.use_rms_map = True else: self.check_rmsmap(img, rms) elif opts.rms_map != None: img.use_rms_map = opts.rms_map if img.use_rms_map is False: mylogger.userinfo(mylog, 'Using constant background rms') else: mylogger.userinfo(mylog, 'Using 2D map for background rms') if opts.mean_map == 'default' and img.mean_map_type is None: self.check_meanmap(img, rms) elif opts.mean_map != 'default': img.mean_map_type = opts.mean_map if img.mean_map_type != 'map': mylogger.userinfo(mylog, 'Using constant background mean') else: mylogger.userinfo(mylog, 'Using 2D map for background mean') ## if rms map is insignificant, or rms_map==False use const value if img.use_rms_map is False: if opts.rms_value == None: rms[:] = crmss[ipol] else: rms[:] = opts.rms_value mylogger.userinfo(mylog, 'Value of background rms' + pol_txt, '%.5f Jy/beam' % rms[0][0]) else: rms_min = N.nanmin(rms) rms_max = N.nanmax(rms) mylogger.userinfo(mylog, 'Min/max values of background rms map' + pol_txt, '(%.5f, %.5f) Jy/beam' % (rms_min, rms_max)) if img.mean_map_type != 'map': if opts.mean_map == 'zero': val = 0.0 else: val = img.clipped_mean mean[:] = val mylogger.userinfo(mylog, 'Value of background mean' + pol_txt, str(round(val,5))+' Jy/beam') else: mean_min = N.nanmin(mean) mean_max = N.nanmax(mean) mylogger.userinfo(mylog, 'Min/max values of background mean map' + pol_txt, '(%.5f, %.5f) Jy/beam' % (mean_min, mean_max)) if pol == 'I': # Apply mask to mean_map and rms_map by setting masked values to NaN if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) mean[pix_masked] = N.nan rms[pix_masked] = N.nan img.mean_arr = mean img.rms_arr = rms if opts.savefits_rmsim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) func.write_image_to_file(img.use_io, img.imagename + '.rmsd_I.fits', rms, img, resdir) mylog.info('%s %s' % ('Writing ', resdir+img.imagename+'.rmsd_I.fits')) if opts.savefits_meanim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) func.write_image_to_file(img.use_io, img.imagename + '.mean_I.fits', mean, img, resdir) mylog.info('%s %s' % ('Writing ', resdir+img.imagename+'.mean_I.fits')) if opts.savefits_normim or opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/background/' else: resdir = img.basedir + '/background/' if not os.path.exists(resdir): os.makedirs(resdir) zero_pixels = N.where(rms <= 0.0) rms_nonzero = rms.copy() rms_nonzero[zero_pixels] = N.NaN func.write_image_to_file(img.use_io, img.imagename + '.norm_I.fits', (image-mean)/rms_nonzero, img, resdir) mylog.info('%s %s' % ('Writing ', resdir+img.imagename+'.norm_I.fits')) else: img.__setattr__('mean_'+pol+'_arr', mean) img.__setattr__('rms_'+pol+'_arr', rms) img.completed_Ops.append('rmsimage') return img
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Collapse") if img.opts.polarisation_do: pols = ["I", "Q", "U", "V"] # make sure I is done first else: pols = ["I"] # assume I is always present img.ch0_Q_arr = None img.ch0_U_arr = None img.ch0_V_arr = None if img.shape[1] > 1: c_mode = img.opts.collapse_mode chan0 = img.opts.collapse_ch0 c_list = img.opts.collapse_av c_wts = img.opts.collapse_wt if c_list == []: c_list = N.arange(img.shape[1]) if len(c_list) == 1: c_mode = "single" chan0 = c_list[0] img.collapse_ch0 = chan0 ch0sh = img.image_arr.shape[2:] if img.opts.polarisation_do: ch0images = ["ch0_arr", "ch0_Q_arr", "ch0_U_arr", "ch0_V_arr"] else: ch0images = ["ch0_arr"] # assume all Stokes images have the same blank pixels as I: blank = N.isnan(img.image_arr[0]) hasblanks = blank.any() if img.opts.kappa_clip is None: kappa = -img.pixel_beamarea() else: kappa = img.opts.kappa_clip mean, rms, cmean, crms = chan_stats(img, kappa) img.channel_mean = mean img.channel_rms = rms img.channel_clippedmean = cmean img.channel_clippedrms = crms for ipol, pol in enumerate(pols): if c_mode == "single": if pol == "I": ch0 = img.image_arr[0, chan0] img.ch0_arr = ch0 mylogger.userinfo( mylog, "Source extraction will be " "done on channel", "%i (%.3f MHz)" % (chan0, img.frequency / 1e6), ) else: ch0[:] = img.image_arr[ipol, chan0][:] img.__setattr__(ch0images[ipol][:], ch0) if c_mode == "average": if not hasblanks: if pol == "I": ch0, wtarr = avspc_direct(c_list, img.image_arr[0], img.channel_clippedrms, c_wts) else: # use wtarr from the I image, which is always collapsed first ch0, wtarr = avspc_direct( c_list, img.image_arr[ipol], img.channel_clippedrms, c_wts, wtarr=wtarr ) else: if pol == "I": ch0, wtarr = avspc_blanks(c_list, img.image_arr[0], img.channel_clippedrms, c_wts) else: # use wtarr from the I image, which is always collapsed first ch0, wtarr = avspc_blanks( c_list, img.image_arr[ipol], img.channel_clippedrms, c_wts, wtarr=wtarr ) img.__setattr__(ch0images[ipol][:], ch0) if pol == "I": img.avspc_wtarr = wtarr init_freq_collapse(img, wtarr) if c_wts == "unity": mylogger.userinfo(mylog, "Channels averaged with " "uniform weights") else: mylogger.userinfo(mylog, "Channels averaged with " "weights=(1/rms)^2") mylogger.userinfo(mylog, "Source extraction will be " 'done on averaged ("ch0") image') mylogger.userinfo(mylog, "Frequency of averaged " "image", "%.3f MHz" % (img.frequency / 1e6,)) str1 = " ".join(str(n) for n in c_list) mylog.debug("%s %s" % ("Channels averaged : ", str1)) str1 = " ".join(["%9.4e" % n for n in wtarr]) mylog.debug("%s %s %s" % ("Channel weights : ", str1, '; unity=zero if c_wts="rms"')) if img.opts.output_all: func.write_image_to_file(img.use_io, img.imagename + ".ch0_" + pol + ".fits", ch0, img) mylog.debug("%s %s " % ("Writing file ", img.imagename + ".ch0_" + pol + ".fits")) else: # Only one channel in image image = img.image_arr img.ch0_arr = image[0, 0] mylogger.userinfo(mylog, "Frequency of image", "%.3f MHz" % (img.frequency / 1e6,)) if img.opts.polarisation_do: for pol in pols[1:]: if pol == "Q": img.ch0_Q_arr = image[1, 0][:] if pol == "U": img.ch0_U_arr = image[2, 0][:] if pol == "V": img.ch0_V_arr = image[3, 0][:] # create mask if needed (assume all pols have the same mask as I) image = img.ch0_arr mask = N.isnan(image) img.blankpix = N.sum(mask) frac_blank = round(float(img.blankpix) / float(image.shape[0] * image.shape[1]), 3) mylogger.userinfo(mylog, "Number of blank pixels", str(img.blankpix) + " (" + str(frac_blank * 100.0) + "%)") # Check whether the input image might be an AWimage. If so, and there # are no blank pixels, tell the user that they might to set blank_limit. # Once the AWimager incorporates blanking, this check can be removed. if img.opts.blank_limit is None and ( img.blankpix == 0 and ("restored" in img.filename.lower() or "corr" in img.filename.lower() or "aw" in img.filename.lower()) ): check_low = True else: check_low = False if img.opts.blank_limit is not None or check_low: import scipy import sys if check_low: threshold = 1e-5 else: threshold = img.opts.blank_limit mylogger.userinfo(mylog, "Blanking pixels with values " "below %.1e Jy/beam" % (threshold,)) bad = abs(image) < threshold original_stdout = sys.stdout # keep a reference to STDOUT sys.stdout = func.NullDevice() # redirect the real STDOUT count = scipy.signal.convolve2d(bad, N.ones((3, 3)), mode="same") sys.stdout = original_stdout # turn STDOUT back on mask_low = count >= 5 if check_low: nlow = len(N.where(mask_low)[0]) if nlow / float(image.shape[0] * image.shape[1]) > 0.2: mylog.warn( "A significant area of the image has very low values. To blank\nthese regions (e.g., because they are outside the primary beam), set the\nblank_limit option (values of 1e-5 to 1e-4 Jy/beam usually work well).\n" ) else: image[N.where(mask_low)] = N.nan mask = N.isnan(image) img.blankpix = N.sum(mask) frac_blank = round(float(img.blankpix) / float(image.shape[0] * image.shape[1]), 3) mylogger.userinfo( mylog, "Total number of blanked pixels", str(img.blankpix) + " (" + str(frac_blank * 100.0) + "%)" ) masked = mask.any() img.masked = masked if masked: img.mask_arr = mask else: img.mask_arr = None if img.blankpix == image.shape[0] * image.shape[1]: # ALL pixels are blanked! raise RuntimeError("All pixels in the image are blanked.") img.completed_Ops.append("collapse")
def __call__(self, img): mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Wavelet") if img.opts.atrous_do: if img.nisl == 0: mylog.warning("No islands found. Skipping wavelet decomposition.") img.completed_Ops.append('wavelet_atrous') return mylog.info("Decomposing gaussian residual image into a-trous wavelets") bdir = img.basedir + '/wavelet/' if img.opts.output_all: if not os.path.isdir(bdir): os.makedirs(bdir) if not os.path.isdir(bdir + '/residual/'): os.makedirs(bdir + '/residual/') if not os.path.isdir(bdir + '/model/'): os.makedirs(bdir + '/model/') dobdsm = img.opts.atrous_bdsm_do filter = {'tr':{'size':3, 'vec':[1. / 4, 1. / 2, 1. / 4], 'name':'Triangle'}, 'b3':{'size':5, 'vec':[1. / 16, 1. / 4, 3. / 8, 1. / 4, 1. / 16], 'name':'B3 spline'}} if dobdsm: wchain, wopts = self.setpara_bdsm(img) n, m = img.ch0_arr.shape # Calculate residual image that results from normal (non-wavelet) Gaussian fitting Op_make_residimage()(img) resid = img.resid_gaus_arr lpf = img.opts.atrous_lpf if lpf not in ['b3', 'tr']: lpf = 'b3' jmax = img.opts.atrous_jmax l = len(filter[lpf]['vec']) # 1st 3 is arbit and 2nd 3 is whats expected for a-trous if jmax < 1 or jmax > 15: # determine jmax # Check if largest island size is # smaller than 1/3 of image size. If so, use it to determine jmax. min_size = min(resid.shape) max_isl_shape = (0, 0) for isl in img.islands: if isl.image.shape[0] * isl.image.shape[1] > max_isl_shape[0] * max_isl_shape[1]: max_isl_shape = isl.image.shape if max_isl_shape != (0, 0) and min(max_isl_shape) < min(resid.shape) / 3.0: min_size = min(max_isl_shape) * 4.0 else: min_size = min(resid.shape) jmax = int(floor(log((min_size / 3.0 * 3.0 - l) / (l - 1) + 1) / log(2.0) + 1.0)) + 1 if min_size * 0.55 <= (l + (l - 1) * (2 ** (jmax) - 1)): jmax = jmax - 1 img.wavelet_lpf = lpf img.wavelet_jmax = jmax mylog.info("Using " + filter[lpf]['name'] + ' filter with J_max = ' + str(jmax)) img.atrous_islands = [] img.atrous_gaussians = [] img.atrous_sources = [] img.atrous_opts = [] img.resid_wavelets_arr = cp(img.resid_gaus_arr) im_old = img.resid_wavelets_arr total_flux = 0.0 ntot_wvgaus = 0 stop_wav = False pix_masked = N.where(N.isnan(resid) == True) jmin = 1 if img.opts.ncores is None: numcores = 1 else: numcores = img.opts.ncores for j in range(jmin, jmax + 1): # extra +1 is so we can do bdsm on cJ as well mylogger.userinfo(mylog, "\nWavelet scale #" + str(j)) im_new = self.atrous(im_old, filter[lpf]['vec'], lpf, j, numcores=numcores, use_scipy_fft=img.opts.use_scipy_fft) im_new[pix_masked] = N.nan # since fftconvolve wont work with blanked pixels if img.opts.atrous_sum: w = im_new else: w = im_old - im_new im_old = im_new suffix = 'w' + `j` filename = img.imagename + '.atrous.' + suffix + '.fits' if img.opts.output_all: func.write_image_to_file('fits', filename, w, img, bdir) mylog.info('%s %s' % ('Wrote ', img.imagename + '.atrous.' + suffix + '.fits')) # now do bdsm on each wavelet image. if dobdsm: wopts['filename'] = filename wopts['basedir'] = bdir box = img.rms_box[0] y1 = (l + (l - 1) * (2 ** (j - 1) - 1)) bs = max(5 * y1, box) # changed from 10 to 5 if bs > min(n, m) / 2: wopts['rms_map'] = False wopts['mean_map'] = 'const' wopts['rms_box'] = None else: wopts['rms_box'] = (bs, bs/3) if hasattr(img, '_adapt_rms_isl_pos'): bs_bright = max(5 * y1, img.rms_box_bright[0]) if bs_bright < bs/1.5: wopts['adaptive_rms_box'] = True wopts['rms_box_bright'] = (bs_bright, bs_bright/3) else: wopts['adaptive_rms_box'] = False if j <= 3: wopts['ini_gausfit'] = 'default' else: wopts['ini_gausfit'] = 'nobeam' wid = (l + (l - 1) * (2 ** (j - 1) - 1))# / 3.0 b1, b2 = img.pixel_beam()[0:2] b1 = b1 * fwsig b2 = b2 * fwsig cdelt = img.wcs_obj.acdelt[:2] wimg = Image(wopts) wimg.beam = (sqrt(wid * wid + b1 * b1) * cdelt[0] * 2.0, sqrt(wid * wid + b2 * b2) * cdelt[1] * 2.0, 0.0) wimg.orig_beam = img.beam wimg.pixel_beam = img.pixel_beam wimg.pixel_beamarea = img.pixel_beamarea wimg.log = 'Wavelet.' wimg.basedir = img.basedir wimg.extraparams['bbsprefix'] = suffix wimg.extraparams['bbsname'] = img.imagename + '.wavelet' wimg.extraparams['bbsappend'] = True wimg.bbspatchnum = img.bbspatchnum wimg.waveletimage = True wimg.j = j if hasattr(img, '_adapt_rms_isl_pos'): wimg._adapt_rms_isl_pos = img._adapt_rms_isl_pos self.init_image_simple(wimg, img, w, '.atrous.' + suffix) for op in wchain: op(wimg) gc.collect() if isinstance(op, Op_islands) and img.opts.atrous_orig_isl: if wimg.nisl > 0: # Find islands that do not share any pixels with # islands in original ch0 image. good_isl = [] # Make original rank image boolean; rank counts from 0, with -1 being # outside any island orig_rankim_bool = N.array(img.pyrank + 1, dtype = bool) # Multiply rank images old_islands = orig_rankim_bool * (wimg.pyrank + 1) - 1 # Exclude islands that don't overlap with a ch0 island. valid_ids = set(old_islands.flatten()) for idx, wvisl in enumerate(wimg.islands): if idx in valid_ids: wvisl.valid = True good_isl.append(wvisl) else: wvisl.valid = False wimg.islands = good_isl wimg.nisl = len(good_isl) mylogger.userinfo(mylog, "Number of islands found", '%i' % wimg.nisl) # Renumber islands: for wvindx, wvisl in enumerate(wimg.islands): wvisl.island_id = wvindx if isinstance(op, Op_gausfit): # If opts.atrous_orig_isl then exclude Gaussians outside of # the original ch0 islands nwvgaus = 0 if img.opts.atrous_orig_isl: gaul = wimg.gaussians tot_flux = 0.0 if img.ngaus == 0: gaus_id = -1 else: gaus_id = img.gaussians[-1].gaus_num wvgaul = [] for g in gaul: if not hasattr(g, 'valid'): g.valid = False if not g.valid: try: isl_id = img.pyrank[int(g.centre_pix[0] + 1), int(g.centre_pix[1] + 1)] except IndexError: isl_id = -1 if isl_id >= 0: isl = img.islands[isl_id] gcenter = (g.centre_pix[0] - isl.origin[0], g.centre_pix[1] - isl.origin[1]) if not isl.mask_active[gcenter]: gaus_id += 1 gcp = Gaussian(img, g.parameters[:], isl.island_id, gaus_id) gcp.gaus_num = gaus_id gcp.wisland_id = g.island_id gcp.jlevel = j g.valid = True isl.gaul.append(gcp) isl.ngaus += 1 img.gaussians.append(gcp) nwvgaus += 1 tot_flux += gcp.total_flux else: g.valid = False g.jlevel = 0 else: g.valid = False g.jlevel = 0 vg = [] for g in wimg.gaussians: if g.valid: vg.append(g) wimg.gaussians = vg mylogger.userinfo(mylog, "Number of valid wavelet Gaussians", str(nwvgaus)) else: # Keep all Gaussians and merge islands that overlap tot_flux = check_islands_for_overlap(img, wimg) # Now renumber the islands and adjust the rank image before going to next wavelet image renumber_islands(img) total_flux += tot_flux if img.opts.interactive and has_pl: dc = '\033[34;1m' nc = '\033[0m' print dc + '--> Displaying islands and rms image...' + nc if max(wimg.ch0_arr.shape) > 4096: print dc + '--> Image is large. Showing islands only.' + nc wimg.show_fit(rms_image=False, mean_image=False, ch0_image=False, ch0_islands=True, gresid_image=False, sresid_image=False, gmodel_image=False, smodel_image=False, pyramid_srcs=False) else: wimg.show_fit() prompt = dc + "Press enter to continue or 'q' stop fitting wavelet images : " + nc answ = raw_input_no_history(prompt) while answ != '': if answ == 'q': img.wavelet_jmax = j stop_wav = True break answ = raw_input_no_history(prompt) if len(wimg.gaussians) > 0: img.resid_wavelets_arr = self.subtract_wvgaus(img.opts, img.resid_wavelets_arr, wimg.gaussians, wimg.islands) if img.opts.atrous_sum: im_old = self.subtract_wvgaus(img.opts, im_old, wimg.gaussians, wimg.islands) if stop_wav == True: break pyrank = N.zeros(img.pyrank.shape, dtype=N.int32) for i, isl in enumerate(img.islands): isl.island_id = i for g in isl.gaul: g.island_id = i for dg in isl.dgaul: dg.island_id = i pyrank[isl.bbox] += N.invert(isl.mask_active) * (i + 1) pyrank -= 1 # align pyrank values with island ids and set regions outside of islands to -1 img.pyrank = pyrank pdir = img.basedir + '/misc/' img.ngaus += ntot_wvgaus img.total_flux_gaus += total_flux mylogger.userinfo(mylog, "Total flux density in model on all scales" , '%.3f Jy' % img.total_flux_gaus) if img.opts.output_all: func.write_image_to_file('fits', img.imagename + '.atrous.cJ.fits', im_new, img, bdir) mylog.info('%s %s' % ('Wrote ', img.imagename + '.atrous.cJ.fits')) func.write_image_to_file('fits', img.imagename + '.resid_wavelets.fits', (img.ch0_arr - img.resid_gaus_arr + img.resid_wavelets_arr), img, bdir + '/residual/') mylog.info('%s %s' % ('Wrote ', img.imagename + '.resid_wavelets.fits')) func.write_image_to_file('fits', img.imagename + '.model_wavelets.fits', (img.resid_gaus_arr - img.resid_wavelets_arr), img, bdir + '/model/') mylog.info('%s %s' % ('Wrote ', img.imagename + '.model_wavelets.fits')) img.completed_Ops.append('wavelet_atrous')
def __call__(self, img): import functions as func from copy import deepcopy as cp import os mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"ResidImage") mylog.info("Calculating residual image after subtracting reconstructed gaussians") shape = img.ch0_arr.shape thresh= img.opts.fittedimage_clip resid_gaus = cp(img.ch0_arr) model_gaus = N.zeros(shape, dtype=N.float32) for g in img.gaussians: C1, C2 = g.centre_pix if hasattr(g, 'wisland_id') and img.waveletimage: isl = img.islands[g.wisland_id] else: isl = img.islands[g.island_id] b = self.find_bbox(thresh*isl.rms, g) bbox = N.s_[max(0, int(C1-b)):min(shape[0], int(C1+b+1)), max(0, int(C2-b)):min(shape[1], int(C2+b+1))] x_ax, y_ax = N.mgrid[bbox] ffimg = func.gaussian_fcn(g, x_ax, y_ax) resid_gaus[bbox] = resid_gaus[bbox] - ffimg model_gaus[bbox] = model_gaus[bbox] + ffimg # Apply mask to model and resid images if hasattr(img, 'rms_mask'): mask = img.rms_mask else: mask = img.mask_arr if isinstance(img.mask_arr, N.ndarray): pix_masked = N.where(img.mask_arr == True) model_gaus[pix_masked] = N.nan resid_gaus[pix_masked] = N.nan img.model_gaus_arr = model_gaus img.resid_gaus_arr = resid_gaus if img.opts.output_all: if img.waveletimage: resdir = img.basedir + '/wavelet/residual/' moddir = img.basedir + '/wavelet/model/' else: resdir = img.basedir + '/residual/' moddir = img.basedir + '/model/' if not os.path.exists(resdir): os.makedirs(resdir) if not os.path.exists(moddir): os.makedirs(moddir) func.write_image_to_file(img.use_io, img.imagename + '.resid_gaus.fits', resid_gaus, img, resdir) mylog.info('%s %s' % ('Writing', resdir+img.imagename+'.resid_gaus.fits')) func.write_image_to_file(img.use_io, img.imagename + '.model.fits', (img.ch0_arr - resid_gaus), img, moddir) mylog.info('%s %s' % ('Writing', moddir+img.imagename+'.model_gaus.fits')) ### residual rms and mean per island for isl in img.islands: resid = resid_gaus[isl.bbox] self.calc_resid_mean_rms(isl, resid, type='gaus') # Calculate some statistics for the Gaussian residual image non_masked = N.where(~N.isnan(img.ch0_arr)) mean = N.mean(resid_gaus[non_masked], axis=None) std_dev = N.std(resid_gaus[non_masked], axis=None) skew = stats.skew(resid_gaus[non_masked], axis=None) kurt = stats.kurtosis(resid_gaus[non_masked], axis=None) stat_msg = "Statistics of the Gaussian residual image:\n" stat_msg += " mean: %.3e (Jy/beam)\n" % mean stat_msg += " std. dev: %.3e (Jy/beam)\n" % std_dev stat_msg += " skew: %.3f\n" % skew stat_msg += " kurtosis: %.3f" % kurt mylog.info(stat_msg) # Now residual image for shapelets if img.opts.shapelet_do: mylog.info("Calculating residual image after subtracting reconstructed shapelets") shape = img.ch0_arr.shape fimg = N.zeros(shape, dtype=N.float32) for isl in img.islands: if isl.shapelet_beta > 0: # make sure shapelet has nonzero scale for this island mask=isl.mask_active cen=isl.shapelet_centre-N.array(isl.origin) basis, beta, nmax, cf = isl.shapelet_basis, isl.shapelet_beta, \ isl.shapelet_nmax, isl.shapelet_cf image_recons=reconstruct_shapelets(isl.shape, mask, basis, beta, cen, nmax, cf) fimg[isl.bbox] += image_recons model_shap = fimg resid_shap = img.ch0_arr - fimg # Apply mask to model and resid images if hasattr(img, 'rms_mask'): mask = img.rms_mask else: mask = img.mask_arr if isinstance(mask, N.ndarray): pix_masked = N.where(mask == True) model_shap[pix_masked] = N.nan resid_shap[pix_masked] = N.nan img.model_shap_arr = model_shap img.resid_shap_arr = resid_shap if img.opts.output_all: func.write_image_to_file(img.use_io, img.imagename + '.resid_shap.fits', resid_shap, img, resdir) mylog.info('%s %s' % ('Writing ', resdir+img.imagename+'.resid_shap.fits')) ### shapelet residual rms and mean per island for isl in img.islands: resid = resid_shap[isl.bbox] self.calc_resid_mean_rms(isl, resid, type='shap') # Calculate some statistics for the Shapelet residual image non_masked = N.where(~N.isnan(img.ch0_arr)) mean = N.mean(resid_shap[non_masked], axis=None) std_dev = N.std(resid_shap[non_masked], axis=None) skew = stats.skew(resid_shap[non_masked], axis=None) kurt = stats.kurtosis(resid_shap[non_masked], axis=None) mylog.info("Statistics of the Shapelet residual image:") mylog.info(" mean: %.3e (Jy/beam)" % mean) mylog.info(" std. dev: %.3e (Jy/beam)" % std_dev) mylog.info(" skew: %.3f" % skew) mylog.info(" kurtosis: %.3f" % kurt) img.completed_Ops.append('make_residimage') return img
def export_image(img, outfile=None, img_format='fits', img_type='gaus_resid', clobber=False): """Write an image to a file. Returns True if successful, False if not. outfile - name of resulting file; if None, file is named automatically. img_type - type of image to export; see below img_format - format of resulting file: 'fits' or 'casa' incl_wavelet - include wavelet Gaussians in model and residual images? clobber - overwrite existing file? The following images may be exported: 'ch0' - image used for source detection 'rms' - rms map image 'mean' - mean map image 'pi' - polarized intensity image 'gaus_resid' - Gaussian model residual image 'gaus_model' - Gaussian model image 'shap_resid' - Shapelet model residual image 'shap_model' - Shapelet model image 'psf_major' - PSF major axis FWHM image (FWHM in arcsec) 'psf_minor' - PSF minor axis FWHM image (FWHM in arcsec) 'psf_pa' - PSF position angle image (degrees east of north) 'psf_ratio' - PSF peak-to-total flux ratio (in units of 1/beam) 'psf_ratio_aper' - PSF peak-to-aperture flux ratio (in units of 1/beam) """ import os import functions as func from const import fwsig import mylogger mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "ExportImage") # First some checking: if not 'gausfit' in img.completed_Ops and 'gaus' in img_type: print '\033[91mERROR\033[0m: Gaussians have not been fit. Please run process_image first.' return False elif not 'shapelets' in img.completed_Ops and 'shap' in img_type: print '\033[91mERROR\033[0m: Shapelets have not been fit. Please run process_image first.' return False elif not 'polarisation' in img.completed_Ops and 'pi' in img_type: print '\033[91mERROR\033[0m: Polarization properties have not been calculated. Please run process_image first.' return False elif not 'psf_vary' in img.completed_Ops and 'psf' in img_type: print '\033[91mERROR\033[0m: PSF variations have not been calculated. Please run process_image first.' return False elif not 'collapse' in img.completed_Ops and 'ch0' in img_type: print '\033[91mERROR\033[0m: ch0 image has not been calculated. Please run process_image first.' return False elif not 'rmsimage' in img.completed_Ops and ('rms' in img_type or 'mean' in img_type): print '\033[91mERROR\033[0m: Mean and rms maps have not been calculated. Please run process_image first.' return False elif not 'make_residimage' in img.completed_Ops and ('resid' in img_type or 'model' in img_type): print '\033[91mERROR\033[0m: Residual and model maps have not been calculated. Please run process_image first.' return False format = img_format.lower() if (format in ['fits', 'casa']) == False: print '\033[91mERROR\033[0m: img_format must be "fits" or "casa"' return False if format == 'casa': print "\033[91mERROR\033[0m: Only img_format = 'fits' is supported at the moment" return False filename = outfile if filename == None or filename == '': filename = img.imagename + '_' + img_type + '.' + format if os.path.exists(filename) and clobber == False: print '\033[91mERROR\033[0m: File exists and clobber = False.' return False if format == 'fits': use_io = 'fits' if format == 'casa': use_io = 'rap' bdir = '' try: if img_type == 'ch0': func.write_image_to_file(use_io, filename, img.ch0_arr, img, bdir, clobber=clobber) elif img_type == 'rms': func.write_image_to_file(use_io, filename, img.rms_arr, img, bdir, clobber=clobber) elif img_type == 'mean': func.write_image_to_file(use_io, filename, img.mean_arr, img, bdir, clobber=clobber) elif img_type == 'pi': func.write_image_to_file(use_io, filename, img.ch0_pi_arr, img, bdir, clobber=clobber) elif img_type == 'psf_major': func.write_image_to_file(use_io, filename, img.psf_vary_maj_arr * fwsig, img, bdir, clobber=clobber) elif img_type == 'psf_minor': func.write_image_to_file(use_io, filename, img.psf_vary_min_arr * fwsig, img, bdir, clobber=clobber) elif img_type == 'psf_pa': func.write_image_to_file(use_io, filename, img.psf_vary_pa_arr, img, bdir, clobber=clobber) elif img_type == 'psf_ratio': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_arr, img, bdir, clobber=clobber) elif img_type == 'psf_ratio_aper': func.write_image_to_file(use_io, filename, img.psf_vary_ratio_aper_arr, img, bdir, clobber=clobber) elif img_type == 'gaus_resid': im = img.resid_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, clobber=clobber) elif img_type == 'gaus_model': im = img.model_gaus_arr func.write_image_to_file(use_io, filename, im, img, bdir, clobber=clobber) elif img_type == 'shap_resid': func.write_image_to_file(use_io, filename, img.resid_shap_arr, img, bdir, clobber=clobber) elif img_type == 'shap_model': func.write_image_to_file(use_io, filename, img.model_shap_arr, img, bdir, clobber=clobber) else: print "\n\033[91mERROR\033[0m: img_type not recognized." return False if filename == 'SAMP': print '--> Image sent to SMAP hub' else: print '--> Wrote file ' + repr(filename) return True except RuntimeError, err: # Catch and log error mylog.error(str(err)) # Re-throw error if the user is not in the interactive shell if img._is_interactive_shell: return False else: raise