def galaxy_norm(self, tim, x=None, y=None): # Galaxy-detection norm from tractor.galaxy import ExpGalaxy from tractor.ellipses import EllipseE from tractor.patch import ModelMask h, w = tim.shape band = tim.band if x is None: x = w / 2. if y is None: y = h / 2. pos = tim.wcs.pixelToPosition(x, y) gal = SimpleGalaxy(pos, NanoMaggies(**{band: 1.})) S = 32 mm = ModelMask(int(x - S), int(y - S), 2 * S + 1, 2 * S + 1) galmod = gal.getModelPatch(tim, modelMask=mm).patch galmod = np.maximum(0, galmod) galmod /= galmod.sum() galnorm = np.sqrt(np.sum(galmod**2)) return galnorm
def psf_residuals(expnum, ccdname, stampsize=35, nstar=30, magrange=(13, 17), verbose=0, splinesky=False): # Set the debugging level. if verbose == 0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl, format='%(message)s', stream=sys.stdout) pngprefix = 'qapsf-{}-{}'.format(expnum, ccdname) # Gather all the info we need about this CCD. decals = Decals() ccd = decals.find_ccds(expnum=expnum, ccdname=ccdname)[0] band = ccd.filter ps1band = dict(g=0, r=1, i=2, z=3, Y=4) print('Band {}'.format(band)) #scales = dict(g=0.0066, r=0.01, z=0.025) #vmin, vmax = np.arcsinh(-1), np.arcsinh(100) #print(scales[band]) im = decals.get_image_object(ccd) iminfo = im.get_image_info() H, W = iminfo['dims'] wcs = im.get_wcs() # Choose a uniformly selected subset of PS1 stars on this CCD. ps1 = ps1cat(ccdwcs=wcs) cat = ps1.get_stars(band=band, magrange=magrange) rand = np.random.RandomState(seed=expnum * ccd.ccdnum) these = rand.choice(len(cat) - 1, nstar, replace=False) #these = rand.random_integers(0,len(cat)-1,nstar) cat = cat[these] cat = cat[np.argsort(cat.median[:, ps1band[band]])] # sort by magnitude #print(cat.nmag_ok) get_tim_kwargs = dict(const2psf=True, splinesky=splinesky) # Make a QAplot of the positions of all the stars. tim = im.get_tractor_image(**get_tim_kwargs) img = tim.getImage() #img = tim.getImage()/scales[band] fig = plt.figure(figsize=(5, 10)) ax = fig.gca() ax.get_xaxis().get_major_formatter().set_useOffset(False) #ax.imshow(np.arcsinh(img),cmap='gray',interpolation='nearest', # origin='lower',vmin=vmax,vmax=vmax) ax.imshow(img, **tim.ima) ax.axis('off') ax.set_title('{}: {}/{} AM={:.2f} Seeing={:.3f}"'.format( band, expnum, ccdname, ccd.airmass, ccd.seeing)) for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ax.text(xpos, ypos, '{:2d}'.format(istar + 1), color='red', horizontalalignment='left') circ = plt.Circle((xpos, ypos), radius=30, color='g', fill=False, lw=1) ax.add_patch(circ) #radec = wcs.radec_bounds() #ax.scatter(cat.ra,cat.dec) #ax.set_xlim([radec[1],radec[0]])#*[1.0002,0.9998]) #ax.set_ylim([radec[2],radec[3]])#*[0.985,1.015]) #ax.set_xlabel('$RA\ (deg)$',fontsize=18) #ax.set_ylabel('$Dec\ (deg)$',fontsize=18) fig.savefig(pngprefix + '-ccd.png', bbox_inches='tight') # Initialize the many-stamp QAplot ncols = 3 nrows = np.ceil(nstar / ncols).astype('int') inchperstamp = 2.0 fig = plt.figure(figsize=(inchperstamp * 3 * ncols, inchperstamp * nrows)) irow = 0 icol = 0 for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) mag = ps1star.median[ps1band[band]] # r-band ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ix, iy = int(xpos), int(ypos) # create a little tractor Image object around the star slc = (slice(max(iy - stampsize, 0), min(iy + stampsize + 1, H)), slice(max(ix - stampsize, 0), min(ix + stampsize + 1, W))) # The PSF model 'const2Psf' is the one used in DR1: a 2-component # Gaussian fit to PsfEx instantiated in the image center. tim = im.get_tractor_image(slc=slc, **get_tim_kwargs) stamp = tim.getImage() ivarstamp = tim.getInvvar() # Initialize a tractor PointSource from PS1 measurements flux = NanoMaggies.magToNanomaggies(mag) star = PointSource(RaDecPos(ra, dec), NanoMaggies(**{band: flux})) # Fit just the source RA,Dec,flux. tractor = Tractor([tim], [star]) tractor.freezeParam('images') print('2-component MOG:', tim.psf) tractor.printThawedParams() for step in range(50): dlnp, X, alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_mog = tractor.getModelImage(0) chi2_mog = -2.0 * tractor.getLogLikelihood() mag_mog = NanoMaggies.nanomaggiesToMag(star.brightness)[0] # Now change the PSF model to a pixelized PSF model from PsfEx instantiated # at this place in the image. psf = PixelizedPsfEx(im.psffn) tim.psf = psf.constantPsfAt(xpos, ypos) #print('PSF model:', tim.psf) #tractor.printThawedParams() for step in range(50): dlnp, X, alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_psfex = tractor.getModelImage(0) chi2_psfex = -2.0 * tractor.getLogLikelihood() mag_psfex = NanoMaggies.nanomaggiesToMag(star.brightness)[0] #mn, mx = np.percentile((stamp-model_psfex)[ivarstamp>0],[1,95]) sig = np.std((stamp - model_psfex)[ivarstamp > 0]) mn, mx = [-2.0 * sig, 5 * sig] # Generate a QAplot. if (istar > 0) and (istar % (ncols) == 0): irow = irow + 1 icol = 3 * istar - 3 * ncols * irow #print(istar, irow, icol, icol+1, icol+2) ax1 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol), aspect='equal') ax1.axis('off') #ax1.imshow(stamp, **tim.ima) ax1.imshow(stamp, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax1.text(0.1, 0.9, '{:2d}'.format(istar + 1), color='white', horizontalalignment='left', verticalalignment='top', transform=ax1.transAxes) ax2 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol + 1), aspect='equal') ax2.axis('off') #ax2.imshow(stamp-model_mog, **tim.ima) ax2.imshow(stamp - model_mog, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax2.text(0.1, 0.9, 'MoG', color='white', horizontalalignment='left', verticalalignment='top', transform=ax2.transAxes) ax2.text(0.08, 0.08, '{:.3f}'.format(mag_mog), color='white', horizontalalignment='left', verticalalignment='bottom', transform=ax2.transAxes) #ax2.set_title('{:.3f}, {:.2f}'.format(mag_psfex,chi2_psfex),fontsize=14) #ax2.set_title('{:.3f}, $\chi^{2}$={:.2f}'.format(mag_psfex,chi2_psfex)) ax3 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol + 2), aspect='equal') ax3.axis('off') #ax3.imshow(stamp-model_psfex, **tim.ima) ax3.imshow(stamp - model_psfex, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax3.text(0.1, 0.9, 'PSFEx', color='white', horizontalalignment='left', verticalalignment='top', transform=ax3.transAxes) ax3.text(0.08, 0.08, '{:.3f}'.format(mag_psfex), color='white', horizontalalignment='left', verticalalignment='bottom', transform=ax3.transAxes) if istar == (nstar - 1): break fig.savefig(pngprefix + '-stargrid.png', bbox_inches='tight')
def _det_one((cowcs, fn, wcsfn, do_img, wise)): print 'Image', fn F = fitsio.FITS(fn)[0] imginf = F.get_info() hdr = F.read_header() H,W = imginf['dims'] wcs = Sip(wcsfn) if wise: # HACK -- assume, incorrectly, single Gaussian ~diffraction limited psf_sigma = 0.873 else: pixscale = wcs.pixel_scale() seeing = hdr['SEEING'] print 'Seeing', seeing psf_sigma = seeing / pixscale / 2.35 print 'Sigma:', psf_sigma psfnorm = 1./(2. * np.sqrt(np.pi) * psf_sigma) if False: # Compare PSF models with Peter's "SEEING" card # (units: arcsec FWHM) from tractor.psfex import * psffn = fn.replace('.p.w.fits', '.p.w.cat.psf') print 'PSF', psffn psfmod = PsfEx(psffn, W, H) psfim = psfmod.instantiateAt(W/2, H/2) print 'psfim', psfim.shape ph,pw = psfim.shape plt.clf() plt.plot(psfim[ph/2,:], 'r-') plt.plot(psfim[:,pw/2], 'b-') xx = np.arange(pw) cc = pw/2 G = np.exp(-0.5 * (xx - cc)**2 / sig**2) plt.plot(G * sum(psfim[:,pw/2]) / sum(G), 'k-') ps.savefig() # Read full image and estimate noise img = F.read() print 'Image', img.shape if wise: ivfn = fn.replace('img-m.fits', 'invvar-m.fits.gz') iv = fitsio.read(ivfn) sig1 = 1./np.sqrt(np.median(iv)) print 'Per-pixel noise estimate:', sig1 mask = (iv == 0) else: # Estimate and subtract background bg = sky_subtract(img, 512, gradient=False) img -= bg diffs = img[:-5:10,:-5:10] - img[5::10,5::10] mad = np.median(np.abs(diffs).ravel()) sig1 = 1.4826 * mad / np.sqrt(2.) print 'Per-pixel noise estimate:', sig1 # Read bad pixel mask maskfn = fn.replace('.p.w.fits', '.p.w.bpm.fits') mask = fitsio.read(maskfn) # FIXME -- mask edge pixels -- some seem to be bad and unmasked mask[:2 ,:] = 1 mask[-2:,:] = 1 mask[:, :2] = 1 mask[:,-2:] = 1 # FIXME -- patch image? img[mask != 0] = 0. # Get image zeropoint for zpkey in ['MAG_ZP', 'UB1_ZP']: zp = hdr.get(zpkey, None) if zp is not None: break zpscale = NanoMaggies.zeropointToScale(zp) # Scale image to nanomaggies img /= zpscale sig1 /= zpscale # Produce detection map detmap = gaussian_filter(img, psf_sigma, mode='constant') / psfnorm**2 detmap_sig1 = sig1 / psfnorm print 'Detection map sig1', detmap_sig1 # Lanczos resample print 'Resampling...' L = 3 try: lims = [detmap] if do_img: lims.append(img) Yo,Xo,Yi,Xi,rims = resample_with_wcs(cowcs, wcs, lims, L) rdetmap = rims[0] except OverlapError: return None print 'Resampled' detmap_iv = (mask[Yo,Xo] == 0) * 1./detmap_sig1**2 if do_img: rimg = rims[1] else: rimg = None return Yo,Xo,rdetmap,detmap_iv,rimg
def psf_residuals(expnum,ccdname,stampsize=35,nstar=30, magrange=(13,17),verbose=0): # Set the debugging level. if verbose==0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl,format='%(message)s',stream=sys.stdout) pngprefix = 'qapsf-{}-{}'.format(expnum,ccdname) # Gather all the info we need about this CCD. decals = Decals() ccd = decals.find_ccds(expnum=expnum,ccdname=ccdname)[0] band = ccd.filter ps1band = dict(g=0,r=1,i=2,z=3,Y=4) print('Band {}'.format(band)) #scales = dict(g=0.0066, r=0.01, z=0.025) #vmin, vmax = np.arcsinh(-1), np.arcsinh(100) #print(scales[band]) im = DecamImage(decals,ccd) iminfo = im.get_image_info() H,W = iminfo['dims'] wcs = im.get_wcs() # Choose a uniformly selected subset of PS1 stars on this CCD. ps1 = ps1cat(ccdwcs=wcs) cat = ps1.get_stars(band=band,magrange=magrange) rand = np.random.RandomState(seed=expnum*ccd.ccdnum) these = rand.choice(len(cat)-1,nstar,replace=False) #these = rand.random_integers(0,len(cat)-1,nstar) cat = cat[these] cat = cat[np.argsort(cat.median[:,ps1band[band]])] # sort by magnitude #print(cat.nmag_ok) # Make a QAplot of the positions of all the stars. tim = im.get_tractor_image(const2psf=True) img = tim.getImage() #img = tim.getImage()/scales[band] fig = plt.figure(figsize=(5,10)) ax = fig.gca() ax.get_xaxis().get_major_formatter().set_useOffset(False) #ax.imshow(np.arcsinh(img),cmap='gray',interpolation='nearest', # origin='lower',vmin=vmax,vmax=vmax) ax.imshow(img, **tim.ima) ax.axis('off') ax.set_title('{}: {}/{} AM={:.2f} Seeing={:.3f}"'. format(band,expnum,ccdname,ccd.airmass,ccd.seeing)) for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ax.text(xpos,ypos,'{:2d}'.format(istar+1),color='red', horizontalalignment='left') circ = plt.Circle((xpos,ypos),radius=30,color='g',fill=False,lw=1) ax.add_patch(circ) #radec = wcs.radec_bounds() #ax.scatter(cat.ra,cat.dec) #ax.set_xlim([radec[1],radec[0]])#*[1.0002,0.9998]) #ax.set_ylim([radec[2],radec[3]])#*[0.985,1.015]) #ax.set_xlabel('$RA\ (deg)$',fontsize=18) #ax.set_ylabel('$Dec\ (deg)$',fontsize=18) fig.savefig(pngprefix+'-ccd.png',bbox_inches='tight') # Initialize the many-stamp QAplot ncols = 3 nrows = np.ceil(nstar/ncols).astype('int') inchperstamp = 2.0 fig = plt.figure(figsize=(inchperstamp*3*ncols,inchperstamp*nrows)) irow = 0 icol = 0 for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) mag = ps1star.median[ps1band[band]] # r-band ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ix,iy = int(xpos), int(ypos) # create a little tractor Image object around the star slc = (slice(max(iy-stampsize, 0), min(iy+stampsize+1, H)), slice(max(ix-stampsize, 0), min(ix+stampsize+1, W))) # The PSF model 'const2Psf' is the one used in DR1: a 2-component # Gaussian fit to PsfEx instantiated in the image center. tim = im.get_tractor_image(slc=slc, const2psf=True) stamp = tim.getImage() ivarstamp = tim.getInvvar() # Initialize a tractor PointSource from PS1 measurements flux = NanoMaggies.magToNanomaggies(mag) star = PointSource(RaDecPos(ra,dec), NanoMaggies(**{band: flux})) # Fit just the source RA,Dec,flux. tractor = Tractor([tim], [star]) tractor.freezeParam('images') print('2-component MOG:', tim.psf) tractor.printThawedParams() for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_mog = tractor.getModelImage(0) chi2_mog = -2.0*tractor.getLogLikelihood() mag_mog = NanoMaggies.nanomaggiesToMag(star.brightness)[0] # Now change the PSF model to a pixelized PSF model from PsfEx instantiated # at this place in the image. psf = PixelizedPsfEx(im.psffn) tim.psf = psf.constantPsfAt(xpos, ypos) #print('PSF model:', tim.psf) #tractor.printThawedParams() for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_psfex = tractor.getModelImage(0) chi2_psfex = -2.0*tractor.getLogLikelihood() mag_psfex = NanoMaggies.nanomaggiesToMag(star.brightness)[0] #mn, mx = np.percentile((stamp-model_psfex)[ivarstamp>0],[1,95]) sig = np.std((stamp-model_psfex)[ivarstamp>0]) mn, mx = [-2.0*sig,5*sig] # Generate a QAplot. if (istar>0) and (istar%(ncols)==0): irow = irow+1 icol = 3*istar - 3*ncols*irow #print(istar, irow, icol, icol+1, icol+2) ax1 = plt.subplot2grid((nrows,3*ncols), (irow,icol), aspect='equal') ax1.axis('off') #ax1.imshow(stamp, **tim.ima) ax1.imshow(stamp,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax1.text(0.1,0.9,'{:2d}'.format(istar+1),color='white', horizontalalignment='left',verticalalignment='top', transform=ax1.transAxes) ax2 = plt.subplot2grid((nrows,3*ncols), (irow,icol+1), aspect='equal') ax2.axis('off') #ax2.imshow(stamp-model_mog, **tim.ima) ax2.imshow(stamp-model_mog,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax2.text(0.1,0.9,'MoG',color='white', horizontalalignment='left',verticalalignment='top', transform=ax2.transAxes) ax2.text(0.08,0.08,'{:.3f}'.format(mag_mog),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax2.transAxes) #ax2.set_title('{:.3f}, {:.2f}'.format(mag_psfex,chi2_psfex),fontsize=14) #ax2.set_title('{:.3f}, $\chi^{2}$={:.2f}'.format(mag_psfex,chi2_psfex)) ax3 = plt.subplot2grid((nrows,3*ncols), (irow,icol+2), aspect='equal') ax3.axis('off') #ax3.imshow(stamp-model_psfex, **tim.ima) ax3.imshow(stamp-model_psfex,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax3.text(0.1,0.9,'PSFEx',color='white', horizontalalignment='left',verticalalignment='top', transform=ax3.transAxes) ax3.text(0.08,0.08,'{:.3f}'.format(mag_psfex),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax3.transAxes) if istar==(nstar-1): break fig.savefig(pngprefix+'-stargrid.png',bbox_inches='tight')
def get_tractor_image(self, slc=None, radecpoly=None, gaussPsf=False, pixPsf=False, hybridPsf=False, splinesky=False, nanomaggies=True, subsky=True, tiny=10, dq=True, invvar=True, pixels=True, constant_invvar=False): ''' Returns a tractor.Image ("tim") object for this image. Options describing a subimage to return: - *slc*: y,x slice objects - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing bounding box to select. Options determining the PSF model to use: - *gaussPsf*: single circular Gaussian PSF based on header FWHM value. - *pixPsf*: pixelized PsfEx model. - *hybridPsf*: combo pixelized PsfEx + Gaussian approx. Options determining the sky model to use: - *splinesky*: median filter chunks of the image, then spline those. Options determining the units of the image: - *nanomaggies*: convert the image to be in units of NanoMaggies; *tim.zpscale* contains the scale value the image was divided by. - *subsky*: instantiate and subtract the initial sky model, leaving a constant zero sky model? ''' get_dq = dq get_invvar = invvar band = self.band imh, imw = self.get_image_shape() wcs = self.get_wcs() x0, y0 = 0, 0 x1 = x0 + imw y1 = y0 + imh # Clip to RA,Dec polygon? if slc is None and radecpoly is not None: from astrometry.util.miscutils import clip_polygon imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)] ok, tx, ty = wcs.radec2pixelxy(radecpoly[:-1, 0], radecpoly[:-1, 1]) tpoly = zip(tx, ty) clip = clip_polygon(imgpoly, tpoly) clip = np.array(clip) if len(clip) == 0: return None x0, y0 = np.floor(clip.min(axis=0)).astype(int) x1, y1 = np.ceil(clip.max(axis=0)).astype(int) slc = slice(y0, y1 + 1), slice(x0, x1 + 1) if y1 - y0 < tiny or x1 - x0 < tiny: print('Skipping tiny subimage') return None # Slice? if slc is not None: sy, sx = slc y0, y1 = sy.start, sy.stop x0, x1 = sx.start, sx.stop # Is part of this image bad? old_extent = (x0, x1, y0, y1) new_extent = self.get_good_image_slice((x0, x1, y0, y1), get_extent=True) if new_extent != old_extent: x0, x1, y0, y1 = new_extent print('Applying good subregion of CCD: slice is', x0, x1, y0, y1) if x0 >= x1 or y0 >= y1: return None slc = slice(y0, y1), slice(x0, x1) # Read image pixels if pixels: print('Reading image slice:', slc) img, imghdr = self.read_image(header=True, slice=slc) self.check_image_header(imghdr) else: img = np.zeros((imh, imw), np.float32) imghdr = self.read_image_header() if slc is not None: img = img[slc] assert (np.all(np.isfinite(img))) # Read inverse-variance (weight) map if get_invvar: invvar = self.read_invvar(slice=slc, clipThresh=0.) else: invvar = np.ones_like(img) assert (np.all(np.isfinite(invvar))) if np.all(invvar == 0.): print('Skipping zero-invvar image') return None # Negative invvars (from, eg, fpack decompression noise) cause havoc assert (np.all(invvar >= 0.)) # Read data-quality (flags) map and zero out the invvars of masked pixels if get_dq: dq = self.read_dq(slice=slc) if dq is not None: invvar[dq != 0] = 0. if np.all(invvar == 0.): print('Skipping zero-invvar image (after DQ masking)') return None # header 'FWHM' is in pixels assert (self.fwhm > 0) psf_fwhm = self.fwhm psf_sigma = psf_fwhm / 2.35 primhdr = self.read_image_primary_header() sky = self.read_sky_model(splinesky=splinesky, slc=slc, primhdr=primhdr, imghdr=imghdr) skysig1 = getattr(sky, 'sig1', None) midsky = 0. if subsky: print('Instantiating and subtracting sky model') from tractor.sky import ConstantSky skymod = np.zeros_like(img) sky.addTo(skymod) img -= skymod midsky = np.median(skymod) zsky = ConstantSky(0.) zsky.version = getattr(sky, 'version', '') zsky.plver = getattr(sky, 'plver', '') del skymod sky = zsky del zsky orig_zpscale = zpscale = NanoMaggies.zeropointToScale(self.ccdzpt) if nanomaggies: # Scale images to Nanomaggies img /= zpscale invvar = invvar * zpscale**2 if not subsky: sky.scale(1. / zpscale) zpscale = 1. # Compute 'sig1', scalar typical per-pixel noise if get_invvar: sig1 = 1. / np.sqrt(np.median(invvar[invvar > 0])) elif skysig1 is not None: sig1 = skysig1 if nanomaggies: # skysig1 is in the native units sig1 /= orig_zpscale else: # Estimate per-pixel noise via Blanton's 5-pixel MAD slice1 = (slice(0, -5, 10), slice(0, -5, 10)) slice2 = (slice(5, None, 10), slice(5, None, 10)) mad = np.median(np.abs(img[slice1] - img[slice2]).ravel()) sig1 = 1.4826 * mad / np.sqrt(2.) print('sig1 estimate:', sig1) invvar *= (1. / sig1**2) assert (np.isfinite(sig1)) if constant_invvar: print('Setting constant invvar', 1. / sig1**2) invvar[invvar > 0] = 1. / sig1**2 if subsky: # Warn if the subtracted sky doesn't seem to work well # (can happen, eg, if sky calibration product is inconsistent with # the data) imgmed = np.median(img[invvar > 0]) if np.abs(imgmed) > sig1: print('WARNING: image median', imgmed, 'is more than 1 sigma', 'away from zero!') # tractor WCS object twcs = self.get_tractor_wcs(wcs, x0, y0, primhdr=primhdr, imghdr=imghdr) if hybridPsf: pixPsf = False psf = self.read_psf_model(x0, y0, gaussPsf=gaussPsf, pixPsf=pixPsf, hybridPsf=hybridPsf, psf_sigma=psf_sigma, cx=(x0 + x1) / 2., cy=(y0 + y1) / 2.) tim = Image(img, invvar=invvar, wcs=twcs, psf=psf, photocal=LinearPhotoCal(zpscale, band=band), sky=sky, name=self.name + ' ' + band) assert (np.all(np.isfinite(tim.getInvError()))) # PSF norm psfnorm = self.psf_norm(tim) # Galaxy-detection norm tim.band = band galnorm = self.galaxy_norm(tim) # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC. import astropy.time mjd_tai = astropy.time.Time(self.mjdobs, format='mjd', scale='utc').tai.mjd tim.time = TAITime(None, mjd=mjd_tai) tim.slice = slc tim.zpscale = orig_zpscale tim.midsky = midsky tim.sig1 = sig1 tim.psf_fwhm = psf_fwhm tim.psf_sigma = psf_sigma tim.propid = self.propid tim.psfnorm = psfnorm tim.galnorm = galnorm tim.sip_wcs = wcs tim.x0, tim.y0 = int(x0), int(y0) tim.imobj = self tim.primhdr = primhdr tim.hdr = imghdr tim.plver = primhdr.get('PLVER', '').strip() tim.skyver = (getattr(sky, 'version', ''), getattr(sky, 'plver', '')) tim.wcsver = (getattr(wcs, 'version', ''), getattr(wcs, 'plver', '')) tim.psfver = (getattr(psf, 'version', ''), getattr(psf, 'plver', '')) if get_dq: tim.dq = dq tim.dq_saturation_bits = self.dq_saturation_bits subh, subw = tim.shape tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh) return tim
def _det_one((cowcs, fn, wcsfn, do_img, wise)): print('Image', fn) F = fitsio.FITS(fn)[0] imginf = F.get_info() hdr = F.read_header() H, W = imginf['dims'] wcs = Sip(wcsfn) if wise: # HACK -- assume, incorrectly, single Gaussian ~diffraction limited psf_sigma = 0.873 else: pixscale = wcs.pixel_scale() seeing = hdr['SEEING'] print('Seeing', seeing) psf_sigma = seeing / pixscale / 2.35 print('Sigma:', psf_sigma) psfnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma) if False: # Compare PSF models with Peter's "SEEING" card # (units: arcsec FWHM) from tractor.psfex import * psffn = fn.replace('.p.w.fits', '.p.w.cat.psf') print('PSF', psffn) psfmod = PsfEx(psffn, W, H) psfim = psfmod.instantiateAt(W / 2, H / 2) print('psfim', psfim.shape) ph, pw = psfim.shape plt.clf() plt.plot(psfim[ph / 2, :], 'r-') plt.plot(psfim[:, pw / 2], 'b-') xx = np.arange(pw) cc = pw / 2 G = np.exp(-0.5 * (xx - cc)**2 / sig**2) plt.plot(G * sum(psfim[:, pw / 2]) / sum(G), 'k-') ps.savefig() # Read full image and estimate noise img = F.read() print('Image', img.shape) if wise: ivfn = fn.replace('img-m.fits', 'invvar-m.fits.gz') iv = fitsio.read(ivfn) sig1 = 1. / np.sqrt(np.median(iv)) print('Per-pixel noise estimate:', sig1) mask = (iv == 0) else: # Estimate and subtract background bg = sky_subtract(img, 512, gradient=False) img -= bg diffs = img[:-5:10, :-5:10] - img[5::10, 5::10] mad = np.median(np.abs(diffs).ravel()) sig1 = 1.4826 * mad / np.sqrt(2.) print('Per-pixel noise estimate:', sig1) # Read bad pixel mask maskfn = fn.replace('.p.w.fits', '.p.w.bpm.fits') mask = fitsio.read(maskfn) # FIXME -- mask edge pixels -- some seem to be bad and unmasked mask[:2, :] = 1 mask[-2:, :] = 1 mask[:, :2] = 1 mask[:, -2:] = 1 # FIXME -- patch image? img[mask != 0] = 0. # Get image zeropoint for zpkey in ['MAG_ZP', 'UB1_ZP']: zp = hdr.get(zpkey, None) if zp is not None: break zpscale = NanoMaggies.zeropointToScale(zp) # Scale image to nanomaggies img /= zpscale sig1 /= zpscale # Produce detection map detmap = gaussian_filter(img, psf_sigma, mode='constant') / psfnorm**2 detmap_sig1 = sig1 / psfnorm print('Detection map sig1', detmap_sig1) # Lanczos resample print('Resampling...') L = 3 try: lims = [detmap] if do_img: lims.append(img) Yo, Xo, Yi, Xi, rims = resample_with_wcs(cowcs, wcs, lims, L) rdetmap = rims[0] except OverlapError: return None print('Resampled') detmap_iv = (mask[Yo, Xo] == 0) * 1. / detmap_sig1**2 if do_img: rimg = rims[1] else: rimg = None return Yo, Xo, rdetmap, detmap_iv, rimg
def fit_psf(galaxy,scale,band,verbose=0): # Set the debugging level. if verbose==0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl,format='%(message)s',stream=sys.stdout) stampsize = 50 pngprefix = 'qapsf-{}-{}-{}'.format(galaxy,scale,band) # Read the image and get a postage stamp of the galaxy (which should be # centered in the image) hizea = HizEA(galaxy,scale,band) wcs = hizea.get_wcs() W,H = wcs.imagew, wcs.imageh xpos,ypos = W/2, H/2 ra,dec = wcs.pixelxy2radec(xpos, ypos) ix,iy = int(xpos), int(ypos) slc = (slice(max(iy-stampsize, 0), min(iy+stampsize+1, H)), slice(max(ix-stampsize, 0), min(ix+stampsize+1, W))) img = hizea.image[slc] # Instantiate the PSFEx model of the PSF at the center psf = PixelizedPsfEx(hizea.psffile).constantPsfAt(xpos,ypos) star = PointSource(RaDecPos(ra,dec), NanoMaggies(**{band: 1.0})) # Fit just the source RA,Dec,flux. tractor = Tractor([img], [star]) tractor.freezeParam('images') for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_psfex = tractor.getModelImage(0) chi2_psfex = -2.0*tractor.getLogLikelihood() mag_psfex = NanoMaggies.nanomaggiesToMag(star.brightness)[0] #mn, mx = np.percentile((stamp-model_psfex)[ivarstamp>0],[1,95]) sig = np.std((stamp-model_psfex)[ivarstamp>0]) mn, mx = [-2.0*sig,5*sig] sys.exit(0) psf = hizea.get_psf() for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) mag = ps1star.median[ps1band[band]] # r-band ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ix,iy = int(xpos), int(ypos) # create a little tractor Image object around the star slc = (slice(max(iy-stampsize, 0), min(iy+stampsize+1, H)), slice(max(ix-stampsize, 0), min(ix+stampsize+1, W))) # The PSF model 'const2Psf' is the one used in DR1: a 2-component # Gaussian fit to PsfEx instantiated in the image center. tim = im.get_tractor_image(slc=slc, const2psf=True) stamp = tim.getImage() ivarstamp = tim.getInvvar() # Initialize a tractor PointSource from PS1 measurements flux = NanoMaggies.magToNanomaggies(mag) star = PointSource(RaDecPos(ra,dec), NanoMaggies(**{band: flux})) # Fit just the source RA,Dec,flux. tractor = Tractor([tim], [star]) tractor.freezeParam('images') print('2-component MOG:', tim.psf) tractor.printThawedParams() for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_mog = tractor.getModelImage(0) chi2_mog = -2.0*tractor.getLogLikelihood() mag_mog = NanoMaggies.nanomaggiesToMag(star.brightness)[0] # Generate a QAplot. if (istar>0) and (istar%(ncols)==0): irow = irow+1 icol = 3*istar - 3*ncols*irow #print(istar, irow, icol, icol+1, icol+2) ax1 = plt.subplot2grid((nrows,3*ncols), (irow,icol), aspect='equal') ax1.axis('off') #ax1.imshow(stamp, **tim.ima) ax1.imshow(stamp,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax1.text(0.1,0.9,'{:2d}'.format(istar+1),color='white', horizontalalignment='left',verticalalignment='top', transform=ax1.transAxes) ax2 = plt.subplot2grid((nrows,3*ncols), (irow,icol+1), aspect='equal') ax2.axis('off') #ax2.imshow(stamp-model_mog, **tim.ima) ax2.imshow(stamp-model_mog,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax2.text(0.1,0.9,'MoG',color='white', horizontalalignment='left',verticalalignment='top', transform=ax2.transAxes) ax2.text(0.08,0.08,'{:.3f}'.format(mag_mog),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax2.transAxes) #ax2.set_title('{:.3f}, {:.2f}'.format(mag_psfex,chi2_psfex),fontsize=14) #ax2.set_title('{:.3f}, $\chi^{2}$={:.2f}'.format(mag_psfex,chi2_psfex)) ax3 = plt.subplot2grid((nrows,3*ncols), (irow,icol+2), aspect='equal') ax3.axis('off') #ax3.imshow(stamp-model_psfex, **tim.ima) ax3.imshow(stamp-model_psfex,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax3.text(0.1,0.9,'PSFEx',color='white', horizontalalignment='left',verticalalignment='top', transform=ax3.transAxes) ax3.text(0.08,0.08,'{:.3f}'.format(mag_psfex),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax3.transAxes) if istar==(nstar-1): break fig.savefig(pngprefix+'-stargrid.png',bbox_inches='tight')
def run_sed_matched_filters(SEDs, bands, detmaps, detivs, omit_xy, targetwcs, nsigma=5, saturated_pix=None, plots=False, ps=None, mp=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) tuple Existing sources to avoid. 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 numpy array, boolean 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. 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. ''' if omit_xy is not None: xx, yy = omit_xy n0 = len(xx) else: xx, yy = [], [] n0 = 0 H, W = detmaps[0].shape hot = np.zeros((H, W), bool) peaksn = [] apsn = [] for sedname, sed in SEDs: print('SED', sedname) if plots: pps = ps else: pps = None t0 = Time() sedhot, px, py, peakval, apval = sed_matched_detection( sedname, sed, detmaps, detivs, bands, xx, yy, nsigma=nsigma, saturated_pix=saturated_pix, ps=pps) print('SED took', Time() - t0) if sedhot is None: continue print(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) peaksn.extend(peakval) apsn.extend(apval) # New peaks: peakx = xx[n0:] peaky = yy[n0:] if len(peakx) == 0: return None, None, None # Add sources for the new peaks we found pr, pd = targetwcs.pixelxy2radec(peakx + 1, peaky + 1) print('Adding', len(pr), 'new sources') # Also create FITS table for new sources Tnew = fits_table() Tnew.ra = pr Tnew.dec = pd Tnew.tx = peakx Tnew.ty = peaky assert (len(peaksn) == len(Tnew)) assert (len(apsn) == len(Tnew)) Tnew.peaksn = np.array(peaksn) Tnew.apsn = np.array(apsn) Tnew.itx = np.clip(np.round(Tnew.tx), 0, W - 1).astype(int) Tnew.ity = np.clip(np.round(Tnew.ty), 0, H - 1).astype(int) newcat = [] for i, (r, d, x, y) in enumerate(zip(pr, pd, peakx, peaky)): fluxes = dict([(band, detmap[Tnew.ity[i], Tnew.itx[i]]) for band, detmap in zip(bands, detmaps)]) newcat.append( PointSource(RaDecPos(r, d), NanoMaggies(order=bands, **fluxes))) return Tnew, newcat, hot
def get_tractor_image(self, slc=None, radecpoly=None, gaussPsf=False, const2psf=False, pixPsf=False, splinesky=False, nanomaggies=True, subsky=True, tiny=5, dq=True, invvar=True, pixels=True): ''' Returns a tractor.Image ("tim") object for this image. Options describing a subimage to return: - *slc*: y,x slice objects - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing bounding box to select. Options determining the PSF model to use: - *gaussPsf*: single circular Gaussian PSF based on header FWHM value. - *const2Psf*: 2-component general Gaussian fit to PsfEx model at image center. - *pixPsf*: pixelized PsfEx model at image center. Options determining the sky model to use: - *splinesky*: median filter chunks of the image, then spline those. Options determining the units of the image: - *nanomaggies*: convert the image to be in units of NanoMaggies; *tim.zpscale* contains the scale value the image was divided by. - *subsky*: instantiate and subtract the initial sky model, leaving a constant zero sky model? ''' from astrometry.util.miscutils import clip_polygon get_dq = dq get_invvar = invvar band = self.band imh,imw = self.get_image_shape() wcs = self.get_wcs() x0,y0 = 0,0 x1 = x0 + imw y1 = y0 + imh if slc is None and radecpoly is not None: imgpoly = [(1,1),(1,imh),(imw,imh),(imw,1)] ok,tx,ty = wcs.radec2pixelxy(radecpoly[:-1,0], radecpoly[:-1,1]) tpoly = zip(tx,ty) clip = clip_polygon(imgpoly, tpoly) clip = np.array(clip) if len(clip) == 0: return None x0,y0 = np.floor(clip.min(axis=0)).astype(int) x1,y1 = np.ceil (clip.max(axis=0)).astype(int) slc = slice(y0,y1+1), slice(x0,x1+1) if y1 - y0 < tiny or x1 - x0 < tiny: print('Skipping tiny subimage') return None if slc is not None: sy,sx = slc y0,y1 = sy.start, sy.stop x0,x1 = sx.start, sx.stop old_extent = (x0,x1,y0,y1) new_extent = self.get_good_image_slice((x0,x1,y0,y1), get_extent=True) if new_extent != old_extent: x0,x1,y0,y1 = new_extent print('Applying good subregion of CCD: slice is', x0,x1,y0,y1) if x0 >= x1 or y0 >= y1: return None slc = slice(y0,y1), slice(x0,x1) if pixels: print('Reading image slice:', slc) img,imghdr = self.read_image(header=True, slice=slc) #print('SATURATE is', imghdr.get('SATURATE', None)) #print('Max value in image is', img.max()) # check consistency... something of a DR1 hangover e = imghdr['EXTNAME'] assert(e.strip() == self.ccdname.strip()) else: img = np.zeros((imh, imw)) imghdr = dict() if slc is not None: img = img[slc] if get_invvar: invvar = self.read_invvar(slice=slc, clipThresh=0.) else: invvar = np.ones_like(img) if get_dq: dq = self.read_dq(slice=slc) invvar[dq != 0] = 0. if np.all(invvar == 0.): print('Skipping zero-invvar image') return None assert(np.all(np.isfinite(img))) assert(np.all(np.isfinite(invvar))) assert(not(np.all(invvar == 0.))) # header 'FWHM' is in pixels # imghdr['FWHM'] psf_fwhm = self.fwhm psf_sigma = psf_fwhm / 2.35 primhdr = self.read_image_primary_header() sky = self.read_sky_model(splinesky=splinesky, slc=slc) midsky = 0. if subsky: print('Instantiating and subtracting sky model...') from tractor.sky import ConstantSky skymod = np.zeros_like(img) sky.addTo(skymod) img -= skymod midsky = np.median(skymod) zsky = ConstantSky(0.) zsky.version = sky.version zsky.plver = sky.plver del skymod del sky sky = zsky del zsky magzp = self.decals.get_zeropoint_for(self) orig_zpscale = zpscale = NanoMaggies.zeropointToScale(magzp) if nanomaggies: # Scale images to Nanomaggies img /= zpscale invvar *= zpscale**2 if not subsky: sky.scale(1./zpscale) zpscale = 1. assert(np.sum(invvar > 0) > 0) if get_invvar: sig1 = 1./np.sqrt(np.median(invvar[invvar > 0])) else: # Estimate from the image? # # Estimate per-pixel noise via Blanton's 5-pixel MAD slice1 = (slice(0,-5,10),slice(0,-5,10)) slice2 = (slice(5,None,10),slice(5,None,10)) mad = np.median(np.abs(img[slice1] - img[slice2]).ravel()) sig1 = 1.4826 * mad / np.sqrt(2.) print('sig1 estimate:', sig1) invvar *= (1. / sig1**2) assert(np.all(np.isfinite(img))) assert(np.all(np.isfinite(invvar))) assert(np.isfinite(sig1)) if subsky: ## imgmed = np.median(img[invvar>0]) if np.abs(imgmed) > sig1: print('WARNING: image median', imgmed, 'is more than 1 sigma away from zero!') # Boom! #assert(False) twcs = ConstantFitsWcs(wcs) if x0 or y0: twcs.setX0Y0(x0,y0) psf = self.read_psf_model(x0, y0, gaussPsf=gaussPsf, pixPsf=pixPsf, const2psf=const2psf, psf_sigma=psf_sigma, cx=(x0+x1)/2., cy=(y0+y1)/2.) tim = Image(img, invvar=invvar, wcs=twcs, psf=psf, photocal=LinearPhotoCal(zpscale, band=band), sky=sky, name=self.name + ' ' + band) assert(np.all(np.isfinite(tim.getInvError()))) # PSF norm psfnorm = self.psf_norm(tim) print('PSF norm', psfnorm, 'vs Gaussian', 1./(2. * np.sqrt(np.pi) * psf_sigma)) # Galaxy-detection norm tim.band = band galnorm = self.galaxy_norm(tim) print('Galaxy norm:', galnorm) # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC. import astropy.time #mjd_utc = mjd=primhdr.get('MJD-OBS', 0) mjd_tai = astropy.time.Time(primhdr['DATE-OBS']).tai.mjd tim.slice = slc tim.time = TAITime(None, mjd=mjd_tai) tim.zr = [-3. * sig1, 10. * sig1] tim.zpscale = orig_zpscale tim.midsky = midsky tim.sig1 = sig1 tim.psf_fwhm = psf_fwhm tim.psf_sigma = psf_sigma tim.propid = self.propid tim.psfnorm = psfnorm tim.galnorm = galnorm tim.sip_wcs = wcs tim.x0,tim.y0 = int(x0),int(y0) tim.imobj = self tim.primhdr = primhdr tim.hdr = imghdr tim.plver = primhdr['PLVER'].strip() tim.skyver = (sky.version, sky.plver) tim.wcsver = (wcs.version, wcs.plver) tim.psfver = (psf.version, psf.plver) if get_dq: tim.dq = dq tim.dq_bits = CP_DQ_BITS tim.saturation = imghdr.get('SATURATE', None) tim.satval = tim.saturation or 0. if subsky: tim.satval -= midsky if nanomaggies: tim.satval /= orig_zpscale subh,subw = tim.shape tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh) mn,mx = tim.zr tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray', vmin=mn, vmax=mx) return tim
def get_tractor_image(self, decals, slc=None, radecpoly=None, mock_psf=False): ''' slc: y,x slices ''' band = self.band imh,imw = self.get_image_shape() wcs = self.read_wcs() x0,y0 = 0,0 if slc is None and radecpoly is not None: imgpoly = [(1,1),(1,imh),(imw,imh),(imw,1)] ok,tx,ty = wcs.radec2pixelxy(radecpoly[:-1,0], radecpoly[:-1,1]) tpoly = zip(tx,ty) clip = clip_polygon(imgpoly, tpoly) clip = np.array(clip) if len(clip) == 0: return None x0,y0 = np.floor(clip.min(axis=0)).astype(int) x1,y1 = np.ceil (clip.max(axis=0)).astype(int) slc = slice(y0,y1+1), slice(x0,x1+1) if y1 - y0 < 5 or x1 - x0 < 5: print 'Skipping tiny subimage' return None if slc is not None: sy,sx = slc y0,y1 = sy.start, sy.stop x0,x1 = sx.start, sx.stop print 'Reading image from', self.imgfn, 'HDU', self.hdu img,imghdr = self.read_image(header=True, slice=slc) print 'Reading invvar from', self.wtfn, 'HDU', self.hdu invvar = self.read_invvar(slice=slc, clip=True) print 'Invvar range:', invvar.min(), invvar.max() if np.all(invvar == 0.): print 'Skipping zero-invvar image' return None assert(np.all(np.isfinite(img))) assert(np.all(np.isfinite(invvar))) assert(not(np.all(invvar == 0.))) # header 'FWHM' is in pixels psf_fwhm = imghdr['FWHM'] psf_sigma = psf_fwhm / 2.35 primhdr = self.read_image_primary_header() magzp = decals.get_zeropoint_for(self) print 'magzp', magzp zpscale = NanoMaggies.zeropointToScale(magzp) print 'zpscale', zpscale sky = self.read_sky_model() midsky = sky.getConstant() img -= midsky sky.subtract(midsky) # Scale images to Nanomaggies img /= zpscale invvar *= zpscale**2 orig_zpscale = zpscale zpscale = 1. assert(np.sum(invvar > 0) > 0) sig1 = 1./np.sqrt(np.median(invvar[invvar > 0])) assert(np.all(np.isfinite(img))) assert(np.all(np.isfinite(invvar))) assert(np.isfinite(sig1)) twcs = ConstantFitsWcs(wcs) if x0 or y0: twcs.setX0Y0(x0,y0) if mock_psf: from tractor.basics import NCircularGaussianPSF psfex = None psf = NCircularGaussianPSF([1.5], [1.0]) print 'WARNING: using mock PSF:', psf else: # read fit PsfEx model -- with ellipse representation psfex = PsfEx.fromFits(self.psffitellfn) print 'Read', psfex psf = psfex tim = Image(img, invvar=invvar, wcs=twcs, psf=psf, photocal=LinearPhotoCal(zpscale, band=band), sky=sky, name=self.name + ' ' + band) assert(np.all(np.isfinite(tim.getInvError()))) tim.zr = [-3. * sig1, 10. * sig1] tim.midsky = midsky tim.sig1 = sig1 tim.band = band tim.psf_fwhm = psf_fwhm tim.psf_sigma = psf_sigma tim.sip_wcs = wcs tim.x0,tim.y0 = int(x0),int(y0) tim.psfex = psfex tim.imobj = self mn,mx = tim.zr subh,subw = tim.shape tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh) tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray', vmin=mn, vmax=mx) return tim
return I if __name__ == '__main__': #(allp, i2magsA, cat) = unpickle_from_file('s2-260-A.pickle') (allp, i2mags, cat) = unpickle_from_file('s2-382.pickle') #print 'i2 mags A:', len(i2magsA) #print 'i2 mags:', len(i2mags) #i2mags = i2magsA from tractor.basics import NanoMaggies #print 'i2mags', i2mags i2mags = np.array([NanoMaggies(i=m).getMag('i') for m in i2mags]) #print 'i2mags', mags #allbands = ['i2','u','g','r','i','z'] allbands = ['i2', 'u', 'g', 'r', 'i', 'z', 'w1', 'w2', 'w3', 'w4'] T = fits_table('cs82data/W4p1m1_i.V2.7A.swarp.cut.deVexp.fit', hdunum=2) #RA = 334.32 #DEC = 0.315 #sz = 0.12 * 3600. #S = sz / 3600. #ra0 ,ra1 = RA-S/2., RA+S/2. #dec0,dec1 = DEC-S/2., DEC+S/2. print 'Read', len(T), 'sources' T.ra = T.alpha_j2000 T.dec = T.delta_j2000
def get_tractor_image(self, slc=None, radecpoly=None, gaussPsf=False, const2psf=False, pixPsf=False, splinesky=False, nanomaggies=True, subsky=True, tiny=5, dq=True, invvar=True, pixels=True): ''' Returns a tractor.Image ("tim") object for this image. Options describing a subimage to return: - *slc*: y,x slice objects - *radecpoly*: numpy array, shape (N,2), RA,Dec polygon describing bounding box to select. Options determining the PSF model to use: - *gaussPsf*: single circular Gaussian PSF based on header FWHM value. - *const2Psf*: 2-component general Gaussian fit to PsfEx model at image center. - *pixPsf*: pixelized PsfEx model at image center. Options determining the sky model to use: - *splinesky*: median filter chunks of the image, then spline those. Options determining the units of the image: - *nanomaggies*: convert the image to be in units of NanoMaggies; *tim.zpscale* contains the scale value the image was divided by. - *subsky*: instantiate and subtract the initial sky model, leaving a constant zero sky model? ''' from astrometry.util.miscutils import clip_polygon get_dq = dq get_invvar = invvar band = self.band imh, imw = self.get_image_shape() wcs = self.get_wcs() x0, y0 = 0, 0 x1 = x0 + imw y1 = y0 + imh if slc is None and radecpoly is not None: imgpoly = [(1, 1), (1, imh), (imw, imh), (imw, 1)] ok, tx, ty = wcs.radec2pixelxy(radecpoly[:-1, 0], radecpoly[:-1, 1]) tpoly = zip(tx, ty) clip = clip_polygon(imgpoly, tpoly) clip = np.array(clip) if len(clip) == 0: return None x0, y0 = np.floor(clip.min(axis=0)).astype(int) x1, y1 = np.ceil(clip.max(axis=0)).astype(int) slc = slice(y0, y1 + 1), slice(x0, x1 + 1) if y1 - y0 < tiny or x1 - x0 < tiny: print('Skipping tiny subimage') return None if slc is not None: sy, sx = slc y0, y1 = sy.start, sy.stop x0, x1 = sx.start, sx.stop old_extent = (x0, x1, y0, y1) new_extent = self.get_good_image_slice((x0, x1, y0, y1), get_extent=True) if new_extent != old_extent: x0, x1, y0, y1 = new_extent print('Applying good subregion of CCD: slice is', x0, x1, y0, y1) if x0 >= x1 or y0 >= y1: return None slc = slice(y0, y1), slice(x0, x1) if pixels: print('Reading image slice:', slc) img, imghdr = self.read_image(header=True, slice=slc) #print('SATURATE is', imghdr.get('SATURATE', None)) #print('Max value in image is', img.max()) # check consistency... something of a DR1 hangover e = imghdr['EXTNAME'] assert (e.strip() == self.ccdname.strip()) else: img = np.zeros((imh, imw)) imghdr = dict() if slc is not None: img = img[slc] if get_invvar: invvar = self.read_invvar(slice=slc, clipThresh=0.) else: invvar = np.ones_like(img) if get_dq: dq = self.read_dq(slice=slc) invvar[dq != 0] = 0. if np.all(invvar == 0.): print('Skipping zero-invvar image') return None assert (np.all(np.isfinite(img))) assert (np.all(np.isfinite(invvar))) assert (not (np.all(invvar == 0.))) # header 'FWHM' is in pixels # imghdr['FWHM'] psf_fwhm = self.fwhm psf_sigma = psf_fwhm / 2.35 primhdr = self.read_image_primary_header() sky = self.read_sky_model(splinesky=splinesky, slc=slc) midsky = 0. if subsky: print('Instantiating and subtracting sky model...') from tractor.sky import ConstantSky skymod = np.zeros_like(img) sky.addTo(skymod) img -= skymod midsky = np.median(skymod) zsky = ConstantSky(0.) zsky.version = sky.version zsky.plver = sky.plver del skymod del sky sky = zsky del zsky magzp = self.decals.get_zeropoint_for(self) orig_zpscale = zpscale = NanoMaggies.zeropointToScale(magzp) if nanomaggies: # Scale images to Nanomaggies img /= zpscale invvar *= zpscale**2 if not subsky: sky.scale(1. / zpscale) zpscale = 1. assert (np.sum(invvar > 0) > 0) if get_invvar: sig1 = 1. / np.sqrt(np.median(invvar[invvar > 0])) else: # Estimate from the image? # # Estimate per-pixel noise via Blanton's 5-pixel MAD slice1 = (slice(0, -5, 10), slice(0, -5, 10)) slice2 = (slice(5, None, 10), slice(5, None, 10)) mad = np.median(np.abs(img[slice1] - img[slice2]).ravel()) sig1 = 1.4826 * mad / np.sqrt(2.) print('sig1 estimate:', sig1) invvar *= (1. / sig1**2) assert (np.all(np.isfinite(img))) assert (np.all(np.isfinite(invvar))) assert (np.isfinite(sig1)) if subsky: ## imgmed = np.median(img[invvar > 0]) if np.abs(imgmed) > sig1: print('WARNING: image median', imgmed, 'is more than 1 sigma away from zero!') # Boom! #assert(False) twcs = ConstantFitsWcs(wcs) if x0 or y0: twcs.setX0Y0(x0, y0) psf = self.read_psf_model(x0, y0, gaussPsf=gaussPsf, pixPsf=pixPsf, const2psf=const2psf, psf_sigma=psf_sigma, cx=(x0 + x1) / 2., cy=(y0 + y1) / 2.) tim = Image(img, invvar=invvar, wcs=twcs, psf=psf, photocal=LinearPhotoCal(zpscale, band=band), sky=sky, name=self.name + ' ' + band) assert (np.all(np.isfinite(tim.getInvError()))) # PSF norm psfnorm = self.psf_norm(tim) print('PSF norm', psfnorm, 'vs Gaussian', 1. / (2. * np.sqrt(np.pi) * psf_sigma)) # Galaxy-detection norm tim.band = band galnorm = self.galaxy_norm(tim) print('Galaxy norm:', galnorm) # CP (DECam) images include DATE-OBS and MJD-OBS, in UTC. import astropy.time #mjd_utc = mjd=primhdr.get('MJD-OBS', 0) mjd_tai = astropy.time.Time(primhdr['DATE-OBS']).tai.mjd tim.slice = slc tim.time = TAITime(None, mjd=mjd_tai) tim.zr = [-3. * sig1, 10. * sig1] tim.zpscale = orig_zpscale tim.midsky = midsky tim.sig1 = sig1 tim.psf_fwhm = psf_fwhm tim.psf_sigma = psf_sigma tim.propid = self.propid tim.psfnorm = psfnorm tim.galnorm = galnorm tim.sip_wcs = wcs tim.x0, tim.y0 = int(x0), int(y0) tim.imobj = self tim.primhdr = primhdr tim.hdr = imghdr tim.plver = primhdr['PLVER'].strip() tim.skyver = (sky.version, sky.plver) tim.wcsver = (wcs.version, wcs.plver) tim.psfver = (psf.version, psf.plver) if get_dq: tim.dq = dq tim.dq_bits = CP_DQ_BITS tim.saturation = imghdr.get('SATURATE', None) tim.satval = tim.saturation or 0. if subsky: tim.satval -= midsky if nanomaggies: tim.satval /= orig_zpscale subh, subw = tim.shape tim.subwcs = tim.sip_wcs.get_subimage(tim.x0, tim.y0, subw, subh) mn, mx = tim.zr tim.ima = dict(interpolation='nearest', origin='lower', cmap='gray', vmin=mn, vmax=mx) return tim