lsst.coadd.utils  15.0-2-g100d730+28
coadd.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 math
24 
25 from builtins import object
26 
27 import lsst.pex.config as pexConfig
28 from lsst.log import Log
29 import lsst.afw.image as afwImage
30 import lsst.afw.math as afwMath
31 from . import addToCoadd, setCoaddEdgeBits
32 
33 __all__ = ["Coadd"]
34 
35 
36 class CoaddConfig(pexConfig.Config):
37  """Config for Coadd
38  """
39  badMaskPlanes = pexConfig.ListField(
40  dtype=str,
41  doc="mask planes that, if set, the associated pixel should not be included in the coadd",
42  default=("NO_DATA", "SAT"),
43  )
44 
45 
46 class Coadd(object):
47  """Coadd by weighted addition
48 
49  This class may be subclassed to implement other coadd techniques.
50  Typically this is done by overriding addExposure.
51  """
52  ConfigClass = CoaddConfig
53 
54  def __init__(self, bbox, wcs, badMaskPlanes, logName="coadd.utils.Coadd"):
55  """Create a coadd
56 
57  @param[in] bbox: bounding box of coadd Exposure with respect to parent (lsst.afw.geom.Box2I):
58  coadd dimensions = bbox.getDimensions(); xy0 = bbox.getMin()
59  @param[in] wcs: WCS of coadd exposure (lsst.afw.geom.SKyWcs)
60  @param[in] badMaskPlanes: mask planes to pay attention to when rejecting masked pixels.
61  Specify as a collection of names.
62  badMaskPlanes should always include "NO_DATA".
63  @param[in] logName: name by which messages are logged
64  """
65  self._log = Log.getLogger(logName)
66  self._bbox = bbox
67  self._wcs = wcs
68  self._badPixelMask = afwImage.Mask.getPlaneBitMask(badMaskPlanes)
69  self._coadd = afwImage.ExposureF(bbox, wcs)
70  self._weightMap = afwImage.ImageF(bbox)
71  self._filterDict = dict() # dict of filter name: filter object for all filters seen so far
72 
73  self._statsControl = afwMath.StatisticsControl()
74  self._statsControl.setNumSigmaClip(3.0)
75  self._statsControl.setNumIter(2)
76  self._statsControl.setAndMask(self._badPixelMask)
77 
78  @classmethod
79  def fromConfig(cls, bbox, wcs, config, logName="coadd.utils.Coadd"):
80  """Create a coadd
81 
82  @param[in] bbox: bounding box of coadd Exposure with respect to parent (lsst.afw.geom.Box2I):
83  coadd dimensions = bbox.getDimensions(); xy0 = bbox.getMin()
84  @param[in] wcs: WCS of coadd exposure (lsst.afw.geom.SKyWcs)
85  @param[in] config: coadd config; an instance of CoaddConfig
86  @param[in] logName: name by which messages are logged
87  """
88  return cls(
89  bbox=bbox,
90  wcs=wcs,
91  badMaskPlanes=config.badMaskPlanes,
92  logName=logName,
93  )
94 
95  def addExposure(self, exposure, weightFactor=1.0):
96  """Add an Exposure to the coadd
97 
98  @param[in] exposure: Exposure to add to coadd; this should be:
99  - background-subtracted or background-matched to the other images being coadded
100  - psf-matched to the desired PSF model (optional)
101  - warped to match the coadd
102  - photometrically scaled to the desired flux magnitude
103  @param[in] weightFactor: extra weight factor for this exposure
104 
105  @return
106  - overlapBBox: region of overlap between exposure and coadd in parent coordinates (afwGeom.Box2I)
107  - weight: weight with which exposure was added to coadd; weight = weightFactor / clipped mean variance
108 
109  Subclasses may override to preprocess the exposure or change the way it is added to the coadd.
110  """
111  maskedImage = exposure.getMaskedImage()
112 
113  # compute the weight
114  statObj = afwMath.makeStatistics(maskedImage.getVariance(), maskedImage.getMask(),
115  afwMath.MEANCLIP, self._statsControl)
116  meanVar = statObj.getResult(afwMath.MEANCLIP)[0]
117  weight = weightFactor / float(meanVar)
118  if math.isnan(weight):
119  raise RuntimeError("Weight is NaN (weightFactor=%s; mean variance=%s)" % (weightFactor, meanVar))
120 
121  # save filter info
122  filter = exposure.getFilter()
123  self._filterDict.setdefault(filter.getName(), filter)
124 
125  self._log.info("Add exposure to coadd with weight=%0.3g", weight)
126 
127  overlapBBox = addToCoadd(self._coadd.getMaskedImage(), self._weightMap,
128  maskedImage, self._badPixelMask, weight)
129 
130  return overlapBBox, weight
131 
132  def getCoadd(self):
133  """Get the coadd exposure for all exposures you have coadded so far
134 
135  If all exposures in this coadd have the same-named filter then that filter is set in the coadd.
136  Otherwise the coadd will have the default unknown filter.
137 
138  @warning: the Calib is not be set.
139  """
140  # make a deep copy so I can scale it
141  coaddMaskedImage = self._coadd.getMaskedImage()
142  scaledMaskedImage = coaddMaskedImage.Factory(coaddMaskedImage, True)
143 
144  # set the edge pixels
145  setCoaddEdgeBits(scaledMaskedImage.getMask(), self._weightMap)
146 
147  # scale non-edge pixels by weight map
148  scaledMaskedImage /= self._weightMap
149 
150  scaledExposure = afwImage.makeExposure(scaledMaskedImage, self._wcs)
151  if len(self._filterDict) == 1:
152  scaledExposure.setFilter(list(self._filterDict.values())[0])
153  return scaledExposure
154 
155  def getFilters(self):
156  """Return a collection of all the filters seen so far in in addExposure
157  """
158  return list(self._filterDict.values())
159 
160  def getBadPixelMask(self):
161  """Return the bad pixel mask
162  """
163  return self._badPixelMask
164 
165  def getBBox(self):
166  """Return the bounding box of the coadd
167  """
168  return self._bbox
169 
170  def getWcs(self):
171  """Return the wcs of the coadd
172  """
173  return self._wcs
174 
175  def getWeightMap(self):
176  """Return the weight map for all exposures you have coadded so far
177 
178  The weight map is a float Image of the same dimensions as the coadd; the value of each pixel
179  is the sum of the weights of all exposures that contributed to that pixel.
180  """
181  return self._weightMap
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
def addExposure(self, exposure, weightFactor=1.0)
Definition: coadd.py:95
def fromConfig(cls, bbox, wcs, config, logName="coadd.utils.Coadd")
Definition: coadd.py:79
def __init__(self, bbox, wcs, badMaskPlanes, logName="coadd.utils.Coadd")
Definition: coadd.py:54