22 from __future__
import absolute_import, division, print_function
23 import lsst.pex.config
as pexConfig
24 import lsst.afw.math
as afwMath
25 import lsst.afw.image
as afwImage
26 import lsst.afw.geom
as afwGeom
27 import lsst.pipe.base
as pipeBase
28 from lsst.ip.diffim
import ModelPsfMatchTask
29 from lsst.meas.algorithms
import WarpedPsf
31 __all__ = [
"WarpAndPsfMatchTask"]
35 """Config for WarpAndPsfMatchTask
37 psfMatch = pexConfig.ConfigurableField(
38 target=ModelPsfMatchTask,
39 doc=
"PSF matching model to model task",
41 warp = pexConfig.ConfigField(
42 dtype=afwMath.Warper.ConfigClass,
43 doc=
"warper configuration",
45 matchThenWarp = pexConfig.Field(
47 doc=
"Reverse order of warp and match operations to replicate legacy coadd temporary exposures",
53 """A task to warp and PSF-match an exposure
55 ConfigClass = WarpAndPsfMatchConfig
58 pipeBase.Task.__init__(self, *args, **kwargs)
59 self.makeSubtask(
"psfMatch")
60 self.
warper = afwMath.Warper.fromConfig(self.config.warp)
62 def run(self, exposure, wcs, modelPsf=None, maxBBox=None, destBBox=None,
63 makeDirect=
True, makePsfMatched=
False):
64 """Warp and PSF-match exposure (if modelPsf is not None)
68 exposure : :cpp:class: `lsst::afw::image::Exposure`
69 Exposure to preprocess.
70 wcs : :cpp:class:`lsst::afw::image::Wcs`
71 Desired WCS of temporary images.
72 modelPsf : :cpp:class: `lsst::meas::algorithms::KernelPsf` or None
73 Target PSF to which to match.
74 maxBBox : :cpp:class:`lsst::afw::geom::Box2I` or None
75 Maximum allowed parent bbox of warped exposure.
76 If None then the warped exposure will be just big enough to contain all warped pixels;
77 if provided then the warped exposure may be smaller, and so missing some warped pixels;
78 ignored if destBBox is not None.
79 destBBox: :cpp:class: `lsst::afw::geom::Box2I` or None
80 Exact parent bbox of warped exposure.
81 If None then maxBBox is used to determine the bbox, otherwise maxBBox is ignored.
83 Return an exposure that has been only warped?
85 Return an exposure that has been warped and PSF-matched?
89 An lsst.pipe.base.Struct with the following fields:
91 direct : :cpp:class:`lsst::afw::image::Exposure`
93 psfMatched : :cpp:class: `lsst::afw::image::Exposure`
94 warped and psf-Matched temporary exposure
96 if makePsfMatched
and modelPsf
is None:
97 raise RuntimeError(
"makePsfMatched=True, but no model PSF was provided")
99 if not makePsfMatched
and not makeDirect:
100 self.log.warn(
"Neither makeDirect nor makePsfMatched requested")
102 if self.config.matchThenWarp:
108 exposurePsfMatched = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure
109 with self.timer(
"warp"):
110 exposurePsfMatched = self.warper.warpExposure(wcs, exposurePsfMatched,
111 maxBBox=maxBBox, destBBox=destBBox)
113 exposurePsfMatched =
None
117 with self.timer(
"warp"):
118 exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox)
124 xyTransform = afwImage.XYTransformFromWcsPair(wcs, exposure.getWcs())
125 psfWarped = WarpedPsf(exposure.getPsf(), xyTransform)
127 if makePsfMatched
and maxBBox
is not None:
129 pixToGrow = 2 * max(self.psfMatch.kConfig.sizeCellX,
130 self.psfMatch.kConfig.sizeCellY)
132 maxBBox = afwGeom.Box2I(maxBBox)
133 maxBBox.grow(pixToGrow)
135 with self.timer(
"warp"):
136 exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox)
137 exposure.setPsf(psfWarped)
141 exposurePsfMatched = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure
142 except Exception
as e:
143 exposurePsfMatched =
None
144 self.log.info(
"Cannot PSF-Match: %s" % (e))
146 return pipeBase.Struct(
147 direct=exposure
if makeDirect
else None,
148 psfMatched=exposurePsfMatched
if makePsfMatched
else None