27 from lsstDebug
import getDebugFrame
29 from .isr
import calcEffectiveGain
31 __all__ = [
"AssembleCcdTask"]
35 setGain = pexConfig.Field(
40 doRenorm = pexConfig.Field(
41 doc=
"renormalize to a gain of 1? (ignored if setGain false). "
42 "Setting to True gives 1 ADU per electron. "
43 "Setting to True is not recommended for mosaic cameras because it breaks normalization across "
44 "the focal plane. However, if the CCDs are sufficiently flat then the resulting error "
49 doTrim = pexConfig.Field(
50 doc=
"trim out non-data regions?",
54 keysToRemove = pexConfig.ListField(
55 doc=
"FITS headers to remove (in addition to DATASEC, BIASSEC, TRIMSEC and perhaps GAIN)",
70 \anchor AssembleCcdTask_
72 \brief Assemble a set of amplifier images into a full detector size set of pixels.
74 \section ip_isr_assemble_Contents Contents
76 - \ref ip_isr_assemble_Purpose
77 - \ref ip_isr_assemble_Initialize
78 - \ref ip_isr_assemble_IO
79 - \ref ip_isr_assemble_Config
80 - \ref ip_isr_assemble_Debug
81 - \ref ip_isr_assemble_Example
83 \section ip_isr_assemble_Purpose Description
85 This task assembles sections of an image into a larger mosaic. The sub-sections
86 are typically amplifier sections and are to be assembled into a detector size pixel grid.
87 The assembly is driven by the entries in the raw amp information. The task can be configured
88 to return a detector image with non-data (e.g. overscan) pixels included. The task can also
89 renormalize the pixel values to a nominal gain of 1. The task also removes exposure metadata that
90 has context in raw amps, but not in trimmed detectors (e.g. 'BIASSEC').
92 \section ip_isr_assemble_Initialize Task initialization
96 \section ip_isr_assemble_IO Inputs/Outputs to the assembleCcd method
100 \section ip_isr_assemble_Config Configuration parameters
102 See \ref AssembleCcdConfig
104 \section ip_isr_assemble_Debug Debug variables
106 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
107 flag \c -d to import \b debug.py from your \c PYTHONPATH; see <a
108 href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
109 Using lsstDebug to control debugging output</a> for more about \b debug.py files.
111 The available variables in AssembleCcdTask are:
114 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
116 <DT> assembledExposure
117 <DD> display assembled exposure
121 \section ip_isr_assemble_Example A complete example of using AssembleCcdTask
123 This code is in runAssembleTask.py in the examples directory, and can be run as \em e.g.
125 python examples/runAssembleTask.py
128 \dontinclude runAssembleTask.py
129 Import the task. There are other imports. Read the source file for more info.
130 \skipline AssembleCcdTask
132 \dontinclude exampleUtils.py
133 Create some input images with the help of some utilities in examples/exampleUtils.py
134 \skip makeAssemblyInput
136 The above numbers can be changed. The assumption that the readout corner is flipped on every other amp is
137 hardcoded in createDetector.
139 \dontinclude runAssembleTask.py
140 Run the assembler task
145 To investigate the \ref ip_isr_assemble_Debug, put something like
149 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
150 if name == "lsst.ip.isr.assembleCcdTask":
151 di.display = {'assembledExposure':2}
154 lsstDebug.Info = DebugInfo
156 into your debug.py file and run runAssembleTask.py with the \c --debug flag.
160 Display code should be updated once we settle on a standard way of controlling what is displayed.
162 ConfigClass = AssembleCcdConfig
163 _DefaultName =
"assembleCcd"
166 """!Initialize the AssembleCcdTask
168 The keys for removal specified in the config are added to a default set:
169 ('DATASEC', 'BIASSEC', 'TRIMSEC', 'GAIN')
171 pipeBase.Task.__init__(self, **kwargs)
173 self.
allKeysToRemove = (
'DATASEC',
'BIASSEC',
'TRIMSEC',
'GAIN') + tuple(self.config.keysToRemove)
176 """!Assemble a set of amps into a single CCD size image
177 \param[in] assembleInput -- Either a dictionary of amp lsst.afw.image.Exposures or a single
178 lsst.afw.image.Exposure containing all raw
179 amps. If a dictionary of amp exposures,
180 the key should be the amp name.
181 \return assembledCcd -- An lsst.afw.image.Exposure of the assembled amp sections.
183 \throws TypeError with the following string:
186 <DT> Expected either a dictionary of amp exposures or a single raw exposure
187 <DD> The input exposures to be assembled do not adhere to the required format.
190 \throws RuntimeError with the following string:
193 <DT> No ccd detector found
194 <DD> The detector set on the input exposure is not set.
198 if hasattr(assembleInput,
"has_key"):
200 ccd = next(assembleInput.values()).getDetector()
203 def getNextExposure(amp):
204 return assembleInput[amp.getName()]
205 elif hasattr(assembleInput,
"getMaskedImage"):
206 ccd = assembleInput.getDetector()
209 def getNextExposure(amp):
212 raise TypeError(
"Expected either a dictionary of amp exposures or a single raw exposure")
215 raise RuntimeError(
"No ccd detector found")
217 if not self.config.doTrim:
218 outBox = cameraGeomUtils.calcRawCcdBBox(ccd)
220 outBox = ccd.getBBox()
221 outExposure = afwImage.ExposureF(outBox)
222 outMI = outExposure.getMaskedImage()
224 if self.config.doTrim:
225 assemble = cameraGeom.assembleAmplifierImage
227 assemble = cameraGeom.assembleAmplifierRawImage
230 inMI = getNextExposure(amp).getMaskedImage()
231 assemble(outMI, inMI, amp)
232 outExposure.setDetector(ccd)
238 """Set exposure non-image attributes, including wcs and metadata and display exposure (if requested)
240 Call after assembling the pixels
242 @param[in,out] outExposure assembled exposure:
243 - removes unwanted keywords
244 - sets calib, filter, and detector
245 @param[in] inExposure input exposure
247 self.
setWcs(outExposure=outExposure, inExposure=inExposure)
249 exposureMetadata = inExposure.getMetadata()
251 if exposureMetadata.exists(key):
252 exposureMetadata.remove(key)
253 outExposure.setMetadata(exposureMetadata)
255 if self.config.setGain:
256 self.
setGain(outExposure=outExposure)
258 inCalib = inExposure.getCalib()
259 outCalib = outExposure.getCalib()
260 outCalib.setExptime(inCalib.getExptime())
261 outCalib.setMidTime(inCalib.getMidTime())
263 outExposure.setFilter(inExposure.getFilter())
269 def setWcs(self, outExposure, inExposure):
270 """Set output WCS = input WCS, adjusted as required for datasecs not starting at lower left corner
272 @param[in,out] outExposure assembled exposure; wcs is set
273 @param[in] inExposure input exposure
275 if inExposure.hasWcs():
276 wcs = inExposure.getWcs()
277 ccd = outExposure.getDetector()
280 raise RuntimeError(
"No amplifier detector information found")
281 cameraGeomUtils.prepareWcsData(wcs, amp0, isTrimmed=self.config.doTrim)
282 outExposure.setWcs(wcs)
284 self.log.warn(
"No WCS found in input exposure")
287 """Renormalize, if requested, and set gain metadata
289 @param[in,out] outExposure assembled exposure:
290 - scales the pixels if config.doRenorm is true
291 - adds some gain keywords to the metadata
293 if outExposure.getMaskedImage().getVariance().getArray().max() == 0:
294 raise RuntimeError(
"Can't calculate the effective gain since the variance plane is set to zero")
295 ccd = outExposure.getDetector()
296 exposureMetadata = outExposure.getMetadata()
300 gain += amp.getGain()
303 if self.config.doRenorm:
304 mi = outExposure.getMaskedImage()
306 exposureMetadata.set(
"GAIN", 1.0)
308 exposureMetadata.add(
"MEDGAIN", medgain)
309 exposureMetadata.add(
"MEANGAIN", meangain)
310 exposureMetadata.add(
"GAINEFF", medgain)
Assemble a set of amplifier images into a full detector size set of pixels.
def assembleCcd
Assemble a set of amps into a single CCD size image.
def __init__
Initialize the AssembleCcdTask.