lsst.pipe.tasks  13.0-29-g7046ce1+1
 All Classes Namespaces Files Functions Variables Groups Pages
warpAndPsfMatch.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011, 2012 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
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
30 
31 __all__ = ["WarpAndPsfMatchTask"]
32 
33 
34 class WarpAndPsfMatchConfig(pexConfig.Config):
35  """Config for WarpAndPsfMatchTask
36  """
37  psfMatch = pexConfig.ConfigurableField(
38  target=ModelPsfMatchTask,
39  doc="PSF matching model to model task",
40  )
41  warp = pexConfig.ConfigField(
42  dtype=afwMath.Warper.ConfigClass,
43  doc="warper configuration",
44  )
45  matchThenWarp = pexConfig.Field(
46  dtype=bool,
47  doc="Reverse order of warp and match operations to replicate legacy coadd temporary exposures",
48  default=False,
49  )
50 
51 
52 class WarpAndPsfMatchTask(pipeBase.Task):
53  """A task to warp and PSF-match an exposure
54  """
55  ConfigClass = WarpAndPsfMatchConfig
56 
57  def __init__(self, *args, **kwargs):
58  pipeBase.Task.__init__(self, *args, **kwargs)
59  self.makeSubtask("psfMatch")
60  self.warper = afwMath.Warper.fromConfig(self.config.warp)
61 
62  def run(self, exposure, wcs, modelPsf=None, maxBBox=None, destBBox=None):
63  """Warp and PSF-match exposure (if modelPsf is not None)
64 
65  Parameters
66  ----------
67  exposure : :cpp:class: `lsst::afw::image::Exposure`
68  Exposure to preprocess. PSF matching is done in place.
69  wcs : :cpp:class:`lsst::afw::image::Wcs`
70  Desired WCS of temporary images.
71  modelPsf : :cpp:class: `lsst::meas::algorithms::KernelPsf` or None
72  Target PSF to which to match.
73  If None then exposures are not PSF matched.
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.
82 
83 
84  Returns
85  -------
86  An lsst.pipe.base.Struct with the following fields:
87 
88  exposure : :cpp:class:`lsst::afw::image::Exposure`
89  processed exposure
90  """
91  if self.config.matchThenWarp:
92  # Legacy order of operations:
93  # PSF-matching is performed before warping, which is incorrect.
94  # a position-dependent warping (as is used in the general case) will
95  # re-introduce a position-dependent PSF.
96  if modelPsf is not None:
97  exposure = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure
98  with self.timer("warp"):
99  exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox)
100  else:
101  if modelPsf is not None:
102  # Warp PSF before overwriting exposure
103  xyTransform = afwImage.XYTransformFromWcsPair(wcs, exposure.getWcs())
104  psfWarped = WarpedPsf(exposure.getPsf(), xyTransform)
105 
106  if maxBBox is not None:
107  # grow warped region to provide sufficient area for PSF-matching
108  pixToGrow = 2 * max(self.config.psfMatch.kernel.active.sizeCellX,
109  self.config.psfMatch.kernel.active.sizeCellY)
110  # replace with copy
111  maxBBox = afwGeom.Box2I(maxBBox)
112  maxBBox.grow(pixToGrow)
113 
114  with self.timer("warp"):
115  exposure = self.warper.warpExposure(wcs, exposure, maxBBox=maxBBox, destBBox=destBBox)
116 
117  if modelPsf is not None:
118  exposure.setPsf(psfWarped)
119  exposure = self.psfMatch.run(exposure, modelPsf).psfMatchedExposure
120 
121  return pipeBase.Struct(
122  exposure=exposure,
123  )