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
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
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