Exemple #1
0
    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
Exemple #3
0
    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')
Exemple #5
0
    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
Exemple #6
0
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
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
    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')
Exemple #10
0
    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')
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #13
0
    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")
Exemple #14
0
    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')
Exemple #15
0
    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
Exemple #16
0
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