Beispiel #1
0
    def fit_island_iteratively(self, img, isl, iter_ngmax=5, opts=None):
        """Fits an island iteratively.

        For large islands, which can require many Gaussians to fit well,
        it is much faster to fit a small number of Gaussians simultaneously
        and iterate. However, this does usually result in larger residuals.
        """
        import functions as func
        sgaul = []; sfgaul = []
        gaul = []; fgaul = []
        if opts == None:
            opts = img.opts
        thresh_isl = opts.thresh_isl
        thresh_pix = opts.thresh_pix
        thresh = opts.fittedimage_clip
        thr = isl.mean + thresh_isl * isl.rms
        rms = isl.rms

        if opts.verbose_fitting:
            print 'Iteratively fitting island ', isl.island_id
        gaul = []; fgaul = []
        ffimg_tot = N.zeros(isl.shape, dtype=N.float32)
        peak_val = N.max(isl.image - isl.islmean)
        while peak_val >= thr:
            sgaul, sfgaul = self.fit_island(isl, opts, img, ffimg=ffimg_tot, ngmax=iter_ngmax, ini_gausfit='simple')
            gaul = gaul + sgaul; fgaul = fgaul + sfgaul

            # Calculate residual image
            if len(sgaul) > 0:
                for g in sgaul:
                    gcopy = g[:]
                    gcopy[1] -= isl.origin[0]
                    gcopy[2] -= isl.origin[1]
                    S1, S2, Th = func.corrected_size(gcopy[3:6])
                    gcopy[3] = S1
                    gcopy[4] = S2
                    gcopy[5] = Th
                    A, C1, C2, S1, S2, Th = gcopy
                    shape = isl.shape
                    b = find_bbox(thresh*isl.rms, gcopy)
                    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(gcopy, x_ax, y_ax)
                    ffimg_tot[bbox] += ffimg
                peak_val_prev = peak_val
                peak_val = N.max(isl.image - isl.islmean - ffimg_tot)
                if func.approx_equal(peak_val, peak_val_prev):
                    break
            else:
                break

        if len(gaul) == 0:
            # Fitting iteratively did not work -- try normal fit
            gaul, fgaul = self.fit_island(isl, opts, img, ini_gausfit='default')

        return gaul, fgaul
