def belongs(self, img, isl): import functions as func # get centroid of island (as integer) mom = func.momanalmask_gaus(isl.image, isl.mask_active, 0, 1.0, False) cen = N.array(mom[1:3]) + isl.origin icen = (int(round(cen[0])), int(round(cen[1]))) belong = False # check if lies within any island of self for i, pyrisl in enumerate(self.islands): if N.sum([pyrisl.bbox[j].start <= cen[j] < pyrisl.bbox[j].stop for j in range(2)]) == 2: pix = tuple([cen[j] - pyrisl.origin[j] for j in range(2)]) if not pyrisl.mask_active[pix]: belong = True return belong
def inigaus_nobeam(self, isl, thr, beam, img): """ To get initial guesses when the source sizes are very different from the beam, and can also be elongated. Mainly in the context of a-trous transform images. Need to arrive at a good guess of the sizes and hence need to partition the image around the maxima first. Tried the IFT watershed algo but with markers, it segments the island only around the minima and not the whole island. Cant find a good weighting scheme for tesselation either. Hence will try this : Calculate number of maxima. If one, then take moment as initial guess. If more than one, then moment of whole island is one of the guesses if mom1 is within n pixels of one of the maxima. Else dont take whole island moment. Instead, find minima on lines connecting all maxima and use geometric mean of all minima of a peak as the size of that peak. """ from math import sqrt from const import fwsig import scipy.ndimage as nd import functions as func im = isl.image-isl.islmean if img.opts.ini_method == 'curvature': im_pos = -1.0 * func.make_curvature_map(isl.image-isl.islmean) thr_pos = 0.0 else: im_pos = im thr_pos = -1e9 mask = isl.mask_active av = img.clipped_mean inipeak, iniposn, im1 = func.get_maxima(im, mask, thr_pos, isl.shape, beam, im_pos=im_pos) npeak = len(iniposn) gaul = [] av, stdnew, maxv, maxp, minv, minp = func.arrstatmask(im, mask) mom = func.momanalmask_gaus(isl.image-isl.islmean, isl.mask_active, 0, 1.0, True) if npeak <= 1: g = (float(maxv), int(round(mom[1])), int(round(mom[2])), mom[3]/fwsig, \ mom[4]/fwsig, mom[5]) gaul.append(g) if npeak > 1: # markers start from 1=background, watershed starts from 1=background watershed, markers = func.watershed(im, mask=isl.mask_active) nshed = N.max(markers)-1 # excluding background xm, ym = N.transpose([N.where(markers==i) for i in range(1,nshed+2)])[0] coords = [c for c in N.transpose([xm,ym])[1:]] alldists = [func.dist_2pt(c1, c2) for c1 in coords for c2 in coords if N.any(c1!=c2)] # has double meandist = N.mean(alldists) # mean dist between all pairs of markers compact = []; invmask = [] for ished in range(nshed): shedmask = N.where(watershed==ished+2, False, True) + isl.mask_active # good unmasked pixels = 0 imm = nd.binary_dilation(~shedmask, N.ones((3,3), int)) xbad, ybad = N.where((imm==1)*(im>im[xm[ished+1], ym[ished+1]])) imm[xbad, ybad] = 0 invmask.append(imm); x, y = N.where(imm); xcen, ycen = N.mean(x), N.mean(y) # good pixels are now = 1 dist = func.dist_2pt([xcen, ycen], [xm[ished+1], ym[ished+1]]) if dist < max(3.0, meandist/4.0): compact.append(True) # if not compact, break source + diffuse else: compact.append(False) if not N.all(compact): avsize = [] ind = N.where(compact)[0] for i in ind: avsize.append(N.sum(invmask[i])) avsize = sqrt(N.mean(N.array(avsize))) for i in range(len(compact)): if not compact[i]: # make them all compact newmask = N.zeros(imm.shape, bool) newmask[max(0,xm[i+1]-avsize/2):min(im.shape[0],xm[i+1]+avsize/2), \ max(0,ym[i+1]-avsize/2):min(im.shape[1],ym[i+1]+avsize/2)] = True invmask[i] = invmask[i]*newmask resid = N.zeros(im.shape, dtype=N.float32) # approx fit all compact ones for i in range(nshed): mask1 = ~invmask[i] size = sqrt(N.sum(invmask))/fwsig xf, yf = coords[i][0], coords[i][1] p_ini = [im[xf, yf], xf, yf, size, size, 0.0] x, y = N.indices(im.shape) p, success = func.fit_gaus2d(im*invmask[i], p_ini, x, y) resid = resid + func.gaus_2d(p, x, y) gaul.append(p) resid = im - resid if not N.all(compact): # just add one gaussian to fit whole unmasked island maxv = N.max(resid) # assuming resid has only diffuse emission. can be false x, y = N.where(~isl.mask_active); xcen = N.mean(x); ycen = N.mean(y) invm = ~isl.mask_active #bound = invm - nd.grey_erosion(invm, footprint = N.ones((3,3), int)) # better to use bound for ellipse fitting mom = func.momanalmask_gaus(invm, N.zeros(invm.shape, dtype=N.int16), 0, 1.0, True) g = (maxv, xcen, ycen, mom[3]/fwsig, mom[4]/fwsig, mom[5]-90.) gaul.append(g) coords.append([xcen, ycen]) return gaul
def fit_island(self, isl, opts, img, ngmax=None, ffimg=None, ini_gausfit=None): """Fit island with a set of 2D gaussians. Parameters: isl: island opts: Opts structure of the image beam: beam parameters which are used as an initial guess for gaussian shape Returns: Function returns 2 lists with parameters of good and flagged gaussians. Gaussian parameters are updated to be image-relative. Note: "fitok" indicates whether fit converged and one or more flagged Gaussians indicate that significant residuals remain (peak > thr). """ from _cbdsm import MGFunction import functions as func from const import fwsig if ffimg == None: fit_image = isl.image-isl.islmean else: fit_image = isl.image-isl.islmean-ffimg fcn = MGFunction(fit_image, isl.mask_active, 1) # For fitting, use img.beam instead of img.pixel_beam, as we want # to pick up the wavelet beam (img.pixel_beam is not changed for # wavelet images, but img.beam is) beam = N.array(img.beam2pix(img.beam)) beam = (beam[0]/fwsig, beam[1]/fwsig, beam[2]+90.0) # change angle from +y-axis to +x-axis and FWHM to sigma if abs(beam[0]/beam[1]) < 1.1: beam = (1.1*beam[0], beam[1], beam[2]) thr1 = isl.mean + opts.thresh_isl*isl.rms thr2 = isl.mean + img.thresh_pix*isl.rms thr0 = thr1 verbose = opts.verbose_fitting g3_only = opts.fix_to_beam peak = fcn.find_peak()[0] dof = isl.size_active shape = isl.shape isl_image = isl.image - isl.islmean size = isl.size_active/img.pixel_beamarea()*2.0 gaul = [] iter = 0 ng1 = 0 if ini_gausfit == None: ini_gausfit = opts.ini_gausfit if ini_gausfit not in ['default', 'simple', 'nobeam']: ini_gausfit = 'default' if ini_gausfit == 'simple' and ngmax == None: ngmax = 25 if ini_gausfit == 'default' or opts.fix_to_beam: gaul, ng1, ngmax = self.inigaus_fbdsm(isl, thr0, beam, img) if ini_gausfit == 'nobeam' and not opts.fix_to_beam: gaul = self.inigaus_nobeam(isl, thr0, beam, img) ng1 = len(gaul); ngmax = ng1+2 while iter < 5: iter += 1 fitok = self.fit_iter(gaul, ng1, fcn, dof, beam, thr0, iter, ini_gausfit, ngmax, verbose, g3_only) gaul, fgaul = self.flag_gaussians(fcn.parameters, opts, beam, thr0, peak, shape, isl.mask_active, isl.image, size) ng1 = len(gaul) if fitok and len(fgaul) == 0: break if (not fitok or len(gaul) == 0) and ini_gausfit != 'simple': # If fits using default or nobeam methods did not work, # try using simple instead gaul = [] iter = 0 ng1 = 0 ngmax = 25 while iter < 5: iter += 1 fitok = self.fit_iter(gaul, ng1, fcn, dof, beam, thr0, iter, 'simple', ngmax, verbose, g3_only) gaul, fgaul = self.flag_gaussians(fcn.parameters, opts, beam, thr0, peak, shape, isl.mask_active, isl.image, size) ng1 = len(gaul) if fitok and len(fgaul) == 0: break sm_isl = nd.binary_dilation(isl.mask_active) if (not fitok or len(gaul) == 0) and N.sum(~sm_isl) >= img.minpix_isl: # If fitting still fails, shrink the island a little and try again fcn = MGFunction(fit_image, nd.binary_dilation(isl.mask_active), 1) gaul = [] iter = 0 ng1 = 0 ngmax = 25 while iter < 5: iter += 1 fitok = self.fit_iter(gaul, ng1, fcn, dof, beam, thr0, iter, 'simple', ngmax, verbose, g3_only) gaul, fgaul = self.flag_gaussians(fcn.parameters, opts, beam, thr0, peak, shape, isl.mask_active, isl.image, size) ng1 = len(gaul) if fitok and len(fgaul) == 0: break lg_isl = nd.binary_erosion(isl.mask_active) if (not fitok or len(gaul) == 0) and N.sum(~lg_isl) >= img.minpix_isl: # If fitting still fails, expand the island a little and try again fcn = MGFunction(fit_image, nd.binary_erosion(isl.mask_active), 1) gaul = [] iter = 0 ng1 = 0 ngmax = 25 while iter < 5: iter += 1 fitok = self.fit_iter(gaul, ng1, fcn, dof, beam, thr0, iter, 'simple', ngmax, verbose, g3_only) gaul, fgaul = self.flag_gaussians(fcn.parameters, opts, beam, thr0, peak, shape, isl.mask_active, isl.image, size) ng1 = len(gaul) if fitok and len(fgaul) == 0: break if not fitok or len(gaul) == 0: # If all else fails, try to use moment analysis inisl = N.where(~isl.mask_active) mask_id = N.zeros(isl.image.shape, dtype=N.int32) - 1 mask_id[inisl] = isl.island_id try: pixel_beamarea = img.pixel_beamarea() mompara = func.momanalmask_gaus(fit_image, mask_id, isl.island_id, pixel_beamarea, True) mompara[5] += 90.0 if not N.isnan(mompara[1]) and not N.isnan(mompara[2]): x1 = N.int(N.floor(mompara[1])) y1 = N.int(N.floor(mompara[2])) xind = slice(x1, x1+2, 1); yind = slice(y1, y1+2, 1) t=(mompara[1]-x1)/(x1+1-x1) u=(mompara[2]-y1)/(y1+1-y1) s_peak=(1.0-t)*(1.0-u)*fit_image[x1,y1]+t*(1.0-u)*fit_image[x1+1,y1]+ \ t*u*fit_image[x1+1,y1+1]+(1.0-t)*u*fit_image[x1,y1+1] mompara[0] = s_peak par = [mompara.tolist()] par[3] /= fwsig par[4] /= fwsig gaul, fgaul = self.flag_gaussians(par, opts, beam, thr0, peak, shape, isl.mask_active, isl.image, size) except: pass ### return whatever we got isl.mg_fcn = fcn gaul = [self.fixup_gaussian(isl, g) for g in gaul] fgaul = [(flag, self.fixup_gaussian(isl, g)) for flag, g in fgaul] if verbose: print 'Number of good Gaussians: %i' % (len(gaul),) print 'Number of flagged Gaussians: %i' % (len(fgaul),) return gaul, fgaul
def process_Multiple(self, img, g_sublist, mask, src_index, isrc, subim, isl, delc, subn, subm): """ Same as gaul_to_source.f. isrc is same as k in the fortran version. """ from math import pi, sqrt from const import fwsig from scipy import ndimage import functions as func mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Gaul2Srl ") dum = img.beam[0] * img.beam[1] cdeltsq = img.wcs_obj.acdelt[0] * img.wcs_obj.acdelt[1] bmar_p = 2.0 * pi * dum / (cdeltsq * fwsig * fwsig) # try subim_src = self.make_subim(subn, subm, g_sublist, delc) mompara = func.momanalmask_gaus(subim_src, mask, isrc, bmar_p, True) # initial peak posn and value maxv = N.max(subim_src) maxx, maxy = N.unravel_index(N.argmax(subim_src), subim_src.shape) # fit gaussian around this posn blc = N.zeros(2) trc = N.zeros(2) n, m = subim_src.shape[0:2] bm_pix = N.array([ img.pixel_beam()[0] * fwsig, img.pixel_beam()[1] * fwsig, img.pixel_beam()[2] ]) ssubimsize = max(N.int(N.round(N.max(bm_pix[0:2]) * 2)) + 1, 5) blc[0] = max(0, maxx - (ssubimsize - 1) / 2) blc[1] = max(0, maxy - (ssubimsize - 1) / 2) trc[0] = min(n, maxx + (ssubimsize - 1) / 2) trc[1] = min(m, maxy + (ssubimsize - 1) / 2) s_imsize = trc - blc + 1 p_ini = [maxv, (s_imsize[0]-1)/2.0*1.1, (s_imsize[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, \ bm_pix[1]/fwsig*1.1, bm_pix[2]*2] data = subim_src[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] smask = mask[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] rmask = N.where(smask == isrc, False, True) x_ax, y_ax = N.indices(data.shape) if N.sum(~rmask) >= 6: para, ierr = func.fit_gaus2d(data, p_ini, x_ax, y_ax, rmask) if (0.0<para[1]<s_imsize[0]) and (0.0<para[2]<s_imsize[1]) and \ para[3]<s_imsize[0] and para[4]<s_imsize[1]: maxpeak = para[0] else: maxpeak = maxv posn = para[1:3] - (0.5 * N.sum(s_imsize) - 1) / 2.0 + N.array( [maxx, maxy]) - 1 + delc else: maxpeak = maxv posn = N.unravel_index(N.argmax(data * ~rmask), data.shape) + N.array(delc) + blc # calculate peak by bilinear interpolation around centroid # First check that moment analysis gave a valid position. If not, use # posn from gaussian fit instead. if N.isnan(mompara[1]): mompara[1] = posn[0] - delc[0] x1 = N.int(N.floor(mompara[1])) if N.isnan(mompara[2]): mompara[2] = posn[1] - delc[1] y1 = N.int(N.floor(mompara[2])) xind = slice(x1, x1 + 2, 1) yind = slice(y1, y1 + 2, 1) if img.opts.flag_smallsrc and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Island = ' + str(isl.island_id)) mylog.debug('Mask = ' + repr(mask[xind, yind]) + 'xind, yind, x1, y1 = ' + repr(xind) + ' ' + repr(yind) + ' ' + repr(x1) + ' ' + repr(y1)) t = (mompara[1] - x1) / (x1 + 1 - x1) # in case u change it later u = (mompara[2] - y1) / (y1 + 1 - y1) s_peak=(1.0-t)*(1.0-u)*subim_src[x1,y1]+t*(1.0-u)*subim_src[x1+1,y1]+ \ t*u*subim_src[x1+1,y1+1]+(1.0-t)*u*subim_src[x1,y1+1] if (not img.opts.flag_smallsrc) and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Speak ' + repr(s_peak) + 'Mompara = ' + repr(mompara)) mylog.debug('x1, y1 : ' + repr(x1) + ', ' + repr(y1)) # import pylab as pl # pl.imshow(N.transpose(subim_src), origin='lower', interpolation='nearest') # pl.suptitle('Image of bad M source '+str(isl.island_id)) # convert pixels to coords try: sra, sdec = img.pix2sky( [mompara[1] + delc[0], mompara[2] + delc[1]]) mra, mdec = img.pix2sky(posn) except RuntimeError, err: # Invalid pixel wcs coordinate sra, sdec = 0.0, 0.0 mra, mdec = 0.0, 0.0
class Op_gaul2srl(Op): """ Slightly modified from fortran. """ def __call__(self, img): # for each island, get the gaussians into a list and then send them to process # src_index is source number, starting from 0 mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Gaul2Srl") mylogger.userinfo(mylog, 'Grouping Gaussians into sources') img.aperture = img.opts.aperture if img.aperture is not None and img.aperture <= 0.0: mylog.warn('Specified aperture is <= 0. Skipping aperture fluxes.') img.aperture = None src_index = -1 dsrc_index = 0 sources = [] dsources = [] no_gaus_islands = [] for iisl, isl in enumerate(img.islands): isl_sources = [] isl_dsources = [] g_list = [] for g in isl.gaul: if g.flag == 0: g_list.append(g) if len(g_list) > 0: if len(g_list) == 1: src_index, source = self.process_single_gaussian(img, g_list, src_index, code='S') sources.append(source) isl_sources.append(source) else: src_index, source = self.process_CM( img, g_list, isl, src_index) sources.extend(source) isl_sources.extend(source) else: if not img.waveletimage: dg = isl.dgaul[0] no_gaus_islands.append( (isl.island_id, dg.centre_pix[0], dg.centre_pix[1])) # Put in the dummy Source as the source and use negative IDs g_list = isl.dgaul dsrc_index, dsource = self.process_single_gaussian( img, g_list, dsrc_index, code='S') dsources.append(dsource) isl_dsources.append(dsource) isl.sources = isl_sources isl.dsources = isl_dsources img.sources = sources img.dsources = dsources img.nsrc = src_index + 1 mylogger.userinfo(mylog, "Number of sources formed from Gaussians", str(img.nsrc)) if not img.waveletimage and not img._pi and len( no_gaus_islands) > 0 and not img.opts.quiet: message = 'All Gaussians were flagged for the following island' if len(no_gaus_islands) == 1: message += ':\n' else: message += 's:\n' for isl_id in no_gaus_islands: message += ' Island #%i (x=%i, y=%i)\n' % isl_id if len(no_gaus_islands) == 1: message += 'Please check this island. If it is a valid island and\n' else: message += 'Please check these islands. If they are valid islands and\n' if img.opts.atrous_do: message += 'should be fit, try adjusting the flagging options (use\n'\ 'show_fit with "ch0_flagged=True" to see the flagged Gaussians).' else: message += 'should be fit, try adjusting the flagging options (use\n'\ 'show_fit with "ch0_flagged=True" to see the flagged Gaussians)\n'\ 'or enabling the wavelet module (with "atrous_do=True").' message += '\nTo include empty islands in output source catalogs, set\n'\ 'incl_empty=True in the write_catalog task.' mylog.warning(message) img.completed_Ops.append('gaul2srl') ################################################################################################# def process_single_gaussian(self, img, g_list, src_index, code): """ Process single gaussian into a source, for both S and C type sources. g is just one Gaussian object (not a list).""" g = g_list[0] total_flux = [g.total_flux, g.total_fluxE] peak_flux_centroid = peak_flux_max = [g.peak_flux, g.peak_fluxE] posn_sky_centroid = posn_sky_max = [g.centre_sky, g.centre_skyE] size_sky = [g.size_sky, g.size_skyE] size_sky_uncorr = [g.size_sky_uncorr, g.size_skyE] deconv_size_sky = [g.deconv_size_sky, g.deconv_size_skyE] deconv_size_sky_uncorr = [g.deconv_size_sky_uncorr, g.deconv_size_skyE] bbox = img.islands[g.island_id].bbox ngaus = 1 island_id = g.island_id if g.gaus_num < 0: gaussians = [] else: gaussians = list([g]) aper_flux = func.ch0_aperture_flux(img, g.centre_pix, img.aperture) source_prop = list([code, total_flux, peak_flux_centroid, peak_flux_max, aper_flux, posn_sky_centroid, \ posn_sky_max, size_sky, size_sky_uncorr, deconv_size_sky, deconv_size_sky_uncorr, bbox, ngaus, island_id, gaussians]) source = Source(img, source_prop) if g.gaussian_idx == -1: src_index -= 1 else: src_index += 1 g.source_id = src_index g.code = code source.source_id = src_index return src_index, source ################################################################################################## def process_CM(self, img, g_list, isl, src_index): """ Bundle errors with the quantities. ngau = number of gaussians in island src_id = the source index array for every gaussian in island nsrc = final number of distinct sources in the island """ ngau = len(g_list) # same as cisl in callgaul2srl.f nsrc = ngau # same as islct; initially make each gaussian as a source src_id = N.arange(nsrc) # same as islnum in callgaul2srl.f boxx, boxy = isl.bbox subn = boxx.stop - boxx.start subm = boxy.stop - boxy.start delc = [boxx.start, boxy.start] subim = self.make_subim(subn, subm, g_list, delc) index = [(i, j) for i in range(ngau) for j in range(ngau) if j > i] for pair in index: same_island = self.in_same_island(pair, img, g_list, isl, subim, subn, subm, delc) if same_island: nsrc -= 1 mmax, mmin = max(src_id[pair[0]], src_id[pair[1]]), min(src_id[pair[0]], src_id[pair[1]]) arr = N.where(src_id == mmax)[0] src_id[arr] = mmin # now reorder src_id so that it is contiguous for i in range(ngau): ind1 = N.where(src_id == i)[0] if len(ind1) == 0: arr = N.where(src_id > i)[0] if len(arr) > 0: decr = N.min(src_id[arr]) - i for j in arr: src_id[j] -= decr nsrc = N.max(src_id) + 1 # now do whats in sub_calc_para_source source_list = [] for isrc in range(nsrc): posn = N.where(src_id == isrc)[0] g_sublist = [] for i in posn: g_sublist.append(g_list[i]) ngau_insrc = len(posn) # Do source type C if ngau_insrc == 1: src_index, source = self.process_single_gaussian(img, g_sublist, src_index, code='C') else: # make mask and subim. Invalid mask value is -1 since 0 is valid srcid mask = self.make_mask(isl, subn, subm, 1, isrc, g_sublist, delc) src_index, source = self.process_Multiple(img, g_sublist, mask, src_index, isrc, subim, \ isl, delc, subn, subm) source_list.append(source) return src_index, source_list ################################################################################################## def in_same_island(self, pair, img, g_list, isl, subim, subn, subm, delc): """ Whether two gaussians belong to the same source or not. """ import functions as func def same_island_min(pair, g_list, subim, delc, tol=0.5): """ If the minimum of the reconstructed fluxes along the line joining the peak positions is greater than thresh_isl times the rms_clip, they belong to different islands. """ g1 = g_list[pair[0]] g2 = g_list[pair[1]] pix1 = N.array(g1.centre_pix) pix2 = N.array(g2.centre_pix) x1, y1 = N.floor(pix1) - delc x2, y2 = N.floor(pix2) - delc pix1 = N.array( N.unravel_index(N.argmax(subim[x1:x1 + 2, y1:y1 + 2]), (2, 2))) + [x1, y1] pix2 = N.array( N.unravel_index(N.argmax(subim[x2:x2 + 2, y2:y2 + 2]), (2, 2))) + [x2, y2] if pix1[1] >= subn: pix1[1] = pix1[1] - 1 if pix2[1] >= subm: pix2[1] = pix2[1] - 1 maxline = int(round(N.max(N.abs(pix1 - pix2) + 1))) flux1 = g1.peak_flux flux2 = g2.peak_flux # get pix values of the line pixdif = pix2 - pix1 same_island_min = False same_island_cont = False if maxline == 1: same_island_min = True same_island_cont = True else: if abs(pixdif[0]) > abs(pixdif[1]): xline = N.round(min(pix1[0], pix2[0]) + N.arange(maxline)) yline = N.round((pix1[1]-pix2[1])/(pix1[0]-pix2[0])* \ (min(pix1[0],pix2[0])+N.arange(maxline)-pix1[0])+pix1[1]) else: yline = N.round(min(pix1[1], pix2[1]) + N.arange(maxline)) xline = N.round((pix1[0]-pix2[0])/(pix1[1]-pix2[1])* \ (min(pix1[1],pix2[1])+N.arange(maxline)-pix1[1])+pix1[0]) rpixval = N.zeros(maxline, dtype=N.float32) xbig = N.where(xline >= N.size(subim, 0)) xline[xbig] = N.size(subim, 0) - 1 ybig = N.where(yline >= N.size(subim, 1)) yline[ybig] = N.size(subim, 1) - 1 for i in range(maxline): pixval = subim[xline[i], yline[i]] rpixval[i] = pixval min_pixval = N.min(rpixval) minind_p = N.argmin(rpixval) maxind_p = N.argmax(rpixval) if minind_p in (0, maxline - 1) and maxind_p in (0, maxline - 1): same_island_cont = True if min_pixval >= min(flux1, flux2): same_island_min = True elif abs(min_pixval - min(flux1, flux2) ) <= tol * isl.rms * img.opts.thresh_isl: same_island_min = True return same_island_min, same_island_cont def same_island_dist(pair, g_list, tol=0.5): """ If the centres are seperated by a distance less than half the sum of their fwhms along the PA of the line joining them, they belong to the same island. """ from math import sqrt g1 = g_list[pair[0]] g2 = g_list[pair[1]] pix1 = N.array(g1.centre_pix) pix2 = N.array(g2.centre_pix) gsize1 = g1.size_pix gsize2 = g2.size_pix fwhm1 = func.gdist_pa(pix1, pix2, gsize1) fwhm2 = func.gdist_pa(pix1, pix2, gsize2) dx = pix2[0] - pix1[0] dy = pix2[1] - pix1[1] dist = sqrt(dy * dy + dx * dx) if dist <= tol * (fwhm1 + fwhm2): same_island = True else: same_island = False return same_island if img.opts.group_by_isl: same_isl1_min = True same_isl1_cont = True same_isl2 = True else: if img.opts.group_method == 'curvature': subim = -1.0 * func.make_curvature_map(subim) tol = img.opts.group_tol same_isl1_min, same_isl1_cont = same_island_min( pair, g_list, subim, delc, tol) same_isl2 = same_island_dist(pair, g_list, tol / 2.0) g1 = g_list[pair[0]] same_island = (same_isl1_min and same_isl2) or same_isl1_cont return same_island ################################################################################################## def process_Multiple(self, img, g_sublist, mask, src_index, isrc, subim, isl, delc, subn, subm): """ Same as gaul_to_source.f. isrc is same as k in the fortran version. """ from math import pi, sqrt from const import fwsig from scipy import ndimage import functions as func mylog = mylogger.logging.getLogger("PyBDSM." + img.log + "Gaul2Srl ") dum = img.beam[0] * img.beam[1] cdeltsq = img.wcs_obj.acdelt[0] * img.wcs_obj.acdelt[1] bmar_p = 2.0 * pi * dum / (cdeltsq * fwsig * fwsig) # try subim_src = self.make_subim(subn, subm, g_sublist, delc) mompara = func.momanalmask_gaus(subim_src, mask, isrc, bmar_p, True) # initial peak posn and value maxv = N.max(subim_src) maxx, maxy = N.unravel_index(N.argmax(subim_src), subim_src.shape) # fit gaussian around this posn blc = N.zeros(2) trc = N.zeros(2) n, m = subim_src.shape[0:2] bm_pix = N.array([ img.pixel_beam()[0] * fwsig, img.pixel_beam()[1] * fwsig, img.pixel_beam()[2] ]) ssubimsize = max(N.int(N.round(N.max(bm_pix[0:2]) * 2)) + 1, 5) blc[0] = max(0, maxx - (ssubimsize - 1) / 2) blc[1] = max(0, maxy - (ssubimsize - 1) / 2) trc[0] = min(n, maxx + (ssubimsize - 1) / 2) trc[1] = min(m, maxy + (ssubimsize - 1) / 2) s_imsize = trc - blc + 1 p_ini = [maxv, (s_imsize[0]-1)/2.0*1.1, (s_imsize[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, \ bm_pix[1]/fwsig*1.1, bm_pix[2]*2] data = subim_src[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] smask = mask[blc[0]:blc[0] + s_imsize[0], blc[1]:blc[1] + s_imsize[1]] rmask = N.where(smask == isrc, False, True) x_ax, y_ax = N.indices(data.shape) if N.sum(~rmask) >= 6: para, ierr = func.fit_gaus2d(data, p_ini, x_ax, y_ax, rmask) if (0.0<para[1]<s_imsize[0]) and (0.0<para[2]<s_imsize[1]) and \ para[3]<s_imsize[0] and para[4]<s_imsize[1]: maxpeak = para[0] else: maxpeak = maxv posn = para[1:3] - (0.5 * N.sum(s_imsize) - 1) / 2.0 + N.array( [maxx, maxy]) - 1 + delc else: maxpeak = maxv posn = N.unravel_index(N.argmax(data * ~rmask), data.shape) + N.array(delc) + blc # calculate peak by bilinear interpolation around centroid # First check that moment analysis gave a valid position. If not, use # posn from gaussian fit instead. if N.isnan(mompara[1]): mompara[1] = posn[0] - delc[0] x1 = N.int(N.floor(mompara[1])) if N.isnan(mompara[2]): mompara[2] = posn[1] - delc[1] y1 = N.int(N.floor(mompara[2])) xind = slice(x1, x1 + 2, 1) yind = slice(y1, y1 + 2, 1) if img.opts.flag_smallsrc and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Island = ' + str(isl.island_id)) mylog.debug('Mask = ' + repr(mask[xind, yind]) + 'xind, yind, x1, y1 = ' + repr(xind) + ' ' + repr(yind) + ' ' + repr(x1) + ' ' + repr(y1)) t = (mompara[1] - x1) / (x1 + 1 - x1) # in case u change it later u = (mompara[2] - y1) / (y1 + 1 - y1) s_peak=(1.0-t)*(1.0-u)*subim_src[x1,y1]+t*(1.0-u)*subim_src[x1+1,y1]+ \ t*u*subim_src[x1+1,y1+1]+(1.0-t)*u*subim_src[x1,y1+1] if (not img.opts.flag_smallsrc) and ( N.sum(mask[xind, yind] == N.ones((2, 2)) * isrc) != 4): mylog.debug('Speak ' + repr(s_peak) + 'Mompara = ' + repr(mompara)) mylog.debug('x1, y1 : ' + repr(x1) + ', ' + repr(y1)) # import pylab as pl # pl.imshow(N.transpose(subim_src), origin='lower', interpolation='nearest') # pl.suptitle('Image of bad M source '+str(isl.island_id)) # convert pixels to coords try: sra, sdec = img.pix2sky( [mompara[1] + delc[0], mompara[2] + delc[1]]) mra, mdec = img.pix2sky(posn) except RuntimeError, err: # Invalid pixel wcs coordinate sra, sdec = 0.0, 0.0 mra, mdec = 0.0, 0.0 # "deconvolve" the sizes gaus_c = [mompara[3], mompara[4], mompara[5]] gaus_bm = [bm_pix[0], bm_pix[1], bm_pix[2]] gaus_dc, err = func.deconv2(gaus_bm, gaus_c) deconv_size_sky = img.pix2gaus( gaus_dc, [mompara[1] + delc[0], mompara[2] + delc[1]]) deconv_size_sky_uncorr = img.pix2gaus( gaus_dc, [mompara[1] + delc[0], mompara[2] + delc[1]], use_wcs=False) # update all objects etc tot = 0.0 totE_sq = 0.0 for g in g_sublist: tot += g.total_flux totE_sq += g.total_fluxE**2 totE = sqrt(totE_sq) size_pix = [mompara[3], mompara[4], mompara[5]] size_sky = img.pix2gaus(size_pix, [mompara[1] + delc[0], mompara[2] + delc[1]]) size_sky_uncorr = img.pix2gaus( size_pix, [mompara[1] + delc[0], mompara[2] + delc[1]], use_wcs=False) # Estimate uncertainties in source size and position due to # errors in the constituent Gaussians using a Monte Carlo technique. # Sum with Condon (1997) errors in quadrature. plist = mompara.tolist() + [tot] plist[0] = s_peak plist[3] /= fwsig plist[4] /= fwsig errors = func.get_errors(img, plist, isl.rms) if img.opts.do_mc_errors: nMC = 20 mompara0_MC = N.zeros(nMC, dtype=N.float32) mompara1_MC = N.zeros(nMC, dtype=N.float32) mompara2_MC = N.zeros(nMC, dtype=N.float32) mompara3_MC = N.zeros(nMC, dtype=N.float32) mompara4_MC = N.zeros(nMC, dtype=N.float32) mompara5_MC = N.zeros(nMC, dtype=N.float32) for i in range(nMC): # Reconstruct source from component Gaussians. Draw the Gaussian # parameters from random distributions given by their errors. subim_src_MC = self.make_subim(subn, subm, g_sublist, delc, mc=True) try: mompara_MC = func.momanalmask_gaus(subim_src_MC, mask, isrc, bmar_p, True) mompara0_MC[i] = mompara_MC[0] mompara1_MC[i] = mompara_MC[1] mompara2_MC[i] = mompara_MC[2] mompara3_MC[i] = mompara_MC[3] mompara4_MC[i] = mompara_MC[4] mompara5_MC[i] = mompara_MC[5] except: mompara0_MC[i] = mompara[0] mompara1_MC[i] = mompara[1] mompara2_MC[i] = mompara[2] mompara3_MC[i] = mompara[3] mompara4_MC[i] = mompara[4] mompara5_MC[i] = mompara[5] mompara0E = N.std(mompara0_MC) mompara1E = N.std(mompara1_MC) if mompara1E > 2.0 * mompara[1]: mompara1E = 2.0 * mompara[1] # Don't let errors get too large mompara2E = N.std(mompara2_MC) if mompara2E > 2.0 * mompara[2]: mompara2E = 2.0 * mompara[2] # Don't let errors get too large mompara3E = N.std(mompara3_MC) if mompara3E > 2.0 * mompara[3]: mompara3E = 2.0 * mompara[3] # Don't let errors get too large mompara4E = N.std(mompara4_MC) if mompara4E > 2.0 * mompara[4]: mompara4E = 2.0 * mompara[4] # Don't let errors get too large mompara5E = N.std(mompara5_MC) if mompara5E > 2.0 * mompara[5]: mompara5E = 2.0 * mompara[5] # Don't let errors get too large else: mompara1E = 0.0 mompara2E = 0.0 mompara3E = 0.0 mompara4E = 0.0 mompara5E = 0.0 # Now add MC errors in quadrature with Condon (1997) errors size_skyE = [ sqrt(mompara3E**2 + errors[3]**2) * sqrt(cdeltsq), sqrt(mompara4E**2 + errors[4]**2) * sqrt(cdeltsq), sqrt(mompara5E**2 + errors[5]**2) ] sraE, sdecE = (sqrt(mompara1E**2 + errors[1]**2) * sqrt(cdeltsq), sqrt(mompara2E**2 + errors[2]**2) * sqrt(cdeltsq)) deconv_size_skyE = size_skyE # set deconvolved errors to non-deconvolved ones # Find aperture flux if img.opts.aperture_posn == 'centroid': aper_pos = [mompara[1] + delc[0], mompara[2] + delc[1]] else: aper_pos = posn aper_flux, aper_fluxE = func.ch0_aperture_flux(img, aper_pos, img.aperture) isl_id = isl.island_id source_prop = list([ 'M', [tot, totE], [s_peak, isl.rms], [maxpeak, isl.rms], [aper_flux, aper_fluxE], [[sra, sdec], [sraE, sdecE]], [[mra, mdec], [sraE, sdecE]], [size_sky, size_skyE], [size_sky_uncorr, size_skyE], [deconv_size_sky, deconv_size_skyE], [deconv_size_sky_uncorr, deconv_size_skyE], isl.bbox, len(g_sublist), isl_id, g_sublist ]) source = Source(img, source_prop) src_index += 1 for g in g_sublist: g.source_id = src_index g.code = 'M' source.source_id = src_index return src_index, source
def process_Multiple(self, img, g_sublist, mask, src_index, isrc, subim, isl, delc, subn, subm): """ Same as gaul_to_source.f. isrc is same as k in the fortran version. """ from math import pi, sqrt from const import fwsig from scipy import ndimage import functions as func mylog = mylogger.logging.getLogger("PyBDSM."+img.log+"Gaul2Srl ") dum = img.beam[0]*img.beam[1] cdeltsq = img.wcs_obj.acdelt[0]*img.wcs_obj.acdelt[1] bmar_p = 2.0*pi*dum/(cdeltsq*fwsig*fwsig) # try subim_src = self.make_subim(subn, subm, g_sublist, delc) mompara = func.momanalmask_gaus(subim_src, mask, isrc, bmar_p, True) # initial peak posn and value maxv = N.max(subim_src) maxx, maxy = N.unravel_index(N.argmax(subim_src), subim_src.shape) # fit gaussian around this posn blc = N.zeros(2); trc = N.zeros(2) n, m = subim_src.shape[0:2] bm_pix = N.array([img.pixel_beam()[0]*fwsig, img.pixel_beam()[1]*fwsig, img.pixel_beam()[2]]) ssubimsize = max(N.int(N.round(N.max(bm_pix[0:2])*2))+1, 5) blc[0] = max(0, maxx-(ssubimsize-1)/2); blc[1] = max(0, maxy-(ssubimsize-1)/2) trc[0] = min(n, maxx+(ssubimsize-1)/2); trc[1] = min(m, maxy+(ssubimsize-1)/2) s_imsize = trc - blc + 1 p_ini = [maxv, (s_imsize[0]-1)/2.0*1.1, (s_imsize[1]-1)/2.0*1.1, bm_pix[0]/fwsig*1.3, \ bm_pix[1]/fwsig*1.1, bm_pix[2]*2] data = subim_src[blc[0]:blc[0]+s_imsize[0], blc[1]:blc[1]+s_imsize[1]] smask = mask[blc[0]:blc[0]+s_imsize[0], blc[1]:blc[1]+s_imsize[1]] rmask = N.where(smask==isrc, False, True) x_ax, y_ax = N.indices(data.shape) if N.sum(~rmask) >=6: para, ierr = func.fit_gaus2d(data, p_ini, x_ax, y_ax, rmask) if (0.0<para[1]<s_imsize[0]) and (0.0<para[2]<s_imsize[1]) and \ para[3]<s_imsize[0] and para[4]<s_imsize[1]: maxpeak = para[0] else: maxpeak = maxv posn = para[1:3]-(0.5*N.sum(s_imsize)-1)/2.0+N.array([maxx, maxy])-1+delc else: maxpeak = maxv posn = N.unravel_index(N.argmax(data*~rmask), data.shape)+N.array(delc) +blc # calculate peak by bilinear interpolation around centroid # First check that moment analysis gave a valid position. If not, use # posn from gaussian fit instead. if N.isnan(mompara[1]): mompara[1] = posn[0] - delc[0] x1 = N.int(N.floor(mompara[1])) if N.isnan(mompara[2]): mompara[2] = posn[1] - delc[1] y1 = N.int(N.floor(mompara[2])) xind = slice(x1, x1+2, 1); yind = slice(y1, y1+2, 1) if img.opts.flag_smallsrc and (N.sum(mask[xind, yind]==N.ones((2,2))*isrc) != 4): mylog.debug('Island = '+str(isl.island_id)) mylog.debug('Mask = '+repr(mask[xind, yind])+'xind, yind, x1, y1 = '+repr(xind)+' '+repr(yind)+' '+repr(x1)+' '+repr(y1)) t=(mompara[1]-x1)/(x1+1-x1) # in case u change it later u=(mompara[2]-y1)/(y1+1-y1) s_peak=(1.0-t)*(1.0-u)*subim_src[x1,y1]+t*(1.0-u)*subim_src[x1+1,y1]+ \ t*u*subim_src[x1+1,y1+1]+(1.0-t)*u*subim_src[x1,y1+1] if (not img.opts.flag_smallsrc) and (N.sum(mask[xind, yind]==N.ones((2,2))*isrc) != 4): mylog.debug('Speak '+repr(s_peak)+'Mompara = '+repr(mompara)) mylog.debug('x1, y1 : '+repr(x1)+', '+repr(y1)) # import pylab as pl # pl.imshow(N.transpose(subim_src), origin='lower', interpolation='nearest') # pl.suptitle('Image of bad M source '+str(isl.island_id)) # convert pixels to coords try: sra, sdec = img.pix2sky([mompara[1]+delc[0], mompara[2]+delc[1]]) mra, mdec = img.pix2sky(posn) except RuntimeError, err: # Invalid pixel wcs coordinate sra, sdec = 0.0, 0.0 mra, mdec = 0.0, 0.0