LSSTApplications  18.0.0+106,18.0.0+50,19.0.0,19.0.0+1,19.0.0+10,19.0.0+11,19.0.0+13,19.0.0+17,19.0.0+2,19.0.0-1-g20d9b18+6,19.0.0-1-g425ff20,19.0.0-1-g5549ca4,19.0.0-1-g580fafe+6,19.0.0-1-g6fe20d0+1,19.0.0-1-g7011481+9,19.0.0-1-g8c57eb9+6,19.0.0-1-gb5175dc+11,19.0.0-1-gdc0e4a7+9,19.0.0-1-ge272bc4+6,19.0.0-1-ge3aa853,19.0.0-10-g448f008b,19.0.0-12-g6990b2c,19.0.0-2-g0d9f9cd+11,19.0.0-2-g3d9e4fb2+11,19.0.0-2-g5037de4,19.0.0-2-gb96a1c4+3,19.0.0-2-gd955cfd+15,19.0.0-3-g2d13df8,19.0.0-3-g6f3c7dc,19.0.0-4-g725f80e+11,19.0.0-4-ga671dab3b+1,19.0.0-4-gad373c5+3,19.0.0-5-ga2acb9c+2,19.0.0-5-gfe96e6c+2,w.2020.01
LSSTDataManagementBasePackage
assembleCcdTask.py
Go to the documentation of this file.
1 # This file is part of ip_isr.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 import lsst.afw.cameraGeom as cameraGeom
23 import lsst.afw.cameraGeom.utils as cameraGeomUtils
24 import lsst.afw.display as afwDisplay
25 import lsst.afw.image as afwImage
26 import lsst.pex.config as pexConfig
27 import lsst.pipe.base as pipeBase
28 from lsstDebug import getDebugFrame
29 
30 __all__ = ["AssembleCcdTask"]
31 
32 
33 class AssembleCcdConfig(pexConfig.Config):
34  doTrim = pexConfig.Field(
35  doc="trim out non-data regions?",
36  dtype=bool,
37  default=True,
38  )
39  keysToRemove = pexConfig.ListField(
40  doc="FITS headers to remove (in addition to DATASEC, BIASSEC, TRIMSEC and perhaps GAIN)",
41  dtype=str,
42  default=(),
43  )
44 
45 
51 
52 
53 class AssembleCcdTask(pipeBase.Task):
54  r"""!
55  @anchor AssembleCcdTask_
56 
57  @brief Assemble a set of amplifier images into a full detector size set of pixels.
58 
59  @section ip_isr_assemble_Contents Contents
60 
61  - @ref ip_isr_assemble_Purpose
62  - @ref ip_isr_assemble_Initialize
63  - @ref ip_isr_assemble_IO
64  - @ref ip_isr_assemble_Config
65  - @ref ip_isr_assemble_Debug
66  - @ref ip_isr_assemble_Example
67 
68  @section ip_isr_assemble_Purpose Description
69 
70  This task assembles sections of an image into a larger mosaic. The sub-sections
71  are typically amplifier sections and are to be assembled into a detector size pixel grid.
72  The assembly is driven by the entries in the raw amp information. The task can be configured
73  to return a detector image with non-data (e.g. overscan) pixels included. The task can also
74  renormalize the pixel values to a nominal gain of 1. The task also removes exposure metadata that
75  has context in raw amps, but not in trimmed detectors (e.g. 'BIASSEC').
76 
77  @section ip_isr_assemble_Initialize Task initialization
78 
79  @copydoc \_\_init\_\_
80 
81  @section ip_isr_assemble_IO Inputs/Outputs to the assembleCcd method
82 
83  @copydoc assembleCcd
84 
85  @section ip_isr_assemble_Config Configuration parameters
86 
87  See @ref AssembleCcdConfig
88 
89  @section ip_isr_assemble_Debug Debug variables
90 
91  The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a
92  flag @c -d to import @b debug.py from your @c PYTHONPATH; see <a
93  href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
94  Using lsstDebug to control debugging output</a> for more about @b debug.py files.
95 
96  The available variables in AssembleCcdTask are:
97  <DL>
98  <DT> @c display
99  <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
100  <DL>
101  <DT> assembledExposure
102  <DD> display assembled exposure
103  </DL>
104  </DL>
105 
106  @section ip_isr_assemble_Example A complete example of using AssembleCcdTask
107 
108  <HR>
109  To investigate the @ref ip_isr_assemble_Debug, put something like
110  @code{.py}
111  import lsstDebug
112  def DebugInfo(name):
113  di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
114  if name == "lsst.ip.isr.assembleCcdTask":
115  di.display = {'assembledExposure':2}
116  return di
117 
118  lsstDebug.Info = DebugInfo
119  @endcode
120  into your debug.py file and run runAssembleTask.py with the @c --debug flag.
121 
122 
123  Conversion notes:
124  Display code should be updated once we settle on a standard way of controlling what is displayed.
125  """
126  ConfigClass = AssembleCcdConfig
127  _DefaultName = "assembleCcd"
128 
129  def __init__(self, **kwargs):
130  """!Initialize the AssembleCcdTask
131 
132  The keys for removal specified in the config are added to a default set:
133  ('DATASEC', 'BIASSEC', 'TRIMSEC', 'GAIN')
134  """
135  pipeBase.Task.__init__(self, **kwargs)
136 
137  self.allKeysToRemove = ('DATASEC', 'BIASSEC', 'TRIMSEC', 'GAIN') + tuple(self.config.keysToRemove)
138 
139  def assembleCcd(self, assembleInput):
140  """!Assemble a set of amps into a single CCD size image
141  @param[in] assembleInput -- Either a dictionary of amp lsst.afw.image.Exposures or a single
142  lsst.afw.image.Exposure containing all raw
143  amps. If a dictionary of amp exposures,
144  the key should be the amp name.
145  @return assembledCcd -- An lsst.afw.image.Exposure of the assembled amp sections.
146 
147  @throws TypeError with the following string:
148 
149  <DL>
150  <DT> Expected either a dictionary of amp exposures or a single raw exposure
151  <DD> The input exposures to be assembled do not adhere to the required format.
152  </DL>
153 
154  @throws RuntimeError with the following string:
155 
156  <DL>
157  <DT> No ccd detector found
158  <DD> The detector set on the input exposure is not set.
159  </DL>
160  """
161  ccd = None
162  if isinstance(assembleInput, dict):
163  # assembleInput is a dictionary of amp name: amp exposure
164 
165  # Assume all amps have the same detector, so get the detector from an arbitrary amp
166  ccd = next(iter(assembleInput.values())).getDetector()
167 
168  def getNextExposure(amp):
169  return assembleInput[amp.getName()]
170  elif hasattr(assembleInput, "getMaskedImage"):
171  # assembleInput is a single exposure
172  ccd = assembleInput.getDetector()
173 
174  def getNextExposure(amp):
175  return assembleInput
176  else:
177  raise TypeError("Expected either a dictionary of amp exposures or a single raw exposure")
178 
179  if ccd is None:
180  raise RuntimeError("No ccd detector found")
181 
182  if not self.config.doTrim:
183  outBox = cameraGeomUtils.calcRawCcdBBox(ccd)
184  else:
185  outBox = ccd.getBBox()
186  outExposure = afwImage.ExposureF(outBox)
187  outMI = outExposure.getMaskedImage()
188 
189  if self.config.doTrim:
190  assemble = cameraGeom.assembleAmplifierImage
191  else:
192  assemble = cameraGeom.assembleAmplifierRawImage
193 
194  for amp in ccd:
195  inMI = getNextExposure(amp).getMaskedImage()
196  assemble(outMI, inMI, amp)
197  #
198  # If we are returning an "untrimmed" image (with overscans and extended register) we
199  # need to update the ampInfo table in the Detector as we've moved the amp images into
200  # place in a single Detector image
201  #
202  if not self.config.doTrim:
203  ccd = cameraGeom.makeUpdatedDetector(ccd)
204 
205  outExposure.setDetector(ccd)
206  self.postprocessExposure(outExposure=outExposure, inExposure=getNextExposure(ccd[0]))
207 
208  return outExposure
209 
210  def postprocessExposure(self, outExposure, inExposure):
211  """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)
212 
213  Call after assembling the pixels
214 
215  @param[in,out] outExposure assembled exposure:
216  - removes unwanted keywords
217  - sets photoCalib, filter, and detector
218  @param[in] inExposure input exposure
219  """
220  self.setWcs(outExposure=outExposure, inExposure=inExposure)
221 
222  exposureMetadata = inExposure.getMetadata()
223  for key in self.allKeysToRemove:
224  if exposureMetadata.exists(key):
225  exposureMetadata.remove(key)
226  outExposure.setMetadata(exposureMetadata)
227 
228  # note: don't copy PhotoCalib, because it is assumed to be unknown in raw data
229  outExposure.setFilter(inExposure.getFilter())
230  outExposure.getInfo().setVisitInfo(inExposure.getInfo().getVisitInfo())
231 
232  frame = getDebugFrame(self._display, "assembledExposure")
233  if frame:
234  afwDisplay.Display(frame=frame).mtv(outExposure, title="postprocessExposure")
235 
236  def setWcs(self, outExposure, inExposure):
237  """Set output WCS = input WCS, adjusted as required for datasecs not starting at lower left corner
238 
239  @param[in,out] outExposure assembled exposure; wcs is set
240  @param[in] inExposure input exposure
241  """
242  if inExposure.hasWcs():
243  wcs = inExposure.getWcs()
244  ccd = outExposure.getDetector()
245  amp0 = ccd[0]
246  if amp0 is None:
247  raise RuntimeError("No amplifier detector information found")
248  adjustedWcs = cameraGeomUtils.prepareWcsData(wcs, amp0, isTrimmed=self.config.doTrim)
249  outExposure.setWcs(adjustedWcs)
Assemble a set of amplifier images into a full detector size set of pixels.
def setWcs(self, outExposure, inExposure)
def __init__(self, kwargs)
Initialize the AssembleCcdTask.
def mtv(data, frame=None, title="", wcs=None, args, kwargs)
Definition: ds9.py:93
def getDebugFrame(debugDisplay, name)
Definition: lsstDebug.py:90
def postprocessExposure(self, outExposure, inExposure)
def assembleCcd(self, assembleInput)
Assemble a set of amps into a single CCD size image.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...