Beispiel #2
0
    def __init__(self, img, gaussian, isl_idx, g_idx, flag=0):
        """Initialize Gaussian object from fitting data

        Parameters:
        img: PyBDSM image object
        gaussian: 6-tuple of fitted numbers
        isl_idx: island serial number
        g_idx: gaussian serial number
        flag: flagging (if any)
        """
        import functions as func
        from const import fwsig
        import numpy as N

        use_wcs = True
        self.gaussian_idx = g_idx
        self.gaus_num = 0 # stored later
        self.island_id = isl_idx
        self.jlevel = img.j
        self.flag = flag
        self.parameters = gaussian

        p = gaussian
        self.peak_flux = p[0]
        self.centre_pix = p[1:3]
        size = p[3:6]
        if func.approx_equal(size[0], img.pixel_beam()[0]*1.1) and \
                func.approx_equal(size[1], img.pixel_beam()[1]) and \
                func.approx_equal(size[2], img.pixel_beam()[2]+90.0) or \
                img.opts.fix_to_beam:
            # Check whether fitted Gaussian is just the distorted pixel beam
            # given as an initial guess or if size was fixed to the beam. If so,
            # reset the size to the undistorted beam.
            # Note: these are sigma sizes, not FWHM sizes.
            size = img.pixel_beam()
            size = (size[0], size[1], size[2]+90.0) # adjust angle so that corrected_size() works correctly
        size = func.corrected_size(size)  # gives fwhm and P.A.
        self.size_pix = size # FWHM in pixels and P.A. CCW from +y axis

        # Use img.orig_beam for flux calculation and deconvolution on wavelet
        # images, as img.beam has been altered to match the wavelet scale.
        # Note: these are all FWHM sizes.
        if img.waveletimage:
            bm_pix = N.array(img.beam2pix(img.orig_beam))
        else:
            bm_pix = N.array(img.beam2pix(img.beam))

        # Calculate fluxes, sky sizes, etc. All sizes are FWHM.
        tot = p[0]*size[0]*size[1]/(bm_pix[0]*bm_pix[1])
        if flag == 0:
            # These are good Gaussians
            errors = func.get_errors(img, p+[tot], img.islands[isl_idx].rms)
            self.centre_sky = img.pix2sky(p[1:3])
            self.centre_skyE = img.pix2coord(errors[1:3], self.centre_pix, use_wcs=use_wcs)
            self.size_sky = img.pix2gaus(size, self.centre_pix, use_wcs=use_wcs) # FWHM in degrees and P.A. east from north
            self.size_sky_uncorr = img.pix2gaus(size, self.centre_pix, use_wcs=False) # FWHM in degrees and P.A. east from +y axis
            self.size_skyE = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=use_wcs)
            self.size_skyE_uncorr = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=False)
            gaus_dc, err = func.deconv2(bm_pix, size)
            self.deconv_size_sky = img.pix2gaus(gaus_dc, self.centre_pix, use_wcs=use_wcs)
            self.deconv_size_sky_uncorr = img.pix2gaus(gaus_dc, self.centre_pix, use_wcs=False)
            self.deconv_size_skyE  = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=use_wcs)
            self.deconv_size_skyE_uncorr = img.pix2gaus(errors[3:6], self.centre_pix, use_wcs=False)
        else:
            # These are flagged Gaussians, so don't calculate sky values or errors
            errors = [0]*7
            self.centre_sky = [0., 0.]
            self.centre_skyE = [0., 0.]
            self.size_sky = [0., 0., 0.]
            self.size_sky_uncorr = [0., 0., 0.]
            self.size_skyE = [0., 0.]
            self.size_skyE_uncorr = [0., 0., 0.]
            self.deconv_size_sky = [0., 0., 0.]
            self.deconv_size_sky_uncorr = [0., 0., 0.]
            self.deconv_size_skyE  = [0., 0., 0.]
            self.deconv_size_skyE_uncorr = [0., 0., 0.]
        self.total_flux = tot
        self.total_fluxE = errors[6]
        self.peak_fluxE = errors[0]
        self.total_fluxE = errors[6]
        self.centre_pixE = errors[1:3]
        self.size_pixE = errors[3:6]
        self.rms = img.islands[isl_idx].rms
        self.mean = img.islands[isl_idx].mean
        self.total_flux_isl = img.islands[isl_idx].total_flux
        self.total_flux_islE = img.islands[isl_idx].total_fluxE
