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