def testTransform(self): """Test constructor for Polygon involving transforms""" box = afwGeom.Box2D(afwGeom.Point2D(0.0, 0.0), afwGeom.Point2D(123.4, 567.8)) poly1 = Polygon(box) scale = (0.2 * afwGeom.arcseconds).asDegrees() wcs = afwImage.makeWcs( afwCoord.Coord(0.0 * afwGeom.degrees, 0.0 * afwGeom.degrees), afwGeom.Point2D(0.0, 0.0), scale, 0.0, 0.0, scale) transform = afwImage.XYTransformFromWcsPair(wcs, wcs) poly2 = Polygon(box, transform) # We lose some very small precision in the XYTransformFromWcsPair # so we can't compare the polygons directly. self.assertEqual(poly1.getNumEdges(), poly2.getNumEdges()) for p1, p2 in zip(poly1.getVertices(), poly2.getVertices()): self.assertAlmostEqual(p1.getX(), p2.getX()) self.assertAlmostEqual(p1.getY(), p2.getY()) transform = afwGeom.AffineTransform.makeScaling(1.0) poly3 = Polygon(box, transform) self.assertEqual(poly1, poly3)
def compareToSwarp(self, kernelName, useWarpExposure=True, useSubregion=False, useDeepCopy=False, interpLength=10, cacheSize=100000, rtol=4e-05, atol=1e-2): """Compare warpExposure to swarp for given warping kernel. Note that swarp only warps the image plane, so only test that plane. Inputs: - kernelName: name of kernel in the form used by afwImage.makeKernel - useWarpExposure: if True, call warpExposure to warp an ExposureF, else call warpImage to warp an ImageF and also call the XYTransform version - useSubregion: if True then the original source exposure (from which the usual test exposure was extracted) is read and the correct subregion extracted - useDeepCopy: if True then the copy of the subimage is a deep copy, else it is a shallow copy; ignored if useSubregion is False - interpLength: interpLength argument for lsst.afw.math.WarpingControl - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl; 0 disables the cache 10000 gives some speed improvement but less accurate results (atol must be increased) 100000 gives better accuracy but no speed improvement in this test - rtol: relative tolerance as used by np.allclose - atol: absolute tolerance as used by np.allclose """ warpingControl = afwMath.WarpingControl( kernelName, "", # there is no point to a separate mask kernel since we aren't testing the mask plane cacheSize, interpLength, ) if useSubregion: originalFullExposure = afwImage.ExposureF(originalExposurePath) # "medsub" is a subregion of med starting at 0-indexed pixel (40, 150) of size 145 x 200 bbox = afwGeom.Box2I(afwGeom.Point2I(40, 150), afwGeom.Extent2I(145, 200)) originalExposure = afwImage.ExposureF(originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) swarpedImageName = "medsubswarp1%s.fits" % (kernelName, ) else: originalExposure = afwImage.ExposureF(originalExposurePath) swarpedImageName = "medswarp1%s.fits" % (kernelName, ) swarpedImagePath = os.path.join(dataDir, swarpedImageName) swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) swarpedImage = swarpedDecoratedImage.getImage() swarpedMetadata = swarpedDecoratedImage.getMetadata() warpedWcs = afwImage.makeWcs(swarpedMetadata) if useWarpExposure: # path for saved afw-warped image afwWarpedImagePath = "afwWarpedExposure1%s.fits" % (kernelName, ) afwWarpedMaskedImage = afwImage.MaskedImageF( swarpedImage.getDimensions()) afwWarpedExposure = afwImage.ExposureF(afwWarpedMaskedImage, warpedWcs) afwMath.warpExposure(afwWarpedExposure, originalExposure, warpingControl) afwWarpedMask = afwWarpedMaskedImage.getMask() if SAVE_FITS_FILES: afwWarpedExposure.writeFits(afwWarpedImagePath) if display: ds9.mtv(afwWarpedExposure, frame=1, title="Warped") swarpedMaskedImage = afwImage.MaskedImageF(swarpedImage) if display: ds9.mtv(swarpedMaskedImage, frame=2, title="SWarped") msg = "afw and swarp %s-warped differ (ignoring bad pixels)" % ( kernelName, ) try: self.assertMaskedImagesAlmostEqual(afwWarpedMaskedImage, swarpedMaskedImage, doImage=True, doMask=False, doVariance=False, skipMask=afwWarpedMask, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: afwWarpedExposure.writeFits(afwWarpedImagePath) print("Saved failed afw-warped exposure as: %s" % (afwWarpedImagePath, )) raise else: # path for saved afw-warped image afwWarpedImagePath = "afwWarpedImage1%s.fits" % (kernelName, ) afwWarpedImage2Path = "afwWarpedImage1%s_xyTransform.fits" % ( kernelName, ) afwWarpedImage = afwImage.ImageF(swarpedImage.getDimensions()) originalImage = originalExposure.getMaskedImage().getImage() originalWcs = originalExposure.getWcs() afwMath.warpImage(afwWarpedImage, warpedWcs, originalImage, originalWcs, warpingControl) if display: ds9.mtv(afwWarpedImage, frame=1, title="Warped") ds9.mtv(swarpedImage, frame=2, title="SWarped") diff = swarpedImage.Factory(swarpedImage, True) diff -= afwWarpedImage ds9.mtv(diff, frame=3, title="swarp - afw") if SAVE_FITS_FILES: afwWarpedImage.writeFits(afwWarpedImagePath) afwWarpedImageArr = afwWarpedImage.getArray() noDataMaskArr = np.isnan(afwWarpedImageArr) msg = "afw and swarp %s-warped images do not match (ignoring NaN pixels)" % \ (kernelName,) try: self.assertImagesAlmostEqual(afwWarpedImage, swarpedImage, skipMask=noDataMaskArr, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: # save the image anyway afwWarpedImage.writeFits(afwWarpedImagePath) print("Saved failed afw-warped image as: %s" % (afwWarpedImagePath, )) raise afwWarpedImage2 = afwImage.ImageF(swarpedImage.getDimensions()) xyTransform = afwImage.XYTransformFromWcsPair( warpedWcs, originalWcs) afwMath.warpImage(afwWarpedImage2, originalImage, xyTransform, warpingControl) msg = "afw xyTransform-based and WCS-based %s-warped images do not match" % ( kernelName, ) try: self.assertImagesAlmostEqual(afwWarpedImage2, afwWarpedImage, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: # save the image anyway afwWarpedImage.writeFits(afwWarpedImagePath) print("Saved failed afw-warped image as: %s" % (afwWarpedImage2Path, )) raise
def run(self, exposure, wcs, modelPsf=None, maxBBox=None, destBBox=None, makeDirect=True, makePsfMatched=False): """Warp and optionally PSF-match exposure Parameters ---------- exposure : :cpp:class: `lsst::afw::image::Exposure` Exposure to preprocess. wcs : :cpp:class:`lsst::afw::image::Wcs` Desired WCS of temporary images. modelPsf : :cpp:class: `lsst::meas::algorithms::KernelPsf` or None Target PSF to which to match. maxBBox : :cpp:class:`lsst::afw::geom::Box2I` or None Maximum allowed parent bbox of warped exposure. 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. destBBox: :cpp:class: `lsst::afw::geom::Box2I` or None Exact parent bbox of warped exposure. If None then maxBBox is used to determine the bbox, otherwise maxBBox is ignored. makeDirect : bool Return an exposure that has been only warped? makePsfMatched : bool Return an exposure that has been warped and PSF-matched? Returns ------- An lsst.pipe.base.Struct with the following fields: direct : :cpp:class:`lsst::afw::image::Exposure` warped exposure psfMatched : :cpp:class: `lsst::afw::image::Exposure` warped and psf-Matched temporary exposure """ if makePsfMatched and modelPsf is None: raise RuntimeError( "makePsfMatched=True, but no model PSF was provided") if not makePsfMatched and not makeDirect: self.log.warn("Neither makeDirect nor makePsfMatched requested") # Warp PSF before overwriting exposure xyTransform = afwImage.XYTransformFromWcsPair(wcs, exposure.getWcs()) psfWarped = WarpedPsf(exposure.getPsf(), xyTransform) if makePsfMatched and maxBBox is not None: # grow warped region to provide sufficient area for PSF-matching pixToGrow = 2 * max(self.psfMatch.kConfig.sizeCellX, self.psfMatch.kConfig.sizeCellY) # replace with copy maxBBox = afwGeom.Box2I(maxBBox) maxBBox.grow(pixToGrow) with self.timer("warp"): exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox) exposure.setPsf(psfWarped) if makePsfMatched: try: exposurePsfMatched = self.psfMatch.run( exposure, modelPsf).psfMatchedExposure except Exception as e: exposurePsfMatched = None self.log.info("Cannot PSF-Match: %s" % (e)) return pipeBase.Struct( direct=exposure if makeDirect else None, psfMatched=exposurePsfMatched if makePsfMatched else None)