def wise_psf_stamp(band): # psf noise: ~roughly 0.1 count in outskirts of W1 and W2 if band >= 3: raise ValueError('Need to stare at W3+ PSF more!') if os.getenv('WISE_PSF_DIR', None) is None: raise ValueError('WISE_PSF_DIR must be set.') psfnoise = 0.1 stamp = fits.getdata( os.path.join(os.getenv('WISE_PSF_DIR'), 'psf_model_w' + str(band) + '.fits')) edges = numpy.concatenate( [stamp[0, 1:-1], stamp[-1, 1:-1], stamp[1:-1, 0], stamp[1:-1, -1]]) medval = numpy.median(edges[edges != 0]) / 2 stamp[stamp == 0] = medval stamp -= medval from scipy import signal stamp[stamp < 0] = 0. # suppress spurious warnings in signal.wiener olderr = numpy.seterr(invalid='ignore', divide='ignore') stamp = signal.wiener(stamp, 11, psfnoise) numpy.seterr(**olderr) # taper linearly over outer 60 pixels? stampszo2 = stamp.shape[0] // 2 xx, yy = numpy.mgrid[-stampszo2:stampszo2 + 1, -stampszo2:stampszo2 + 1] edgedist = numpy.clip(stampszo2 - numpy.abs(xx), 0, stampszo2 - numpy.abs(yy)) stamp = stamp * numpy.clip(edgedist / 60., stamp < 10, 1) import psf stamp = psf.center_psf(stamp, censize=19) stamp = stamp / numpy.sum(stamp) return stamp
def blend_psf(dstamp, mstamp, innerrad, outerrad): stampsz = dstamp.shape[-1] stampszo2 = stampsz // 2 xx = numpy.arange(stampsz, dtype='f4').reshape(1, -1) - stampszo2 yy = xx.copy().reshape(-1, 1) rr = numpy.sqrt(xx**2 + yy**2) weight = numpy.clip(1 - (rr - innerrad) / float(outerrad - innerrad), 0, 1) dstamp = (dstamp + dstamp[::-1, :] + dstamp[:, ::-1] + dstamp[::-1, ::-1]) / 4. blended = dstamp * weight + mstamp * (1 - weight) blended = psf.center_psf(blended) return blended
def make_new_psfs(write=False, **kw): path = os.path.join(os.environ['DECAM_DIR'], 'data', 'psfs', 'includeu') res = {} for f in filt: tpsf = fits.getdata(os.path.join(path, 'psf_%s.fits.gz' % f)) tpsf = psf.center_psf(tpsf) fitres = psf.fit_moffat(psf.central_stamp(tpsf, censize=19).copy()) fit = fitres[0] deconvfac = 0.7 kernel = psf.moffat_psf(fit[1] * deconvfac, yy=fit[4], beta=fit[2], xy=fit[3], stampsz=69, deriv=False) psfde = restoration.richardson_lucy(tpsf, kernel, 20) psfde = psf.center_psf(psfde) res[f] = psfde if write: for f in filt: fits.writeto(os.path.join(path, 'psf_%s_deconv.fits.gz' % f), res[f], **kw) return res
def wise_psf_stamp(band, nosmooth=False): # psf noise: ~roughly 0.1 count in outskirts of W1 and W2 if band >= 3: raise ValueError('Need to stare at W3+ PSF more!') psfnoise = 0.1 stampfn = resource_filename('unwise_psf', 'data/psf_model_w'+str(band)+'.fits') stamp = fits.getdata(stampfn) edges = numpy.concatenate([stamp[0, 1:-1], stamp[-1, 1:-1], stamp[1:-1, 0], stamp[1:-1, -1]]) medval = numpy.median(edges[edges != 0]) / 2 stamp[stamp == 0] = medval stamp -= medval from scipy import signal stamp[stamp < 0] = 0. # suppress spurious warnings in signal.wiener olderr = numpy.seterr(invalid='ignore', divide='ignore') # update to scipy.signal means that Wiener filter uses an FFT # to perform the various convolutions, which causes bad errors # here unless we cast to f8. It's not that hard to do something # a bit better than scipy.signal.wiener---morally we really want to do # something like smooth in log space on radial lines---but I don't # want to go further down that rabbit hole today. stamp = signal.wiener(stamp.astype('f8'), 11, psfnoise) stamp = stamp.astype('f4') numpy.seterr(**olderr) # taper linearly over outer 60 pixels? stampszo2 = stamp.shape[0] // 2 xx, yy = numpy.mgrid[-stampszo2:stampszo2+1, -stampszo2:stampszo2+1] edgedist = numpy.clip(stampszo2-numpy.abs(xx), 0, stampszo2-numpy.abs(yy)) stamp = stamp * numpy.clip(edgedist / 60., stamp < 10, 1) import psf stamp = psf.center_psf(stamp, censize=19) stamp = stamp / numpy.sum(stamp) return stamp
def find_psf(xcen, shiftx, ycen, shifty, psfstack, weightstack, imstack, stampsz=59, nkeep=100): """Find PSF from stamps.""" # let's just go ahead and correlate the noise xr = numpy.round(shiftx) yr = numpy.round(shifty) psfqf = (numpy.sum(psfstack*(weightstack > 0), axis=(1, 2)) / numpy.sum(psfstack, axis=(1, 2))) totalflux = numpy.sum(psfstack, axis=(1, 2)) timflux = numpy.sum(imstack, axis=(1, 2)) toneflux = numpy.sum(psfstack, axis=(1, 2)) tmedflux = numpy.median(psfstack, axis=(1, 2)) tfracflux = toneflux / numpy.clip(timflux, 100, numpy.inf) tfracflux2 = ((toneflux-tmedflux*psfstack.shape[1]*psfstack.shape[2]) / numpy.clip(timflux, 100, numpy.inf)) okpsf = ((numpy.abs(psfqf - 1) < 0.03) & (tfracflux > 0.5) & (tfracflux2 > 0.2)) if numpy.sum(okpsf) > 0: shiftxm = numpy.median(shiftx[okpsf]) shiftym = numpy.median(shifty[okpsf]) okpsf = (okpsf & (numpy.abs(shiftx-shiftxm) < 1.) & (numpy.abs(shifty-shiftym) < 1.)) if numpy.sum(okpsf) <= 5: print('Fewer than 5 stars accepted in image, keeping original PSF') return None if numpy.sum(okpsf) > nkeep: okpsf = okpsf & (totalflux > -numpy.sort(-totalflux[okpsf])[nkeep-1]) psfstack = psfstack[okpsf, :, :] weightstack = weightstack[okpsf, :, :] totalflux = totalflux[okpsf] xcen = xcen[okpsf] ycen = ycen[okpsf] shiftx = shiftx[okpsf] shifty = shifty[okpsf] for i in range(psfstack.shape[0]): psfstack[i, :, :] = shift(psfstack[i, :, :], [-shiftx[i], -shifty[i]]) if (numpy.abs(xr[i]) > 0) or (numpy.abs(yr[i]) > 0): weightstack[i, :, :] = shift(weightstack[i, :, :], [-xr[i], -yr[i]], mode='constant', cval=0.) # our best guess as to the PSFs & their weights # select some reasonable sample of the PSFs totalflux = numpy.sum(psfstack, axis=(1, 2)) psfstack /= totalflux.reshape(-1, 1, 1) weightstack *= totalflux.reshape(-1, 1, 1) tpsf = numpy.median(psfstack, axis=0) tpsf = psfmod.center_psf(tpsf) if tpsf.shape == stampsz: return tpsf xc = numpy.arange(tpsf.shape[0]).reshape(-1, 1)-tpsf.shape[0]//2 yc = xc.reshape(1, -1) rc = numpy.sqrt(xc**2.+yc**2.) stampszo2 = psfstack[0].shape[0] // 2 wt = numpy.clip((stampszo2+1-rc)/4., 0., 1.) overlap = (wt != 1) & (wt != 0) def objective(par): mod = psfmod.moffat_psf(par[0], beta=2.5, xy=par[2], yy=par[3], deriv=False, stampsz=tpsf.shape[0]) mod /= numpy.sum(mod) return ((tpsf-mod)[overlap]).reshape(-1) from scipy.optimize import leastsq par = leastsq(objective, [4., 3., 0., 1.])[0] modpsf = psfmod.moffat_psf(par[0], beta=2.5, xy=par[2], yy=par[3], deriv=False, stampsz=stampsz) modpsf /= numpy.sum(psfmod.central_stamp(modpsf)) npsf = modpsf.copy() npsfcen = psfmod.central_stamp(npsf, tpsf.shape[0]) npsfcen[:, :] = tpsf*wt+(1-wt)*npsfcen[:, :] npsf /= numpy.sum(npsf) return psfmod.SimplePSF(npsf, normalize=-1)