def addSnaps(self, snap0, snap1): """Add two snap exposures together, returning a new exposure @param[in] snap0 snap exposure 0 @param[in] snap1 snap exposure 1 @return combined exposure """ self.log.info("snapCombine addSnaps") combinedExp = snap0.Factory(snap0, True) combinedMi = combinedExp.getMaskedImage() combinedMi.set(0) weightMap = combinedMi.getImage().Factory(combinedMi.getBBox()) weight = 1.0 badPixelMask = afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes) addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight) addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight) # pre-scaling the weight map instead of post-scaling the combinedMi saves a bit of time # because the weight map is a simple Image instead of a MaskedImage weightMap *= 0.5 # so result is sum of both images, instead of average combinedMi /= weightMap setCoaddEdgeBits(combinedMi.getMask(), weightMap) # note: none of the inputs has a valid PhotoCalib object, so that is not touched # Filter was already copied combinedMetadata = combinedExp.getMetadata() metadata0 = snap0.getMetadata() metadata1 = snap1.getMetadata() self.fixMetadata(combinedMetadata, metadata0, metadata1) return combinedExp
def testAssertions(self): """Test that addToCoadd requires coadd and weightMap to have the same dimensions and xy0""" maskedImage = afwImage.MaskedImageF(geom.Extent2I(10, 10)) coadd = afwImage.MaskedImageF(geom.Extent2I(11, 11)) coadd.setXY0(5, 6) for dw, dh in (1, 0), (0, 1), (-1, 0), (0, -1): weightMapBBox = geom.Box2I( coadd.getXY0(), coadd.getDimensions() + geom.Extent2I(dw, dh)) weightMap = afwImage.ImageF(weightMapBBox) weightMap.setXY0(coadd.getXY0()) try: coaddUtils.addToCoadd(coadd, weightMap, maskedImage, 0x0, 0.1) self.fail("should have raised exception") except pexExcept.Exception: pass for dx0, dy0 in (1, 0), (0, 1), (-1, 0), (0, -1): weightMapBBox = geom.Box2I( coadd.getXY0() + geom.Extent2I(dx0, dy0), coadd.getDimensions()) weightMap = afwImage.ImageF(weightMapBBox) try: coaddUtils.addToCoadd(coadd, weightMap, maskedImage, 0x0, 0.1) self.fail("should have raised exception") except pexExcept.Exception: pass
def addSnaps(self, snap0, snap1): """Add two snap exposures together, returning a new exposure @param[in] snap0 snap exposure 0 @param[in] snap1 snap exposure 1 @return combined exposure """ self.log.info("snapCombine addSnaps") combinedExp = snap0.Factory(snap0, True) combinedMi = combinedExp.getMaskedImage() combinedMi.set(0) weightMap = combinedMi.getImage().Factory(combinedMi.getBBox(afwImage.PARENT)) weight = 1.0 badPixelMask = afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes) addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight) addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight) # pre-scaling the weight map instead of post-scaling the combinedMi saves a bit of time # because the weight map is a simple Image instead of a MaskedImage weightMap *= 0.5 # so result is sum of both images, instead of average combinedMi /= weightMap setCoaddEdgeBits(combinedMi.getMask(), weightMap) # note: none of the inputs has a valid Calib object, so that is not touched # Filter was already copied combinedMetadata = combinedExp.getMetadata() metadata0 = snap0.getMetadata() metadata1 = snap1.getMetadata() self.fixMetadata(combinedMetadata, metadata0, metadata1) return combinedExp
def _testAddToCoaddImpl(self, useMask, uniformWeight=True): """Test coadd""" trueImageValue = 10.0 imBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 20)) if useMask: coadd = afwImage.MaskedImageF(imBBox) weightMap = coadd.getImage().Factory(coadd.getBBox()) badBits = 0x1 badPixel = (float("NaN"), badBits, 0) truth = (trueImageValue, 0x0, 0) else: coadd = afwImage.ImageF(imBBox) weightMap = coadd.Factory(coadd.getBBox()) badPixel = float("NaN") truth = trueImageValue for i in range(0, 20, 3): image = coadd.Factory(coadd.getDimensions()) image.set(badPixel) subBBox = afwGeom.Box2I( afwGeom.Point2I(0, i), image.getDimensions() - afwGeom.Extent2I(0, i)) subImage = image.Factory(image, subBBox, afwImage.LOCAL) subImage.set(truth) del subImage weight = 1.0 if uniformWeight else 1.0 + 0.1 * i if useMask: coaddUtils.addToCoadd(coadd, weightMap, image, badBits, weight) else: coaddUtils.addToCoadd(coadd, weightMap, image, weight) self.assertEqual( image.get(image.getWidth() - 1, image.getHeight() - 1), truth) coadd /= weightMap if display: ds9.mtv(image, title="image", frame=1) ds9.mtv(coadd, title="coadd", frame=2) ds9.mtv(weightMap, title="weightMap", frame=3) stats = afwMath.makeStatistics(coadd, afwMath.MEAN | afwMath.STDEV) return [ trueImageValue, stats.getValue(afwMath.MEAN), 0.0, stats.getValue(afwMath.STDEV) ]
def process(self, clipboard): """ Combine two Exposures, omitting any pixels in either positiveDetection or negativeDetection """ self.log.log(Log.INFO, "Combining two Exposures in process") #grab exposure from clipboard exposures = [] for k in self.policy.getArray("inputKeys.exposures"): exposures.append(clipboard.get(k)) assert len(exposures) == 2 badPixelMask = 0x0 mi0 = exposures[0].getMaskedImage() combined = mi0.Factory(mi0.getDimensions()) combined.setXY0(mi0.getXY0()) del mi0 weightMap = combined.getImage().Factory(combined.getDimensions()) weightMap.setXY0(combined.getXY0()) for i in range(0, 2): e = exposures[i] # # Get the FootprintSet associated with object that are only in this Exposure # if i == 0: footprintKey = "positiveDetection" else: footprintKey = "negativeDetection" fs = clipboard.get(self.policy.get("inputKeys.%s" % footprintKey)) mi = e.getMaskedImage() fs.setMask(mi.getMask(), "CR") fs.setMask(combined.getMask(), "CR") coaddUtils.addToCoadd(combined, weightMap, mi, mi.getMask().getPlaneBitMask("CR"), 1.0) combined /= weightMap combinedExposure = afwImage.makeExposure(combined, exposures[0].getWcs()) combinedExposure.setMetadata(exposures[0].getMetadata()) #output products clipboard.put(self.policy.get("outputKeys.combinedExposure"), combinedExposure)
def referenceTest(self, coadd, weightMap, image, badPixelMask, weight): """Compare lsst implemenation of addToCoadd to a reference implementation. Returns the overlap bounding box """ # this call leaves coadd and weightMap alone: overlapBBox, refCoaddArrayList, refweightMapArray = \ referenceAddToCoadd(coadd, weightMap, image, badPixelMask, weight) # this updates coadd and weightMap: afwOverlapBox = coaddUtils.addToCoadd(coadd, weightMap, image, badPixelMask, weight) self.assertEqual(overlapBBox, afwOverlapBox) coaddArrayList = coadd.getArrays() weightMapArray = weightMap.getArray() for name, ind in (("image", 0), ("mask", 1), ("variance", 2)): if not np.allclose(coaddArrayList[ind], refCoaddArrayList[ind]): errMsgList = ( "Computed %s does not match reference for badPixelMask=%s:" % (name, badPixelMask), "computed= %s" % (coaddArrayList[ind],), "reference= %s" % (refCoaddArrayList[ind],), ) errMsg = "\n".join(errMsgList) self.fail(errMsg) if not np.allclose(weightMapArray, refweightMapArray): errMsgList = ( "Computed weight map does not match reference for badPixelMask=%s:" % (badPixelMask,), "computed= %s" % (weightMapArray,), "reference= %s" % (refweightMapArray,), ) errMsg = "\n".join(errMsgList) self.fail(errMsg) return overlapBBox
def _testAddToCoaddImpl(self, useMask, uniformWeight=True): """Test coadd""" trueImageValue = 10.0 imBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 20)) if useMask: coadd = afwImage.MaskedImageF(imBBox) weightMap = coadd.getImage().Factory(coadd.getBBox()) badBits = 0x1 badPixel = (float("NaN"), badBits, 0) truth = (trueImageValue, 0x0, 0) else: coadd = afwImage.ImageF(imBBox) weightMap = coadd.Factory(coadd.getBBox()) badPixel = float("NaN") truth = trueImageValue for i in range(0, 20, 3): image = coadd.Factory(coadd.getDimensions()) image.set(badPixel) subBBox = afwGeom.Box2I(afwGeom.Point2I(0, i), image.getDimensions() - afwGeom.Extent2I(0, i)) subImage = image.Factory(image, subBBox, afwImage.LOCAL) subImage.set(truth) del subImage weight = 1.0 if uniformWeight else 1.0 + 0.1*i if useMask: coaddUtils.addToCoadd(coadd, weightMap, image, badBits, weight) else: coaddUtils.addToCoadd(coadd, weightMap, image, weight) self.assertEqual(image[-1, -1, afwImage.LOCAL], truth) coadd /= weightMap if display: ds9.mtv(image, title="image", frame=1) ds9.mtv(coadd, title="coadd", frame=2) ds9.mtv(weightMap, title="weightMap", frame=3) stats = afwMath.makeStatistics(coadd, afwMath.MEAN | afwMath.STDEV) return [trueImageValue, stats.getValue(afwMath.MEAN), 0.0, stats.getValue(afwMath.STDEV)]
def run(self, identMatrix, butler, ra, dec, scale, xSize, ySize, ignore=False): """Warp and stack images @param[in] identMatrix Matrix of warp identifiers @param[in] butler Data butler @param[in] ra Right Ascension (radians) of skycell centre @param[in] dec Declination (radians) of skycell centre @param[in] scale Scale (arcsec/pixel) of skycell @param[in] xSize Size in x @param[in] ySize Size in y @param[in] ignore Ignore missing files? @output Stacked exposure """ assert identMatrix, "No identMatrix provided" skycell = self.skycell(ra, dec, scale, xSize, ySize) coaddDim = afwGeom.Extent2I(xSize, ySize) coadd = afwImage.ExposureF(coaddDim, skycell.getWcs()) weight = afwImage.ImageF(coaddDim) badpix = afwImage.MaskU.getPlaneBitMask( "EDGE") # Allow everything else through for identList in identMatrix: warp = self.warp(identList, butler, skycell, ignore=ignore) # XXX Save for later? coaddUtils.addToCoadd(coadd.getMaskedImage(), weight, warp.getMaskedImage(), badpix, 1.0) coaddUtils.setCoaddEdgeBits(coadd.getMaskedImage().getMask(), weight) coaddImage = coadd.getMaskedImage() coaddImage /= weight # XXX Coadd has NANs where weight=0 return coadd
def testAssertions(self): """Test that addToCoadd requires coadd and weightMap to have the same dimensions and xy0""" maskedImage = afwImage.MaskedImageF(afwGeom.Extent2I(10, 10)) coadd = afwImage.MaskedImageF(afwGeom.Extent2I(11, 11)) coadd.setXY0(5, 6) for dw, dh in (1, 0), (0, 1), (-1, 0), (0, -1): weightMapBBox = afwGeom.Box2I(coadd.getXY0(), coadd.getDimensions() + afwGeom.Extent2I(dw, dh)) weightMap = afwImage.ImageF(weightMapBBox) weightMap.setXY0(coadd.getXY0()) try: coaddUtils.addToCoadd(coadd, weightMap, maskedImage, 0x0, 0.1) self.fail("should have raised exception") except pexExcept.Exception: pass for dx0, dy0 in (1, 0), (0, 1), (-1, 0), (0, -1): weightMapBBox = afwGeom.Box2I(coadd.getXY0() + afwGeom.Extent2I(dx0, dy0), coadd.getDimensions()) weightMap = afwImage.ImageF(weightMapBBox) try: coaddUtils.addToCoadd(coadd, weightMap, maskedImage, 0x0, 0.1) self.fail("should have raised exception") except pexExcept.Exception: pass
def addExposure(self, exposure, weightFactor=1.0): """Add an Exposure to the coadd Parameters ---------- exposure: `afwImage.Exposure` Exposure to add to coadd; this should be: - background-subtracted or background-matched to the other images being coadded - psf-matched to the desired PSF model (optional) - warped to match the coadd - photometrically scaled to the desired flux magnitude weightFactor : `float` the extra weight factor for this exposure Returns -------- overlapBBox, weight : `afwGeom.Box2I`, `float` the region of overlap between exposure and coadd in parent coordinates. weight with which exposure was added to coadd; weight = weightFactor / clipped mean variance Subclasses may override to preprocess the exposure or change the way it is added to the coadd. """ maskedImage = exposure.getMaskedImage() # compute the weight statObj = afwMath.makeStatistics(maskedImage.getVariance(), maskedImage.getMask(), afwMath.MEANCLIP, self._statsControl) meanVar = statObj.getResult(afwMath.MEANCLIP)[0] weight = weightFactor / float(meanVar) if math.isnan(weight): raise RuntimeError( "Weight is NaN (weightFactor=%s; mean variance=%s)" % (weightFactor, meanVar)) # save filter info filter = exposure.getFilter() self._filterDict.setdefault(filter.getName(), filter) self._log.info("Add exposure to coadd with weight=%0.3g", weight) overlapBBox = coadd_utils.addToCoadd(self._coadd.getMaskedImage(), self._weightMap, maskedImage, self._badPixelMask, weight) return overlapBBox, weight
def referenceTest(self, coadd, weightMap, image, badPixelMask, weight): """Compare lsst implemenation of addToCoadd to a reference implementation. Returns the overlap bounding box """ # this call leaves coadd and weightMap alone: overlapBBox, refCoaddArrayList, refweightMapArray = \ referenceAddToCoadd(coadd, weightMap, image, badPixelMask, weight) # this updates coadd and weightMap: afwOverlapBox = coaddUtils.addToCoadd(coadd, weightMap, image, badPixelMask, weight) self.assertEqual(overlapBBox, afwOverlapBox) coaddArrayList = coadd.getArrays() weightMapArray = weightMap.getArray() for name, ind in (("image", 0), ("mask", 1), ("variance", 2)): if not np.allclose(coaddArrayList[ind], refCoaddArrayList[ind]): errMsgList = ( "Computed %s does not match reference for badPixelMask=%s:" % (name, badPixelMask), "computed= %s" % (coaddArrayList[ind], ), "reference= %s" % (refCoaddArrayList[ind], ), ) errMsg = "\n".join(errMsgList) self.fail(errMsg) if not np.allclose(weightMapArray, refweightMapArray): errMsgList = ( "Computed weight map does not match reference for badPixelMask=%s:" % (badPixelMask, ), "computed= %s" % (weightMapArray, ), "reference= %s" % (refweightMapArray, ), ) errMsg = "\n".join(errMsgList) self.fail(errMsg) return overlapBBox
print " Compute coadd component" coaddComponent = coaddKaiser.CoaddComponent(exposure, psfKernel, normalizePsf) print " Divide exposure by sigma squared = %s" % (coaddComponent.getSigmaSq(),) blurredExposure = coaddComponent.getBlurredExposure() blurredMaskedImage = blurredExposure.getMaskedImage() sigmaSq = coaddComponent.getSigmaSq() if saveImages: blurredExposure.writeFits("blurred%s" % (fileName,)) blurredMaskedImage /= sigmaSq if saveImages: blurredExposure.writeFits("scaledBlurred%s" % (fileName,)) print " Remap blurred exposure to match coadd WCS" remappedBlurredMaskedImage = afwImage.MaskedImageD( coaddExposure.getWidth(), coaddExposure.getHeight()) remappedBlurredExposure = afwImage.ExposureD(remappedBlurredMaskedImage, coaddWcs) if saveImages: remappedBlurredExposure.writeFits("remappedBlurred%s" % (fileName,)) nGoodPix = afwMath.warpExposure(remappedBlurredExposure, blurredExposure, afwMath.LanczosWarpingKernel(3)) nPix = coaddExposure.getWidth() * coaddExposure.getHeight() print " Remapped image has %d good pixels (%0.0f %%)" % (nGoodPix, 100 * nGoodPix / float(nPix)) print " Add remapped blurred exposure to coadd and save updated coadd exposure" coaddUtils.addToCoadd(coaddMaskedImage, depthMap, remappedBlurredExposure.getMaskedImage(), coaddMask) coaddExposure.writeFits(outName) depthMap.writeFits(depthOutName) coaddUtils.setCoaddEdgeBits(coaddMaskedImage.getMask(), depthMap) coaddExposure.writeFits(outName)
blurredMaskedImage = blurredExposure.getMaskedImage() sigmaSq = coaddComponent.getSigmaSq() if saveImages: blurredExposure.writeFits("blurred%s" % (fileName, )) blurredMaskedImage /= sigmaSq if saveImages: blurredExposure.writeFits("scaledBlurred%s" % (fileName, )) print " Remap blurred exposure to match coadd WCS" remappedBlurredMaskedImage = afwImage.MaskedImageD( coaddExposure.getWidth(), coaddExposure.getHeight()) remappedBlurredExposure = afwImage.ExposureD( remappedBlurredMaskedImage, coaddWcs) if saveImages: remappedBlurredExposure.writeFits("remappedBlurred%s" % (fileName, )) nGoodPix = afwMath.warpExposure(remappedBlurredExposure, blurredExposure, afwMath.LanczosWarpingKernel(3)) nPix = coaddExposure.getWidth() * coaddExposure.getHeight() print " Remapped image has %d good pixels (%0.0f %%)" % ( nGoodPix, 100 * nGoodPix / float(nPix)) print " Add remapped blurred exposure to coadd and save updated coadd exposure" coaddUtils.addToCoadd(coaddMaskedImage, depthMap, remappedBlurredExposure.getMaskedImage(), coaddMask) coaddExposure.writeFits(outName) depthMap.writeFits(depthOutName) coaddUtils.setCoaddEdgeBits(coaddMaskedImage.getMask(), depthMap) coaddExposure.writeFits(outName)