LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
isrTask.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 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 import math
23 import lsst.afw.geom as afwGeom
24 import lsst.afw.image as afwImage
25 import lsst.meas.algorithms as measAlg
26 import lsst.pex.config as pexConfig
27 import lsst.pipe.base as pipeBase
28 from . import isr
29 from .isrLib import maskNans
30 from .assembleCcdTask import AssembleCcdTask
31 from .fringe import FringeTask
32 
33 class IsrTaskConfig(pexConfig.Config):
34  doBias = pexConfig.Field(
35  dtype = bool,
36  doc = "Apply bias frame correction?",
37  default = True,
38  )
39  doDark = pexConfig.Field(
40  dtype = bool,
41  doc = "Apply dark frame correction?",
42  default = True,
43  )
44  doFlat = pexConfig.Field(
45  dtype = bool,
46  doc = "Apply flat field correction?",
47  default = True,
48  )
49  doFringe = pexConfig.Field(
50  dtype = bool,
51  doc = "Apply fringe correction?",
52  default = True,
53  )
54  doWrite = pexConfig.Field(
55  dtype = bool,
56  doc = "Persist postISRCCD?",
57  default = True,
58  )
59  assembleCcd = pexConfig.ConfigurableField(
60  target = AssembleCcdTask,
61  doc = "CCD assembly task",
62  )
63  gain = pexConfig.Field(
64  dtype = float,
65  doc = "The gain to use if no Detector is present in the Exposure (ignored if NaN)",
66  default = float("NaN"),
67  )
68  readNoise = pexConfig.Field(
69  dtype = float,
70  doc = "The read noise to use if no Detector is present in the Exposure",
71  default = 0.0,
72  )
73  saturation = pexConfig.Field(
74  dtype = float,
75  doc = "The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
76  default = float("NaN"),
77  )
78  fringeAfterFlat = pexConfig.Field(
79  dtype = bool,
80  doc = "Do fringe subtraction after flat-fielding?",
81  default = True,
82  )
83  fringe = pexConfig.ConfigurableField(
84  target = FringeTask,
85  doc = "Fringe subtraction task",
86  )
87  fwhm = pexConfig.Field(
88  dtype = float,
89  doc = "FWHM of PSF (arcsec)",
90  default = 1.0,
91  )
92  saturatedMaskName = pexConfig.Field(
93  dtype = str,
94  doc = "Name of mask plane to use in saturation detection and interpolation",
95  default = "SAT",
96  )
97  flatScalingType = pexConfig.ChoiceField(
98  dtype = str,
99  doc = "The method for scaling the flat on the fly.",
100  default = 'USER',
101  allowed = {
102  "USER": "Scale by flatUserScale",
103  "MEAN": "Scale by the inverse of the mean",
104  "MEDIAN": "Scale by the inverse of the median",
105  },
106  )
107  flatUserScale = pexConfig.Field(
108  dtype = float,
109  doc = "If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
110  default = 1.0,
111  )
112  overscanFitType = pexConfig.ChoiceField(
113  dtype = str,
114  doc = "The method for fitting the overscan bias level.",
115  default = 'MEDIAN',
116  allowed = {
117  "POLY": "Fit ordinary polynomial to the longest axis of the overscan region",
118  "CHEB": "Fit Chebyshev polynomial to the longest axis of the overscan region",
119  "LEG": "Fit Legendre polynomial to the longest axis of the overscan region",
120  "NATURAL_SPLINE": "Fit natural spline to the longest axis of the overscan region",
121  "CUBIC_SPLINE": "Fit cubic spline to the longest axis of the overscan region",
122  "AKIMA_SPLINE": "Fit Akima spline to the longest axis of the overscan region",
123  "MEAN": "Correct using the mean of the overscan region",
124  "MEDIAN": "Correct using the median of the overscan region",
125  },
126  )
127  overscanOrder = pexConfig.Field(
128  dtype = int,
129  doc = ("Order of polynomial or to fit if overscan fit type is a polynomial, " +
130  "or number of spline knots if overscan fit type is a spline."),
131  default = 1,
132  )
133  overscanRej = pexConfig.Field(
134  dtype = float,
135  doc = "Rejection threshold (sigma) for collapsing overscan before fit",
136  default = 3.0,
137  )
138  growSaturationFootprintSize = pexConfig.Field(
139  dtype = int,
140  doc = "Number of pixels by which to grow the saturation footprints",
141  default = 1,
142  )
143  fluxMag0T1 = pexConfig.Field(
144  dtype = float,
145  doc = "The approximate flux of a zero-magnitude object in a one-second exposure",
146  default = 1e10,
147  )
148  setGainAssembledCcd = pexConfig.Field(
149  dtype = bool,
150  doc = "update exposure metadata in the assembled ccd to reflect the effective gain of the assembled chip",
151  default = True,
152  )
153  keysToRemoveFromAssembledCcd = pexConfig.ListField(
154  dtype = str,
155  doc = "fields to remove from the metadata of the assembled ccd.",
156  default = [],
157  )
158  doAssembleIsrExposures = pexConfig.Field(
159  dtype = bool,
160  default = False,
161  doc = "Assemble amp-level calibration exposures into ccd-level exposure?"
162  )
163  doAssembleCcd = pexConfig.Field(
164  dtype = bool,
165  default = True,
166  doc = "Assemble amp-level exposures into a ccd-level exposure?"
167  )
168 
169 ## \addtogroup LSST_task_documentation
170 ## \{
171 ## \page IsrTask
172 ## \ref IsrTask_ "IsrTask"
173 ## \copybrief IsrTask
174 ## \}
175 
176 class IsrTask(pipeBase.CmdLineTask):
177  """!
178  \anchor IsrTask_
179 
180  \brief Apply common instrument signature correction algorithms to a raw frame.
181 
182  \section ip_isr_isr_Contents Contents
183 
184  - \ref ip_isr_isr_Purpose
185  - \ref ip_isr_isr_Initialize
186  - \ref ip_isr_isr_IO
187  - \ref ip_isr_isr_Config
188  - \ref ip_isr_isr_Debug
189  - \ref ip_isr_isr_Example
190 
191  \section ip_isr_isr_Purpose Description
192 
193  The process for correcting imaging data is very similar from camera to camera.
194  This task provides a vanilla implementation of doing these corrections, including
195  the ability to turn certain corrections off if they are not needed.
196  The inputs to the primary method, run, are a raw exposure to be corrected and the
197  calibration data products. The raw input is a single chip sized mosaic of all amps
198  including overscans and other non-science pixels.
199  The method runDataRef() is intended for use by a lsst.pipe.base.cmdLineTask.CmdLineTask
200  and takes as input only a daf.persistence.butlerSubset.ButlerDataRef.
201  This task may not meet all needs and it is expected that it will be subclassed for
202  specific applications.
203 
204  \section ip_isr_isr_Initialize Task initialization
205 
206  \copydoc \_\_init\_\_
207 
208  \section ip_isr_isr_IO Inputs/Outputs to the run method
209 
210  \copydoc run
211 
212  \section ip_isr_isr_Config Configuration parameters
213 
214  See \ref IsrTaskConfig
215 
216  \section ip_isr_isr_Debug Debug variables
217 
218  The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
219  flag \c --debug, \c -d to import \b debug.py from your \c PYTHONPATH; see <a
220  href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
221  Using lsstDebug to control debugging output</a> for more about \b debug.py files.
222 
223  The available variables in IsrTask are:
224  <DL>
225  <DT> \c display
226  <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
227  <DL>
228  <DT> postISRCCD
229  <DD> display exposure after ISR has been applied
230  </DL>
231  </DL>
232 
233  For example, put something like
234  \code{.py}
235  import lsstDebug
236  def DebugInfo(name):
237  di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
238  if name == "lsst.ip.isr.isrTask":
239  di.display = {'postISRCCD':2}
240  return di
241  lsstDebug.Info = DebugInfo
242  \endcode
243  into your debug.py file and run the commandline task with the \c --debug flag.
244 
245  \section ip_isr_isr_Example A complete example of using IsrTask
246 
247  This code is in runIsrTask.py in the examples directory and can be run as \em e.g.
248  \code
249  python examples/runIsrTask.py
250 
251  # optional arguments:
252  # --debug, -d Load debug.py?
253  # --ds9 Display the result?
254  # --write Write the result?
255  \endcode
256 
257  <HR>
258  Stepping through the example:
259 
260  \dontinclude runIsrTask.py
261  Import the task. There are other imports. Read the source file for more info.
262  \skipline IsrTask
263  \skipline exampleUtils
264 
265  Create the raw input data with the help of some utilities in \link exampleUtils.py \endlink
266  also in the examples directory.
267  \dontinclude runIsrTask.py
268  We will only do overscan, dark and flat correction.
269  The data are constructed by hand so that all effects will be corrected for essentially perfectly.
270  \skip DARKVAL
271  \until rawExposure
272  The above numbers can be changed to modify the gradient in the flat, for example.
273  For the parameters in this particular example,
274  the image after ISR will be a constant 5000 counts
275  (with some variation in floating point represenation).
276 
277  \note Alternatively, images can be read from disk, either using the Butler or manually:
278  \code
279  import lsst.afw.image as afwImage
280  darkExposure = afwImage.ExposureF("/path/to/dark.fits")
281  flatExposure = afwImage.ExposureF("/path/to/flat.fits")
282  rawExposure = afwImage.ExposureF("/path/to/raw.fits")
283  \endcode
284  In order to perform overscanCorrection IsrTask.run() requires Exposures which have
285  a \link lsst.afw.cameraGeom.Detector \endlink.
286  Detector objects describe details such as data dimensions, number of amps,
287  orientation and overscan dimensions.
288  If requesting images from the Butler, Exposures will automatically have detector information.
289  If running IsrTask on arbitrary images from a camera without an obs_ package,
290  a lsst.afw.cameraGeom.Detector can be generated using lsst.afw.cameraGeom.fitsUtils.DetectorBuilder
291  and set by calling
292  \code
293  rawExposure.setDetector(myDetectorObject)
294  \endcode
295  See \link lsst.afw.cameraGeom.fitsUtils.DetectorBuilder \endlink for more details.
296 
297  \note The updateVariance and saturationDetection steps are not run for Exposures
298  without a \link lsst.afw.cameraGeom.Detector \endlink, unless \ref IsrTaskConfig.gain,
299  \ref IsrTaskConfig.readNoise,
300  and/or \ref IsrTaskConfig.saturation
301  are set in the config, in which case they are applied to the entire image rather than per amp.
302 
303  \dontinclude runIsrTask.py
304  Construct the task and set some config parameters. Specifically, we don't want to
305  do zero or fringe correction. We also don't want the assembler messing with the gain.
306  \skip Create
307  \until config=isrConfig
308 
309  Finally, run the exposures through ISR.
310  \skipline isrTask.run
311 
312  <HR>
313  """
314  ConfigClass = IsrTaskConfig
315  _DefaultName = "isr"
316 
317  def __init__(self, *args, **kwargs):
318  '''!Constructor for IsrTask
319  \param[in] *args -- a list of positional arguments passed on to the Task constructor
320  \param[in] **kwargs -- a dictionary of keyword arguments passed on to the Task constructor
321  Call the lsst.pipe.base.task.Task.__init__ method
322  Then setup the assembly and fringe correction subtasks
323  '''
324  pipeBase.Task.__init__(self, *args, **kwargs)
325  self.makeSubtask("assembleCcd")
326  self.makeSubtask("fringe")
327 
328 
329  def readIsrData(self, dataRef):
330  """!Retrieve necessary frames for instrument signature removal
331  \param[in] dataRef -- a daf.persistence.butlerSubset.ButlerDataRef
332  of the detector data to be processed
333  \return a pipeBase.Struct with fields containing kwargs expected by run()
334  - bias: exposure of bias frame
335  - dark: exposure of dark frame
336  - flat: exposure of flat field
337  - defects: list of detects
338  - fringes: exposure of fringe frame or list of fringe exposure
339  """
340  biasExposure = self.getIsrExposure(dataRef, "bias") if self.config.doBias else None
341  darkExposure = self.getIsrExposure(dataRef, "dark") if self.config.doDark else None
342  flatExposure = self.getIsrExposure(dataRef, "flat") if self.config.doFlat else None
343 
344  defectList = dataRef.get("defects")
345 
346  if self.config.doFringe:
347  fringes = self.fringe.readFringes(dataRef, assembler=self.assembleCcd \
348  if self.config.doAssembleIsrExposures else None)
349  else:
350  fringes = None
351 
352  #Struct should include only kwargs to run()
353  return pipeBase.Struct(bias = biasExposure,
354  dark = darkExposure,
355  flat = flatExposure,
356  defects = defectList,
357  fringes = fringes,
358  )
359 
360  @pipeBase.timeMethod
361  def run(self, ccdExposure, bias=None, dark=None, flat=None, defects=None, fringes=None):
362  """!Perform instrument signature removal on an exposure
363 
364  Steps include:
365  - Detect saturation, apply overscan correction, bias, dark and flat
366  - Perform CCD assembly
367  - Interpolate over defects, saturated pixels and all NaNs
368 
369  \param[in] ccdExposure -- lsst.afw.image.exposure of detector data
370  \param[in] bias -- exposure of bias frame
371  \param[in] dark -- exposure of dark frame
372  \param[in] flat -- exposure of flatfield
373  \param[in] defects -- list of detects
374  \param[in] fringes -- exposure of fringe frame or list of fringe exposure
375 
376  \return a pipeBase.Struct with field:
377  - exposure
378  """
379 
380  #Validate Input
381  if self.config.doBias and bias is None:
382  raise RuntimeError("Must supply a bias exposure if config.doBias True")
383  if self.config.doDark and dark is None:
384  raise RuntimeError("Must supply a dark exposure if config.doDark True")
385  if self.config.doFlat and flat is None:
386  raise RuntimeError("Must supply a flat exposure if config.doFlat True")
387  if self.config.doFringe and fringes is None:
388  raise RuntimeError("Must supply fringe list or exposure if config.doFringe True")
389 
390  defects = [] if defects is None else defects
391 
392  ccd = ccdExposure.getDetector()
393  ccdExposure = self.convertIntToFloat(ccdExposure)
394 
395  if not ccd:
396  assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
397  ccd = [FakeAmp(ccdExposure, self.config)]
398 
399  for amp in ccd:
400  #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
401  if ccdExposure.getBBox().contains(amp.getBBox()):
402  self.saturationDetection(ccdExposure, amp)
403  self.overscanCorrection(ccdExposure, amp)
404 
405  if self.config.doAssembleCcd:
406  ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
407 
408  if self.config.doBias:
409  self.biasCorrection(ccdExposure, bias)
410 
411  if self.config.doDark:
412  self.darkCorrection(ccdExposure, dark)
413 
414  for amp in ccd:
415  #if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
416  if ccdExposure.getBBox().contains(amp.getBBox()):
417  ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
418  self.updateVariance(ampExposure, amp)
419 
420  if self.config.doFringe and not self.config.fringeAfterFlat:
421  self.fringe.removeFringe(ccdExposure, fringes)
422 
423  if self.config.doFlat:
424  self.flatCorrection(ccdExposure, flat)
425 
426  self.maskAndInterpDefect(ccdExposure, defects)
427 
428  self.saturationInterpolation(ccdExposure)
429 
430  self.maskAndInterpNan(ccdExposure)
431 
432  if self.config.doFringe and self.config.fringeAfterFlat:
433  self.fringe.removeFringe(ccdExposure, fringes)
434 
435  ccdExposure.getCalib().setFluxMag0(self.config.fluxMag0T1 * ccdExposure.getCalib().getExptime())
436 
437  self.display("postISRCCD", ccdExposure)
438 
439  return pipeBase.Struct(
440  exposure = ccdExposure,
441  )
442 
443 
444  @pipeBase.timeMethod
445  def runDataRef(self, sensorRef):
446  """!Perform instrument signature removal on a ButlerDataRef of a Sensor
447 
448  - Read in necessary detrending/isr/calibration data
449  - Process raw exposure in run()
450  - Persist the ISR-corrected exposure as "postISRCCD" if config.doWrite is True
451 
452  \param[in] sensorRef -- daf.persistence.butlerSubset.ButlerDataRef of the
453  detector data to be processed
454  \return a pipeBase.Struct with fields:
455  - exposure: the exposure after application of ISR
456  """
457  self.log.info("Performing ISR on sensor %s" % (sensorRef.dataId))
458  ccdExposure = sensorRef.get('raw')
459  isrData = self.readIsrData(sensorRef)
460 
461  result = self.run(ccdExposure, **isrData.getDict())
462 
463  if self.config.doWrite:
464  sensorRef.put(result.exposure, "postISRCCD")
465 
466  return result
467 
468  def convertIntToFloat(self, exposure):
469  """Convert an exposure from uint16 to float, set variance plane to 1 and mask plane to 0
470  """
471  if isinstance(exposure, afwImage.ExposureF):
472  # Nothing to be done
473  return exposure
474  if not hasattr(exposure, "convertF"):
475  raise RuntimeError("Unable to convert exposure (%s) to float" % type(exposure))
476 
477  newexposure = exposure.convertF()
478  maskedImage = newexposure.getMaskedImage()
479  varArray = maskedImage.getVariance().getArray()
480  varArray[:,:] = 1
481  maskArray = maskedImage.getMask().getArray()
482  maskArray[:,:] = 0
483  return newexposure
484 
485  def biasCorrection(self, exposure, biasExposure):
486  """!Apply bias correction in place
487 
488  \param[in,out] exposure exposure to process
489  \param[in] biasExposure bias exposure of same size as exposure
490  """
491  isr.biasCorrection(exposure.getMaskedImage(), biasExposure.getMaskedImage())
492 
493  def darkCorrection(self, exposure, darkExposure):
494  """!Apply dark correction in place
495 
496  \param[in,out] exposure exposure to process
497  \param[in] darkExposure dark exposure of same size as exposure
498  """
499  darkCalib = darkExposure.getCalib()
500  isr.darkCorrection(
501  maskedImage = exposure.getMaskedImage(),
502  darkMaskedImage = darkExposure.getMaskedImage(),
503  expScale = exposure.getCalib().getExptime(),
504  darkScale = darkCalib.getExptime(),
505  )
506 
507  def updateVariance(self, ampExposure, amp):
508  """!Set the variance plane based on the image plane, plus amplifier gain and read noise
509 
510  \param[in,out] ampExposure exposure to process
511  \param[in] amp amplifier detector information
512  """
513  if not math.isnan(amp.getGain()):
514  isr.updateVariance(
515  maskedImage = ampExposure.getMaskedImage(),
516  gain = amp.getGain(),
517  readNoise = amp.getReadNoise(),
518  )
519 
520  def flatCorrection(self, exposure, flatExposure):
521  """!Apply flat correction in place
522 
523  \param[in,out] exposure exposure to process
524  \param[in] flatExposure flatfield exposure same size as exposure
525  """
526  isr.flatCorrection(
527  maskedImage = exposure.getMaskedImage(),
528  flatMaskedImage = flatExposure.getMaskedImage(),
529  scalingType = self.config.flatScalingType,
530  userScale = self.config.flatUserScale,
531  )
532 
533  def getIsrExposure(self, dataRef, datasetType, immediate=True):
534  """!Retrieve a calibration dataset for removing instrument signature
535 
536  \param[in] dataRef data reference for exposure
537  \param[in] datasetType type of dataset to retrieve (e.g. 'bias', 'flat')
538  \param[in] immediate if True, disable butler proxies to enable error
539  handling within this routine
540  \return exposure
541  """
542  try:
543  exp = dataRef.get(datasetType, immediate=immediate)
544  except Exception, e:
545  raise RuntimeError("Unable to retrieve %s for %s: %s" % (datasetType, dataRef.dataId, e))
546  if self.config.doAssembleIsrExposures:
547  exp = self.assembleCcd.assembleCcd(exp)
548  return exp
549 
550  def saturationDetection(self, exposure, amp):
551  """!Detect saturated pixels and mask them using mask plane "SAT", in place
552 
553  \param[in,out] exposure exposure to process; only the amp DataSec is processed
554  \param[in] amp amplifier device data
555  """
556  if not math.isnan(amp.getSaturation()):
557  maskedImage = exposure.getMaskedImage()
558  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
559  isr.makeThresholdMask(
560  maskedImage = dataView,
561  threshold = amp.getSaturation(),
562  growFootprints = 0,
563  maskName = self.config.saturatedMaskName,
564  )
565 
566  def saturationInterpolation(self, ccdExposure):
567  """!Interpolate over saturated pixels, in place
568 
569  \param[in,out] ccdExposure exposure to process
570 
571  \warning:
572  - Call saturationDetection first, so that saturated pixels have been identified in the "SAT" mask.
573  - Call this after CCD assembly, since saturated regions may cross amplifier boundaries
574  """
575  isr.interpolateFromMask(
576  maskedImage = ccdExposure.getMaskedImage(),
577  fwhm = self.config.fwhm,
578  growFootprints = self.config.growSaturationFootprintSize,
579  maskName = self.config.saturatedMaskName,
580  )
581 
582  def maskAndInterpDefect(self, ccdExposure, defectBaseList):
583  """!Mask defects using mask plane "BAD" and interpolate over them, in place
584 
585  \param[in,out] ccdExposure exposure to process
586  \param[in] defectBaseList a list of defects to mask and interpolate
587 
588  \warning: call this after CCD assembly, since defects may cross amplifier boundaries
589  """
590  maskedImage = ccdExposure.getMaskedImage()
591  defectList = measAlg.DefectListT()
592  for d in defectBaseList:
593  bbox = d.getBBox()
594  nd = measAlg.Defect(bbox)
595  defectList.append(nd)
596  isr.maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')
597  isr.interpolateDefectList(
598  maskedImage = maskedImage,
599  defectList = defectList,
600  fwhm = self.config.fwhm,
601  )
602 
603  def maskAndInterpNan(self, exposure):
604  """!Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place
605 
606  We mask and interpolate over all NaNs, including those
607  that are masked with other bits (because those may or may
608  not be interpolated over later, and we want to remove all
609  NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane
610  is used to preserve the historical name.
611 
612  \param[in,out] exposure exposure to process
613  """
614  maskedImage = exposure.getMaskedImage()
615 
616  # Find and mask NaNs
617  maskedImage.getMask().addMaskPlane("UNMASKEDNAN")
618  maskVal = maskedImage.getMask().getPlaneBitMask("UNMASKEDNAN")
619  numNans = maskNans(maskedImage, maskVal)
620  self.metadata.set("NUMNANS", numNans)
621 
622  # Interpolate over these previously-unmasked NaNs
623  if numNans > 0:
624  self.log.log(self.log.WARN, "There were %i unmasked NaNs" % (numNans,))
625  nanDefectList = isr.getDefectListFromMask(
626  maskedImage = maskedImage,
627  maskName = 'UNMASKEDNAN',
628  growFootprints = 0,
629  )
630  isr.interpolateDefectList(
631  maskedImage = exposure.getMaskedImage(),
632  defectList = nanDefectList,
633  fwhm = self.config.fwhm,
634  )
635 
636  def overscanCorrection(self, exposure, amp):
637  """!Apply overscan correction, in place
638 
639  \param[in,out] exposure exposure to process; must include both DataSec and BiasSec pixels
640  \param[in] amp amplifier device data
641  """
642  if not amp.getHasRawInfo():
643  raise RuntimeError("This method must be executed on an amp with raw information.")
644 
645  if amp.getRawHorizontalOverscanBBox().isEmpty():
646  self.log.info("No Overscan region. Not performing Overscan Correction.")
647  return None
648 
649  maskedImage = exposure.getMaskedImage()
650  dataView = maskedImage.Factory(maskedImage, amp.getRawDataBBox())
651 
652  expImage = exposure.getMaskedImage().getImage()
653  overscanImage = expImage.Factory(expImage, amp.getRawHorizontalOverscanBBox())
654 
655  isr.overscanCorrection(
656  ampMaskedImage = dataView,
657  overscanImage = overscanImage,
658  fitType = self.config.overscanFitType,
659  order = self.config.overscanOrder,
660  collapseRej = self.config.overscanRej,
661  )
662 
663 class FakeAmp(object):
664  """A Detector-like object that supports returning gain and saturation level"""
665  def __init__(self, exposure, config):
666  self._bbox = exposure.getBBox(afwImage.LOCAL)
668  self._gain = config.gain
669  self._readNoise = config.readNoise
670  self._saturation = config.saturation
671 
672  def getBBox(self):
673  return self._bbox
674 
675  def getRawBBox(self):
676  return self._bbox
677 
678  def getHasRawInfo(self):
679  return True # but see getRawHorizontalOverscanBBox()
680 
682  return self._RawHorizontalOverscanBBox
683 
684  def getGain(self):
685  return self._gain
686 
687  def getReadNoise(self):
688  return self._readNoise
689 
690  def getSaturation(self):
691  return self._saturation
def getIsrExposure
Retrieve a calibration dataset for removing instrument signature.
Definition: isrTask.py:533
def maskAndInterpNan
Mask NaNs using mask plane &quot;UNMASKEDNAN&quot; and interpolate over them, in place.
Definition: isrTask.py:603
def flatCorrection
Apply flat correction in place.
Definition: isrTask.py:520
def run
Perform instrument signature removal on an exposure.
Definition: isrTask.py:361
def __init__
Constructor for IsrTask.
Definition: isrTask.py:317
Apply common instrument signature correction algorithms to a raw frame.
Definition: isrTask.py:176
def runDataRef
Perform instrument signature removal on a ButlerDataRef of a Sensor.
Definition: isrTask.py:445
An integer coordinate rectangle.
Definition: Box.h:53
def updateVariance
Set the variance plane based on the image plane, plus amplifier gain and read noise.
Definition: isrTask.py:507
def maskAndInterpDefect
Mask defects using mask plane &quot;BAD&quot; and interpolate over them, in place.
Definition: isrTask.py:582
def overscanCorrection
Apply overscan correction, in place.
Definition: isrTask.py:636
def saturationDetection
Detect saturated pixels and mask them using mask plane &quot;SAT&quot;, in place.
Definition: isrTask.py:550
def darkCorrection
Apply dark correction in place.
Definition: isrTask.py:493
size_t maskNans(afw::image::MaskedImage< PixelT > const &mi, afw::image::MaskPixel maskVal, afw::image::MaskPixel allow=0)
Definition: Isr.cc:61
Encapsulate information about a bad portion of a detector.
Definition: Interp.h:70
def saturationInterpolation
Interpolate over saturated pixels, in place.
Definition: isrTask.py:566
def biasCorrection
Apply bias correction in place.
Definition: isrTask.py:485
def readIsrData
Retrieve necessary frames for instrument signature removal.
Definition: isrTask.py:329