Beispiel #3
0
    def __call__(self, img):
        mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Preprocess")
        bstat = func.bstat
        if img.opts.kappa_clip is None:
            kappa = -img.pixel_beamarea()
        else:
            kappa = img.opts.kappa_clip

        if img.opts.polarisation_do:
          pols = ['I', 'Q', 'U', 'V']
          ch0images = [img.ch0_arr, img.ch0_Q_arr, img.ch0_U_arr, img.ch0_V_arr]
          img.clipped_mean_QUV = []
          img.clipped_rms_QUV = []
        else:
          pols = ['I'] # assume I is always present
          ch0images = [img.ch0_arr]

        if hasattr(img, 'rms_mask'):
            mask = img.rms_mask
        else:
            mask = img.mask_arr
        opts = img.opts

        for ipol, pol in enumerate(pols):
          image = ch0images[ipol]

          ### basic stats
          mean, rms, cmean, crms, cnt = bstat(image, mask, kappa)
          if cnt > 198: cmean = mean; crms = rms
          if pol == 'I':
            if func.approx_equal(crms, 0.0, rel=None):
                raise RuntimeError('Clipped rms appears to be zero. Check for regions '\
                                       'with values of 0 and\nblank them (with NaNs) '\
                                       'or use trim_box to exclude them.')
            img.raw_mean    = mean
            img.raw_rms     = rms
            img.clipped_mean= cmean
            img.clipped_rms = crms
            mylog.info('%s %.4f %s %.4f %s ' % ("Raw mean (Stokes I) = ", mean*1000.0, \
                       'mJy and raw rms = ',rms*1000.0, 'mJy'))
            mylog.info('%s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes I) = ", cmean*1000.0, \
                       'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))
          else:
            img.clipped_mean_QUV.append(cmean)
            img.clipped_rms_QUV.append(crms)
            mylog.info('%s %s %s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes ", pol, ") = ", cmean*1000.0, \
                       'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))

        image = img.ch0_arr
        # Check if pixels are outside the universe
        if opts.check_outsideuniv:
            mylogger.userinfo(mylog, "Checking for pixels outside the universe")
            noutside_univ = self.outside_univ(img)
            img.noutside_univ = noutside_univ
            frac_blank = round(float(noutside_univ)/float(image.shape[0]*image.shape[1]),3)
            mylogger.userinfo(mylog, "Number of additional pixels blanked", str(noutside_univ)
                              +' ('+str(frac_blank*100.0)+'%)')
        else:
            noutside_univ = 0

        # If needed, (re)mask the image
        if noutside_univ > 0:
            mask = N.isnan(img.ch0_arr)
            masked = mask.any()
            img.masked = masked
            if masked:
                img.mask_arr = mask
            img.blankpix = N.sum(mask)


        ### max/min pixel value & coordinates
        shape = image.shape[0:2]
        if mask != None:
            img.blankpix = N.sum(mask)
        if img.blankpix == 0:
          max_idx = image.argmax()
          min_idx = image.argmin()
        else:
          max_idx = N.nanargmax(image)
          min_idx = N.nanargmin(image)

        img.maxpix_coord = N.unravel_index(max_idx, shape)
        img.minpix_coord = N.unravel_index(min_idx, shape)
        img.max_value    = image.flat[max_idx]
        img.min_value    = image.flat[min_idx]

        ### Solid angle of the image
        cdelt = N.array(img.wcs_obj.acdelt[:2])
        img.omega = N.product(shape)*abs(N.product(cdelt))/(180.*180./pi/pi)

        ### Total flux in ch0 image
        if 'atrous' in img.filename or img._pi or img.log == 'Detection image':
            # Don't do this estimate for atrous wavelet images
            # or polarized intensity image,
            # as it doesn't give the correct flux. Also, ignore
            # the flux in the detection image, as it's likely
            # wrong (e.g., not corrected for the primary beam).
            img.ch0_sum_jy = 0
        else:
            im_flux = N.nansum(image)/img.pixel_beamarea() # Jy
            img.ch0_sum_jy = im_flux
            mylogger.userinfo(mylog, 'Flux from sum of (non-blank) pixels',
                              '%.3f Jy' % (im_flux,))

        ### if image seems confused, then take background mean as zero instead
        alpha_sourcecounts = 2.5  # approx diff src count slope. 2.2?
        if opts.bmpersrc_th is None:
          n = (image >= 5.*crms).sum()
          if n <= 0:
            n = 1
            mylog.info('No pixels in image > 5-sigma.')
            mylog.info('Taking number of pixels above 5-sigma as 1.')
          img.bmpersrc_th = N.product(shape)/((alpha_sourcecounts-1.)*n)
          mylog.info('%s %6.2f' % ('Estimated bmpersrc_th = ', img.bmpersrc_th))
        else:
          img.bmpersrc_th = opts.bmpersrc_th
          mylog.info('%s %6.2f' % ('Taking default bmpersrc_th = ', img.bmpersrc_th))

        confused = False
        if opts.mean_map == 'default':
          if opts.bmpersrc_th <= 25. or cmean/crms >= 0.1:
            confused = True
        img.confused = confused
        mylog.info('Parameter confused is '+str(img.confused))

        img.completed_Ops.append('preprocess')
        return img
    def __call__(self, img):
        mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Preprocess")
        bstat = func.bstat
        if img.opts.kappa_clip is None:
            kappa = -img.pixel_beamarea()
        else:
            kappa = img.opts.kappa_clip

        if img.opts.polarisation_do:
            pols = ['I', 'Q', 'U', 'V']
            ch0images = [
                img.ch0_arr, img.ch0_Q_arr, img.ch0_U_arr, img.ch0_V_arr
            ]
            img.clipped_mean_QUV = []
            img.clipped_rms_QUV = []
        else:
            pols = ['I']  # assume I is always present
            ch0images = [img.ch0_arr]

        if hasattr(img, 'rms_mask'):
            mask = img.rms_mask
        else:
            mask = img.mask_arr
        opts = img.opts

        for ipol, pol in enumerate(pols):
            image = ch0images[ipol]

            ### basic stats
            mean, rms, cmean, crms, cnt = bstat(image, mask, kappa)
            if cnt > 198:
                cmean = mean
                crms = rms
            if pol == 'I':
                if func.approx_equal(crms, 0.0, rel=None):
                    raise RuntimeError('Clipped rms appears to be zero. Check for regions '\
                                           'with values of 0 and\nblank them (with NaNs) '\
                                           'or use trim_box to exclude them.')
                img.raw_mean = mean
                img.raw_rms = rms
                img.clipped_mean = cmean
                img.clipped_rms = crms
                mylog.info('%s %.4f %s %.4f %s ' % ("Raw mean (Stokes I) = ", mean*1000.0, \
                           'mJy and raw rms = ',rms*1000.0, 'mJy'))
                mylog.info('%s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes I) = ", cmean*1000.0, \
                           'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))
            else:
                img.clipped_mean_QUV.append(cmean)
                img.clipped_rms_QUV.append(crms)
                mylog.info('%s %s %s %.4f %s %s %.4f %s ' % ("sigma clipped mean (Stokes ", pol, ") = ", cmean*1000.0, \
                           'mJy and ','sigma clipped rms = ',crms*1000.0, 'mJy'))

        image = img.ch0_arr
        # Check if pixels are outside the universe
        if opts.check_outsideuniv:
            mylogger.userinfo(mylog,
                              "Checking for pixels outside the universe")
            noutside_univ = self.outside_univ(img)
            img.noutside_univ = noutside_univ
            frac_blank = round(
                float(noutside_univ) / float(image.shape[0] * image.shape[1]),
                3)
            mylogger.userinfo(
                mylog, "Number of additional pixels blanked",
                str(noutside_univ) + ' (' + str(frac_blank * 100.0) + '%)')
        else:
            noutside_univ = 0

        # If needed, (re)mask the image
        if noutside_univ > 0:
            mask = N.isnan(img.ch0_arr)
            masked = mask.any()
            img.masked = masked
            if masked:
                img.mask_arr = mask
            img.blankpix = N.sum(mask)

        ### max/min pixel value & coordinates
        shape = image.shape[0:2]
        if mask != None:
            img.blankpix = N.sum(mask)
        if img.blankpix == 0:
            max_idx = image.argmax()
            min_idx = image.argmin()
        else:
            max_idx = N.nanargmax(image)
            min_idx = N.nanargmin(image)

        img.maxpix_coord = N.unravel_index(max_idx, shape)
        img.minpix_coord = N.unravel_index(min_idx, shape)
        img.max_value = image.flat[max_idx]
        img.min_value = image.flat[min_idx]

        ### Solid angle of the image
        cdelt = N.array(img.wcs_obj.acdelt[:2])
        img.omega = N.product(shape) * abs(
            N.product(cdelt)) / (180. * 180. / pi / pi)

        ### Total flux in ch0 image
        if 'atrous' in img.filename or img._pi or img.log == 'Detection image':
            # Don't do this estimate for atrous wavelet images
            # or polarized intensity image,
            # as it doesn't give the correct flux. Also, ignore
            # the flux in the detection image, as it's likely
            # wrong (e.g., not corrected for the primary beam).
            img.ch0_sum_jy = 0
        else:
            im_flux = N.nansum(image) / img.pixel_beamarea()  # Jy
            img.ch0_sum_jy = im_flux
            mylogger.userinfo(mylog, 'Flux from sum of (non-blank) pixels',
                              '%.3f Jy' % (im_flux, ))

        ### if image seems confused, then take background mean as zero instead
        alpha_sourcecounts = 2.5  # approx diff src count slope. 2.2?
        if opts.bmpersrc_th is None:
            n = (image >= 5. * crms).sum()
            if n <= 0:
                n = 1
                mylog.info('No pixels in image > 5-sigma.')
                mylog.info('Taking number of pixels above 5-sigma as 1.')
            img.bmpersrc_th = N.product(shape) / (
                (alpha_sourcecounts - 1.) * n)
            mylog.info('%s %6.2f' %
                       ('Estimated bmpersrc_th = ', img.bmpersrc_th))
        else:
            img.bmpersrc_th = opts.bmpersrc_th
            mylog.info('%s %6.2f' %
                       ('Taking default bmpersrc_th = ', img.bmpersrc_th))

        confused = False
        if opts.mean_map == 'default':
            if opts.bmpersrc_th <= 25. or cmean / crms >= 0.1:
                confused = True
        img.confused = confused
        mylog.info('Parameter confused is ' + str(img.confused))

        img.completed_Ops.append('preprocess')
        return img