lsst.pipe.tasks  13.0-29-g7046ce1+1
 All Classes Namespaces Files Functions Variables Groups Pages
coaddBase.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 AURA/LSST.
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 numpy
24 
25 import lsst.pex.config as pexConfig
26 import lsst.afw.geom as afwGeom
27 import lsst.afw.image as afwImage
28 import lsst.pipe.base as pipeBase
29 import lsst.meas.algorithms as measAlg
30 
31 from lsst.afw.fits import FitsError
32 from lsst.coadd.utils import CoaddDataIdContainer
33 from .selectImages import WcsSelectImagesTask, SelectStruct
34 from .coaddInputRecorder import CoaddInputRecorderTask
35 
36 try:
37  from lsst.meas.mosaic import applyMosaicResults
38 except ImportError:
39  applyMosaicResults = None
40 
41 __all__ = ["CoaddBaseTask", "getSkyInfo"]
42 
43 
44 class CoaddBaseConfig(pexConfig.Config):
45  """Config for CoaddBaseTask
46  """
47  coaddName = pexConfig.Field(
48  doc="Coadd name: typically one of deep or goodSeeing.",
49  dtype=str,
50  default="deep",
51  )
52  select = pexConfig.ConfigurableField(
53  doc="Image selection subtask.",
54  target=WcsSelectImagesTask,
55  )
56  badMaskPlanes = pexConfig.ListField(
57  dtype=str,
58  doc="Mask planes that, if set, the associated pixel should not be included in the coaddTempExp.",
59  default=("NO_DATA",),
60  )
61  inputRecorder = pexConfig.ConfigurableField(
62  doc="Subtask that helps fill CoaddInputs catalogs added to the final Exposure",
63  target=CoaddInputRecorderTask
64  )
65  doPsfMatch = pexConfig.Field(dtype=bool, doc="Match to modelPsf?", default=False)
66  modelPsf = measAlg.GaussianPsfFactory.makeField(doc="Model Psf factory")
67  doApplyUberCal = pexConfig.Field(
68  dtype=bool,
69  doc="Apply meas_mosaic ubercal results to input calexps?",
70  default=False
71  )
72 
73 
74 class CoaddTaskRunner(pipeBase.TaskRunner):
75 
76  @staticmethod
77  def getTargetList(parsedCmd, **kwargs):
78  return pipeBase.TaskRunner.getTargetList(parsedCmd, selectDataList=parsedCmd.selectId.dataList,
79  **kwargs)
80 
81 
82 class CoaddBaseTask(pipeBase.CmdLineTask):
83  """Base class for coaddition.
84 
85  Subclasses must specify _DefaultName
86  """
87  ConfigClass = CoaddBaseConfig
88  RunnerClass = CoaddTaskRunner
89 
90  def __init__(self, *args, **kwargs):
91  pipeBase.Task.__init__(self, *args, **kwargs)
92  self.makeSubtask("select")
93  self.makeSubtask("inputRecorder")
94 
95  def selectExposures(self, patchRef, skyInfo=None, selectDataList=[]):
96  """!
97  \brief Select exposures to coadd
98 
99  Get the corners of the bbox supplied in skyInfo using \ref afwGeom.Box2D and convert the pixel
100  positions of the bbox corners to sky coordinates using \ref skyInfo.wcs.pixelToSky. Use the
101  \ref WcsSelectImagesTask_ "WcsSelectImagesTask" to select exposures that lie inside the patch
102  indicated by the dataRef.
103 
104  \param[in] patchRef data reference for sky map patch. Must include keys "tract", "patch",
105  plus the camera-specific filter key (e.g. "filter" or "band")
106  \param[in] skyInfo geometry for the patch; output from getSkyInfo
107  \return a list of science exposures to coadd, as butler data references
108  """
109  if skyInfo is None:
110  skyInfo = self.getSkyInfo(patchRef)
111  cornerPosList = afwGeom.Box2D(skyInfo.bbox).getCorners()
112  coordList = [skyInfo.wcs.pixelToSky(pos) for pos in cornerPosList]
113  return self.select.runDataRef(patchRef, coordList, selectDataList=selectDataList).dataRefList
114 
115  def getSkyInfo(self, patchRef):
116  """!
117  \brief Use \ref getSkyinfo to return the skyMap, tract and patch information, wcs and the outer bbox
118  of the patch.
119 
120  \param[in] patchRef data reference for sky map. Must include keys "tract" and "patch"
121 
122  \return pipe_base Struct containing:
123  - skyMap: sky map
124  - tractInfo: information for chosen tract of sky map
125  - patchInfo: information about chosen patch of tract
126  - wcs: WCS of tract
127  - bbox: outer bbox of patch, as an afwGeom Box2I
128  """
129  return getSkyInfo(coaddName=self.config.coaddName, patchRef=patchRef)
130 
131  def getCalExp(self, dataRef, bgSubtracted):
132  """!Return one "calexp" calibrated exposure
133 
134  @param[in] dataRef a sensor-level data reference
135  @param[in] bgSubtracted return calexp with background subtracted? If False get the
136  calexp's background background model and add it to the calexp.
137  @return calibrated exposure
138 
139  If config.doApplyUberCal, meas_mosaic calibrations will be applied to
140  the returned exposure using applyMosaicResults.
141  """
142  exposure = dataRef.get("calexp", immediate=True)
143  if not bgSubtracted:
144  background = dataRef.get("calexpBackground", immediate=True)
145  mi = exposure.getMaskedImage()
146  mi += background.getImage()
147  del mi
148  if not self.config.doApplyUberCal:
149  return exposure
150  if applyMosaicResults is None:
151  raise RuntimeError(
152  "Cannot use improved calibrations for %s because meas_mosaic could not be imported."
153  % dataRef.dataId
154  )
155  else:
156  applyMosaicResults(dataRef, calexp=exposure)
157  return exposure
158 
160  return self.config.coaddName + "Coadd"
161 
163  return self.config.coaddName + "Coadd_tempExp"
164 
165  @classmethod
166  def _makeArgumentParser(cls):
167  """Create an argument parser
168  """
169  parser = pipeBase.ArgumentParser(name=cls._DefaultName)
170  parser.add_id_argument("--id", "deepCoadd", help="data ID, e.g. --id tract=12345 patch=1,2",
171  ContainerClass=CoaddDataIdContainer)
172  parser.add_id_argument("--selectId", "calexp", help="data ID, e.g. --selectId visit=6789 ccd=0..9",
173  ContainerClass=SelectDataIdContainer)
174  return parser
175 
176  def _getConfigName(self):
177  """Return the name of the config dataset
178  """
179  return "%s_%s_config" % (self.config.coaddName, self._DefaultName)
180 
181  def _getMetadataName(self):
182  """Return the name of the metadata dataset
183  """
184  return "%s_%s_metadata" % (self.config.coaddName, self._DefaultName)
185 
186  def getBadPixelMask(self):
187  """!
188  \brief Convenience method to provide the bitmask from the mask plane names
189  """
190  return afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes)
191 
192  def writeCoaddOutput(self, dataRef, obj, suffix=None):
193  """!
194  \brief Write a coadd product through the butler
195 
196  \param[in] dataRef data reference for coadd
197  \param[in,out] obj coadd product to write
198  \param[in] suffix suffix to apply to coadd dataset name
199  """
200  objName = self.getCoaddDatasetName()
201  if suffix is not None:
202  objName += "_" + suffix
203  self.log.info("Persisting %s" % objName)
204  dataRef.put(obj, objName)
205 
206 
207 class SelectDataIdContainer(pipeBase.DataIdContainer):
208  """!
209  \brief A dataId container for inputs to be selected.
210 
211  Read the header (including the size and Wcs) for all specified
212  inputs and pass those along, ultimately for the SelectImagesTask.
213  This is most useful when used with multiprocessing, as input headers are
214  only read once.
215  """
216 
217  def makeDataRefList(self, namespace):
218  """Add a dataList containing useful information for selecting images"""
219  super(SelectDataIdContainer, self).makeDataRefList(namespace)
220  self.dataList = []
221  for ref in self.refList:
222  try:
223  md = ref.get("calexp_md", immediate=True)
224  wcs = afwImage.makeWcs(md)
225  data = SelectStruct(dataRef=ref, wcs=wcs, dims=(md.get("NAXIS1"), md.get("NAXIS2")))
226  except FitsError as e:
227  namespace.log.warn("Unable to construct Wcs from %s" % (ref.dataId))
228  continue
229  self.dataList.append(data)
230 
231 
232 def getSkyInfo(coaddName, patchRef):
233  """!
234  \brief Return the SkyMap, tract and patch information, wcs, and outer bbox of the patch to be coadded.
235 
236  \param[in] coaddName coadd name; typically one of deep or goodSeeing
237  \param[in] patchRef data reference for sky map. Must include keys "tract" and "patch"
238 
239  \return pipe_base Struct containing:
240  - skyMap: sky map
241  - tractInfo: information for chosen tract of sky map
242  - patchInfo: information about chosen patch of tract
243  - wcs: WCS of tract
244  - bbox: outer bbox of patch, as an afwGeom Box2I
245  """
246  skyMap = patchRef.get(coaddName + "Coadd_skyMap")
247  tractId = patchRef.dataId["tract"]
248  tractInfo = skyMap[tractId]
249 
250  # patch format is "xIndex,yIndex"
251  patchIndex = tuple(int(i) for i in patchRef.dataId["patch"].split(","))
252  patchInfo = tractInfo.getPatchInfo(patchIndex)
253 
254  return pipeBase.Struct(
255  skyMap=skyMap,
256  tractInfo=tractInfo,
257  patchInfo=patchInfo,
258  wcs=tractInfo.getWcs(),
259  bbox=patchInfo.getOuterBBox(),
260  )
261 
262 
263 def scaleVariance(maskedImage, maskPlanes, log=None):
264  """!
265  \brief Scale the variance in a maskedImage
266 
267  The variance plane in a convolved or warped image (or a coadd derived
268  from warped images) does not accurately reflect the noise properties of
269  the image because variance has been lost to covariance. This function
270  attempts to correct for this by scaling the variance plane to match
271  the observed variance in the image. This is not perfect (because we're
272  not tracking the covariance) but it's simple and is often good enough.
273 
274  @param maskedImage MaskedImage to operate on; variance will be scaled
275  @param maskPlanes List of mask planes for pixels to reject
276  @param log Log for reporting the renormalization factor; or None
277  @return renormalisation factor
278  """
279  variance = maskedImage.getVariance()
280  sigNoise = maskedImage.getImage().getArray()/numpy.sqrt(variance.getArray())
281  maskVal = maskedImage.getMask().getPlaneBitMask(maskPlanes)
282  good = (maskedImage.getMask().getArray() & maskVal) == 0
283  # Robust measurement of stdev
284  q1, q3 = numpy.percentile(sigNoise[good], (25, 75))
285  stdev = 0.74*(q3 - q1)
286  ratio = stdev**2
287  if log:
288  log.info("Renormalizing variance by %f" % (ratio,))
289  variance *= ratio
290  return ratio
def getBadPixelMask
Convenience method to provide the bitmask from the mask plane names.
Definition: coaddBase.py:186
A dataId container for inputs to be selected.
Definition: coaddBase.py:207
def getCalExp
Return one &quot;calexp&quot; calibrated exposure.
Definition: coaddBase.py:131
def scaleVariance
Scale the variance in a maskedImage.
Definition: coaddBase.py:263
def selectExposures
Select exposures to coadd.
Definition: coaddBase.py:95
def writeCoaddOutput
Write a coadd product through the butler.
Definition: coaddBase.py:192
def getSkyInfo
Use getSkyinfo to return the skyMap, tract and patch information, wcs and the outer bbox of the patch...
Definition: coaddBase.py:115
def getSkyInfo
Return the SkyMap, tract and patch information, wcs, and outer bbox of the patch to be coadded...
Definition: coaddBase.py:232