def write_color_image(self, survey, brickname, coimgs, comods): from legacypipe.survey import imsave_jpeg # W1/W2 color jpeg rgb = _unwise_to_rgb(coimgs[:2]) with survey.write_output('wise-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn) rgb = _unwise_to_rgb(comods[:2]) with survey.write_output('wisemodel-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn)
def write_color_image(self, survey, brickname, coimgs, comods): from legacypipe.survey import imsave_jpeg rgbfunc = _galex_rgb_moustakas # W1/W2 color jpeg rgb = rgbfunc(coimgs) with survey.write_output('galex-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn) rgb = rgbfunc(comods) with survey.write_output('galexmodel-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn)
def finish(self, survey, brickname, version_header): from legacypipe.survey import imsave_jpeg for band, co, n, com, coiv in zip([1, 2, 3, 4], self.unwise_co, self.unwise_con, self.unwise_com, self.unwise_coiv): hdr = fitsio.FITSHDR() for r in version_header.records(): hdr.add_record(r) hdr.add_record(dict(name='TELESCOP', value='WISE')) hdr.add_record( dict(name='FILTER', value='W%i' % band, comment='WISE band')) self.unwise_wcs.add_to_header(hdr) hdr.delete('IMAGEW') hdr.delete('IMAGEH') hdr.add_record(dict(name='EQUINOX', value=2000.)) hdr.add_record( dict(name='MAGZERO', value=22.5, comment='Magnitude zeropoint')) hdr.add_record( dict(name='MAGSYS', value='Vega', comment='This WISE image is in Vega fluxes')) co /= np.maximum(n, 1) com /= np.maximum(n, 1) with survey.write_output('image', brick=brickname, band='W%i' % band, shape=co.shape) as out: out.fits.write(co, header=hdr) with survey.write_output('model', brick=brickname, band='W%i' % band, shape=com.shape) as out: out.fits.write(com, header=hdr) with survey.write_output('invvar', brick=brickname, band='W%i' % band, shape=co.shape) as out: out.fits.write(coiv, header=hdr) # W1/W2 color jpeg rgb = _unwise_to_rgb(self.unwise_co[:2]) with survey.write_output('wise-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn) rgb = _unwise_to_rgb(self.unwise_com[:2]) with survey.write_output('wisemodel-jpeg', brick=brickname) as out: imsave_jpeg(out.fn, rgb, origin='lower') info('Wrote', out.fn)
def main(): survey = LegacySurveyData() brickname = '2351p137' # RA,Dec = 235.0442, 13.7125 bx, by = 3300, 1285 sz = 50 bbox = [bx - sz, bx + sz, by - sz, by + sz] objid = 1394 bands = ['g', 'r', 'z'] from legacypipe.runbrick import stage_tims, _get_mod, rgbkwargs, rgbkwargs_resid from legacypipe.survey import get_rgb, imsave_jpeg from legacypipe.coadds import make_coadds from astrometry.util.multiproc import multiproc from astrometry.util.fits import fits_table from legacypipe.catalog import read_fits_catalog # brick = survey.get_brick_by_name(brickname) # # Get WCS object describing brick # targetwcs = wcs_for_brick(brick) # (x0,x1,y0,y1) = bbox # W = x1-x0 # H = y1-y0 # targetwcs = targetwcs.get_subimage(x0, y0, W, H) # H,W = targetwcs.shape mp = multiproc() P = stage_tims(brickname=brickname, survey=survey, target_extent=bbox, pixPsf=True, hybridPsf=True, depth_cut=False, mp=mp) print('Got', P.keys()) tims = P['tims'] targetwcs = P['targetwcs'] H, W = targetwcs.shape # Read Tractor catalog fn = survey.find_file('tractor', brick=brickname) print('Trying to read catalog', fn) cat = fits_table(fn) print('Read', len(cat), 'sources') ok, xx, yy = targetwcs.radec2pixelxy(cat.ra, cat.dec) I = np.flatnonzero((xx > 0) * (xx < W) * (yy > 0) * (yy < H)) cat.cut(I) print('Cut to', len(cat), 'sources within box') I = np.flatnonzero(cat.objid != objid) cat.cut(I) print('Cut to', len(cat), 'sources with objid !=', objid) #cat.about() # Convert FITS catalog into tractor source objects print('Creating tractor sources...') srcs = read_fits_catalog(cat, fluxPrefix='') print('Sources:') for src in srcs: print(' ', src) print('Rendering model images...') mods = [_get_mod((tim, srcs)) for tim in tims] print('Producing coadds...') C = make_coadds(tims, bands, targetwcs, mods=mods, mp=mp) print('Coadds:', dir(C)) coadd_list = [('image', C.coimgs, rgbkwargs), ('model', C.comods, rgbkwargs), ('resid', C.coresids, rgbkwargs_resid)] #C.coimgs, C.comods, C.coresids for name, ims, rgbkw in coadd_list: rgb = get_rgb(ims, bands, **rgbkw) kwa = {} #with survey.write_output(name + '-jpeg', brick=brickname) as out: # imsave_jpeg(out.fn, rgb, origin='lower', **kwa) # print('Wrote', out.fn) outfn = name + '.jpg' imsave_jpeg(outfn, rgb, origin='lower', **kwa) del rgb
def _tractor_coadds(galaxycat, targetwcs, tims, mods, version_header, objid=None, brickname=None, survey=None, mp=None, verbose=False, bands=['g', 'r', 'z']): """Generate individual-band FITS and color coadds for each central using Tractor. """ from legacypipe.coadds import make_coadds, write_coadd_images from legacypipe.runbrick import rgbkwargs, rgbkwargs_resid from legacypipe.survey import get_rgb, imsave_jpeg if brickname is None: brickname = galaxycat['brickname'] if verbose: print('Producing coadds...') C = make_coadds(tims, bands, targetwcs, mods=mods, mp=mp, callback=write_coadd_images, callback_args=(survey, brickname, version_header, tims, targetwcs)) # Move (rename) the coadds into the desired output directory. for suffix in ('chi2', 'image', 'invvar', 'model'): for band in bands: oldfile = os.path.join( survey.output_dir, 'coadd', brickname[:3], brickname, 'legacysurvey-{}-{}-{}.fits.fz'.format(brickname, suffix, band)) newfile = os.path.join( survey.output_dir, '{}-{}-{}.fits.fz'.format(objid, suffix, band)) shutil.copy(oldfile, newfile) if True: shutil.rmtree(os.path.join(survey.output_dir, 'coadd')) # Build png postage stamps of the coadds. coadd_list = [('image', C.coimgs, rgbkwargs), ('model', C.comods, rgbkwargs), ('resid', C.coresids, rgbkwargs_resid)] for name, ims, rgbkw in coadd_list: rgb = get_rgb(ims, bands, **rgbkw) kwa = {} outfn = os.path.join(survey.output_dir, '{}-{}.jpg'.format(objid, name)) if verbose: print('Writing {}'.format(outfn)) imsave_jpeg(outfn, rgb, origin='lower', **kwa) del rgb
def unwise_coadds(onegal, galaxy=None, radius_mosaic=30, radius_mask=None, pixscale=2.75, ref_pixscale=0.262, output_dir=None, unwise_dir=None, verbose=False, log=None, centrals=True): '''Generate custom unWISE cutouts. radius_mosaic and radius_mask in arcsec pixscale: WISE pixel scale in arcsec/pixel; make this smaller than 2.75 to oversample. ''' import fitsio import matplotlib.pyplot as plt from astrometry.util.util import Tan from astrometry.util.fits import fits_table from astrometry.libkd.spherematch import match_radec from astrometry.util.resample import resample_with_wcs, ResampleError from wise.forcedphot import unwise_tiles_touching_wcs from wise.unwise import get_unwise_tractor_image from tractor import Tractor, Image, NanoMaggies from legacypipe.survey import imsave_jpeg from legacypipe.catalog import read_fits_catalog if galaxy is None: galaxy = 'galaxy' if output_dir is None: output_dir = '.' if unwise_dir is None: unwise_dir = os.environ.get('UNWISE_COADDS_DIR') if radius_mask is None: radius_mask = radius_mosaic radius_search = 5.0 # [arcsec] else: radius_search = radius_mask # Initialize the WCS object. W = H = np.ceil(2 * radius_mosaic / pixscale).astype('int') # [pixels] targetwcs = Tan(onegal['RA'], onegal['DEC'], (W + 1) / 2.0, (H + 1) / 2.0, -pixscale / 3600.0, 0.0, 0.0, pixscale / 3600.0, float(W), float(H)) # Read the custom Tractor catalog. tractorfile = os.path.join(output_dir, '{}-tractor.fits'.format(galaxy)) if not os.path.isfile(tractorfile): print('Missing Tractor catalog {}'.format(tractorfile), flush=True, file=log) return 0 primhdr = fitsio.read_header(tractorfile) cat = fits_table(tractorfile) print('Read {} sources from {}'.format(len(cat), tractorfile), flush=True, file=log) keep = np.ones(len(cat)).astype(bool) if centrals: # Find the large central galaxy and mask out (ignore) all the models # which are within its elliptical mask. # This algorithm will have to change for mosaics not centered on large # galaxies, e.g., in galaxy groups. m1, m2, d12 = match_radec(cat.ra, cat.dec, onegal['RA'], onegal['DEC'], radius_search / 3600.0, nearest=False) if len(m1) == 0: print('No central galaxies found at the central coordinates!', flush=True, file=log) else: pixfactor = ref_pixscale / pixscale # shift the optical Tractor positions for mm in m1: morphtype = cat.type[mm].strip() if morphtype == 'EXP' or morphtype == 'COMP': e1, e2, r50 = cat.shapeexp_e1[mm], cat.shapeexp_e2[ mm], cat.shapeexp_r[mm] # [arcsec] elif morphtype == 'DEV' or morphtype == 'COMP': e1, e2, r50 = cat.shapedev_e1[mm], cat.shapedev_e2[ mm], cat.shapedev_r[mm] # [arcsec] else: r50 = None if r50: majoraxis = r50 * 5 / pixscale # [pixels] ba, phi = SGA.misc.convert_tractor_e1e2(e1, e2) these = SGA.misc.ellipse_mask(W / 2, W / 2, majoraxis, ba * majoraxis, np.radians(phi), cat.bx * pixfactor, cat.by * pixfactor) if np.sum(these) > 0: #keep[these] = False pass print('Hack!') keep[mm] = False #srcs = read_fits_catalog(cat) #_srcs = np.array(srcs)[~keep].tolist() #mod = SGA.misc.srcs2image(_srcs, ConstantFitsWcs(targetwcs), psf_sigma=3.0) #import matplotlib.pyplot as plt ##plt.imshow(mod, origin='lower') ; plt.savefig('junk.png') #plt.imshow(np.log10(mod), origin='lower') ; plt.savefig('junk.png') #pdb.set_trace() srcs = read_fits_catalog(cat) for src in srcs: src.freezeAllBut('brightness') #srcs_nocentral = np.array(srcs)[keep].tolist() cat_nocentral = cat[keep] ## Find and remove all the objects within XX arcsec of the target ## coordinates. #m1, m2, d12 = match_radec(T.ra, T.dec, onegal['RA'], onegal['DEC'], 5/3600.0, nearest=False) #if len(d12) == 0: # print('No matching galaxies found -- probably not what you wanted.') # #raise ValueError # nocentral = np.ones(len(T)).astype(bool) #else: # nocentral = ~np.isin(T.objid, T[m1].objid) #T_nocentral = T[nocentral] # Find and read the overlapping unWISE tiles. Assume the targetwcs is # axis-aligned and that the edge midpoints yield the RA, Dec limits (true # for TAN). Note: the way the roiradec box is used, the min/max order # doesn't matter. r, d = targetwcs.pixelxy2radec(np.array([1, W, W / 2, W / 2]), np.array([H / 2, H / 2, 1, H])) roiradec = [r[0], r[1], d[2], d[3]] tiles = unwise_tiles_touching_wcs(targetwcs) wbands = [1, 2, 3, 4] wanyband = 'w' vega_to_ab = dict(w1=2.699, w2=3.339, w3=5.174, w4=6.620) # Convert the AB WISE fluxes in the Tractor catalog to Vega nanomaggies so # they're consistent with the coadds, below. for band in wbands: f = cat.get('flux_w{}'.format(band)) e = cat.get('flux_ivar_w{}'.format(band)) print('Setting negative fluxes equal to zero!') f[f < 0] = 0 #f[f/e < 3] = 0 f *= 10**(0.4 * vega_to_ab['w{}'.format(band)]) coimgs = [np.zeros((H, W), np.float32) for b in wbands] comods = [np.zeros((H, W), np.float32) for b in wbands] comods_nocentral = [np.zeros((H, W), np.float32) for b in wbands] con = [np.zeros((H, W), np.uint8) for b in wbands] for iband, band in enumerate(wbands): for ii, src in enumerate(srcs): src.setBrightness( NanoMaggies( **{wanyband: cat.get('flux_w{}'.format(band))[ii]})) srcs_nocentral = np.array(srcs)[keep].tolist() #srcs_nocentral = np.array(srcs)[nocentral].tolist() # The tiles have some overlap, so for each source, keep the fit in the # tile whose center is closest to the source. for tile in tiles: #print('Reading tile {}'.format(tile.coadd_id)) tim = get_unwise_tractor_image(unwise_dir, tile.coadd_id, band, bandname=wanyband, roiradecbox=roiradec) if tim is None: print('Actually, no overlap with tile {}'.format( tile.coadd_id)) continue print('Read image {} with shape {}'.format(tile.coadd_id, tim.shape)) def _unwise_mod(tim, use_cat, use_srcs, margin=10): # Select sources in play. wisewcs = tim.wcs.wcs timH, timW = tim.shape ok, x, y = wisewcs.radec2pixelxy(use_cat.ra, use_cat.dec) x = (x - 1.).astype(np.float32) y = (y - 1.).astype(np.float32) I = np.flatnonzero((x >= -margin) * (x < timW + margin) * (y >= -margin) * (y < timH + margin)) #print('Found {} sources within the image + margin = {} pixels'.format(len(I), margin)) subcat = [use_srcs[i] for i in I] tractor = Tractor([tim], subcat) mod = tractor.getModelImage(0) return mod mod = _unwise_mod(tim, cat, srcs) mod_nocentral = _unwise_mod(tim, cat_nocentral, srcs_nocentral) try: Yo, Xo, Yi, Xi, nil = resample_with_wcs(targetwcs, tim.wcs.wcs) except ResampleError: continue if len(Yo) == 0: continue # The models are already in AB nanomaggies, but the tiles / tims are # in Vega nanomaggies, so convert them here. coimgs[iband][Yo, Xo] += tim.getImage()[Yi, Xi] comods[iband][Yo, Xo] += mod[Yi, Xi] comods_nocentral[iband][Yo, Xo] += mod_nocentral[Yi, Xi] con[iband][Yo, Xo] += 1 ## Convert back to nanomaggies. #vega2ab = vega_to_ab['w{}'.format(band)] #coimgs[iband] *= 10**(-0.4 * vega2ab) #comods[iband] *= 10**(-0.4 * vega2ab) #comods_nocentral[iband] *= 10**(-0.4 * vega2ab) for img, mod, mod_nocentral, n in zip(coimgs, comods, comods_nocentral, con): img /= np.maximum(n, 1) mod /= np.maximum(n, 1) mod_nocentral /= np.maximum(n, 1) coresids = [img - mod for img, mod in list(zip(coimgs, comods))] # Subtract the model image which excludes the central (comod_nocentral) # from the data (coimg) to isolate the light of the central # (coimg_central). coimgs_central = [ img - mod for img, mod in list(zip(coimgs, comods_nocentral)) ] # Write out the final images with and without the central and converted into # AB nanomaggies. for coadd, imtype in zip((coimgs, comods, comods_nocentral), ('image', 'model', 'model-nocentral')): for img, band in zip(coadd, wbands): vega2ab = vega_to_ab['w{}'.format(band)] fitsfile = os.path.join( output_dir, '{}-{}-W{}.fits'.format(galaxy, imtype, band)) if verbose: print('Writing {}'.format(fitsfile)) fitsio.write(fitsfile, img * 10**(-0.4 * vega2ab), clobber=True) # Generate color WISE images. kwa = dict(mn=-1, mx=100, arcsinh=0.5) #kwa = dict(mn=-0.05, mx=1., arcsinh=0.5) #kwa = dict(mn=-0.1, mx=2., arcsinh=None) for imgs, imtype in zip( (coimgs, comods, coresids, comods_nocentral, coimgs_central), ('image', 'model', 'resid', 'model-nocentral', 'image-central')): rgb = _unwise_to_rgb(imgs[:2], **kwa) # W1, W2 jpgfile = os.path.join(output_dir, '{}-{}-W1W2.jpg'.format(galaxy, imtype)) if verbose: print('Writing {}'.format(jpgfile)) imsave_jpeg(jpgfile, rgb, origin='lower') return 1
def _tractor_coadds(sample, targetwcs, tims, mods, version_header, objid=None, brickname=None, survey=None, mp=None, log=None, bands=['g', 'r', 'z']): """Generate individual-band FITS and color coadds for each central using Tractor. """ from legacypipe.coadds import make_coadds, write_coadd_images #from legacypipe.runbrick import rgbkwargs, rgbkwargs_resid from legacypipe.survey import get_rgb, imsave_jpeg if brickname is None: brickname = sample['brickname'] print('Producing coadds...', flush=True, file=log) if log: with redirect_stdout(log), redirect_stderr(log): C = make_coadds(tims, bands, targetwcs, mods=mods, mp=mp, callback=write_coadd_images, callback_args=(survey, brickname, version_header, tims, targetwcs)) else: C = make_coadds(tims, bands, targetwcs, mods=mods, mp=mp, callback=write_coadd_images, callback_args=(survey, brickname, version_header, tims, targetwcs)) # Move (rename) the coadds into the desired output directory. for suffix in np.atleast_1d('model'): for band in bands: shutil.copy( os.path.join( survey.output_dir, 'coadd', brickname[:3], brickname, 'legacysurvey-{}-{}-{}.fits.fz'.format( brickname, suffix, band)), os.path.join( survey.output_dir, '{}-{}-nocentral-{}.fits.fz'.format(objid, suffix, band))) if True: shutil.rmtree(os.path.join(survey.output_dir, 'coadd')) # Build png postage stamps of the coadds. rgbkwargs = dict(mnmx=(-1, 100), arcsinh=1) #rgbkwargs_resid = dict(mnmx=(0.1, 2), arcsinh=1) rgbkwargs_resid = dict(mnmx=(-1, 100), arcsinh=1) #coadd_list = [('image-central', C.coimgs, rgbkwargs), # ('model-central', C.comods, rgbkwargs), # ('resid-central', C.coresids, rgbkwargs_resid)] coadd_list = [('model-nocentral', C.comods, rgbkwargs), ('image-central', C.coresids, rgbkwargs_resid)] for name, ims, rgbkw in coadd_list: rgb = get_rgb(ims, bands, **rgbkw) kwa = {} outfn = os.path.join(survey.output_dir, '{}-{}.jpg'.format(objid, name)) print('Writing {}'.format(outfn), flush=True, file=log) imsave_jpeg(outfn, rgb, origin='lower', **kwa) del rgb
def galex_coadds(onegal, galaxy=None, radius_mosaic=30, radius_mask=None, pixscale=1.5, ref_pixscale=0.262, output_dir=None, galex_dir=None, log=None, centrals=True, verbose=False): '''Generate custom GALEX cutouts. radius_mosaic and radius_mask in arcsec pixscale: GALEX pixel scale in arcsec/pixel. ''' import fitsio import matplotlib.pyplot as plt from astrometry.libkd.spherematch import match_radec from astrometry.util.resample import resample_with_wcs, OverlapError from tractor import (Tractor, NanoMaggies, Image, LinearPhotoCal, NCircularGaussianPSF, ConstantFitsWcs, ConstantSky) from legacypipe.survey import imsave_jpeg from legacypipe.catalog import read_fits_catalog if galaxy is None: galaxy = 'galaxy' if galex_dir is None: galex_dir = os.environ.get('GALEX_DIR') if output_dir is None: output_dir = '.' if radius_mask is None: radius_mask = radius_mosaic radius_search = 5.0 # [arcsec] else: radius_search = radius_mask W = H = np.ceil(2 * radius_mosaic / pixscale).astype('int') # [pixels] targetwcs = Tan(onegal['RA'], onegal['DEC'], (W + 1) / 2.0, (H + 1) / 2.0, -pixscale / 3600.0, 0.0, 0.0, pixscale / 3600.0, float(W), float(H)) # Read the custom Tractor catalog tractorfile = os.path.join(output_dir, '{}-tractor.fits'.format(galaxy)) if not os.path.isfile(tractorfile): print('Missing Tractor catalog {}'.format(tractorfile)) return 0 cat = fits_table(tractorfile) print('Read {} sources from {}'.format(len(cat), tractorfile), flush=True, file=log) keep = np.ones(len(cat)).astype(bool) if centrals: # Find the large central galaxy and mask out (ignore) all the models # which are within its elliptical mask. # This algorithm will have to change for mosaics not centered on large # galaxies, e.g., in galaxy groups. m1, m2, d12 = match_radec(cat.ra, cat.dec, onegal['RA'], onegal['DEC'], radius_search / 3600.0, nearest=False) if len(m1) == 0: print('No central galaxies found at the central coordinates!', flush=True, file=log) else: pixfactor = ref_pixscale / pixscale # shift the optical Tractor positions for mm in m1: morphtype = cat.type[mm].strip() if morphtype == 'EXP' or morphtype == 'COMP': e1, e2, r50 = cat.shapeexp_e1[mm], cat.shapeexp_e2[ mm], cat.shapeexp_r[mm] # [arcsec] elif morphtype == 'DEV' or morphtype == 'COMP': e1, e2, r50 = cat.shapedev_e1[mm], cat.shapedev_e2[ mm], cat.shapedev_r[mm] # [arcsec] else: r50 = None if r50: majoraxis = r50 * 5 / pixscale # [pixels] ba, phi = SGA.misc.convert_tractor_e1e2(e1, e2) these = SGA.misc.ellipse_mask(W / 2, W / 2, majoraxis, ba * majoraxis, np.radians(phi), cat.bx * pixfactor, cat.by * pixfactor) if np.sum(these) > 0: #keep[these] = False pass print('Hack!') keep[mm] = False #srcs = read_fits_catalog(cat) #_srcs = np.array(srcs)[~keep].tolist() #mod = SGA.misc.srcs2image(_srcs, ConstantFitsWcs(targetwcs), psf_sigma=3.0) #import matplotlib.pyplot as plt ##plt.imshow(mod, origin='lower') ; plt.savefig('junk.png') #plt.imshow(np.log10(mod), origin='lower') ; plt.savefig('junk.png') #pdb.set_trace() srcs = read_fits_catalog(cat) for src in srcs: src.freezeAllBut('brightness') #srcs_nocentral = np.array(srcs)[keep].tolist() # Find all overlapping GALEX tiles and then read the tims. galex_tiles = _read_galex_tiles(targetwcs, galex_dir, log=log, verbose=verbose) gbands = ['n', 'f'] nicegbands = ['NUV', 'FUV'] zps = dict(n=20.08, f=18.82) coimgs, comods, coresids, coimgs_central, comods_nocentral = [], [], [], [], [] for niceband, band in zip(nicegbands, gbands): J = np.flatnonzero(galex_tiles.get('has_' + band)) print(len(J), 'GALEX tiles have coverage in band', band) coimg = np.zeros((H, W), np.float32) comod = np.zeros((H, W), np.float32) cowt = np.zeros((H, W), np.float32) comod_nocentral = np.zeros((H, W), np.float32) for src in srcs: src.setBrightness(NanoMaggies(**{band: 1})) for j in J: brick = galex_tiles[j] fn = os.path.join( galex_dir, brick.tilename.strip(), '%s-%sd-intbgsub.fits.gz' % (brick.brickname, band)) #print(fn) gwcs = Tan(*[ float(f) for f in [ brick.crval1, brick.crval2, brick.crpix1, brick.crpix2, brick.cdelt1, 0., 0., brick.cdelt2, 3840., 3840. ] ]) img = fitsio.read(fn) #print('Read', img.shape) try: Yo, Xo, Yi, Xi, nil = resample_with_wcs(targetwcs, gwcs, [], 3) except OverlapError: continue K = np.flatnonzero(img[Yi, Xi] != 0.) if len(K) == 0: continue Yo, Xo, Yi, Xi = Yo[K], Xo[K], Yi[K], Xi[K] wt = brick.get(band + 'exptime') coimg[Yo, Xo] += wt * img[Yi, Xi] cowt[Yo, Xo] += wt x0, x1, y0, y1 = min(Xi), max(Xi), min(Yi), max(Yi) subwcs = gwcs.get_subimage(x0, y0, x1 - x0 + 1, y1 - y0 + 1) twcs = ConstantFitsWcs(subwcs) timg = img[y0:y1 + 1, x0:x1 + 1] tie = np.ones_like(timg) ## HACK! #hdr = fitsio.read_header(fn) #zp = hdr[''] zp = zps[band] photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=band) tsky = ConstantSky(0.0) # HACK -- circular Gaussian PSF of fixed size... # in arcsec #fwhms = dict(NUV=6.0, FUV=6.0) # -> sigma in pixels #sig = fwhms[band] / 2.35 / twcs.pixel_scale() sig = 6.0 / np.sqrt(8 * np.log(2)) / twcs.pixel_scale() tpsf = NCircularGaussianPSF([sig], [1.]) tim = Image(data=timg, inverr=tie, psf=tpsf, wcs=twcs, sky=tsky, photocal=photocal, name='GALEX ' + band + brick.brickname) ## Build the model image with and without the central galaxy model. tractor = Tractor([tim], srcs) mod = tractor.getModelImage(0) tractor.freezeParam('images') tractor.optimize_forced_photometry(priors=False, shared_params=False) mod = tractor.getModelImage(0) srcs_nocentral = np.array(srcs)[keep].tolist() #srcs_nocentral = np.array(srcs)[nocentral].tolist() tractor_nocentral = Tractor([tim], srcs_nocentral) mod_nocentral = tractor_nocentral.getModelImage(0) comod[Yo, Xo] += wt * mod[Yi - y0, Xi - x0] comod_nocentral[Yo, Xo] += wt * mod_nocentral[Yi - y0, Xi - x0] coimg /= np.maximum(cowt, 1e-18) comod /= np.maximum(cowt, 1e-18) comod_nocentral /= np.maximum(cowt, 1e-18) coresid = coimg - comod # Subtract the model image which excludes the central (comod_nocentral) # from the data (coimg) to isolate the light of the central # (coimg_central). coimg_central = coimg - comod_nocentral coimgs.append(coimg) comods.append(comod) coresids.append(coresid) comods_nocentral.append(comod_nocentral) coimgs_central.append(coimg_central) # Write out the final images with and without the central, making sure # to apply the zeropoint to go from counts/s to AB nanomaggies. # https://asd.gsfc.nasa.gov/archive/galex/FAQ/counts_background.html for thisimg, imtype in zip((coimg, comod, comod_nocentral), ('image', 'model', 'model-nocentral')): fitsfile = os.path.join( output_dir, '{}-{}-{}.fits'.format(galaxy, imtype, niceband)) if verbose: print('Writing {}'.format(fitsfile)) fitsio.write(fitsfile, thisimg * 10**(-0.4 * (zp - 22.5)), clobber=True) # Build a color mosaic (but note that the images here are in units of # background-subtracted counts/s). #_galex_rgb = _galex_rgb_moustakas #_galex_rgb = _galex_rgb_dstn _galex_rgb = _galex_rgb_official for imgs, imtype in zip( (coimgs, comods, coresids, comods_nocentral, coimgs_central), ('image', 'model', 'resid', 'model-nocentral', 'image-central')): rgb = _galex_rgb(imgs) jpgfile = os.path.join(output_dir, '{}-{}-FUVNUV.jpg'.format(galaxy, imtype)) if verbose: print('Writing {}'.format(jpgfile)) imsave_jpeg(jpgfile, rgb, origin='lower') return 1
def ubercal_skysub(tims, targetwcs, survey, brickname, bands, mp, subsky_radii=None, plots=False, plots2=False, ps=None, verbose=False): """With the ubercal option, we (1) read the full-field mosaics ('bandtims') for a given bandpass and put them all on the same 'system' using the overlapping pixels; (2) apply the derived corrections to the in-field 'tims'; (3) build the coadds (per bandpass) from the 'tims'; and (4) subtract the median sky from the mosaic (after aggressively masking objects and reference sources). """ from tractor.sky import ConstantSky from legacypipe.reference import get_reference_sources, get_reference_map from legacypipe.coadds import make_coadds from legacypipe.survey import get_rgb, imsave_jpeg from astropy.stats import sigma_clipped_stats if plots or plots2: import os import matplotlib.pyplot as plt if plots: plt.figure(figsize=(8, 6)) mods = [] for tim in tims: imcopy = tim.getImage().copy() tim.sky.addTo(imcopy, -1) mods.append(imcopy) C = make_coadds(tims, bands, targetwcs, mods=mods, callback=None, mp=mp) imsave_jpeg(os.path.join(survey.output_dir, 'metrics', 'cus', '{}-pipelinesky.jpg'.format(ps.basefn)), get_rgb(C.comods, bands), origin='lower') skydict = {} if subsky_radii is not None: skydict.update( {'NSKYANN': (len(subsky_radii) // 2, 'number of sky annuli')}) for irad, (rin, rout) in enumerate( zip(subsky_radii[0::2], subsky_radii[1::2])): skydict.update({ 'SKYRIN{:02d}'.format(irad): (np.float32(rin), 'inner sky radius {} [arcsec]'.format(irad)) }) skydict.update({ 'SKYROT{:02d}'.format(irad): (np.float32(rout), 'outer sky radius {} [arcsec]'.format(irad)) }) allbands = np.array([tim.band for tim in tims]) # we need the full-field mosaics if doing annular sky-subtraction if subsky_radii is not None: allbandtims = [] else: allbandtims = None for band in sorted(set(allbands)): print('Working on band {}'.format(band)) I = np.where(allbands == band)[0] bandtims = [ tims[ii].imobj.get_tractor_image(gaussPsf=True, pixPsf=False, subsky=False, dq=True, apodize=False) for ii in I ] # Derive the ubercal correction and then apply it. x = coadds_ubercal(bandtims, coaddtims=[tims[ii] for ii in I], plots=plots, plots2=plots2, ps=ps, verbose=True) for ii, corr in zip(I, x): skydict.update({ 'SKCCD{:03d}'.format(ii): (tims[ii].name, 'ubersky CCD {:03d}'.format(ii)) }) skydict.update({ 'SKCOR{:03d}'.format(ii): (corr, 'ubersky corr CCD {:03d}'.format(ii)) }) # Apply the correction and return the tims for jj, (correction, ii) in enumerate(zip(x, I)): tims[ii].data += correction tims[ii].sky = ConstantSky(0.0) # Also correct the full-field mosaics bandtims[jj].data += correction bandtims[jj].sky = ConstantSky(0.0) ## Check-- #for jj, correction in enumerate(x): # fulltims[jj].data += correction #newcorrection = coadds_ubercal(fulltims) #print(newcorrection) if allbandtims is not None: allbandtims = allbandtims + bandtims # Estimate the sky background from an annulus surrounding the object # (assumed to be at the center of the mosaic, targetwcs.crval). if subsky_radii is not None: from astrometry.util.util import Tan # the inner and outer radii / annuli are nested in subsky_radii allrin = subsky_radii[0::2] allrout = subsky_radii[1::2] pixscale = targetwcs.pixel_scale() bigH = float(np.ceil(2 * np.max(allrout) / pixscale)) bigW = bigH # if doing annulur sky-subtraction we need a bigger mosaic bigtargetwcs = Tan(targetwcs.crval[0], targetwcs.crval[1], bigW / 2. + 0.5, bigH / 2. + 0.5, -pixscale / 3600.0, 0., 0., pixscale / 3600.0, float(bigW), float(bigH)) C = make_coadds(allbandtims, bands, bigtargetwcs, callback=None, sbscale=False, mp=mp) _, x0, y0 = bigtargetwcs.radec2pixelxy(bigtargetwcs.crval[0], bigtargetwcs.crval[1]) xcen, ycen = np.round(x0 - 1).astype('int'), np.round(y0 - 1).astype('int') ymask, xmask = np.ogrid[-ycen:bigH - ycen, -xcen:bigW - xcen] refs, _ = get_reference_sources(survey, bigtargetwcs, bigtargetwcs.pixel_scale(), ['r'], tycho_stars=True, gaia_stars=True, large_galaxies=True, star_clusters=True) refmask = get_reference_map(bigtargetwcs, refs) == 0 # True=skypix for coimg, coiv, band in zip(C.coimgs, C.cowimgs, bands): skypix = _build_objmask(coimg, coiv, refmask * (coiv > 0)) for irad, (rin, rout) in enumerate(zip(allrin, allrout)): inmask = (xmask**2 + ymask**2) <= (rin / pixscale)**2 outmask = (xmask**2 + ymask**2) <= (rout / pixscale)**2 skymask = (outmask * 1 - inmask * 1) == 1 # True=skypix # Find and mask objects, then get the sky. skypix_annulus = np.logical_and(skypix, skymask) #import matplotlib.pyplot as plt ; plt.imshow(skypix_annulus, origin='lower') ; plt.savefig('junk3.png') #import pdb ; pdb.set_trace() if np.sum(skypix_annulus) == 0: raise ValueError('No pixels in sky!') _skymean, _skymedian, _skysig = sigma_clipped_stats( coimg, mask=np.logical_not(skypix_annulus), sigma=3.0) skydict.update({ '{}SKYMN{:02d}'.format(band.upper(), irad): (np.float32(_skymean), 'mean {} sky in annulus {}'.format(band, irad)) }) skydict.update({ '{}SKYMD{:02d}'.format(band.upper(), irad): (np.float32(_skymedian), 'median {} sky in annulus {}'.format(band, irad)) }) skydict.update({ '{}SKYSG{:02d}'.format(band.upper(), irad): (np.float32(_skysig), 'sigma {} sky in annulus {}'.format(band, irad)) }) skydict.update({ '{}SKYNP{:02d}'.format(band.upper(), irad): (np.sum(skypix_annulus), 'npix {} sky in annulus {}'.format(band, irad)) }) # the reference annulus is the first one if irad == 0: skymean, skymedian, skysig = _skymean, _skymedian, _skysig skypix_mask = skypix_annulus I = np.where(allbands == band)[0] for ii in I: tims[ii].data -= skymedian #print('Tim', tims[ii], 'after subtracting skymedian: median', np.median(tims[ii].data)) else: # regular mosaic C = make_coadds(tims, bands, targetwcs, callback=None, sbscale=False, mp=mp) refs, _ = get_reference_sources(survey, targetwcs, targetwcs.pixel_scale(), ['r'], tycho_stars=True, gaia_stars=True, large_galaxies=True, star_clusters=True) refmask = get_reference_map(targetwcs, refs) == 0 # True=skypix for coimg, coiv, band in zip(C.coimgs, C.cowimgs, bands): skypix = refmask * (coiv > 0) skypix_mask = _build_objmask(coimg, coiv, skypix) skymean, skymedian, skysig = sigma_clipped_stats( coimg, mask=np.logical_not(skypix_mask), sigma=3.0) skydict.update({ '{}SKYMN00'.format(band.upper(), irad): (np.float32(_skymean), 'mean {} sky'.format(band)) }) skydict.update({ '{}SKYMD00'.format(band.upper(), irad): (np.float32(_skymedian), 'median {} sky'.format(band)) }) skydict.update({ '{}SKYSG00'.format(band.upper(), irad): (np.float32(_skysig), 'sigma {} sky'.format(band)) }) skydict.update({ '{}SKYNP00'.format(band.upper(), irad): (np.sum(skypix_mask), 'npix {} sky'.format(band)) }) I = np.where(allbands == band)[0] for ii in I: tims[ii].data -= skymedian # print('Tim', tims[ii], 'after subtracting skymedian: median', np.median(tims[ii].data)) #print('Band', band, 'Coadd sky:', skymedian) if plots2: plt.clf() plt.hist(coimg.ravel(), bins=50, range=(-3, 3), density=True) plt.axvline(skymedian, color='k') for ii in I: #print('Tim', tims[ii], 'median', np.median(tims[ii].data)) plt.hist((tims[ii].data - skymedian).ravel(), bins=50, range=(-3, 3), histtype='step', density=True) plt.title('Band %s: tim pix & skymedian' % band) ps.savefig() # Produce skymedian-subtracted, masked image for later RGB plot coimg -= skymedian coimg[~skypix_mask] = 0. #coimg[np.logical_not(skymask * (coiv > 0))] = 0. if plots2: plt.clf() plt.imshow(get_rgb(C.coimgs, bands), origin='lower', interpolation='nearest') ps.savefig() for band in bands: for tim in tims: if tim.band != band: continue plt.clf() C = make_coadds([tim], bands, targetwcs, callback=None, sbscale=False, mp=mp) plt.imshow(get_rgb(C.coimgs, bands).sum(axis=2), cmap='gray', interpolation='nearest', origin='lower') plt.title('Band %s: tim %s' % (band, tim.name)) ps.savefig() if plots: import pylab as plt import matplotlib.patches as patches # skysub QA C = make_coadds(tims, bands, targetwcs, callback=None, mp=mp) imsave_jpeg(os.path.join(survey.output_dir, 'metrics', 'cus', '{}-customsky.jpg'.format(ps.basefn)), get_rgb(C.coimgs, bands), origin='lower') # ccdpos QA refs, _ = get_reference_sources(survey, targetwcs, targetwcs.pixel_scale(), ['r'], tycho_stars=False, gaia_stars=False, large_galaxies=True, star_clusters=False) pixscale = targetwcs.pixel_scale() width = targetwcs.get_width() * pixscale / 3600 # [degrees] bb, bbcc = targetwcs.radec_bounds(), targetwcs.radec_center( ) # [degrees] pad = 0.5 * width # [degrees] delta = np.max((np.diff(bb[0:2]), np.diff(bb[2:4]))) / 2 + pad / 2 xlim = bbcc[0] - delta, bbcc[0] + delta ylim = bbcc[1] - delta, bbcc[1] + delta plt.clf() _, allax = plt.subplots(1, 3, figsize=(12, 5), sharey=True, sharex=True) for ax, band in zip(allax, ('g', 'r', 'z')): ax.set_xlabel('RA (deg)') ax.text(0.9, 0.05, band, ha='center', va='bottom', transform=ax.transAxes, fontsize=18) if band == 'g': ax.set_ylabel('Dec (deg)') ax.get_xaxis().get_major_formatter().set_useOffset(False) # individual CCDs these = np.where([tim.band == band for tim in tims])[0] col = plt.cm.Set1(np.linspace(0, 1, len(tims))) for ii, indx in enumerate(these): tim = tims[indx] #wcs = tim.subwcs wcs = tim.imobj.get_wcs() cc = wcs.radec_bounds() ax.add_patch( patches.Rectangle((cc[0], cc[2]), cc[1] - cc[0], cc[3] - cc[2], fill=False, lw=2, edgecolor=col[these[ii]], label='ccd{:02d}'.format(these[ii]))) ax.legend(ncol=2, frameon=False, loc='upper left', fontsize=10) # output mosaic footprint cc = targetwcs.radec_bounds() ax.add_patch( patches.Rectangle((cc[0], cc[2]), cc[1] - cc[0], cc[3] - cc[2], fill=False, lw=2, edgecolor='k')) if subsky_radii is not None: racen, deccen = targetwcs.crval for rad in subsky_radii: ax.add_patch( patches.Circle((racen, deccen), rad / 3600, fill=False, edgecolor='black', lw=2)) else: for gal in refs: ax.add_patch( patches.Circle((gal.ra, gal.dec), gal.radius, fill=False, edgecolor='black', lw=2)) ax.set_ylim(ylim) ax.set_xlim(xlim) ax.invert_xaxis() ax.set_aspect('equal') plt.subplots_adjust(bottom=0.12, wspace=0.05, left=0.12, right=0.97, top=0.95) plt.savefig( os.path.join(survey.output_dir, 'metrics', 'cus', '{}-ccdpos.jpg'.format(ps.basefn))) if plots2: plt.clf() for coimg, band in zip(C.coimgs, bands): plt.hist(coimg.ravel(), bins=50, range=(-0.5, 0.5), histtype='step', label=band) plt.legend() plt.title('After adjustment: coadds (sb scaled)') ps.savefig() return tims, skydict