def get_cfht_catalog(mags=['i'], maglim=27., returnTable=False): from astrometry.util.pyfits_utils import fits_table from tractor import Mags, RaDecPos, PointSource, Images, Catalog from tractor.galaxy import DevGalaxy, ExpGalaxy, CompositeGalaxy, GalaxyShape T = fits_table( '/project/projectdirs/bigboss/data/cs82/W4p1m1_i.V2.7A.swarp.cut.deVexp.fit', hdunum=2) print 'Read', len(T), 'sources' # Cut to ROI (ra0, ra1, dec0, dec1) = radecroi T.ra = T.alpha_j2000 T.dec = T.delta_j2000 T = T[(T.ra > ra0) * (T.ra < ra1) * (T.dec > dec0) * (T.dec < dec1)] print 'Cut to', len(T), 'objects in ROI.' srcs = Catalog() keepi = [] for i, t in enumerate(T): if t.chi2_psf < t.chi2_model and t.mag_psf <= maglim: #print 'PSF' themag = t.mag_psf m = Mags(order=mags, **dict([(k, themag) for k in mags])) srcs.append(PointSource(RaDecPos(t.ra, t.dec), m)) keepi.append(i) continue if t.mag_disk > maglim and t.mag_spheroid > maglim: #print 'Faint' continue keepi.append(i) themag = t.mag_spheroid m_exp = Mags(order=mags, **dict([(k, themag) for k in mags])) themag = t.mag_disk m_dev = Mags(order=mags, **dict([(k, themag) for k in mags])) # SPHEROID_REFF [for Sersic index n= 1] = 1.68 * DISK_SCALE shape_dev = GalaxyShape(t.disk_scale_world * 1.68 * 3600., t.disk_aspect_world, t.disk_theta_world + 90.) shape_exp = GalaxyShape(t.spheroid_reff_world * 3600., t.spheroid_aspect_world, t.spheroid_theta_world + 90.) pos = RaDecPos(t.alphamodel_j2000, t.deltamodel_j2000) if t.mag_disk > maglim and t.mag_spheroid <= maglim: #print 'Exp' srcs.append(ExpGalaxy(pos, m_exp, shape_exp)) continue if t.mag_disk <= maglim and t.mag_spheroid > maglim: #print 'deV' srcs.append(DevGalaxy(pos, m_dev, shape_dev)) continue # exp + deV srcs.append(CompositeGalaxy(pos, m_exp, shape_exp, m_dev, shape_dev)) if returnTable: import numpy as np T.cut(np.array(keepi)) return srcs, T return srcs
def getModelPatch(self, img): # We start by rendering the visible lens galaxy. patch = self.light.getModelPatch(img) # We will use the lens model to predict the quasar's image positions. positions,mags = self.mass.getLensedImages(self.light.position, self.quasar) # 'positions' should be a list of RaDecPos objects # 'mags' should be a list of Mags objects for pos,mag,fudge in zip(positions, mags, self.magfudge): # For each image of the quasar, we will create a PointSource ps = PointSource(pos, mag + fudge) # ... and add it to the patch. patch += ps.getModelPatch(img) return patch
def getModelPatch(self, img): # We start by rendering the visible lens galaxy. patch = self.light.getModelPatch(img) # We will use the lens model to predict the quasar's image positions. positions, mags = self.mass.getLensedImages(self.light.position, self.quasar) # 'positions' should be a list of RaDecPos objects # 'mags' should be a list of Mags objects for pos, mag, fudge in zip(positions, mags, self.magfudge): # For each image of the quasar, we will create a PointSource ps = PointSource(pos, mag + fudge) # ... and add it to the patch. patch += ps.getModelPatch(img) return patch
#x0,y0 = s.ix - margin, s.iy - margin x0,y0 = s.x0, s.y0 slc = (slice(y0, s.iy + margin+1), slice(x0, s.ix + margin+1)) subpsf = tim.psf.constantPsfAt(s.ix, s.iy) subtim = Image(data=tim.data[slc], inverr=tim.inverr[slc], psf=subpsf, photocal=tim.photocal, name=tim.name + '/star %i' % i) flux = NanoMaggies.magToNanomaggies(s.mag) print 'Flux:', flux flux = max(flux, 1.) src = PointSource(PixPos(s.xx - x0, s.yy - y0), NanoMaggies(**{tim.band: flux})) tr = Tractor([subtim], [src]) mod = tr.getModelImage(0) #mod = src.getModelImage(tsubtim) tractors.append(tr) resids.append((subtim.data - mod) * (subtim.inverr > 0)) chis.append((subtim.data - mod) * subtim.inverr) plt.subplot(R, C, i+1) dimshow(mod, **ima) plt.suptitle('Models for Pan-STARRS stars: ' + tim.name) ps.savefig() resa = dict(vmin=-5.*tim.sig1, vmax=5.*tim.sig1, ticks=False)
def run_forced_phot(cat, tim, ceres=True, derivs=False, agn=False, do_forced=True, do_apphot=True, get_model=False, ps=None, timing=False, fixed_also=False, ceres_threads=1): ''' fixed_also: if derivs=True, also run without derivatives and report that flux too? ''' if timing: tlast = Time() if ps is not None: import pylab as plt opti = None forced_kwargs = {} if ceres: from tractor.ceres_optimizer import CeresOptimizer B = 8 try: opti = CeresOptimizer(BW=B, BH=B, threads=ceres_threads) except: if ceres_threads > 1: raise RuntimeError( 'ceres_threads requested but not supported by tractor.ceres version' ) opti = CeresOptimizer(BW=B, BH=B) #forced_kwargs.update(verbose=True) # nsize = 0 for src in cat: # Limit sizes of huge models # from tractor.galaxy import ProfileGalaxy # if isinstance(src, ProfileGalaxy): # px,py = tim.wcs.positionToPixel(src.getPosition()) # h = src._getUnitFluxPatchSize(tim, px, py, tim.modelMinval) # MAXHALF = 128 # if h > MAXHALF: # #print('halfsize', h,'for',src,'-> setting to',MAXHALF) # nsize += 1 # src.halfsize = MAXHALF src.freezeAllBut('brightness') src.getBrightness().freezeAllBut(tim.band) #print('Limited the size of', nsize, 'large galaxy models') if derivs: realsrcs = [] derivsrcs = [] Iderivs = [] for i, src in enumerate(cat): from tractor import PointSource realsrcs.append(src) if not isinstance(src, PointSource): continue Iderivs.append(i) brightness_dra = src.getBrightness().copy() brightness_ddec = src.getBrightness().copy() brightness_dra.setParams(np.zeros(brightness_dra.numberOfParams())) brightness_ddec.setParams( np.zeros(brightness_ddec.numberOfParams())) brightness_dra.freezeAllBut(tim.band) brightness_ddec.freezeAllBut(tim.band) dsrc = SourceDerivatives(src, [brightness_dra, brightness_ddec], tim, ps) derivsrcs.append(dsrc) Iderivs = np.array(Iderivs) if fixed_also: pass else: # For convenience, put all the real sources at the front of # the list, so we can pull the IVs off the front of the list. cat = realsrcs + derivsrcs if agn: from tractor.galaxy import ExpGalaxy, DevGalaxy, FixedCompositeGalaxy from tractor import PointSource from legacypipe.survey import SimpleGalaxy, RexGalaxy realsrcs = [] agnsrcs = [] iagn = [] for i, src in enumerate(cat): realsrcs.append(src) ## ?? if isinstance(src, (SimpleGalaxy, RexGalaxy)): #print('Skipping SIMP or REX:', src) continue if isinstance(src, (ExpGalaxy, DevGalaxy, FixedCompositeGalaxy)): iagn.append(i) bright = src.getBrightness().copy() bright.setParams(np.zeros(bright.numberOfParams())) bright.freezeAllBut(tim.band) agn = PointSource(src.pos, bright) agn.freezeAllBut('brightness') #print('Adding "agn"', agn, 'to', src) #print('agn params:', agn.getParamNames()) agnsrcs.append(src) iagn = np.array(iagn) cat = realsrcs + agnsrcs print('Added AGN to', len(iagn), 'galaxies') tr = Tractor([tim], cat, optimizer=opti) tr.freezeParam('images') disable_galaxy_cache() F = fits_table() if do_forced: if timing and (derivs or agn): t = Time() print('Setting up:', t - tlast) tlast = t if derivs: if fixed_also: print('Forced photom with fixed positions:') R = tr.optimize_forced_photometry(variance=True, fitstats=False, shared_params=False, priors=False, **forced_kwargs) F.flux_fixed = np.array([ src.getBrightness().getFlux(tim.band) for src in cat ]).astype(np.float32) N = len(cat) F.flux_fixed_ivar = R.IV[:N].astype(np.float32) if timing: t = Time() print('Forced photom with fixed positions finished:', t - tlast) tlast = t cat = realsrcs + derivsrcs tr.setCatalog(Catalog(*cat)) print('Forced photom with position derivatives:') if ps is None and not get_model: forced_kwargs.update(wantims=False) R = tr.optimize_forced_photometry(variance=True, fitstats=True, shared_params=False, priors=False, **forced_kwargs) if ps is not None or get_model: (data, mod, ie, chi, roi) = R.ims1[0] if ps is not None: ima = dict(vmin=-2. * tim.sig1, vmax=5. * tim.sig1, interpolation='nearest', origin='lower', cmap='gray') imchi = dict(interpolation='nearest', origin='lower', vmin=-5, vmax=5, cmap='RdBu') plt.clf() plt.imshow(data, **ima) plt.title('Data: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(mod, **ima) plt.title('Model: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(chi, **imchi) plt.title('Chi: %s' % tim.name) ps.savefig() if derivs: trx = Tractor([tim], realsrcs) trx.freezeParam('images') modx = trx.getModelImage(0) chix = (data - modx) * tim.getInvError() plt.clf() plt.imshow(modx, **ima) plt.title('Model without derivatives: %s' % tim.name) ps.savefig() plt.clf() plt.imshow(chix, **imchi) plt.title('Chi without derivatives: %s' % tim.name) ps.savefig() if derivs or agn: cat = realsrcs N = len(cat) F.flux = np.array([ src.getBrightness().getFlux(tim.band) for src in cat ]).astype(np.float32) F.flux_ivar = R.IV[:N].astype(np.float32) F.fracflux = R.fitstats.profracflux[:N].astype(np.float32) F.rchisq = R.fitstats.prochi2[:N].astype(np.float32) try: F.fracmasked = R.fitstats.promasked[:N].astype(np.float32) except: print( 'No "fracmasked" available (only in recent Tractor versions)') if derivs: F.flux_dra = np.zeros(len(F), np.float32) F.flux_ddec = np.zeros(len(F), np.float32) F.flux_dra[Iderivs] = np.array( [src.getParams()[0] for src in derivsrcs]).astype(np.float32) F.flux_ddec[Iderivs] = np.array( [src.getParams()[1] for src in derivsrcs]).astype(np.float32) F.flux_dra_ivar = np.zeros(len(F), np.float32) F.flux_ddec_ivar = np.zeros(len(F), np.float32) F.flux_dra_ivar[Iderivs] = R.IV[N::2].astype(np.float32) F.flux_ddec_ivar[Iderivs] = R.IV[N + 1::2].astype(np.float32) if agn: F.flux_agn = np.zeros(len(F), np.float32) F.flux_agn_ivar = np.zeros(len(F), np.float32) F.flux_agn[iagn] = np.array( [src.getParams()[0] for src in agnsrcs]) F.flux_agn_ivar[iagn] = R.IV[N:].astype(np.float32) if timing: t = Time() print('Forced photom:', t - tlast) tlast = t if do_apphot: import photutils img = tim.getImage() ie = tim.getInvError() with np.errstate(divide='ignore'): imsigma = 1. / ie imsigma[ie == 0] = 0. apimg = [] apimgerr = [] # Aperture photometry locations xxyy = np.vstack( [tim.wcs.positionToPixel(src.getPosition()) for src in cat]).T apxy = xxyy - 1. apertures = apertures_arcsec / tim.wcs.pixel_scale() #print('Apertures:', apertures, 'pixels') #print('apxy shape', apxy.shape) # --> (2,N) # The aperture photometry routine doesn't like pixel positions outside the image H, W = img.shape Iap = np.flatnonzero((apxy[0, :] >= 0) * (apxy[1, :] >= 0) * (apxy[0, :] <= W - 1) * (apxy[1, :] <= H - 1)) print('Aperture photometry for', len(Iap), 'of', len(apxy[0, :]), 'sources within image bounds') for rad in apertures: aper = photutils.CircularAperture(apxy[:, Iap], rad) p = photutils.aperture_photometry(img, aper, error=imsigma) apimg.append(p.field('aperture_sum')) apimgerr.append(p.field('aperture_sum_err')) ap = np.vstack(apimg).T ap[np.logical_not(np.isfinite(ap))] = 0. F.apflux = np.zeros((len(F), len(apertures)), np.float32) F.apflux[Iap, :] = ap.astype(np.float32) apimgerr = np.vstack(apimgerr).T apiv = np.zeros(apimgerr.shape, np.float32) apiv[apimgerr != 0] = 1. / apimgerr[apimgerr != 0]**2 F.apflux_ivar = np.zeros((len(F), len(apertures)), np.float32) F.apflux_ivar[Iap, :] = apiv if timing: print('Aperture photom:', Time() - tlast) if get_model: return F, mod return F
def main(): # Where are the data? datadir = os.path.join(os.path.dirname(__file__), 'data-decam') name = 'decam-520206-S16' imagefn = os.path.join(datadir, '%s-image-sub.fits' % name) invvarfn = os.path.join(datadir, '%s-invvar-sub.fits' % name) psfexfn = os.path.join(datadir, '%s-psfex.fits' % name) catfn = os.path.join(datadir, 'tractor-1816p325-sub.fits') # Read the image and inverse-variance maps. image = fitsio.read(imagefn) invvar = fitsio.read(invvarfn) # The DECam inverse-variance maps are unfortunately corrupted # by fpack, causing zeros to become negative. Fix those. invvar[invvar < np.median(invvar) * 0.1] = 0. H, W = image.shape print('Subimage size:', image.shape) # For the PSF model, we need to know what subimage region this is: subimage_offset = (35, 1465) # We also need the calibrated zeropoint. zeropoint = 24.7787 # What filter was this image taken in? (z) prim_header = fitsio.read_header(imagefn) band = prim_header['FILTER'].strip()[0] print('Band:', band) # These DECam images were calibrated so that the zeropoints need # an exposure-time factor, so add that in. exptime = prim_header['EXPTIME'] zeropoint += 2.5 * np.log10(exptime) # Read the PsfEx model file psf = PixelizedPsfEx(psfexfn) # Instantiate a constant pixelized PSF at the image center # (of the subimage) x0, y0 = subimage_offset psf = psf.constantPsfAt(x0 + W / 2., y0 + H / 2.) # Load the WCS model from the header # We convert from the RA---TPV type to RA---SIP header = fitsio.read_header(imagefn, ext=1) wcsobj = wcs_pv2sip_hdr(header, stepsize=10) # We'll just use a rough sky estimate... skyval = np.median(image) # Create the Tractor Image (tim). tim = Image(data=image, invvar=invvar, psf=psf, wcs=ConstantFitsWcs(wcsobj), sky=ConstantSky(skyval), photocal=LinearPhotoCal( NanoMaggies.zeropointToScale(zeropoint), band=band)) # Read the official DECaLS DR3 catalog -- it has only two sources in this subimage. catalog = fits_table(catfn) print('Read', len(catalog), 'sources') print('Source types:', catalog.type) # Create Tractor sources corresponding to these two catalog # entries. # In DECaLS, the "SIMP" type is a round Exponential galaxy with a # fixed 0.45" radius, but we'll treat it as a general Exp galaxy. sources = [] for c in catalog: # Create a "position" object given the catalog RA,Dec position = RaDecPos(c.ra, c.dec) # Create a "brightness" object; in the catalog, the fluxes are # stored in a [ugrizY] array, so pull out the right index band_index = 'ugrizY'.index(band) flux = c.decam_flux[band_index] brightness = NanoMaggies(**{band: flux}) # Depending on the source classification in the catalog, pull # out different fields for the galaxy shape, and for the # galaxy type. The DECaLS catalogs, conveniently, store # galaxy shapes as (radius, e1, e2) ellipses. if c.type.strip() == 'DEV': shape = EllipseE(c.shapedev_r, c.shapedev_e1, c.shapedev_e2) galclass = DevGalaxy elif c.type.strip() == 'SIMP': shape = EllipseE(c.shapeexp_r, c.shapeexp_e1, c.shapeexp_e2) galclass = ExpGalaxy else: assert (False) # Create the tractor galaxy object source = galclass(position, brightness, shape) print('Created', source) sources.append(source) # Create the Tractor object -- a list of tractor Images and a list of tractor sources. tractor = Tractor([tim], sources) # Render the initial model image. print('Getting initial model...') mod = tractor.getModelImage(0) make_plot(tim, mod, 'Initial Scene', 'mod0.png') # Instantiate a new source at the location of the unmodelled peak. print('Adding new source...') # Find the peak very naively... ipeak = np.argmax((image - mod) * tim.inverr) iy, ix = np.unravel_index(ipeak, tim.shape) print('Residual peak at', ix, iy) # Compute the RA,Dec location of the peak... radec = tim.getWcs().pixelToPosition(ix, iy) print('RA,Dec', radec) # Try modelling it as a point source. # We'll initialize the brightness arbitrarily to 1 nanomaggy (= mag 22.5) brightness = NanoMaggies(**{band: 1.}) source = PointSource(radec, brightness) # Add it to the catalog! tractor.catalog.append(source) # Render the new model image with this source added. mod = tractor.getModelImage(0) make_plot(tim, mod, 'New Source (Before Fit)', 'mod1.png') print('Fitting new source...') # Now we're going to fit for the properties of the new source we # added. # We don't want to fit for any of the image calibration properties: tractor.freezeParam('images') # And we don't (yet) want to fit the existing sources. The new # source is index number 2, so freeze everything else in the catalog. tractor.catalog.freezeAllBut(2) print('Fitting parameters:') tractor.printThawedParams() # Do the actual optimization: tractor.optimize_loop() mod = tractor.getModelImage(0) make_plot(tim, mod, 'New Source Fit', 'mod2.png') print('Fitting sources simultaneously...') # Now let's unfreeze all the sources and fit them simultaneously. tractor.catalog.thawAllParams() tractor.printThawedParams() tractor.optimize_loop() mod = tractor.getModelImage(0) make_plot(tim, mod, 'Simultaneous Fit', 'mod3.png')
def main(): import optparse from astrometry.util.plotutils import PlotSequence from astrometry.util.util import Tan parser = optparse.OptionParser(usage='%prog [options] incat.fits out.fits') parser.add_option('-r', '--ralo', dest='ralo', type=float, help='Minimum RA') parser.add_option('-R', '--rahi', dest='rahi', type=float, help='Maximum RA') parser.add_option('-d', '--declo', dest='declo', type=float, help='Minimum Dec') parser.add_option('-D', '--dechi', dest='dechi', type=float, help='Maximum Dec') parser.add_option('-b', '--band', dest='bands', action='append', type=int, default=[], help='WISE band to photometer (default: 1,2)') parser.add_option('-u', '--unwise', dest='unwise_dir', default='unwise-coadds', help='Directory containing unWISE coadds') parser.add_option('--no-ceres', dest='ceres', action='store_false', default=True, help='Use scipy lsqr rather than Ceres Solver?') parser.add_option('--ceres-block', '-B', dest='ceresblock', type=int, default=8, help='Ceres image block size (default: %default)') parser.add_option('--plots', dest='plots', default=False, action='store_true') parser.add_option('--save-fits', dest='save_fits', default=False, action='store_true') # parser.add_option('--ellipses', action='store_true', # help='Assume catalog shapes are ellipse descriptions (not r,ab,phi)') # parser.add_option('--ra', help='Center RA') # parser.add_option('--dec', help='Center Dec') # parser.add_option('--width', help='Degrees width (in RA*cos(Dec))') # parser.add_option('--height', help='Degrees height (Dec)') opt, args = parser.parse_args() if len(args) != 2: parser.print_help() sys.exit(-1) if len(opt.bands) == 0: opt.bands = [1, 2] # Allow specifying bands like "123" bb = [] for band in opt.bands: for s in str(band): bb.append(int(s)) opt.bands = bb print('Bands', opt.bands) ps = None if opt.plots: ps = PlotSequence('unwise') infn, outfn = args T = fits_table(infn) print('Read', len(T), 'sources from', infn) if opt.declo is not None: T.cut(T.dec >= opt.declo) if opt.dechi is not None: T.cut(T.dec <= opt.dechi) # Let's be a bit smart about RA wrap-around. Compute the 'center' # of the RA points, use the cross product against that to define # inequality (clockwise-of). r = np.deg2rad(T.ra) x = np.mean(np.cos(r)) y = np.mean(np.sin(r)) rr = np.hypot(x, y) x /= rr y /= rr midra = np.rad2deg(np.arctan2(y, x)) midra += 360. * (midra < 0) xx = np.cos(r) yy = np.sin(r) T.cross = x * yy - y * xx minra = T.ra[np.argmin(T.cross)] maxra = T.ra[np.argmax(T.cross)] if opt.ralo is not None: r = np.deg2rad(opt.ralo) xx = np.cos(r) yy = np.sin(r) crosscut = x * yy - y * xx T.cut(T.cross >= crosscut) print('Cut to', len(T), 'with RA >', opt.ralo) if opt.rahi is not None: r = np.deg2rad(opt.rahi) xx = np.cos(r) yy = np.sin(r) crosscut = x * yy - y * xx T.cut(T.cross <= crosscut) print('Cut to', len(T), 'with RA <', opt.rahi) if opt.declo is None: opt.declo = T.dec.min() if opt.dechi is None: opt.dechi = T.dec.max() if opt.ralo is None: opt.ralo = T.ra[np.argmin(T.cross)] if opt.rahi is None: opt.rahi = T.ra[np.argmax(T.cross)] T.delete_column('cross') print('RA range:', opt.ralo, opt.rahi) print('Dec range:', opt.declo, opt.dechi) x = np.mean([np.cos(np.deg2rad(r)) for r in (opt.ralo, opt.rahi)]) y = np.mean([np.sin(np.deg2rad(r)) for r in (opt.ralo, opt.rahi)]) midra = np.rad2deg(np.arctan2(y, x)) midra += 360. * (midra < 0) middec = (opt.declo + opt.dechi) / 2. print('RA,Dec center:', midra, middec) pixscale = 2.75 / 3600. H = (opt.dechi - opt.declo) / pixscale dra = 2. * min(np.abs(midra - opt.ralo), np.abs(midra - opt.rahi)) W = dra * np.cos(np.deg2rad(middec)) / pixscale margin = 5 W = int(W) + margin * 2 H = int(H) + margin * 2 print('W,H', W, H) targetwcs = Tan(midra, middec, (W + 1) / 2., (H + 1) / 2., -pixscale, 0., 0., pixscale, float(W), float(H)) #print('Target WCS:', targetwcs) ra0, dec0 = targetwcs.pixelxy2radec(0.5, 0.5) ra1, dec1 = targetwcs.pixelxy2radec(W + 0.5, H + 0.5) roiradecbox = [ra0, ra1, dec0, dec1] #print('ROI RA,Dec box', roiradecbox) tiles = unwise_tiles_touching_wcs(targetwcs) print('Cut to', len(tiles), 'unWISE tiles') disable_galaxy_cache() cols = T.get_columns() all_ptsrcs = not('type' in cols) if not all_ptsrcs: assert('shapeexp' in cols) assert('shapedev' in cols) assert('fracdev' in cols) wanyband = 'w' print('Creating Tractor catalog...') cat = [] for i, t in enumerate(T): pos = RaDecPos(t.ra, t.dec) flux = NanoMaggies(**{wanyband: 1.}) if all_ptsrcs: cat.append(PointSource(pos, flux)) continue tt = t.type.strip() if tt in ['PTSRC', 'STAR', 'S']: cat.append(PointSource(pos, flux)) elif tt in ['EXP', 'E']: shape = EllipseE(*t.shapeexp) cat.append(ExpGalaxy(pos, flux, shape)) elif tt in ['DEV', 'D']: shape = EllipseE(*t.shapedev) cat.append(DevGalaxy(pos, flux, shape)) elif tt in ['COMP', 'C']: eshape = EllipseE(*t.shapeexp) dshape = EllipseE(*t.shapedev) cat.append(FixedCompositeGalaxy(pos, flux, t.fracdev, eshape, dshape)) else: print('Did not understand row', i, 'of input catalog:') t.about() assert(False) W = unwise_forcedphot(cat, tiles, roiradecbox=roiradecbox, bands=opt.bands, unwise_dir=opt.unwise_dir, use_ceres=opt.ceres, ceres_block=opt.ceresblock, save_fits=opt.save_fits, ps=ps) W.writeto(outfn)
def scan_dchisq(seeing, target_dchisq, ps, e1=0.): pixscale = 0.262 psfsigma = seeing / pixscale / 2.35 print('PSF sigma:', psfsigma, 'pixels') psf = GaussianMixturePSF(1., 0., 0., psfsigma**2, psfsigma**2, 0.) sig1 = 0.01 psfnorm = 1./(2. * np.sqrt(np.pi) * psfsigma) detsig1 = sig1 / psfnorm sz = 50 cd = pixscale / 3600. wcs = Tan(0., 0., float(sz/2), float(sz/2), -cd, 0., 0., cd, float(sz), float(sz)) band = 'r' tim = Image(data=np.zeros((sz,sz)), inverr=np.ones((sz,sz)) / sig1, psf=psf, wcs = ConstantFitsWcs(wcs), photocal = LinearPhotoCal(1., band=band)) re_vals = np.logspace(-1., 0., 50) all_runs = [] mods = [] for i,re in enumerate(re_vals): true_src = ExpGalaxy(RaDecPos(0., 0.), NanoMaggies(**{band: 1.}), EllipseE(re, e1, 0.)) print('True source:', true_src) tr = Tractor([tim], [true_src]) tr.freezeParams('images') true_mod = tr.getModelImage(0) dchisq_none = np.sum((true_mod * tim.inverr)**2) scale = np.sqrt(target_dchisq / dchisq_none) true_src.brightness.setParams([scale]) true_mod = tr.getModelImage(0) dchisq_none = np.sum((true_mod * tim.inverr)**2) mods.append(true_mod) tim.data = true_mod exp_src = true_src.copy() psf_src = PointSource(true_src.pos.copy(), true_src.brightness.copy()) simp_src = SimpleGalaxy(true_src.pos.copy(), true_src.brightness.copy()) dchisqs = [] #for src in [psf_src, simp_src, exp_src]: for src in [psf_src, simp_src]: src.freezeParam('pos') #print('Fitting source:', src) #src.printThawedParams() tr.catalog[0] = src tr.optimize_loop() #print('Fitted:', src) mod = tr.getModelImage(0) dchisqs.append(dchisq_none - np.sum(((true_mod - mod) * tim.inverr)**2)) #print('dchisq:', dchisqs[-1]) dchisqs.append(dchisq_none) all_runs.append([re,] + dchisqs) all_runs = np.array(all_runs) re = all_runs[:,0] dchi_psf = all_runs[:,1] dchi_simp = all_runs[:,2] dchi_exp = all_runs[:,3] dchi_ps = np.maximum(dchi_psf, dchi_simp) dchi_cut1 = dchi_ps + 3+9 dchi_cut2 = dchi_ps + dchi_psf * 0.02 dchi_cut3 = dchi_ps + dchi_psf * 0.008 plt.clf() plt.plot(re, dchi_psf, 'k-', label='PSF') plt.plot(re, dchi_simp, 'b-', label='SIMP') plt.plot(re, dchi_exp, 'r-', label='EXP') plt.plot(re, dchi_cut2, 'm--', alpha=0.5, lw=2, label='Cut: 2%') plt.plot(re, dchi_cut3, 'm:', alpha=0.5, lw=2, label='Cut: 0.08%') plt.plot(re, dchi_cut1, 'm-', alpha=0.5, lw=2, label='Cut: 12') plt.xlabel('True r_e (arcsec)') plt.ylabel('dchisq') #plt.legend(loc='lower left') plt.legend(loc='upper right') tt = 'Seeing = %g arcsec, S/N ~ %i' % (seeing, int(np.round(np.sqrt(target_dchisq)))) if e1 != 0.: tt += ', Ellipticity %g' % e1 plt.title(tt) plt.ylim(0.90 * target_dchisq, 1.05 * target_dchisq) # aspect = 1.2 # ax = plt.axis() # dre = (ax[1]-ax[0]) / 20 / aspect # dchi = (ax[3]-ax[2]) / 20 # I = np.linspace(0, len(re_vals)-1, 8).astype(int) # for mod,re in [(mods[i], re_vals[i]) for i in I]: # print('extent:', [re-dre, re+dre, ax[2], ax[2]+dchi]) # plt.imshow(mod, interpolation='nearest', origin='lower', aspect='auto', # extent=[re-dre, re+dre, ax[2], ax[2]+dchi], cmap='gray') # plt.axis(ax) ps.savefig()
for k in range(Nper): noise = np.random.normal(scale=sig1, size=true_mod.shape) tim.data = true_mod + noise if k == 0 and False: plt.clf() plt.subplot(1,2,1) plt.imshow(true_mod, **ima) plt.subplot(1,2,2) plt.imshow(tim.data, **ima) plt.title('S/N %f, r_e %f' % (sn, re)) ps.savefig() ## run one_blob code? Or shortcut? src = PointSource(RaDecPos(0., 0.), NanoMaggies(**{band: flux})) nblob,iblob,Isrcs = 0, 1, np.array([0]) brickwcs = wcs bx0, by0, blobw, blobh = 0, 0, sz, sz blobmask = np.ones((sz,sz), bool) timargs = [(tim.data, tim.getInvError(), tim.wcs, tim.wcs.wcs, tim.getPhotoCal(), tim.getSky(), tim.psf, 'tim', 0, sz, 0, sz, band, sig1, tim.modelMinval, None)] srcs = [src] bands = band plots,psx = False, None simul_opt, use_ceres, hastycho = False, False, False X = (nblob, iblob, Isrcs, brickwcs, bx0, by0, blobw, blobh,
def run_sed_matched_filters(SEDs, bands, detmaps, detivs, omit_xy, targetwcs, nsigma=5, saddle_fraction=0.1, saddle_min=2., saturated_pix=None, exclusion_radius=4., veto_map=None, mp=None, plots=False, ps=None, rgbimg=None): ''' Runs a given set of SED-matched filters. Parameters ---------- SEDs : list of (name, sed) tuples The SEDs to run. The `sed` values are lists the same length as `bands`. bands : list of string The band names of `detmaps` and `detivs`. detmaps : numpy array, float Detection maps for each of the listed `bands`. detivs : numpy array, float Inverse-variances of the `detmaps`. omit_xy : None, or (xx,yy,rr) tuple Existing sources to avoid: x, y, radius. targetwcs : WCS object WCS object to use to convert pixel values into RA,Decs for the returned Tractor PointSource objects. nsigma : float, optional Detection threshold saturated_pix : None or list of numpy arrays, booleans Passed through to sed_matched_detection. A map of pixels that are always considered "hot" when determining whether a new source touches hot pixels of an existing source. exclusion_radius: int How many pixels around an existing source to veto plots : boolean, optional Create plots? ps : PlotSequence object Create plots? mp : multiproc object Multiprocessing Returns ------- Tnew : fits_table Table of new sources detected newcat : list of PointSource objects Newly detected objects, with positions and fluxes, as Tractor PointSource objects. hot : numpy array of bool "Hot pixels" containing sources. See also -------- sed_matched_detection : run a single SED-matched filter. ''' from astrometry.util.fits import fits_table from tractor import PointSource, RaDecPos, NanoMaggies if omit_xy is not None: xx, yy, rr = omit_xy n0 = len(xx) else: xx, yy, rr = [], [], [] n0 = 0 H, W = detmaps[0].shape hot = np.zeros((H, W), bool) peaksn = [] apsn = [] for sedname, sed in SEDs: if plots: pps = ps else: pps = None sedhot, px, py, peakval, apval = sed_matched_detection( sedname, sed, detmaps, detivs, bands, xx, yy, rr, nsigma=nsigma, saddle_fraction=saddle_fraction, saddle_min=saddle_min, saturated_pix=saturated_pix, veto_map=veto_map, ps=pps, rgbimg=rgbimg) if sedhot is None: continue info('SED', sedname, ':', len(px), 'new peaks') hot |= sedhot # With an empty xx, np.append turns it into a double! xx = np.append(xx, px).astype(int) yy = np.append(yy, py).astype(int) rr = np.append(rr, np.zeros_like(px) + exclusion_radius).astype(int) peaksn.extend(peakval) apsn.extend(apval) # New peaks: peakx = xx[n0:] peaky = yy[n0:] Tnew = None newcat = [] if len(peakx): # Add sources for the new peaks we found pr, pd = targetwcs.pixelxy2radec(peakx + 1, peaky + 1) info('Adding', len(pr), 'new sources') # Also create FITS table for new sources Tnew = fits_table() Tnew.ra = pr Tnew.dec = pd Tnew.ibx = peakx Tnew.iby = peaky assert (len(peaksn) == len(Tnew)) assert (len(apsn) == len(Tnew)) Tnew.peaksn = np.array(peaksn) Tnew.apsn = np.array(apsn) for r, d, x, y in zip(pr, pd, peakx, peaky): fluxes = dict([(band, detmap[y, x]) for band, detmap in zip(bands, detmaps)]) newcat.append( PointSource(RaDecPos(r, d), NanoMaggies(order=bands, **fluxes))) return Tnew, newcat, hot
# (but in SDSS we'll be using multi-band NanoMaggies) band = 'r' # image sensitivity: photocal = LinearPhotoCal(10., band=band) # flat sky sky = ConstantSky(0.) # Create tractor.Image object tim = Image(data, iv, psf=psf, wcs=wcs, sky=sky, photocal=photocal) def brightness(x): return NanoMaggies(**{band: x}) # Create some sources ptsrc = PointSource(RaDecPos(ra, dec), brightness(10.)) gal1 = ExpGalaxy(RaDecPos(ra - 10 * pscale, dec), brightness(50.), GalaxyShape(5., 0.5, 45.)) gal2 = DevGalaxy(RaDecPos(ra + 10 * pscale, dec), brightness(50.), GalaxyShape(5., 0.25, 135.)) gal3 = FixedCompositeGalaxy( RaDecPos(ra, dec + 10 * pscale), brightness(50.), 0.6, # fraction of light in deV component GalaxyShape(5., 0.6, 30.), GalaxyShape(5., 0.3, 45.)) cat = Catalog(ptsrc, gal1, gal2, gal3)