1 from __future__
import division
2 from builtins
import range
3 from builtins
import object
35 from lsstDebug
import getDebugFrame
38 from .isrLib
import maskNans
39 from .assembleCcdTask
import AssembleCcdTask
40 from .fringe
import FringeTask
43 from contextlib
import contextmanager
47 doBias = pexConfig.Field(
49 doc=
"Apply bias frame correction?",
52 doDark = pexConfig.Field(
54 doc=
"Apply dark frame correction?",
57 doFlat = pexConfig.Field(
59 doc=
"Apply flat field correction?",
62 doFringe = pexConfig.Field(
64 doc=
"Apply fringe correction?",
67 doWrite = pexConfig.Field(
69 doc=
"Persist postISRCCD?",
72 assembleCcd = pexConfig.ConfigurableField(
73 target=AssembleCcdTask,
74 doc=
"CCD assembly task",
76 gain = pexConfig.Field(
78 doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
81 readNoise = pexConfig.Field(
83 doc=
"The read noise to use if no Detector is present in the Exposure",
86 saturation = pexConfig.Field(
88 doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
91 fringeAfterFlat = pexConfig.Field(
93 doc=
"Do fringe subtraction after flat-fielding?",
96 fringe = pexConfig.ConfigurableField(
98 doc=
"Fringe subtraction task",
100 fwhm = pexConfig.Field(
102 doc=
"FWHM of PSF (arcsec)",
105 saturatedMaskName = pexConfig.Field(
107 doc=
"Name of mask plane to use in saturation detection and interpolation",
110 suspectMaskName = pexConfig.Field(
112 doc=
"Name of mask plane to use for suspect pixels",
115 flatScalingType = pexConfig.ChoiceField(
117 doc=
"The method for scaling the flat on the fly.",
120 "USER":
"Scale by flatUserScale",
121 "MEAN":
"Scale by the inverse of the mean",
122 "MEDIAN":
"Scale by the inverse of the median",
125 flatUserScale = pexConfig.Field(
127 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
130 overscanFitType = pexConfig.ChoiceField(
132 doc=
"The method for fitting the overscan bias level.",
135 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
136 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
137 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
138 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
139 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
140 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
141 "MEAN":
"Correct using the mean of the overscan region",
142 "MEDIAN":
"Correct using the median of the overscan region",
145 overscanOrder = pexConfig.Field(
147 doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
148 "or number of spline knots if overscan fit type is a spline."),
151 overscanRej = pexConfig.Field(
153 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
156 growSaturationFootprintSize = pexConfig.Field(
158 doc=
"Number of pixels by which to grow the saturation footprints",
161 fluxMag0T1 = pexConfig.Field(
163 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure",
166 setGainAssembledCcd = pexConfig.Field(
168 doc=
"update exposure metadata in the assembled ccd to reflect the "
169 "effective gain of the assembled chip",
172 keysToRemoveFromAssembledCcd = pexConfig.ListField(
174 doc=
"fields to remove from the metadata of the assembled ccd.",
177 doAssembleIsrExposures = pexConfig.Field(
180 doc=
"Assemble amp-level calibration exposures into ccd-level exposure?"
182 doAssembleCcd = pexConfig.Field(
185 doc=
"Assemble amp-level exposures into a ccd-level exposure?"
187 doLinearize = pexConfig.Field(
189 doc=
"Correct for nonlinearity of the detector's response?",
192 doBrighterFatter = pexConfig.Field(
195 doc=
"Apply the brighter fatter correction"
197 brighterFatterKernelFile = pexConfig.Field(
200 doc=
"Kernel file used for the brighter fatter correction"
202 brighterFatterMaxIter = pexConfig.Field(
205 doc=
"Maximum number of iterations for the brighter fatter correction"
207 brighterFatterThreshold = pexConfig.Field(
210 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the "
211 " absolute value of the difference between the current corrected image and the one"
212 " from the previous iteration summed over all the pixels."
214 brighterFatterApplyGain = pexConfig.Field(
217 doc=
"Should the gain be applied when applying the brighter fatter correction?"
219 datasetType = pexConfig.Field(
221 doc=
"Dataset type for input data; users will typically leave this alone, "
222 "but camera-specific ISR tasks will override it",
225 fallbackFilterName = pexConfig.Field(dtype=str,
226 doc=
"Fallback default filter name for calibrations", optional=
True)
240 \brief Apply common instrument signature correction algorithms to a raw frame.
242 \section ip_isr_isr_Contents Contents
244 - \ref ip_isr_isr_Purpose
245 - \ref ip_isr_isr_Initialize
247 - \ref ip_isr_isr_Config
248 - \ref ip_isr_isr_Debug
251 \section ip_isr_isr_Purpose Description
253 The process for correcting imaging data is very similar from camera to camera.
254 This task provides a vanilla implementation of doing these corrections, including
255 the ability to turn certain corrections off if they are not needed.
256 The inputs to the primary method, run, are a raw exposure to be corrected and the
257 calibration data products. The raw input is a single chip sized mosaic of all amps
258 including overscans and other non-science pixels.
259 The method runDataRef() is intended for use by a lsst.pipe.base.cmdLineTask.CmdLineTask
260 and takes as input only a daf.persistence.butlerSubset.ButlerDataRef.
261 This task may not meet all needs and it is expected that it will be subclassed for
262 specific applications.
264 \section ip_isr_isr_Initialize Task initialization
266 \copydoc \_\_init\_\_
268 \section ip_isr_isr_IO Inputs/Outputs to the run method
272 \section ip_isr_isr_Config Configuration parameters
274 See \ref IsrTaskConfig
276 \section ip_isr_isr_Debug Debug variables
278 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
279 flag \c --debug, \c -d to import \b debug.py from your \c PYTHONPATH; see <a
280 href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
281 Using lsstDebug to control debugging output</a> for more about \b debug.py files.
283 The available variables in IsrTask are:
286 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
289 <DD> display exposure after ISR has been applied
293 For example, put something like
297 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
298 if name == "lsst.ip.isr.isrTask":
299 di.display = {'postISRCCD':2}
301 lsstDebug.Info = DebugInfo
303 into your debug.py file and run the commandline task with the \c --debug flag.
307 ConfigClass = IsrTaskConfig
311 '''!Constructor for IsrTask
312 \param[in] *args -- a list of positional arguments passed on to the Task constructor
313 \param[in] **kwargs -- a dictionary of keyword arguments passed on to the Task constructor
314 Call the lsst.pipe.base.task.Task.__init__ method
315 Then setup the assembly and fringe correction subtasks
317 pipeBase.Task.__init__(self, *args, **kwargs)
318 self.makeSubtask(
"assembleCcd")
319 self.makeSubtask(
"fringe")
322 """!Retrieve necessary frames for instrument signature removal
323 \param[in] dataRef -- a daf.persistence.butlerSubset.ButlerDataRef
324 of the detector data to be processed
325 \param[in] rawExposure -- a reference raw exposure that will later be
326 corrected with the retrieved calibration data;
327 should not be modified in this method.
328 \return a pipeBase.Struct with fields containing kwargs expected by run()
329 - bias: exposure of bias frame
330 - dark: exposure of dark frame
331 - flat: exposure of flat field
332 - defects: list of detects
333 - fringeStruct: a pipeBase.Struct with field fringes containing
334 exposure of fringe frame or list of fringe exposure
336 ccd = rawExposure.getDetector()
338 biasExposure = self.
getIsrExposure(dataRef,
"bias")
if self.config.doBias
else None
340 linearizer = dataRef.get(
"linearizer", immediate=
True)
if self.
doLinearize(ccd)
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 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
if self.config.doBrighterFatter
else None
345 defectList = dataRef.get(
"defects")
347 if self.config.doFringe
and self.fringe.checkFilter(rawExposure):
348 fringeStruct = self.fringe.readFringes(dataRef, assembler=self.assembleCcd
349 if self.config.doAssembleIsrExposures
else None)
351 fringeStruct = pipeBase.Struct(fringes=
None)
354 return pipeBase.Struct(bias=biasExposure,
355 linearizer=linearizer,
359 fringes=fringeStruct,
360 bfKernel=brighterFatterKernel
364 def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, defects=None,
365 fringes=
None, bfKernel=
None):
366 """!Perform instrument signature removal on an exposure
369 - Detect saturation, apply overscan correction, bias, dark and flat
370 - Perform CCD assembly
371 - Interpolate over defects, saturated pixels and all NaNs
373 \param[in] ccdExposure -- lsst.afw.image.exposure of detector data
374 \param[in] bias -- exposure of bias frame
375 \param[in] linearizer -- linearizing functor; a subclass of lsst.ip.isr.LinearizeBase
376 \param[in] dark -- exposure of dark frame
377 \param[in] flat -- exposure of flatfield
378 \param[in] defects -- list of detects
379 \param[in] fringes -- a pipeBase.Struct with field fringes containing
380 exposure of fringe frame or list of fringe exposure
381 \param[in] bfKernel -- kernel for brighter-fatter correction
383 \return a pipeBase.Struct with field:
388 if isinstance(ccdExposure, ButlerDataRef):
391 ccd = ccdExposure.getDetector()
394 if self.config.doBias
and bias
is None:
395 raise RuntimeError(
"Must supply a bias exposure if config.doBias True")
397 raise RuntimeError(
"Must supply a linearizer if config.doBias True")
398 if self.config.doDark
and dark
is None:
399 raise RuntimeError(
"Must supply a dark exposure if config.doDark True")
400 if self.config.doFlat
and flat
is None:
401 raise RuntimeError(
"Must supply a flat exposure if config.doFlat True")
402 if self.config.doBrighterFatter
and bfKernel
is None:
403 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter True")
405 fringes = pipeBase.Struct(fringes=
None)
406 if self.config.doFringe
and not isinstance(fringes, pipeBase.Struct):
407 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct")
409 defects = []
if defects
is None else defects
414 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd"
415 ccd = [
FakeAmp(ccdExposure, self.config)]
419 if ccdExposure.getBBox().contains(amp.getBBox()):
424 if self.config.doAssembleCcd:
425 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
427 if self.config.doBias:
431 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
433 if self.config.doBrighterFatter:
435 self.config.brighterFatterMaxIter,
436 self.config.brighterFatterThreshold,
437 self.config.brighterFatterApplyGain,
440 if self.config.doDark:
445 if ccdExposure.getBBox().contains(amp.getBBox()):
446 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
449 if self.config.doFringe
and not self.config.fringeAfterFlat:
450 self.fringe.run(ccdExposure, **fringes.getDict())
452 if self.config.doFlat:
461 if self.config.doFringe
and self.config.fringeAfterFlat:
462 self.fringe.run(ccdExposure, **fringes.getDict())
464 exposureTime = ccdExposure.getInfo().getVisitInfo().getExposureTime()
465 ccdExposure.getCalib().setFluxMag0(self.config.fluxMag0T1*exposureTime)
471 return pipeBase.Struct(
472 exposure=ccdExposure,
477 """!Perform instrument signature removal on a ButlerDataRef of a Sensor
479 - Read in necessary detrending/isr/calibration data
480 - Process raw exposure in run()
481 - Persist the ISR-corrected exposure as "postISRCCD" if config.doWrite is True
483 \param[in] sensorRef -- daf.persistence.butlerSubset.ButlerDataRef of the
484 detector data to be processed
485 \return a pipeBase.Struct with fields:
486 - exposure: the exposure after application of ISR
488 self.log.info(
"Performing ISR on sensor %s" % (sensorRef.dataId))
489 ccdExposure = sensorRef.get(
'raw')
492 result = self.
run(ccdExposure, **isrData.getDict())
494 if self.config.doWrite:
495 sensorRef.put(result.exposure,
"postISRCCD")
500 """Convert an exposure from uint16 to float, set variance plane to 1 and mask plane to 0
502 if isinstance(exposure, afwImage.ExposureF):
505 if not hasattr(exposure,
"convertF"):
506 raise RuntimeError(
"Unable to convert exposure (%s) to float" % type(exposure))
508 newexposure = exposure.convertF()
509 maskedImage = newexposure.getMaskedImage()
510 varArray = maskedImage.getVariance().getArray()
512 maskArray = maskedImage.getMask().getArray()
517 """!Apply bias correction in place
519 \param[in,out] exposure exposure to process
520 \param[in] biasExposure bias exposure of same size as exposure
522 isr.biasCorrection(exposure.getMaskedImage(), biasExposure.getMaskedImage())
525 """!Apply dark correction in place
527 \param[in,out] exposure exposure to process
528 \param[in] darkExposure dark exposure of same size as exposure
530 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
531 if math.isnan(expScale):
532 raise RuntimeError(
"Exposure darktime is NAN")
533 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
534 if math.isnan(darkScale):
535 raise RuntimeError(
"Dark calib darktime is NAN")
537 maskedImage=exposure.getMaskedImage(),
538 darkMaskedImage=darkExposure.getMaskedImage(),
544 """!Is linearization wanted for this detector?
546 Checks config.doLinearize and the linearity type of the first amplifier.
548 \param[in] detector detector information (an lsst.afw.cameraGeom.Detector)
550 return self.config.doLinearize
and \
551 detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
554 """!Set the variance plane based on the image plane, plus amplifier gain and read noise
556 \param[in,out] ampExposure exposure to process
557 \param[in] amp amplifier detector information
559 if not math.isnan(amp.getGain()):
561 maskedImage=ampExposure.getMaskedImage(),
563 readNoise=amp.getReadNoise(),
567 """!Apply flat correction in place
569 \param[in,out] exposure exposure to process
570 \param[in] flatExposure flatfield exposure same size as exposure
573 maskedImage=exposure.getMaskedImage(),
574 flatMaskedImage=flatExposure.getMaskedImage(),
575 scalingType=self.config.flatScalingType,
576 userScale=self.config.flatUserScale,
580 """!Retrieve a calibration dataset for removing instrument signature
582 \param[in] dataRef data reference for exposure
583 \param[in] datasetType type of dataset to retrieve (e.g. 'bias', 'flat')
584 \param[in] immediate if True, disable butler proxies to enable error
585 handling within this routine
589 exp = dataRef.get(datasetType, immediate=immediate)
590 except Exception
as exc1:
591 if not self.config.fallbackFilterName:
592 raise RuntimeError(
"Unable to retrieve %s for %s: %s" % (datasetType, dataRef.dataId, exc1))
594 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
595 except Exception
as exc2:
596 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s" %
597 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
598 self.log.warn(
"Using fallback calibration from filter %s" % self.config.fallbackFilterName)
600 if self.config.doAssembleIsrExposures:
601 exp = self.assembleCcd.assembleCcd(exp)
605 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place
607 \param[in,out] exposure exposure to process; only the amp DataSec is processed
608 \param[in] amp amplifier device data
610 if not math.isnan(amp.getSaturation()):
611 maskedImage = exposure.getMaskedImage()
612 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
613 isr.makeThresholdMask(
614 maskedImage=dataView,
615 threshold=amp.getSaturation(),
617 maskName=self.config.saturatedMaskName,
621 """!Interpolate over saturated pixels, in place
623 \param[in,out] ccdExposure exposure to process
626 - Call saturationDetection first, so that saturated pixels have been identified in the "SAT" mask.
627 - Call this after CCD assembly, since saturated regions may cross amplifier boundaries
629 isr.interpolateFromMask(
630 maskedImage=ccdExposure.getMaskedImage(),
631 fwhm=self.config.fwhm,
632 growFootprints=self.config.growSaturationFootprintSize,
633 maskName=self.config.saturatedMaskName,
637 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place
639 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel().
640 This is intended to indicate pixels that may be affected by unknown systematics;
641 for example if non-linearity corrections above a certain level are unstable
642 then that would be a useful value for suspectLevel. A value of `nan` indicates
643 that no such level exists and no pixels are to be masked as suspicious.
645 \param[in,out] exposure exposure to process; only the amp DataSec is processed
646 \param[in] amp amplifier device data
648 suspectLevel = amp.getSuspectLevel()
649 if math.isnan(suspectLevel):
652 maskedImage = exposure.getMaskedImage()
653 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
654 isr.makeThresholdMask(
655 maskedImage=dataView,
656 threshold=suspectLevel,
658 maskName=self.config.suspectMaskName,
662 """!Mask defects using mask plane "BAD" and interpolate over them, in place
664 \param[in,out] ccdExposure exposure to process
665 \param[in] defectBaseList a list of defects to mask and interpolate
667 \warning: call this after CCD assembly, since defects may cross amplifier boundaries
669 maskedImage = ccdExposure.getMaskedImage()
670 defectList = measAlg.DefectListT()
671 for d
in defectBaseList:
674 defectList.append(nd)
675 isr.maskPixelsFromDefectList(maskedImage, defectList, maskName=
'BAD')
676 isr.interpolateDefectList(
677 maskedImage=maskedImage,
678 defectList=defectList,
679 fwhm=self.config.fwhm,
683 """!Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place
685 We mask and interpolate over all NaNs, including those
686 that are masked with other bits (because those may or may
687 not be interpolated over later, and we want to remove all
688 NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane
689 is used to preserve the historical name.
691 \param[in,out] exposure exposure to process
693 maskedImage = exposure.getMaskedImage()
696 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
697 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
698 numNans =
maskNans(maskedImage, maskVal)
699 self.metadata.set(
"NUMNANS", numNans)
703 self.log.warn(
"There were %i unmasked NaNs", numNans)
704 nanDefectList = isr.getDefectListFromMask(
705 maskedImage=maskedImage,
706 maskName=
'UNMASKEDNAN',
709 isr.interpolateDefectList(
710 maskedImage=exposure.getMaskedImage(),
711 defectList=nanDefectList,
712 fwhm=self.config.fwhm,
716 """!Apply overscan correction, in place
718 \param[in,out] exposure exposure to process; must include both DataSec and BiasSec pixels
719 \param[in] amp amplifier device data
721 if not amp.getHasRawInfo():
722 raise RuntimeError(
"This method must be executed on an amp with raw information.")
724 if amp.getRawHorizontalOverscanBBox().isEmpty():
725 self.log.info(
"No Overscan region. Not performing Overscan Correction.")
728 maskedImage = exposure.getMaskedImage()
729 dataView = maskedImage.Factory(maskedImage, amp.getRawDataBBox())
731 expImage = exposure.getMaskedImage().getImage()
732 overscanImage = expImage.Factory(expImage, amp.getRawHorizontalOverscanBBox())
734 isr.overscanCorrection(
735 ampMaskedImage=dataView,
736 overscanImage=overscanImage,
737 fitType=self.config.overscanFitType,
738 order=self.config.overscanOrder,
739 collapseRej=self.config.overscanRej,
743 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners
745 \param[in,out] ccdExposure exposure to process
746 \param[in] fpPolygon Polygon in focal plane coordinates
749 ccd = ccdExposure.getDetector()
750 fpCorners = ccd.getCorners(FOCAL_PLANE)
751 ccdPolygon =
Polygon(fpCorners)
754 intersect = ccdPolygon.intersectionSingle(fpPolygon)
757 ccdPoints = [ccd.transform(ccd.makeCameraPoint(x, FOCAL_PLANE), PIXELS).getPoint()
for x
in intersect]
758 validPolygon =
Polygon(ccdPoints)
759 ccdExposure.getInfo().setValidPolygon(validPolygon)
762 """Apply brighter fatter correction in place for the image
764 This correction takes a kernel that has been derived from flat field images to
765 redistribute the charge. The gradient of the kernel is the deflection
766 field due to the accumulated charge.
768 Given the original image I(x) and the kernel K(x) we can compute the corrected image Ic(x)
769 using the following equation:
771 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
773 To evaluate the derivative term we expand it as follows:
775 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y))) + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )
777 Because we use the measured counts instead of the incident counts we apply the correction
778 iteratively to reconstruct the original counts and the correction. We stop iterating when the
779 summed difference between the current corrected image and the one from the previous iteration
780 is below the threshold. We do not require convergence because the number of iterations is
781 too large a computational cost. How we define the threshold still needs to be evaluated, the
782 current default was shown to work reasonably well on a small set of images. For more information
783 on the method see DocuShare Document-19407.
785 The edges as defined by the kernel are not corrected because they have spurious values
786 due to the convolution.
788 self.log.info(
"Applying brighter fatter correction")
790 image = exposure.getMaskedImage().getImage()
795 kLx = numpy.shape(kernel)[0]
796 kLy = numpy.shape(kernel)[1]
797 kernelImage = afwImage.ImageD(kernel.astype(numpy.float64))
798 tempImage = image.clone()
800 nanIndex = numpy.isnan(tempImage.getArray())
801 tempImage.getArray()[nanIndex] = 0.
803 outImage = afwImage.ImageF(image.getDimensions())
804 corr = numpy.zeros_like(image.getArray())
805 prev_image = numpy.zeros_like(image.getArray())
817 for iteration
in range(maxIter):
820 tmpArray = tempImage.getArray()
821 outArray = outImage.getArray()
824 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
825 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
826 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])
829 diffOut20 = numpy.gradient(gradOut[0])
830 diffOut21 = numpy.gradient(gradOut[1])
831 second = tmpArray[startY:endY, startX:endX]*(diffOut20[0] + diffOut21[1])
833 corr[startY:endY, startX:endX] = 0.5*(first + second)
836 tmpArray[:, :] = image.getArray()[:, :]
837 tmpArray[nanIndex] = 0.
838 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
841 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
845 prev_image[:, :] = tmpArray[:, :]
847 if iteration == maxIter - 1:
848 self.log.warn(
"Brighter fatter correction did not converge, final difference %f" % diff)
850 self.log.info(
"Finished brighter fatter in %d iterations" % (iteration))
851 image.getArray()[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
855 """Context manager that applies and removes gain
858 ccd = exp.getDetector()
860 sim = image.Factory(image, amp.getBBox())
867 ccd = exp.getDetector()
869 sim = image.Factory(image, amp.getBBox())
874 """A Detector-like object that supports returning gain and saturation level"""
877 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
def doLinearize
Is linearization wanted for this detector?
def getIsrExposure
Retrieve a calibration dataset for removing instrument signature.
def maskAndInterpNan
Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place.
Parameters to control convolution.
def flatCorrection
Apply flat correction in place.
def run
Perform instrument signature removal on an exposure.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def __init__
Constructor for IsrTask.
Apply common instrument signature correction algorithms to a raw frame.
def runDataRef
Perform instrument signature removal on a ButlerDataRef of a Sensor.
An integer coordinate rectangle.
size_t maskNans(afw::image::MaskedImage< PixelT > const &mi, afw::image::MaskPixel maskVal, afw::image::MaskPixel allow)
Mask NANs in an image.
def suspectDetection
Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place.
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 setValidPolygonIntersect
Set the valid polygon as the intersection of fpPolygon and the ccd corners.
def overscanCorrection
Apply overscan correction, in place.
_RawHorizontalOverscanBBox
def saturationDetection
Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place...
def darkCorrection
Apply dark correction in place.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
Encapsulate information about a bad portion of a detector.
def saturationInterpolation
Interpolate over saturated pixels, in place.
A kernel created from an Image.
def getRawHorizontalOverscanBBox
def brighterFatterCorrection
def biasCorrection
Apply bias correction in place.
def readIsrData
Retrieve necessary frames for instrument signature removal.