27 from .isrLib
import maskNans
28 from .assembleCcdTask
import AssembleCcdTask
29 from .fringe
import FringeTask
32 doBias = pexConfig.Field(
34 doc =
"Apply bias frame correction?",
37 doDark = pexConfig.Field(
39 doc =
"Apply dark frame correction?",
42 doFlat = pexConfig.Field(
44 doc =
"Apply flat field correction?",
47 doFringe = pexConfig.Field(
49 doc =
"Apply fringe correction?",
52 doWrite = pexConfig.Field(
54 doc =
"Persist postISRCCD?",
57 assembleCcd = pexConfig.ConfigurableField(
58 target = AssembleCcdTask,
59 doc =
"CCD assembly task",
61 fringeAfterFlat = pexConfig.Field(
63 doc =
"Do fringe subtraction after flat-fielding?",
66 fringe = pexConfig.ConfigurableField(
68 doc =
"Fringe subtraction task",
70 fwhm = pexConfig.Field(
72 doc =
"FWHM of PSF (arcsec)",
75 saturatedMaskName = pexConfig.Field(
77 doc =
"Name of mask plane to use in saturation detection and interpolation",
80 flatScalingType = pexConfig.ChoiceField(
82 doc =
"The method for scaling the flat on the fly.",
85 "USER":
"Scale by flatUserScale",
86 "MEAN":
"Scale by the inverse of the mean",
87 "MEDIAN":
"Scale by the inverse of the median",
90 flatUserScale = pexConfig.Field(
92 doc =
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
95 overscanFitType = pexConfig.ChoiceField(
97 doc =
"The method for fitting the overscan bias level.",
100 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
101 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
102 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
103 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
104 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
105 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
106 "MEAN":
"Correct using the mean of the overscan region",
107 "MEDIAN":
"Correct using the median of the overscan region",
110 overscanOrder = pexConfig.Field(
112 doc = (
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
113 "or number of spline knots if overscan fit type is a spline."),
116 overscanRej = pexConfig.Field(
118 doc =
"Rejection threshold (sigma) for collapsing overscan before fit",
121 growSaturationFootprintSize = pexConfig.Field(
123 doc =
"Number of pixels by which to grow the saturation footprints",
126 fluxMag0T1 = pexConfig.Field(
128 doc =
"The approximate flux of a zero-magnitude object in a one-second exposure",
131 setGainAssembledCcd = pexConfig.Field(
133 doc =
"update exposure metadata in the assembled ccd to reflect the effective gain of the assembled chip",
136 keysToRemoveFromAssembledCcd = pexConfig.ListField(
138 doc =
"fields to remove from the metadata of the assembled ccd.",
141 doAssembleDetrends = pexConfig.Field(
144 doc =
"Assemble detrend/calibration frames?"
158 \brief Apply common instrument signature correction algorithms to a raw frame.
160 \section ip_isr_isr_Contents Contents
162 - \ref ip_isr_isr_Purpose
163 - \ref ip_isr_isr_Initialize
165 - \ref ip_isr_isr_Config
166 - \ref ip_isr_isr_Debug
167 - \ref ip_isr_isr_Example
169 \section ip_isr_isr_Purpose Description
171 The process for correcting imaging data is very similar from camera to camera.
172 This task provides a vanilla implementation of doing these corrections, including
173 the ability to turn certain corrections off if they are not needed. The input
174 is a daf.persistence.butlerSubset.ButlerDataRef. The data reference can return
175 the raw input and all the calibration products. The raw input is a single chip
176 sized mosaic of all amps including overscans and other non-science pixels. This
177 task may not meet all needs and it is expected that it will be subclassed for specific
180 \section ip_isr_isr_Initialize Task initialization
182 \copydoc \_\_init\_\_
184 \section ip_isr_isr_IO Inputs/Outputs to the assembleCcd method
188 \section ip_isr_isr_Config Configuration parameters
190 See \ref IsrTaskConfig
192 \section ip_isr_isr_Debug Debug variables
194 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
195 flag \c -d to import \b debug.py from your \c PYTHONPATH; see <a
196 href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
197 Using lsstDebug to control debugging output</a> for more about \b debug.py files.
199 The available variables in AssembleCcdTask are:
202 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
205 <DD> display exposure after ISR has been applied
209 \section ip_isr_isr_Example A complete example of using IsrTask
211 This code is in runIsrTask.py in the examples directory, and can be run as \em e.g.
213 python examples/runIsrTask.py
216 \dontinclude runIsrTask.py
217 Import the task. There are other imports. Read the source file for more info.
220 \dontinclude exampleUtils.py
221 Create the input data reference with the help of some utilities in examples/exampleUtils.py. This
222 is a mock data reference that has all the right methods to run through ISR. We will only
223 do overscan, dark and flat correction, so it only needs to know how to get those products (and an
224 empty list of defects).
227 The above numbers can be changed to modify the gradient in the flat, for example.
229 \dontinclude exampleUtils.py
230 The data are constructed by hand so that all effects will be corrected for essentially perfectly
232 \until return flatExposure
235 \dontinclude runIsrTask.py
236 Construct the task and set some config parameters. Specifically, we don't want to
237 do zero or fringe correction. We also don't want the assembler messing with the gain.
239 \until config=isrConfig
241 Now make the fake data reference and run it through ISR.
246 To investigate the \ref ip_isr_isr_Debug, put something like
250 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
251 if name == "lsst.ip.isr.isrTask":
252 di.display = {'postISRCCD':2}
255 lsstDebug.Info = DebugInfo
257 into your debug.py file and run runAssembleTask.py with the \c --debug flag.
261 Display code should be updated once we settle on a standard way of controlling what is displayed.
263 ConfigClass = IsrTaskConfig
267 '''!Constructor for IsrTask
268 \param[in] *args -- a list of positional arguments passed on to the Task constructor
269 \param[in] **kwargs -- a dictionary of keyword arguments passed on to the Task constructor
270 Call the lsst.pipe.base.task.Task.__init__ method
271 Then setup the assembly and fringe correction subtasks
273 pipeBase.Task.__init__(self, *args, **kwargs)
274 self.makeSubtask(
"assembleCcd")
275 self.makeSubtask(
"fringe")
279 """!Perform instrument signature removal on an exposure
282 - Detect saturation, apply overscan correction, bias, dark and flat
283 - Perform CCD assembly
284 - Interpolate over defects, saturated pixels and all NaNs
285 - Persist the ISR-corrected exposure as "postISRCCD" if config.doWrite is True
287 \param[in] sensorRef -- daf.persistence.butlerSubset.ButlerDataRef of the detector data to be processed
288 \return a pipeBase.Struct with fields:
289 - exposure: the exposure after application of ISR
291 self.log.log(self.log.INFO,
"Performing ISR on sensor %s" % (sensorRef.dataId))
292 ccdExposure = sensorRef.get(
'raw')
293 ccd = ccdExposure.getDetector()
301 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
303 ccd = ccdExposure.getDetector()
305 if self.config.doBias:
308 if self.config.doDark:
312 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
316 if self.config.doFringe
and not self.config.fringeAfterFlat:
317 self.fringe.run(ccdExposure, sensorRef,
318 assembler=self.assembleCcd
if self.config.doAssembleDetrends
else None)
320 if self.config.doFlat:
323 defects = sensorRef.get(
'defects')
330 if self.config.doFringe
and self.config.fringeAfterFlat:
331 self.fringe.run(ccdExposure, sensorRef,
332 assembler=self.assembleCcd
if self.config.doAssembleDetrends
else None)
334 ccdExposure.getCalib().setFluxMag0(self.config.fluxMag0T1 * ccdExposure.getCalib().getExptime())
336 if self.config.doWrite:
337 sensorRef.put(ccdExposure,
"postISRCCD")
339 self.display(
"postISRCCD", ccdExposure)
341 return pipeBase.Struct(
342 exposure = ccdExposure,
346 """Convert an exposure from uint16 to float, set variance plane to 1 and mask plane to 0
348 if isinstance(exposure, afwImage.ExposureF):
351 if not hasattr(exposure,
"convertF"):
352 raise RuntimeError(
"Unable to convert exposure (%s) to float" % type(exposure))
354 newexposure = exposure.convertF()
355 maskedImage = newexposure.getMaskedImage()
356 varArray = maskedImage.getVariance().getArray()
358 maskArray = maskedImage.getMask().getArray()
363 """!Apply bias correction in place
365 \param[in,out] exposure exposure to process
366 \param[in] dataRef data reference at same level as exposure
369 isr.biasCorrection(exposure.getMaskedImage(), bias.getMaskedImage())
372 """!Apply dark correction in place
374 \param[in,out] exposure exposure to process
375 \param[in] dataRef data reference at same level as exposure
377 darkExposure = self.
getDetrend(dataRef,
"dark")
378 darkCalib = darkExposure.getCalib()
380 maskedImage = exposure.getMaskedImage(),
381 darkMaskedImage = darkExposure.getMaskedImage(),
382 expScale = exposure.getCalib().getExptime(),
383 darkScale = darkCalib.getExptime(),
387 """!Set the variance plane based on the image plane, plus amplifier gain and read noise
389 \param[in,out] ampExposure exposure to process
390 \param[in] amp amplifier detector information
393 maskedImage = ampExposure.getMaskedImage(),
394 gain = amp.getGain(),
395 readNoise = amp.getReadNoise(),
399 """!Apply flat correction in place
401 \param[in,out] exposure exposure to process
402 \param[in] dataRef data reference at same level as exposure
406 maskedImage = exposure.getMaskedImage(),
407 flatMaskedImage = flatfield.getMaskedImage(),
408 scalingType = self.config.flatScalingType,
409 userScale = self.config.flatUserScale,
413 """!Get a detrend exposure
415 \param[in] dataRef data reference for exposure
416 \param[in] detrend detrend/calibration to read
417 \param[in] immediate if True, disable butler proxies to enable error
418 handling within this routine
419 \return Detrend exposure
422 exp = dataRef.get(detrend, immediate=immediate)
424 raise RuntimeError(
"Unable to retrieve %s for %s: %s" % (detrend, dataRef.dataId, e))
425 if self.config.doAssembleDetrends:
426 exp = self.assembleCcd.assembleCcd(exp)
430 """!Detect saturated pixels and mask them using mask plane "SAT", in place
432 \param[in,out] exposure exposure to process; only the amp DataSec is processed
433 \param[in] amp amplifier device data
435 maskedImage = exposure.getMaskedImage()
436 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
437 isr.makeThresholdMask(
438 maskedImage = dataView,
439 threshold = amp.getSaturation(),
441 maskName = self.config.saturatedMaskName,
445 """!Interpolate over saturated pixels, in place
447 \param[in,out] ccdExposure exposure to process
450 - Call saturationDetection first, so that saturated pixels have been identified in the "SAT" mask.
451 - Call this after CCD assembly, since saturated regions may cross amplifier boundaries
453 isr.interpolateFromMask(
454 maskedImage = ccdExposure.getMaskedImage(),
455 fwhm = self.config.fwhm,
456 growFootprints = self.config.growSaturationFootprintSize,
457 maskName = self.config.saturatedMaskName,
461 """!Mask defects using mask plane "BAD" and interpolate over them, in place
463 \param[in,out] ccdExposure exposure to process
464 \param[in] defectBaseList a list of defects to mask and interpolate
466 \warning: call this after CCD assembly, since defects may cross amplifier boundaries
468 maskedImage = ccdExposure.getMaskedImage()
469 defectList = measAlg.DefectListT()
470 for d
in defectBaseList:
473 defectList.append(nd)
474 isr.maskPixelsFromDefectList(maskedImage, defectList, maskName=
'BAD')
475 isr.interpolateDefectList(
476 maskedImage = maskedImage,
477 defectList = defectList,
478 fwhm = self.config.fwhm,
482 """!Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place
484 We mask and interpolate over all NaNs, including those
485 that are masked with other bits (because those may or may
486 not be interpolated over later, and we want to remove all
487 NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane
488 is used to preserve the historical name.
490 \param[in,out] exposure exposure to process
492 maskedImage = exposure.getMaskedImage()
495 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
496 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
497 numNans =
maskNans(maskedImage, maskVal)
498 self.metadata.set(
"NUMNANS", numNans)
502 self.log.log(self.log.WARN,
"There were %i unmasked NaNs" % (numNans,))
503 nanDefectList = isr.getDefectListFromMask(
504 maskedImage = maskedImage,
505 maskName =
'UNMASKEDNAN',
508 isr.interpolateDefectList(
509 maskedImage = exposure.getMaskedImage(),
510 defectList = nanDefectList,
511 fwhm = self.config.fwhm,
515 """!Apply overscan correction, in place
517 \param[in,out] exposure exposure to process; must include both DataSec and BiasSec pixels
518 \param[in] amp amplifier device data
520 if not amp.getHasRawInfo():
521 raise RuntimeError(
"This method must be executed on an amp with raw information.")
522 maskedImage = exposure.getMaskedImage()
523 dataView = maskedImage.Factory(maskedImage, amp.getRawDataBBox())
525 expImage = exposure.getMaskedImage().getImage()
526 overscanImage = expImage.Factory(expImage, amp.getRawHorizontalOverscanBBox())
528 isr.overscanCorrection(
529 ampMaskedImage = dataView,
530 overscanImage = overscanImage,
531 fitType = self.config.overscanFitType,
532 order = self.config.overscanOrder,
533 collapseRej = self.config.overscanRej,
def maskAndInterpNan
Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place.
def flatCorrection
Apply flat correction in place.
def run
Perform instrument signature removal on an exposure.
def __init__
Constructor for IsrTask.
Apply common instrument signature correction algorithms to a raw frame.
def updateVariance
Set the variance plane based on the image plane, plus amplifier gain and read noise.
def maskAndInterpDefect
Mask defects using mask plane "BAD" and interpolate over them, in place.
def getDetrend
Get a detrend exposure.
def overscanCorrection
Apply overscan correction, in place.
def saturationDetection
Detect saturated pixels and mask them using mask plane "SAT", in place.
def darkCorrection
Apply dark correction in place.
size_t maskNans(afw::image::MaskedImage< PixelT > const &mi, afw::image::MaskPixel maskVal, afw::image::MaskPixel allow)
Encapsulate information about a bad portion of a detector.
def saturationInterpolation
Interpolate over saturated pixels, in place.
def biasCorrection
Apply bias correction in place.