def testKernelPsf(self): """Test creating a Psf from a Kernel""" x,y = 10.4999, 10.4999 ksize = 15 sigma1 = 1 # # Make a PSF from that kernel # kPsf = afwDetect.createPsf("Kernel", afwMath.AnalyticKernel(ksize, ksize, afwMath.GaussianFunction2D(sigma1, sigma1))) kIm = kPsf.computeImage(afwGeom.Point2D(x, y)) # # And now via the dgPsf model # dgPsf = afwDetect.createPsf("DoubleGaussian", ksize, ksize, sigma1) dgIm = dgPsf.computeImage(afwGeom.Point2D(x, y)) # # Check that they're the same # diff = type(kIm)(kIm, True); diff -= dgIm stats = afwMath.makeStatistics(diff, afwMath.MAX | afwMath.MIN) self.assertAlmostEqual(stats.getValue(afwMath.MAX), 0.0, places=16) self.assertAlmostEqual(stats.getValue(afwMath.MIN), 0.0, places=16) if display: mos = displayUtils.Mosaic() mos.setBackground(-0.1) ds9.mtv(mos.makeMosaic([kIm, dgIm, diff], mode="x"), frame=1)
def testInvalidDgPsf(self): """Test parameters of dgPsfs, both valid and not""" sigma1, sigma2, b = 1, 0, 0 # sigma2 may be 0 iff b == 0 afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, sigma1, sigma2, b) def badSigma1(): sigma1 = 0 afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, sigma1, sigma2, b) utilsTests.assertRaisesLsstCpp(self, pexExceptions.DomainErrorException, badSigma1) def badSigma2(): sigma2, b = 0, 1 afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, sigma1, sigma2, b) utilsTests.assertRaisesLsstCpp(self, pexExceptions.DomainErrorException, badSigma2)
def run(self, exposure, wcs, maxBBox=None, destBBox=None): """PSF-match exposure (if self.config.desiredFwhm is not None) and warp @param[in,out] exposure: exposure to preprocess; PSF matching is done in place @param[in] wcs: desired WCS of temporary images @param maxBBox: maximum allowed parent bbox of warped exposure (an afwGeom.Box2I or None); if None then the warped exposure will be just big enough to contain all warped pixels; if provided then the warped exposure may be smaller, and so missing some warped pixels; ignored if destBBox is not None @param destBBox: exact parent bbox of warped exposure (an afwGeom.Box2I or None); if None then maxBBox is used to determine the bbox, otherwise maxBBox is ignored @return a pipe_base Struct containing: - exposure: processed exposure """ if self.config.desiredFwhm is not None: self.log.info("PSF-match exposure") fwhmPixels = self.config.desiredFwhm / wcs.pixelScale().asArcseconds() kernelDim = exposure.getPsf().getKernel().getDimensions() coreSigma = fwhmPixels / FwhmPerSigma modelPsf = afwDetection.createPsf("DoubleGaussian", kernelDim[0], kernelDim[1], coreSigma, coreSigma * 2.5, 0.1) exposure = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure self.log.info("Warp exposure") with self.timer("warp"): exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox) return pipeBase.Struct( exposure = exposure, )
def setUp(self): self.ksize = 25 # size of desired kernel FWHM = 5 self.sigma1 = FWHM/(2*sqrt(2*log(2))) self.sigma2 = 2*self.sigma1 self.b = 0.1 self.psf = roundTripPsf(1, afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, self.sigma1, self.sigma2, self.b))
def getFakePsf(pixscale): #fwhmarcsec = 0.7 #1.0 #0.5 fwhmarcsec = 1.0 fwhm = fwhmarcsec / pixscale print 'fwhm', fwhm psfsize = 25 model = 'DoubleGaussian' sig = fwhm/(2.*math.sqrt(2.*math.log(2.))) print 'sigma', sig psf = afwDet.createPsf(model, psfsize, psfsize, sig, 0., 0.) print 'psf', psf return psf
def getFakePsf(pixscale): #fwhmarcsec = 0.7 #1.0 #0.5 fwhmarcsec = 1.0 fwhm = fwhmarcsec / pixscale print 'fwhm', fwhm psfsize = 25 model = 'DoubleGaussian' sig = fwhm / (2. * math.sqrt(2. * math.log(2.))) print 'sigma', sig psf = afwDet.createPsf(model, psfsize, psfsize, sig, 0., 0.) print 'psf', psf return psf
def testComputeImage3(self): """Test the computation of the PSF's image at a point for non-native sizes""" # # First an analytic Kernel # ksize = 15 aPsf = afwDetect.createPsf("Kernel", afwMath.AnalyticKernel(ksize, ksize, afwMath.GaussianFunction2D(1, 1))) # # Then an image-based Kernel # iPsf = afwDetect.createPsf("Kernel", afwMath.FixedKernel(aPsf.computeImage())) for dy in range(-1, 2): for dx in range(-1, 2): dimen = afwGeom.Extent2I(ksize + 2*dx, ksize + 2*dy) aIm = aPsf.computeImage(dimen) self.assertTrue(aIm.getDimensions() == dimen) iIm = iPsf.computeImage(dimen) self.assertTrue(iIm.getDimensions() == dimen)
def makeModelPsf(self, fwhmPixels, kernelDim): """Construct a model PSF, or reuse the prior model, if possible The model PSF is a double Gaussian with core FWHM = fwhmPixels and wings of amplitude 1/10 of core and FWHM = 2.5 * core. @param fwhmPixels: desired FWHM of core Gaussian, in pixels @param kernelDim: desired dimensions of PSF kernel, in pixels @return model PSF """ self.log.logdebug("Create double Gaussian PSF model with core fwhm %0.1f pixels and size %dx%d" % \ (fwhmPixels, kernelDim[0], kernelDim[1])) coreSigma = fwhmPixels / FwhmPerSigma return afwDetection.createPsf("DoubleGaussian", kernelDim[0], kernelDim[1], coreSigma, coreSigma * 2.5, 0.1)
def testCopyExposure(self): """Copy an Exposure (maybe changing type)""" exposureU = afwImage.ExposureU(inFilePathSmall) exposureU.setWcs(self.wcs) exposureU.setDetector(cameraGeom.Detector(cameraGeom.Id(666))) exposureU.setFilter(afwImage.Filter("g")) exposureU.getCalib().setExptime(666) exposureU.setPsf(afwDetection.createPsf("DoubleGaussian", 11, 11, 1)) exposureF = exposureU.convertF() self.cmpExposure(exposureF, exposureU) nexp = exposureF.Factory(exposureF, False) self.cmpExposure(exposureF, nexp)
def installInitialPsf(self, exposure): """Initialise the calibration procedure by setting the PSF to a configuration-defined guess. @param[in,out] exposure Exposure to process; fake PSF will be installed here. """ assert exposure, "No exposure provided" wcs = exposure.getWcs() assert wcs, "No wcs in exposure" model = self.config.initialPsf.model fwhm = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds() size = self.config.initialPsf.size self.log.info("installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhm, size)) psf = afwDet.createPsf(model, size, size, fwhm/(2*math.sqrt(2*math.log(2)))) exposure.setPsf(psf)
def interpolateOnePlane(self, maskedImage, planeName, fwhmPixels): """Interpolate over one mask plane, in place @param[in,out] maskedImage: MaskedImage over which to interpolate over edge pixels @param[in] fwhmPixels: FWHM of double Gaussian model to use for interpolation (pixels) @param[in] planeName: mask plane over which to interpolate @param[in] PSF to use to detect NaNs """ self.log.info("Interpolate over %s pixels" % (planeName,)) kernelSize = int(round(fwhmPixels * self.config.interpKernelSizeFactor)) kernelDim = afwGeom.Point2I(kernelSize, kernelSize) coreSigma = fwhmPixels / FwhmPerSigma psfModel = afwDetection.createPsf("DoubleGaussian", kernelDim[0], kernelDim[1], coreSigma, coreSigma * 2.5, 0.1) nanDefectList = ipIsr.getDefectListFromMask(maskedImage, planeName, growFootprints=0) measAlg.interpolateOverDefects(maskedImage, psfModel, nanDefectList, 0.0)
def findCosmicRays(exposure, crRejectPolicy, defaultFwhm, keepCRs): """defaultFwhm is in arcsec""" mi = exposure.getMaskedImage() wcs = exposure.getWcs() scale = wcs.pixelScale().asArcseconds() defaultFwhm /= scale # convert to pixels ksize = 4 * int(defaultFwhm) + 1 psf = afwDetection.createPsf( 'DoubleGaussian', ksize, ksize, defaultFwhm / (2 * math.sqrt(2 * math.log(2)))) bg = afwMath.makeStatistics(mi, afwMath.MEDIAN).getValue() crs = measAlg.findCosmicRays(mi, psf, bg, crRejectPolicy, keepCRs) return crs
def fakePsf(self, exposure): """Initialise the calibration procedure by setting the PSF and WCS @param exposure Exposure to process @return PSF, WCS """ assert exposure, "No exposure provided" wcs = exposure.getWcs() assert wcs, "No wcs in exposure" calibrate = self.config['calibrate'] model = calibrate['model'] fwhm = calibrate['fwhm'] / wcs.pixelScale().asArcseconds() size = calibrate['size'] psf = afwDet.createPsf(model, size, size, fwhm / (2 * math.sqrt(2 * math.log(2)))) return psf, wcs
def makeInitialPsf(self, exposure, fwhmPix=None): """Initialise the detection procedure by setting the PSF and WCS @param exposure Exposure to process @return PSF, WCS """ assert exposure, "No exposure provided" wcs = exposure.getWcs() assert wcs, "No wcs in exposure" if fwhmPix is None: fwhmPix = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds() size = self.config.initialPsf.size model = self.config.initialPsf.model self.log.info("installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhmPix, size)) psf = afwDet.createPsf(model, size, size, fwhmPix/(2.0*num.sqrt(2*num.log(2.0)))) return psf
def testKernel(self): """Test the creation of the Psf's kernel""" kIm = self.psf.computeImage() if False: ds9.mtv(kIm) self.assertTrue(kIm.getWidth() == self.ksize) # # Check that the image is as expected # dgPsf = afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, self.sigma1, self.sigma2, self.b) dgIm = dgPsf.computeImage() # # Check that they're the same # diff = type(kIm)(kIm, True); diff -= dgIm stats = afwMath.makeStatistics(diff, afwMath.MAX | afwMath.MIN) self.assertEqual(stats.getValue(afwMath.MAX), 0.0) self.assertEqual(stats.getValue(afwMath.MIN), 0.0)
def testPsfDistortion(self): #distorter = cameraGeom.NullDistortion() #self.sCamDistorter #Exag distorter = self.sCamDistorter # set the psf kwid = 55 psfSigma = 4.5 psf = afwDet.createPsf("DoubleGaussian", kwid, kwid, psfSigma, psfSigma, 0.0) # create a detector which is offset from the boresight pixelSize = 0.01 # mm allPixels = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(self.nx, self.ny)) detector = cameraUtils.makeDefaultCcd(allPixels, pixelSize=pixelSize) detector.setCenterPixel(afwGeom.Point2D(self.nx/2, self.ny/2)) # try the upper right corner of chip 0 on suprimecam cenPixX, cenPixY = 5000.0, 4000.0 detector.setCenter(cameraGeom.FpPoint(cenPixX*pixelSize, cenPixY*pixelSize)) detector.setDistortion(distorter) psf.setDetector(detector) settings = {'scale': 'minmax', 'zoom':"to fit", 'mask':'transparency 80'} # use a point in the middle of the test image x = self.nx//2 y = self.ny//2 p = afwGeom.Point2D(x,y) # this is our **measured** coordinate pOrig = distorter.undistort(p, detector) # this is where p would be without optical distortion ######################################################## # check that the camera behaves as expected pos = detector.getPositionFromPixel(p) pix = detector.getPixelFromPosition(pos) print "posmm, pospix, pix", pos.getMm(), pos.getPixels(detector.getPixelSize()), pix posPix = pos.getPixels(detector.getPixelSize()) # note that p is in the center of the ccd self.assertEqual(posPix.getX(), cenPixX) self.assertEqual(posPix.getY(), cenPixY) self.assertEqual(pix.getX(), x) self.assertEqual(pix.getY(), y) ######################################################## # compare the measured shear in a psf image to the expected value # get the expected shear at p q = distorter.distort(pOrig, geomEllip.Quadrupole(), detector) ax = geomEllip.Axes(q) aKnown = ax.getA() bKnown = ax.getB() thetaKnown = ax.getTheta()*180.0/math.pi print "Shear at p: ", ax, thetaKnown # make a plain PSF doDistort = False # the default is True psfImg = psf.computeImage(p, True, doDistort) # compute a PSF at p psfImgDistInternally = psf.computeImage(p) # make a plain one and distort it ourselves # --> note that we use the undistorted pOrig ... that's where p was before the optics #psfImgOrig = psf.computeImage(pOrig, True, doDistort) #psfImgDistByUs = distorter.distort(pOrig, psfImgOrig, detector, 0.0) #shift = p - afwGeom.Extent2D(pOrig) #afwMath.offsetImage(psfImgDistByUs, shift.getX(), shift.getY(), "lanczos5", 5) psfImgDistByUs = distorter.distort(p, psfImg, detector, 0.0) # to display, we'll trim off the edge of the original so it's the same size as the distorted. wid2 = psfImgDistInternally.getWidth() edge = (psfImg.getWidth() - wid2)/2 box = afwGeom.Box2I(afwGeom.Point2I(edge, edge), afwGeom.Extent2I(wid2,wid2)) if display: ds9.mtv(afwImage.ImageD(psfImg, box), frame=1, title="psf", settings=settings) ds9.mtv(psfImgDistInternally, frame=2, title="psfDist", settings=settings) ds9.mtv(afwImage.ImageD(psfImgDistByUs, box), frame=3, title="psfDist2", settings=settings) # first make sure we can plant a known quantity and measure it # quickAndDirtyShape() must be tested to be used itself as a tester sigma = 1.0 img = plantEllipse(kwid, kwid, sigma, sigma, 0.0) a, b, theta, ixx, iyy, ixy = quickAndDirtyShape(img, afwGeom.Point2D(kwid/2,kwid/2)) print "planted:", a/sigma, b/sigma, theta, ixx/sigma**2, iyy/sigma**2, ixy/sigma**2 prec = 6 self.assertAlmostEqual(a, sigma, prec) self.assertAlmostEqual(b, sigma, prec) self.assertAlmostEqual(ixx, sigma**2, prec) self.assertAlmostEqual(iyy, sigma**2, prec) # try 4% shear along theta=45 shear = 1.04 q = geomEllip.Quadrupole(geomEllip.Axes(shear*sigma, sigma, math.pi/4.0)) img = plantEllipse(kwid, kwid, q.getIxx(), q.getIyy(), q.getIxy()) a, b, theta, ixx, iyy, ixy = quickAndDirtyShape(img, afwGeom.Point2D(kwid/2,kwid/2)) print "sheared 4%:", a/sigma, b/sigma, theta, ixx/sigma**2, iyy/sigma**2, ixy/sigma**2 self.assertAlmostEqual(a, shear*sigma, prec) self.assertAlmostEqual(b, sigma, prec) self.assertAlmostEqual(theta, 45.0, prec) # now use quickAndDirty to measure the PSFs we created a, b, theta, ixx, iyy, ixy = quickAndDirtyShape(psfImg, p) print "psfImg:", a/psfSigma, b/psfSigma, theta, ixx/psfSigma**2, iyy/psfSigma**2, ixy/psfSigma**2 self.assertAlmostEqual(a, psfSigma, prec) self.assertAlmostEqual(b, psfSigma, prec) print "known Theta = ", thetaKnown a, b, theta, ixx, iyy, ixy = quickAndDirtyShape(psfImgDistInternally, p) print "warpIntern:", a/psfSigma, b/psfSigma, theta, ixx/psfSigma**2, iyy/psfSigma**2, ixy/psfSigma**2 self.assertTrue(abs(a/psfSigma - aKnown) < 0.01) self.assertTrue(abs(b/psfSigma - bKnown) < 0.01) self.assertTrue(abs(theta - thetaKnown) < 0.5) # half a degree a, b, theta, ixx, iyy, ixy = quickAndDirtyShape(psfImgDistByUs, p) print "warpExtern:", a/psfSigma, b/psfSigma, theta, ixx/psfSigma**2, iyy/psfSigma**2, ixy/psfSigma**2 self.assertTrue(abs(a/psfSigma - aKnown) < 0.01) self.assertTrue(abs(b/psfSigma - bKnown) < 0.01) self.assertTrue(abs(theta - thetaKnown) < 0.5)
def setUp(self): FWHM = 5 self.ksize = 25 # size of desired kernel self.psf = afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, FWHM/(2*sqrt(2*log(2))), 1, 0.1)
def coadd(idList, butler, desFwhm, coaddWcs, coaddBBox, policy): """PSF-match (if desFwhm is specified), warp and coadd images PSF matching is to a double gaussian model with core FWHM = desFwhm and wings of amplitude 1/10 of core and FWHM = 2.5 * core. The size of the PSF matching kernel is the same as the size of the kernel found in the first calibrated science exposure, since there is no benefit to making it any other size. PSF-matching is performed before warping so the code can use the PSF models associated with the calibrated science exposures (without having to warp those models). @param[in] idList: list of data identity dictionaries @param[in] butler: data butler for input images @param[in] desFwhm: desired PSF of coadd, but in science exposure pixels (the coadd usually has a different scale!); if 0 then no PSF matching is performed. @param[in] coaddWcs: WCS for coadd @param[in] coaddBBox: bounding box for coadd @param[in] policy: a Policy object that must contain these policies: psfMatchPolicy: see ip_diffim/policy/PsfMatchingDictionary.paf (may omit if desFwhm <= 0) warpPolicy: see afw/policy/WarpDictionary.paf coaddPolicy: see coadd_utils/policy/CoaddDictionary.paf @output: - coaddExposure: coadd exposure - weightMap: a float Image of the same dimensions as the coadd; the value of each pixel is the sum of the weights of all the images that contributed to that pixel. """ numExp = len(idList) if numExp < 1: print "Warning: no exposures to coadd!" sys.exit(1) print "Coadd %s calexp" % (numExp, ) warpPolicy = policy.getPolicy("warpPolicy") coaddPolicy = policy.getPolicy("coaddPolicy") if desFwhm > 0: psfMatchPolicy = policy.getPolicy("psfMatchPolicy") psfMatchPolicy = ipDiffIm.modifyForModelPsfMatch(psfMatchPolicy) psfMatcher = ipDiffIm.ModelPsfMatch(psfMatchPolicy) else: print "No PSF matching will be done (desFwhm <= 0)" warper = afwMath.Warper.fromPolicy(warpPolicy) coadd = coaddUtils.Coadd.fromPolicy(coaddBBox, coaddWcs, coaddPolicy) prevKernelDim = afwGeom.Extent2I( 0, 0) # use this because the test Extent2I == None is an error for ind, id in enumerate(idList): print "Processing exposure %d of %d: id=%s" % (ind + 1, numExp, id) exposure = butler.get("calexp", id) psf = butler.get("psf", id) exposure.setPsf(psf) if desFwhm > 0: psfKernel = psf.getKernel() kernelDim = psfKernel.getDimensions() if kernelDim != prevKernelDim: print "Create double Gaussian PSF model with core fwhm %0.1f and size %dx%d" % \ (desFwhm, kernelDim[0], kernelDim[1]) coreSigma = desFwhm / FWHMPerSigma modelPsf = afwDetection.createPsf("DoubleGaussian", kernelDim[0], kernelDim[1], coreSigma, coreSigma * 2.5, 0.1) prevKernelDim = kernelDim print "PSF-match exposure" exposure, psfMatchingKernel, kernelCellSet = psfMatcher.matchExposure( exposure, modelPsf) print "Warp exposure" exposure = warper.warpExposure(coaddWcs, exposure, maxBBox=coaddBBox) coadd.addExposure(exposure) coaddExposure = coadd.getCoadd() weightMap = coadd.getWeightMap() return coaddExposure, weightMap
def badSigma2(): sigma2, b = 0, 1 afwDetect.createPsf("DoubleGaussian", self.ksize, self.ksize, sigma1, sigma2, b)
def psfMatchAndWarp(idList, butler, desFwhm, coaddWcs, coaddBBox, policy): """Normalize, PSF-match (if desFWhm > 0) and warp exposures; save the resulting exposures as FITS files @param[in] idList: a list of IDs of calexp (and associated PSFs) to coadd @param[in] butler: data butler for retrieving input calexp and associated PSFs @param[in] desFwhm: desired FWHM (pixels) @param[in] coaddWcs: desired WCS of coadd @param[in] coaddBBox: bounding box for coadd @param[in] policy: policy: see policy/outlierRejectedCoaddDictionary.paf @return - coaddCalib: Calib object for coadd - exposureMetadataList: a list of ExposureMetadata objects describing the saved psf-matched and warped exposures """ numExp = len(idList) if numExp < 1: return [] warpPolicy = policy.getPolicy("warpPolicy") coaddPolicy = policy.getPolicy("coaddPolicy") badPixelMask = afwImage.MaskU.getPlaneBitMask( coaddPolicy.getArray("badMaskPlanes")) coaddZeroPoint = coaddPolicy.get("coaddZeroPoint") coddFluxMag0 = 10**(0.4 * coaddZeroPoint) coaddCalib = afwImage.Calib() coaddCalib.setFluxMag0(coddFluxMag0) if desFwhm > 0: psfMatchPolicy = policy.getPolicy("psfMatchPolicy") psfMatchPolicy = ipDiffIm.modifyForModelPsfMatch(psfMatchPolicy) psfMatcher = ipDiffIm.ModelPsfMatch(psfMatchPolicy) else: print "No PSF matching will be done (desFwhm <= 0)" warper = afwMath.Warper.fromPolicy(warpPolicy) exposureMetadataList = [] prevKernelDim = afwGeom.Extent2I( 0, 0) # use this because the test Extent2I == None is an error for ind, id in enumerate(idList): outPath = "_".join(["%s_%s" % (k, id[k]) for k in sorted(id.keys())]) outPath = outPath.replace(",", "_") outPath = outPath + ".fits" if True: print "Processing exposure %d of %d: id=%s" % (ind + 1, numExp, id) print "Saving intermediate exposure as %s" % (outPath, ) exposure = butler.get("calexp", id) psf = butler.get("psf", id) exposure.setPsf(psf) srcCalib = exposure.getCalib() scaleFac = 1.0 / srcCalib.getFlux(coaddZeroPoint) maskedImage = exposure.getMaskedImage() maskedImage *= scaleFac print "Normalized using scaleFac=%0.3g" % (scaleFac, ) if desFwhm > 0: psfKernel = psf.getKernel() kernelDim = psfKernel.getDimensions() if kernelDim != prevKernelDim: print "Create double Gaussian PSF model with core fwhm %0.1f and size %dx%d" % \ (desFwhm, kernelDim[0], kernelDim[1]) coreSigma = desFwhm / FWHMPerSigma modelPsf = afwDetection.createPsf("DoubleGaussian", kernelDim[0], kernelDim[1], coreSigma, coreSigma * 2.5, 0.1) prevKernelDim = kernelDim print "PSF-match exposure" exposure, psfMatchingKernel, kernelCellSet = psfMatcher.matchExposure( exposure, modelPsf) print "Warp exposure" exposure = warper.warpExposure(coaddWcs, exposure, maxBBox=coaddBBox) exposure.setCalib(coaddCalib) print "Saving intermediate exposure %s" % (outPath, ) exposure.writeFits(outPath) else: # debug mode; exposures already exist print "WARNING: DEBUG MODE; Processing id=%s; retrieving from %s" % ( id, outPath) exposure = afwImage.ExposureF(outPath) expMetadata = ExposureMetadata( path=outPath, exposure=exposure, badPixelMask=badPixelMask, ) exposureMetadataList.append(expMetadata) return coaddCalib, exposureMetadataList