32 from contextlib
import contextmanager
33 from lsstDebug
import getDebugFrame
43 from .
import isrFunctions
45 from .
import linearize
47 from .assembleCcdTask
import AssembleCcdTask
48 from .crosstalk
import CrosstalkTask
49 from .fringe
import FringeTask
50 from .isr
import maskNans
51 from .masking
import MaskingTask
52 from .straylight
import StrayLightTask
53 from .vignette
import VignetteTask
55 __all__ = [
"IsrTask",
"IsrTaskConfig",
"RunIsrTask",
"RunIsrConfig"]
59 """Configuration parameters for IsrTask. 61 Items are grouped in the order in which they are executed by the task. 66 isrName = pexConfig.Field(
73 ccdExposure = pipeBase.InputDatasetField(
74 doc=
"Input exposure to process",
77 storageClass=
"Exposure",
78 dimensions=[
"instrument",
"exposure",
"detector"],
80 camera = pipeBase.InputDatasetField(
81 doc=
"Input camera to construct complete exposures.",
84 storageClass=
"TablePersistableCamera",
85 dimensions=[
"instrument",
"calibration_label"],
87 bias = pipeBase.InputDatasetField(
88 doc=
"Input bias calibration.",
91 storageClass=
"ImageF",
92 dimensions=[
"instrument",
"calibration_label",
"detector"],
94 dark = pipeBase.InputDatasetField(
95 doc=
"Input dark calibration.",
98 storageClass=
"ImageF",
99 dimensions=[
"instrument",
"calibration_label",
"detector"],
101 flat = pipeBase.InputDatasetField(
102 doc=
"Input flat calibration.",
105 storageClass=
"MaskedImageF",
106 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
108 bfKernel = pipeBase.InputDatasetField(
109 doc=
"Input brighter-fatter kernel.",
112 storageClass=
"NumpyArray",
113 dimensions=[
"instrument",
"calibration_label"],
115 defects = pipeBase.InputDatasetField(
116 doc=
"Input defect tables.",
119 storageClass=
"DefectsList",
120 dimensions=[
"instrument",
"calibration_label",
"detector"],
122 opticsTransmission = pipeBase.InputDatasetField(
123 doc=
"Transmission curve due to the optics.",
124 name=
"transmission_optics",
126 storageClass=
"TablePersistableTransmissionCurve",
127 dimensions=[
"instrument",
"calibration_label"],
129 filterTransmission = pipeBase.InputDatasetField(
130 doc=
"Transmission curve due to the filter.",
131 name=
"transmission_filter",
133 storageClass=
"TablePersistableTransmissionCurve",
134 dimensions=[
"instrument",
"physical_filter",
"calibration_label"],
136 sensorTransmission = pipeBase.InputDatasetField(
137 doc=
"Transmission curve due to the sensor.",
138 name=
"transmission_sensor",
140 storageClass=
"TablePersistableTransmissionCurve",
141 dimensions=[
"instrument",
"calibration_label",
"detector"],
143 atmosphereTransmission = pipeBase.InputDatasetField(
144 doc=
"Transmission curve due to the atmosphere.",
145 name=
"transmission_atmosphere",
147 storageClass=
"TablePersistableTransmissionCurve",
148 dimensions=[
"instrument"],
150 illumMaskedImage = pipeBase.InputDatasetField(
151 doc=
"Input illumination correction.",
154 storageClass=
"MaskedImageF",
155 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
159 outputExposure = pipeBase.OutputDatasetField(
160 doc=
"Output ISR processed exposure.",
163 storageClass=
"ExposureF",
164 dimensions=[
"instrument",
"visit",
"detector"],
166 outputOssThumbnail = pipeBase.OutputDatasetField(
167 doc=
"Output Overscan-subtracted thumbnail image.",
170 storageClass=
"Thumbnail",
171 dimensions=[
"instrument",
"visit",
"detector"],
173 outputFlattenedThumbnail = pipeBase.OutputDatasetField(
174 doc=
"Output flat-corrected thumbnail image.",
175 name=
"FlattenedThumb",
177 storageClass=
"TextStorage",
178 dimensions=[
"instrument",
"visit",
"detector"],
181 quantum = pipeBase.QuantumConfig(
182 dimensions=[
"visit",
"detector",
"instrument"],
186 datasetType = pexConfig.Field(
188 doc=
"Dataset type for input data; users will typically leave this alone, " 189 "but camera-specific ISR tasks will override it",
193 fallbackFilterName = pexConfig.Field(
195 doc=
"Fallback default filter name for calibrations.",
198 expectWcs = pexConfig.Field(
201 doc=
"Expect input science images to have a WCS (set False for e.g. spectrographs)." 203 fwhm = pexConfig.Field(
205 doc=
"FWHM of PSF in arcseconds.",
208 qa = pexConfig.ConfigField(
210 doc=
"QA related configuration options.",
214 doConvertIntToFloat = pexConfig.Field(
216 doc=
"Convert integer raw images to floating point values?",
221 doSaturation = pexConfig.Field(
223 doc=
"Mask saturated pixels? NB: this is totally independent of the" 224 " interpolation option - this is ONLY setting the bits in the mask." 225 " To have them interpolated make sure doSaturationInterpolation=True",
228 saturatedMaskName = pexConfig.Field(
230 doc=
"Name of mask plane to use in saturation detection and interpolation",
233 saturation = pexConfig.Field(
235 doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
236 default=float(
"NaN"),
238 growSaturationFootprintSize = pexConfig.Field(
240 doc=
"Number of pixels by which to grow the saturation footprints",
245 doSuspect = pexConfig.Field(
247 doc=
"Mask suspect pixels?",
250 suspectMaskName = pexConfig.Field(
252 doc=
"Name of mask plane to use for suspect pixels",
255 numEdgeSuspect = pexConfig.Field(
257 doc=
"Number of edge pixels to be flagged as untrustworthy.",
262 doSetBadRegions = pexConfig.Field(
264 doc=
"Should we set the level of all BAD patches of the chip to the chip's average value?",
267 badStatistic = pexConfig.ChoiceField(
269 doc=
"How to estimate the average value for BAD regions.",
272 "MEANCLIP":
"Correct using the (clipped) mean of good data",
273 "MEDIAN":
"Correct using the median of the good data",
278 doOverscan = pexConfig.Field(
280 doc=
"Do overscan subtraction?",
283 overscanFitType = pexConfig.ChoiceField(
285 doc=
"The method for fitting the overscan bias level.",
288 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
289 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
290 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
291 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
292 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
293 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
294 "MEAN":
"Correct using the mean of the overscan region",
295 "MEANCLIP":
"Correct using a clipped mean of the overscan region",
296 "MEDIAN":
"Correct using the median of the overscan region",
299 overscanOrder = pexConfig.Field(
301 doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
302 "or number of spline knots if overscan fit type is a spline."),
305 overscanNumSigmaClip = pexConfig.Field(
307 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
310 overscanIsInt = pexConfig.Field(
312 doc=
"Treat overscan as an integer image for purposes of overscan.FitType=MEDIAN",
315 overscanNumLeadingColumnsToSkip = pexConfig.Field(
317 doc=
"Number of columns to skip in overscan, i.e. those closest to amplifier",
320 overscanNumTrailingColumnsToSkip = pexConfig.Field(
322 doc=
"Number of columns to skip in overscan, i.e. those farthest from amplifier",
325 overscanMaxDev = pexConfig.Field(
327 doc=
"Maximum deviation from the median for overscan",
328 default=1000.0, check=
lambda x: x > 0
330 overscanBiasJump = pexConfig.Field(
332 doc=
"Fit the overscan in a piecewise-fashion to correct for bias jumps?",
335 overscanBiasJumpKeyword = pexConfig.Field(
337 doc=
"Header keyword containing information about devices.",
338 default=
"NO_SUCH_KEY",
340 overscanBiasJumpDevices = pexConfig.ListField(
342 doc=
"List of devices that need piecewise overscan correction.",
345 overscanBiasJumpLocation = pexConfig.Field(
347 doc=
"Location of bias jump along y-axis.",
352 doAssembleCcd = pexConfig.Field(
355 doc=
"Assemble amp-level exposures into a ccd-level exposure?" 357 assembleCcd = pexConfig.ConfigurableField(
358 target=AssembleCcdTask,
359 doc=
"CCD assembly task",
363 doAssembleIsrExposures = pexConfig.Field(
366 doc=
"Assemble amp-level calibration exposures into ccd-level exposure?" 368 doTrimToMatchCalib = pexConfig.Field(
371 doc=
"Trim raw data to match calibration bounding boxes?" 375 doBias = pexConfig.Field(
377 doc=
"Apply bias frame correction?",
380 biasDataProductName = pexConfig.Field(
382 doc=
"Name of the bias data product",
387 doVariance = pexConfig.Field(
389 doc=
"Calculate variance?",
392 gain = pexConfig.Field(
394 doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
395 default=float(
"NaN"),
397 readNoise = pexConfig.Field(
399 doc=
"The read noise to use if no Detector is present in the Exposure",
402 doEmpiricalReadNoise = pexConfig.Field(
405 doc=
"Calculate empirical read noise instead of value from AmpInfo data?" 409 doLinearize = pexConfig.Field(
411 doc=
"Correct for nonlinearity of the detector's response?",
416 doCrosstalk = pexConfig.Field(
418 doc=
"Apply intra-CCD crosstalk correction?",
421 doCrosstalkBeforeAssemble = pexConfig.Field(
423 doc=
"Apply crosstalk correction before CCD assembly, and before trimming?",
426 crosstalk = pexConfig.ConfigurableField(
427 target=CrosstalkTask,
428 doc=
"Intra-CCD crosstalk correction",
432 doDefect = pexConfig.Field(
434 doc=
"Apply correction for CCD defects, e.g. hot pixels?",
437 numEdgeSuspect = pexConfig.Field(
439 doc=
"Number of edge pixels to be flagged as untrustworthy.",
442 doNanMasking = pexConfig.Field(
444 doc=
"Mask NAN pixels?",
447 doWidenSaturationTrails = pexConfig.Field(
449 doc=
"Widen bleed trails based on their width?",
454 doBrighterFatter = pexConfig.Field(
457 doc=
"Apply the brighter fatter correction" 459 brighterFatterLevel = pexConfig.ChoiceField(
462 doc=
"The level at which to correct for brighter-fatter.",
464 "AMP":
"Every amplifier treated separately.",
465 "DETECTOR":
"One kernel per detector",
468 brighterFatterKernelFile = pexConfig.Field(
471 doc=
"Kernel file used for the brighter fatter correction" 473 brighterFatterMaxIter = pexConfig.Field(
476 doc=
"Maximum number of iterations for the brighter fatter correction" 478 brighterFatterThreshold = pexConfig.Field(
481 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the " 482 " absolute value of the difference between the current corrected image and the one" 483 " from the previous iteration summed over all the pixels." 485 brighterFatterApplyGain = pexConfig.Field(
488 doc=
"Should the gain be applied when applying the brighter fatter correction?" 492 doDark = pexConfig.Field(
494 doc=
"Apply dark frame correction?",
497 darkDataProductName = pexConfig.Field(
499 doc=
"Name of the dark data product",
504 doStrayLight = pexConfig.Field(
506 doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
509 strayLight = pexConfig.ConfigurableField(
510 target=StrayLightTask,
511 doc=
"y-band stray light correction" 515 doFlat = pexConfig.Field(
517 doc=
"Apply flat field correction?",
520 flatDataProductName = pexConfig.Field(
522 doc=
"Name of the flat data product",
525 flatScalingType = pexConfig.ChoiceField(
527 doc=
"The method for scaling the flat on the fly.",
530 "USER":
"Scale by flatUserScale",
531 "MEAN":
"Scale by the inverse of the mean",
532 "MEDIAN":
"Scale by the inverse of the median",
535 flatUserScale = pexConfig.Field(
537 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
540 doTweakFlat = pexConfig.Field(
542 doc=
"Tweak flats to match observed amplifier ratios?",
547 doApplyGains = pexConfig.Field(
549 doc=
"Correct the amplifiers for their gains instead of applying flat correction",
552 normalizeGains = pexConfig.Field(
554 doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
559 doFringe = pexConfig.Field(
561 doc=
"Apply fringe correction?",
564 fringe = pexConfig.ConfigurableField(
566 doc=
"Fringe subtraction task",
568 fringeAfterFlat = pexConfig.Field(
570 doc=
"Do fringe subtraction after flat-fielding?",
575 doAddDistortionModel = pexConfig.Field(
577 doc=
"Apply a distortion model based on camera geometry to the WCS?",
582 doMeasureBackground = pexConfig.Field(
584 doc=
"Measure the background level on the reduced image?",
589 doCameraSpecificMasking = pexConfig.Field(
591 doc=
"Mask camera-specific bad regions?",
594 masking = pexConfig.ConfigurableField(
601 doInterpolate = pexConfig.Field(
603 doc=
"Interpolate masked pixels?",
606 doSaturationInterpolation = pexConfig.Field(
608 doc=
"Perform interpolation over pixels masked as saturated?" 609 " NB: This is independent of doSaturation; if that is False this plane" 610 " will likely be blank, resulting in a no-op here.",
613 doNanInterpolation = pexConfig.Field(
615 doc=
"Perform interpolation over pixels masked as NaN?" 616 " NB: This is independent of doNanMasking; if that is False this plane" 617 " will likely be blank, resulting in a no-op here.",
620 doNanInterpAfterFlat = pexConfig.Field(
622 doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 623 "also have to interpolate them before flat-fielding."),
626 maskListToInterpolate = pexConfig.ListField(
628 doc=
"List of mask planes that should be interpolated.",
629 default=[
'SAT',
'BAD',
'UNMASKEDNAN'],
631 doSaveInterpPixels = pexConfig.Field(
633 doc=
"Save a copy of the pre-interpolated pixel values?",
638 fluxMag0T1 = pexConfig.DictField(
641 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
642 default=dict((f, pow(10.0, 0.4*m))
for f, m
in ((
"Unknown", 28.0),
645 defaultFluxMag0T1 = pexConfig.Field(
647 doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
648 default=pow(10.0, 0.4*28.0)
652 doVignette = pexConfig.Field(
654 doc=
"Apply vignetting parameters?",
657 vignette = pexConfig.ConfigurableField(
659 doc=
"Vignetting task.",
663 doAttachTransmissionCurve = pexConfig.Field(
666 doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 668 doUseOpticsTransmission = pexConfig.Field(
671 doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 673 doUseFilterTransmission = pexConfig.Field(
676 doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 678 doUseSensorTransmission = pexConfig.Field(
681 doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 683 doUseAtmosphereTransmission = pexConfig.Field(
686 doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 690 doIlluminationCorrection = pexConfig.Field(
693 doc=
"Perform illumination correction?" 695 illuminationCorrectionDataProductName = pexConfig.Field(
697 doc=
"Name of the illumination correction data product.",
700 illumScale = pexConfig.Field(
702 doc=
"Scale factor for the illumination correction.",
705 illumFilters = pexConfig.ListField(
708 doc=
"Only perform illumination correction for these filters." 712 doWrite = pexConfig.Field(
714 doc=
"Persist postISRCCD?",
721 raise ValueError(
"You may not specify both doFlat and doApplyGains")
723 self.config.maskListToInterpolate.append(
"SAT")
725 self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
728 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
729 """Apply common instrument signature correction algorithms to a raw frame. 731 The process for correcting imaging data is very similar from 732 camera to camera. This task provides a vanilla implementation of 733 doing these corrections, including the ability to turn certain 734 corrections off if they are not needed. The inputs to the primary 735 method, `run()`, are a raw exposure to be corrected and the 736 calibration data products. The raw input is a single chip sized 737 mosaic of all amps including overscans and other non-science 738 pixels. The method `runDataRef()` identifies and defines the 739 calibration data products, and is intended for use by a 740 `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 741 `daf.persistence.butlerSubset.ButlerDataRef`. This task may be 742 subclassed for different camera, although the most camera specific 743 methods have been split into subtasks that can be redirected 746 The __init__ method sets up the subtasks for ISR processing, using 747 the defaults from `lsst.ip.isr`. 752 Positional arguments passed to the Task constructor. None used at this time. 753 kwargs : `dict`, optional 754 Keyword arguments passed on to the Task constructor. None used at this time. 756 ConfigClass = IsrTaskConfig
761 self.makeSubtask(
"assembleCcd")
762 self.makeSubtask(
"crosstalk")
763 self.makeSubtask(
"strayLight")
764 self.makeSubtask(
"fringe")
765 self.makeSubtask(
"masking")
766 self.makeSubtask(
"vignette")
775 if config.doBias
is not True:
776 inputTypeDict.pop(
"bias",
None)
777 if config.doLinearize
is not True:
778 inputTypeDict.pop(
"linearizer",
None)
779 if config.doCrosstalk
is not True:
780 inputTypeDict.pop(
"crosstalkSources",
None)
781 if config.doBrighterFatter
is not True:
782 inputTypeDict.pop(
"bfKernel",
None)
783 if config.doDefect
is not True:
784 inputTypeDict.pop(
"defects",
None)
785 if config.doDark
is not True:
786 inputTypeDict.pop(
"dark",
None)
787 if config.doFlat
is not True:
788 inputTypeDict.pop(
"flat",
None)
789 if config.doAttachTransmissionCurve
is not True:
790 inputTypeDict.pop(
"opticsTransmission",
None)
791 inputTypeDict.pop(
"filterTransmission",
None)
792 inputTypeDict.pop(
"sensorTransmission",
None)
793 inputTypeDict.pop(
"atmosphereTransmission",
None)
794 if config.doUseOpticsTransmission
is not True:
795 inputTypeDict.pop(
"opticsTransmission",
None)
796 if config.doUseFilterTransmission
is not True:
797 inputTypeDict.pop(
"filterTransmission",
None)
798 if config.doUseSensorTransmission
is not True:
799 inputTypeDict.pop(
"sensorTransmission",
None)
800 if config.doUseAtmosphereTransmission
is not True:
801 inputTypeDict.pop(
"atmosphereTransmission",
None)
802 if config.doIlluminationCorrection
is not True:
803 inputTypeDict.pop(
"illumMaskedImage",
None)
811 if config.qa.doThumbnailOss
is not True:
812 outputTypeDict.pop(
"outputOssThumbnail",
None)
813 if config.qa.doThumbnailFlattened
is not True:
814 outputTypeDict.pop(
"outputFlattenedThumbnail",
None)
815 if config.doWrite
is not True:
816 outputTypeDict.pop(
"outputExposure",
None)
818 return outputTypeDict
827 names.remove(
"ccdExposure")
836 return frozenset([
"calibration_label"])
840 inputData[
'detectorNum'] = int(inputDataIds[
'ccdExposure'][
'detector'])
841 except Exception
as e:
842 raise ValueError(f
"Failure to find valid detectorNum value for Dataset {inputDataIds}: {e}")
844 inputData[
'isGen3'] =
True 846 if self.config.doLinearize
is True:
847 if 'linearizer' not in inputData.keys():
848 detector = inputData[
'camera'][inputData[
'detectorNum']]
849 linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
850 inputData[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
852 if inputData[
'defects']
is not None:
855 if not isinstance(inputData[
"defects"], Defects):
856 inputData[
"defects"] = Defects.fromTable(inputData[
"defects"])
873 return super().
adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
879 """!Retrieve necessary frames for instrument signature removal. 881 Pre-fetching all required ISR data products limits the IO 882 required by the ISR. Any conflict between the calibration data 883 available and that needed for ISR is also detected prior to 884 doing processing, allowing it to fail quickly. 888 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 889 Butler reference of the detector data to be processed 890 rawExposure : `afw.image.Exposure` 891 The raw exposure that will later be corrected with the 892 retrieved calibration data; should not be modified in this 897 result : `lsst.pipe.base.Struct` 898 Result struct with components (which may be `None`): 899 - ``bias``: bias calibration frame (`afw.image.Exposure`) 900 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 901 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 902 - ``dark``: dark calibration frame (`afw.image.Exposure`) 903 - ``flat``: flat calibration frame (`afw.image.Exposure`) 904 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 905 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 906 - ``fringes``: `lsst.pipe.base.Struct` with components: 907 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 908 - ``seed``: random seed derived from the ccdExposureId for random 909 number generator (`uint32`). 910 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 911 A ``TransmissionCurve`` that represents the throughput of the optics, 912 to be evaluated in focal-plane coordinates. 913 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 914 A ``TransmissionCurve`` that represents the throughput of the filter 915 itself, to be evaluated in focal-plane coordinates. 916 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 917 A ``TransmissionCurve`` that represents the throughput of the sensor 918 itself, to be evaluated in post-assembly trimmed detector coordinates. 919 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 920 A ``TransmissionCurve`` that represents the throughput of the 921 atmosphere, assumed to be spatially constant. 922 - ``strayLightData`` : `object` 923 An opaque object containing calibration information for 924 stray-light correction. If `None`, no correction will be 926 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 930 NotImplementedError : 931 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 933 ccd = rawExposure.getDetector()
934 filterName = afwImage.Filter(rawExposure.getFilter().getId()).getName()
935 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
936 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
937 if self.config.doBias
else None)
939 linearizer = (dataRef.get(
"linearizer", immediate=
True)
941 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
942 if self.config.doCrosstalk
else None)
943 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
944 if self.config.doDark
else None)
945 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
946 if self.config.doFlat
else None)
948 brighterFatterKernel =
None 949 if self.config.doBrighterFatter
is True:
953 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
957 brighterFatterKernel = dataRef.get(
"bfKernel")
959 brighterFatterKernel =
None 960 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
963 if self.config.brighterFatterLevel ==
'DETECTOR':
964 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
967 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
969 defectList = (dataRef.get(
"defects")
970 if self.config.doDefect
else None)
971 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
972 if self.config.doAssembleIsrExposures
else None)
973 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
974 else pipeBase.Struct(fringes=
None))
976 if self.config.doAttachTransmissionCurve:
977 opticsTransmission = (dataRef.get(
"transmission_optics")
978 if self.config.doUseOpticsTransmission
else None)
979 filterTransmission = (dataRef.get(
"transmission_filter")
980 if self.config.doUseFilterTransmission
else None)
981 sensorTransmission = (dataRef.get(
"transmission_sensor")
982 if self.config.doUseSensorTransmission
else None)
983 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
984 if self.config.doUseAtmosphereTransmission
else None)
986 opticsTransmission =
None 987 filterTransmission =
None 988 sensorTransmission =
None 989 atmosphereTransmission =
None 991 if self.config.doStrayLight:
992 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
994 strayLightData =
None 997 self.config.illuminationCorrectionDataProductName).getMaskedImage()
998 if (self.config.doIlluminationCorrection
and 999 filterName
in self.config.illumFilters)
1003 return pipeBase.Struct(bias=biasExposure,
1004 linearizer=linearizer,
1005 crosstalkSources=crosstalkSources,
1008 bfKernel=brighterFatterKernel,
1010 fringes=fringeStruct,
1011 opticsTransmission=opticsTransmission,
1012 filterTransmission=filterTransmission,
1013 sensorTransmission=sensorTransmission,
1014 atmosphereTransmission=atmosphereTransmission,
1015 strayLightData=strayLightData,
1016 illumMaskedImage=illumMaskedImage
1019 @pipeBase.timeMethod
1020 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1021 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
1022 opticsTransmission=
None, filterTransmission=
None,
1023 sensorTransmission=
None, atmosphereTransmission=
None,
1024 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1027 """!Perform instrument signature removal on an exposure. 1029 Steps included in the ISR processing, in order performed, are: 1030 - saturation and suspect pixel masking 1031 - overscan subtraction 1032 - CCD assembly of individual amplifiers 1034 - variance image construction 1035 - linearization of non-linear response 1037 - brighter-fatter correction 1040 - stray light subtraction 1042 - masking of known defects and camera specific features 1043 - vignette calculation 1044 - appending transmission curve and distortion model 1048 ccdExposure : `lsst.afw.image.Exposure` 1049 The raw exposure that is to be run through ISR. The 1050 exposure is modified by this method. 1051 camera : `lsst.afw.cameraGeom.Camera`, optional 1052 The camera geometry for this exposure. Used to select the 1053 distortion model appropriate for this data. 1054 bias : `lsst.afw.image.Exposure`, optional 1055 Bias calibration frame. 1056 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1057 Functor for linearization. 1058 crosstalkSources : `list`, optional 1059 List of possible crosstalk sources. 1060 dark : `lsst.afw.image.Exposure`, optional 1061 Dark calibration frame. 1062 flat : `lsst.afw.image.Exposure`, optional 1063 Flat calibration frame. 1064 bfKernel : `numpy.ndarray`, optional 1065 Brighter-fatter kernel. 1066 defects : `lsst.meas.algorithms.Defects`, optional 1068 fringes : `lsst.pipe.base.Struct`, optional 1069 Struct containing the fringe correction data, with 1071 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1072 - ``seed``: random seed derived from the ccdExposureId for random 1073 number generator (`uint32`) 1074 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1075 A ``TransmissionCurve`` that represents the throughput of the optics, 1076 to be evaluated in focal-plane coordinates. 1077 filterTransmission : `lsst.afw.image.TransmissionCurve` 1078 A ``TransmissionCurve`` that represents the throughput of the filter 1079 itself, to be evaluated in focal-plane coordinates. 1080 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1081 A ``TransmissionCurve`` that represents the throughput of the sensor 1082 itself, to be evaluated in post-assembly trimmed detector coordinates. 1083 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1084 A ``TransmissionCurve`` that represents the throughput of the 1085 atmosphere, assumed to be spatially constant. 1086 detectorNum : `int`, optional 1087 The integer number for the detector to process. 1088 isGen3 : bool, optional 1089 Flag this call to run() as using the Gen3 butler environment. 1090 strayLightData : `object`, optional 1091 Opaque object containing calibration information for stray-light 1092 correction. If `None`, no correction will be performed. 1093 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1094 Illumination correction image. 1098 result : `lsst.pipe.base.Struct` 1099 Result struct with component: 1100 - ``exposure`` : `afw.image.Exposure` 1101 The fully ISR corrected exposure. 1102 - ``outputExposure`` : `afw.image.Exposure` 1103 An alias for `exposure` 1104 - ``ossThumb`` : `numpy.ndarray` 1105 Thumbnail image of the exposure after overscan subtraction. 1106 - ``flattenedThumb`` : `numpy.ndarray` 1107 Thumbnail image of the exposure after flat-field correction. 1112 Raised if a configuration option is set to True, but the 1113 required calibration data has not been specified. 1117 The current processed exposure can be viewed by setting the 1118 appropriate lsstDebug entries in the `debug.display` 1119 dictionary. The names of these entries correspond to some of 1120 the IsrTaskConfig Boolean options, with the value denoting the 1121 frame to use. The exposure is shown inside the matching 1122 option check and after the processing of that step has 1123 finished. The steps with debug points are: 1134 In addition, setting the "postISRCCD" entry displays the 1135 exposure after all ISR processing has finished. 1143 self.config.doFringe =
False 1146 if detectorNum
is None:
1147 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1149 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1154 if isinstance(ccdExposure, ButlerDataRef):
1157 ccd = ccdExposure.getDetector()
1158 filterName = afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1161 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1162 ccd = [
FakeAmp(ccdExposure, self.config)]
1165 if self.config.doBias
and bias
is None:
1166 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1168 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1169 if self.config.doBrighterFatter
and bfKernel
is None:
1170 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1171 if self.config.doDark
and dark
is None:
1172 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1173 if self.config.doFlat
and flat
is None:
1174 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1175 if self.config.doDefect
and defects
is None:
1176 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1177 if self.config.doAddDistortionModel
and camera
is None:
1178 raise RuntimeError(
"Must supply camera if config.doAddDistortionModel=True.")
1179 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1180 fringes.fringes
is None):
1185 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1186 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1187 illumMaskedImage
is None):
1188 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1191 if self.config.doConvertIntToFloat:
1192 self.log.info(
"Converting exposure to floating point values.")
1199 if ccdExposure.getBBox().contains(amp.getBBox()):
1203 if self.config.doOverscan
and not badAmp:
1206 self.log.debug(f
"Corrected overscan for amplifier {amp.getName()}.")
1207 if overscanResults
is not None and \
1208 self.config.qa
is not None and self.config.qa.saveStats
is True:
1209 if isinstance(overscanResults.overscanFit, float):
1210 qaMedian = overscanResults.overscanFit
1211 qaStdev = float(
"NaN")
1213 qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1214 afwMath.MEDIAN | afwMath.STDEVCLIP)
1215 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1216 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1218 self.metadata.set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1219 self.metadata.set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1220 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f" %
1221 (amp.getName(), qaMedian, qaStdev))
1222 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1225 self.log.warn(f
"Amplifier {amp.getName()} is bad.")
1226 overscanResults =
None 1228 overscans.append(overscanResults
if overscanResults
is not None else None)
1230 self.log.info(f
"Skipped OSCAN for {amp.getName()}.")
1232 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1233 self.log.info(
"Applying crosstalk correction.")
1234 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1235 self.
debugView(ccdExposure,
"doCrosstalk")
1237 if self.config.doAssembleCcd:
1238 self.log.info(
"Assembling CCD from amplifiers.")
1239 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1241 if self.config.expectWcs
and not ccdExposure.getWcs():
1242 self.log.warn(
"No WCS found in input exposure.")
1243 self.
debugView(ccdExposure,
"doAssembleCcd")
1246 if self.config.qa.doThumbnailOss:
1247 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1249 if self.config.doBias:
1250 self.log.info(
"Applying bias correction.")
1251 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1252 trimToFit=self.config.doTrimToMatchCalib)
1255 if self.config.doVariance:
1256 for amp, overscanResults
in zip(ccd, overscans):
1257 if ccdExposure.getBBox().contains(amp.getBBox()):
1258 self.log.debug(f
"Constructing variance map for amplifer {amp.getName()}.")
1259 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1260 if overscanResults
is not None:
1262 overscanImage=overscanResults.overscanImage)
1266 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1267 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1268 afwMath.MEDIAN | afwMath.STDEVCLIP)
1269 self.metadata.set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1270 qaStats.getValue(afwMath.MEDIAN))
1271 self.metadata.set(f
"ISR VARIANCE {amp.getName()} STDEV",
1272 qaStats.getValue(afwMath.STDEVCLIP))
1273 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f." %
1274 (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1275 qaStats.getValue(afwMath.STDEVCLIP)))
1278 self.log.info(
"Applying linearizer.")
1279 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1281 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1282 self.log.info(
"Applying crosstalk correction.")
1283 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1284 self.
debugView(ccdExposure,
"doCrosstalk")
1288 if self.config.doDefect:
1289 self.log.info(
"Masking defects.")
1292 if self.config.doNanMasking:
1293 self.log.info(
"Masking NAN value pixels.")
1296 if self.config.doWidenSaturationTrails:
1297 self.log.info(
"Widening saturation trails.")
1298 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1300 if self.config.doCameraSpecificMasking:
1301 self.log.info(
"Masking regions for camera specific reasons.")
1302 self.masking.
run(ccdExposure)
1304 if self.config.doBrighterFatter:
1313 interpExp = ccdExposure.clone()
1315 isrFunctions.interpolateFromMask(
1316 maskedImage=interpExp.getMaskedImage(),
1317 fwhm=self.config.fwhm,
1318 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1319 maskNameList=self.config.maskListToInterpolate
1321 bfExp = interpExp.clone()
1323 self.log.info(
"Applying brighter fatter correction.")
1324 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1325 self.config.brighterFatterMaxIter,
1326 self.config.brighterFatterThreshold,
1327 self.config.brighterFatterApplyGain
1329 if bfResults[1] == self.config.brighterFatterMaxIter:
1330 self.log.warn(
"Brighter fatter correction did not converge, final difference {bfResults[0]}.")
1332 self.log.info(
"Finished brighter fatter correction in {bfResults[1]} iterations.")
1333 image = ccdExposure.getMaskedImage().getImage()
1334 bfCorr = bfExp.getMaskedImage().getImage()
1335 bfCorr -= interpExp.getMaskedImage().getImage()
1338 self.
debugView(ccdExposure,
"doBrighterFatter")
1340 if self.config.doDark:
1341 self.log.info(
"Applying dark correction.")
1345 if self.config.doFringe
and not self.config.fringeAfterFlat:
1346 self.log.info(
"Applying fringe correction before flat.")
1347 self.fringe.
run(ccdExposure, **fringes.getDict())
1350 if self.config.doStrayLight:
1351 if strayLightData
is not None:
1352 self.log.info(
"Applying stray light correction.")
1353 self.strayLight.
run(ccdExposure, strayLightData)
1354 self.
debugView(ccdExposure,
"doStrayLight")
1356 self.log.debug(
"Skipping stray light correction: no data found for this image.")
1358 if self.config.doFlat:
1359 self.log.info(
"Applying flat correction.")
1363 if self.config.doApplyGains:
1364 self.log.info(
"Applying gain correction instead of flat.")
1365 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1367 if self.config.doFringe
and self.config.fringeAfterFlat:
1368 self.log.info(
"Applying fringe correction after flat.")
1369 self.fringe.
run(ccdExposure, **fringes.getDict())
1371 if self.config.doVignette:
1372 self.log.info(
"Constructing Vignette polygon.")
1375 if self.config.vignette.doWriteVignettePolygon:
1378 if self.config.doAttachTransmissionCurve:
1379 self.log.info(
"Adding transmission curves.")
1380 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1381 filterTransmission=filterTransmission,
1382 sensorTransmission=sensorTransmission,
1383 atmosphereTransmission=atmosphereTransmission)
1385 if self.config.doAddDistortionModel:
1386 self.log.info(
"Adding a distortion model to the WCS.")
1387 isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1389 flattenedThumb =
None 1390 if self.config.qa.doThumbnailFlattened:
1391 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1393 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1394 self.log.info(
"Performing illumination correction.")
1395 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1396 illumMaskedImage, illumScale=self.config.illumScale,
1397 trimToFit=self.config.doTrimToMatchCalib)
1400 if self.config.doSaveInterpPixels:
1401 preInterpExp = ccdExposure.clone()
1416 if self.config.doSetBadRegions:
1417 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1418 if badPixelCount > 0:
1419 self.log.info(
"Set %d BAD pixels to %f." % (badPixelCount, badPixelValue))
1421 if self.config.doInterpolate:
1422 self.log.info(
"Interpolating masked pixels.")
1423 isrFunctions.interpolateFromMask(
1424 maskedImage=ccdExposure.getMaskedImage(),
1425 fwhm=self.config.fwhm,
1426 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1427 maskNameList=list(self.config.maskListToInterpolate)
1432 if self.config.doMeasureBackground:
1433 self.log.info(
"Measuring background level.")
1436 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1438 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1439 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1440 afwMath.MEDIAN | afwMath.STDEVCLIP)
1441 self.metadata.set(
"ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1442 qaStats.getValue(afwMath.MEDIAN))
1443 self.metadata.set(
"ISR BACKGROUND {} STDEV".format(amp.getName()),
1444 qaStats.getValue(afwMath.STDEVCLIP))
1445 self.log.debug(
" Background stats for amplifer %s: %f +/- %f" %
1446 (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1447 qaStats.getValue(afwMath.STDEVCLIP)))
1449 self.
debugView(ccdExposure,
"postISRCCD")
1451 return pipeBase.Struct(
1452 exposure=ccdExposure,
1454 flattenedThumb=flattenedThumb,
1456 preInterpolatedExposure=preInterpExp,
1457 outputExposure=ccdExposure,
1458 outputOssThumbnail=ossThumb,
1459 outputFlattenedThumbnail=flattenedThumb,
1462 @pipeBase.timeMethod
1464 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1466 This method contains the `CmdLineTask` interface to the ISR 1467 processing. All IO is handled here, freeing the `run()` method 1468 to manage only pixel-level calculations. The steps performed 1470 - Read in necessary detrending/isr/calibration data. 1471 - Process raw exposure in `run()`. 1472 - Persist the ISR-corrected exposure as "postISRCCD" if 1473 config.doWrite=True. 1477 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1478 DataRef of the detector data to be processed 1482 result : `lsst.pipe.base.Struct` 1483 Result struct with component: 1484 - ``exposure`` : `afw.image.Exposure` 1485 The fully ISR corrected exposure. 1490 Raised if a configuration option is set to True, but the 1491 required calibration data does not exist. 1494 self.log.info(
"Performing ISR on sensor %s." % (sensorRef.dataId))
1496 ccdExposure = sensorRef.get(self.config.datasetType)
1498 camera = sensorRef.get(
"camera")
1499 if camera
is None and self.config.doAddDistortionModel:
1500 raise RuntimeError(
"config.doAddDistortionModel is True " 1501 "but could not get a camera from the butler.")
1502 isrData = self.
readIsrData(sensorRef, ccdExposure)
1504 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1506 if self.config.doWrite:
1507 sensorRef.put(result.exposure,
"postISRCCD")
1508 if result.preInterpolatedExposure
is not None:
1509 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1510 if result.ossThumb
is not None:
1511 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1512 if result.flattenedThumb
is not None:
1513 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1518 """!Retrieve a calibration dataset for removing instrument signature. 1523 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1524 DataRef of the detector data to find calibration datasets 1527 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1529 If True, disable butler proxies to enable error handling 1530 within this routine. 1534 exposure : `lsst.afw.image.Exposure` 1535 Requested calibration frame. 1540 Raised if no matching calibration frame can be found. 1543 exp = dataRef.get(datasetType, immediate=immediate)
1544 except Exception
as exc1:
1545 if not self.config.fallbackFilterName:
1546 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1548 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1549 except Exception
as exc2:
1550 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1551 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1552 self.log.warn(
"Using fallback calibration from filter %s." % self.config.fallbackFilterName)
1554 if self.config.doAssembleIsrExposures:
1555 exp = self.assembleCcd.assembleCcd(exp)
1559 """Ensure that the data returned by Butler is a fully constructed exposure. 1561 ISR requires exposure-level image data for historical reasons, so if we did 1562 not recieve that from Butler, construct it from what we have, modifying the 1567 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1568 `lsst.afw.image.ImageF` 1569 The input data structure obtained from Butler. 1570 camera : `lsst.afw.cameraGeom.camera` 1571 The camera associated with the image. Used to find the appropriate 1574 The detector this exposure should match. 1578 inputExp : `lsst.afw.image.Exposure` 1579 The re-constructed exposure, with appropriate detector parameters. 1584 Raised if the input data cannot be used to construct an exposure. 1586 if isinstance(inputExp, afwImage.DecoratedImageU):
1587 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1588 elif isinstance(inputExp, afwImage.ImageF):
1589 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1590 elif isinstance(inputExp, afwImage.MaskedImageF):
1591 inputExp = afwImage.makeExposure(inputExp)
1592 elif isinstance(inputExp, afwImage.Exposure):
1594 elif inputExp
is None:
1598 raise TypeError(f
"Input Exposure is not known type in isrTask.ensureExposure: {type(inputExp)}.")
1600 if inputExp.getDetector()
is None:
1601 inputExp.setDetector(camera[detectorNum])
1606 """Convert exposure image from uint16 to float. 1608 If the exposure does not need to be converted, the input is 1609 immediately returned. For exposures that are converted to use 1610 floating point pixels, the variance is set to unity and the 1615 exposure : `lsst.afw.image.Exposure` 1616 The raw exposure to be converted. 1620 newexposure : `lsst.afw.image.Exposure` 1621 The input ``exposure``, converted to floating point pixels. 1626 Raised if the exposure type cannot be converted to float. 1629 if isinstance(exposure, afwImage.ExposureF):
1631 self.log.debug(
"Exposure already of type float.")
1633 if not hasattr(exposure,
"convertF"):
1634 raise RuntimeError(
"Unable to convert exposure (%s) to float." % type(exposure))
1636 newexposure = exposure.convertF()
1637 newexposure.variance[:] = 1
1638 newexposure.mask[:] = 0x0
1643 """Identify bad amplifiers, saturated and suspect pixels. 1647 ccdExposure : `lsst.afw.image.Exposure` 1648 Input exposure to be masked. 1649 amp : `lsst.afw.table.AmpInfoCatalog` 1650 Catalog of parameters defining the amplifier on this 1652 defects : `lsst.meas.algorithms.Defects` 1653 List of defects. Used to determine if the entire 1659 If this is true, the entire amplifier area is covered by 1660 defects and unusable. 1663 maskedImage = ccdExposure.getMaskedImage()
1669 if defects
is not None:
1670 badAmp = bool(sum([v.getBBox().contains(amp.getBBox())
for v
in defects]))
1675 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1677 maskView = dataView.getMask()
1678 maskView |= maskView.getPlaneBitMask(
"BAD")
1685 if self.config.doSaturation
and not badAmp:
1686 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1687 if self.config.doSuspect
and not badAmp:
1688 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1689 if math.isfinite(self.config.saturation):
1690 limits.update({self.config.saturatedMaskName: self.config.saturation})
1692 for maskName, maskThreshold
in limits.items():
1693 if not math.isnan(maskThreshold):
1694 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1695 isrFunctions.makeThresholdMask(
1696 maskedImage=dataView,
1697 threshold=maskThreshold,
1703 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1705 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1706 self.config.suspectMaskName])
1707 if numpy.all(maskView.getArray() & maskVal > 0):
1709 maskView |= maskView.getPlaneBitMask(
"BAD")
1714 """Apply overscan correction in place. 1716 This method does initial pixel rejection of the overscan 1717 region. The overscan can also be optionally segmented to 1718 allow for discontinuous overscan responses to be fit 1719 separately. The actual overscan subtraction is performed by 1720 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1721 which is called here after the amplifier is preprocessed. 1725 ccdExposure : `lsst.afw.image.Exposure` 1726 Exposure to have overscan correction performed. 1727 amp : `lsst.afw.table.AmpInfoCatalog` 1728 The amplifier to consider while correcting the overscan. 1732 overscanResults : `lsst.pipe.base.Struct` 1733 Result struct with components: 1734 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1735 Value or fit subtracted from the amplifier image data. 1736 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1737 Value or fit subtracted from the overscan image data. 1738 - ``overscanImage`` : `lsst.afw.image.Image` 1739 Image of the overscan region with the overscan 1740 correction applied. This quantity is used to estimate 1741 the amplifier read noise empirically. 1746 Raised if the ``amp`` does not contain raw pixel information. 1750 lsst.ip.isr.isrFunctions.overscanCorrection 1752 if not amp.getHasRawInfo():
1753 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1755 if amp.getRawHorizontalOverscanBBox().isEmpty():
1756 self.log.info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1759 statControl = afwMath.StatisticsControl()
1760 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1763 dataBBox = amp.getRawDataBBox()
1764 oscanBBox = amp.getRawHorizontalOverscanBBox()
1768 prescanBBox = amp.getRawPrescanBBox()
1769 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1770 dx0 += self.config.overscanNumLeadingColumnsToSkip
1771 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1773 dx0 += self.config.overscanNumTrailingColumnsToSkip
1774 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1780 if ((self.config.overscanBiasJump
and 1781 self.config.overscanBiasJumpLocation)
and 1782 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1783 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1784 self.config.overscanBiasJumpDevices)):
1785 if amp.getReadoutCorner()
in (afwTable.LL, afwTable.LR):
1786 yLower = self.config.overscanBiasJumpLocation
1787 yUpper = dataBBox.getHeight() - yLower
1789 yUpper = self.config.overscanBiasJumpLocation
1790 yLower = dataBBox.getHeight() - yUpper
1809 oscanBBox.getHeight())))
1812 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1813 ampImage = ccdExposure.maskedImage[imageBBox]
1814 overscanImage = ccdExposure.maskedImage[overscanBBox]
1816 overscanArray = overscanImage.image.array
1817 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1818 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1819 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1821 statControl = afwMath.StatisticsControl()
1822 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1824 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1825 overscanImage=overscanImage,
1826 fitType=self.config.overscanFitType,
1827 order=self.config.overscanOrder,
1828 collapseRej=self.config.overscanNumSigmaClip,
1829 statControl=statControl,
1830 overscanIsInt=self.config.overscanIsInt
1834 levelStat = afwMath.MEDIAN
1835 sigmaStat = afwMath.STDEVCLIP
1837 sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1838 self.config.qa.flatness.nIter)
1839 metadata = ccdExposure.getMetadata()
1840 ampNum = amp.getName()
1841 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1842 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1843 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1845 stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1846 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1847 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1849 return overscanResults
1852 """Set the variance plane using the amplifier gain and read noise 1854 The read noise is calculated from the ``overscanImage`` if the 1855 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1856 the value from the amplifier data is used. 1860 ampExposure : `lsst.afw.image.Exposure` 1861 Exposure to process. 1862 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1863 Amplifier detector data. 1864 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1865 Image of overscan, required only for empirical read noise. 1869 lsst.ip.isr.isrFunctions.updateVariance 1871 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1872 gain = amp.getGain()
1874 if math.isnan(gain):
1876 self.log.warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1879 self.log.warn(
"Gain for amp %s == %g <= 0; setting to %f." %
1880 (amp.getName(), gain, patchedGain))
1883 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1884 self.log.info(
"Overscan is none for EmpiricalReadNoise.")
1886 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1887 stats = afwMath.StatisticsControl()
1888 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1889 readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1890 self.log.info(
"Calculated empirical read noise for amp %s: %f.", amp.getName(), readNoise)
1892 readNoise = amp.getReadNoise()
1894 isrFunctions.updateVariance(
1895 maskedImage=ampExposure.getMaskedImage(),
1897 readNoise=readNoise,
1901 """!Apply dark correction in place. 1905 exposure : `lsst.afw.image.Exposure` 1906 Exposure to process. 1907 darkExposure : `lsst.afw.image.Exposure` 1908 Dark exposure of the same size as ``exposure``. 1909 invert : `Bool`, optional 1910 If True, re-add the dark to an already corrected image. 1915 Raised if either ``exposure`` or ``darkExposure`` do not 1916 have their dark time defined. 1920 lsst.ip.isr.isrFunctions.darkCorrection 1922 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1923 if math.isnan(expScale):
1924 raise RuntimeError(
"Exposure darktime is NAN.")
1925 if darkExposure.getInfo().getVisitInfo()
is not None:
1926 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1930 self.log.warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1933 if math.isnan(darkScale):
1934 raise RuntimeError(
"Dark calib darktime is NAN.")
1935 isrFunctions.darkCorrection(
1936 maskedImage=exposure.getMaskedImage(),
1937 darkMaskedImage=darkExposure.getMaskedImage(),
1939 darkScale=darkScale,
1941 trimToFit=self.config.doTrimToMatchCalib
1945 """!Check if linearization is needed for the detector cameraGeom. 1947 Checks config.doLinearize and the linearity type of the first 1952 detector : `lsst.afw.cameraGeom.Detector` 1953 Detector to get linearity type from. 1957 doLinearize : `Bool` 1958 If True, linearization should be performed. 1960 return self.config.doLinearize
and \
1961 detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1964 """!Apply flat correction in place. 1968 exposure : `lsst.afw.image.Exposure` 1969 Exposure to process. 1970 flatExposure : `lsst.afw.image.Exposure` 1971 Flat exposure of the same size as ``exposure``. 1972 invert : `Bool`, optional 1973 If True, unflatten an already flattened image. 1977 lsst.ip.isr.isrFunctions.flatCorrection 1979 isrFunctions.flatCorrection(
1980 maskedImage=exposure.getMaskedImage(),
1981 flatMaskedImage=flatExposure.getMaskedImage(),
1982 scalingType=self.config.flatScalingType,
1983 userScale=self.config.flatUserScale,
1985 trimToFit=self.config.doTrimToMatchCalib
1989 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1993 exposure : `lsst.afw.image.Exposure` 1994 Exposure to process. Only the amplifier DataSec is processed. 1995 amp : `lsst.afw.table.AmpInfoCatalog` 1996 Amplifier detector data. 2000 lsst.ip.isr.isrFunctions.makeThresholdMask 2002 if not math.isnan(amp.getSaturation()):
2003 maskedImage = exposure.getMaskedImage()
2004 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2005 isrFunctions.makeThresholdMask(
2006 maskedImage=dataView,
2007 threshold=amp.getSaturation(),
2009 maskName=self.config.saturatedMaskName,
2013 """!Interpolate over saturated pixels, in place. 2015 This method should be called after `saturationDetection`, to 2016 ensure that the saturated pixels have been identified in the 2017 SAT mask. It should also be called after `assembleCcd`, since 2018 saturated regions may cross amplifier boundaries. 2022 exposure : `lsst.afw.image.Exposure` 2023 Exposure to process. 2027 lsst.ip.isr.isrTask.saturationDetection 2028 lsst.ip.isr.isrFunctions.interpolateFromMask 2030 isrFunctions.interpolateFromMask(
2031 maskedImage=exposure.getMaskedImage(),
2032 fwhm=self.config.fwhm,
2033 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2034 maskNameList=list(self.config.saturatedMaskName),
2038 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2042 exposure : `lsst.afw.image.Exposure` 2043 Exposure to process. Only the amplifier DataSec is processed. 2044 amp : `lsst.afw.table.AmpInfoCatalog` 2045 Amplifier detector data. 2049 lsst.ip.isr.isrFunctions.makeThresholdMask 2053 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2054 This is intended to indicate pixels that may be affected by unknown systematics; 2055 for example if non-linearity corrections above a certain level are unstable 2056 then that would be a useful value for suspectLevel. A value of `nan` indicates 2057 that no such level exists and no pixels are to be masked as suspicious. 2059 suspectLevel = amp.getSuspectLevel()
2060 if math.isnan(suspectLevel):
2063 maskedImage = exposure.getMaskedImage()
2064 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2065 isrFunctions.makeThresholdMask(
2066 maskedImage=dataView,
2067 threshold=suspectLevel,
2069 maskName=self.config.suspectMaskName,
2073 """!Mask defects using mask plane "BAD", in place. 2077 exposure : `lsst.afw.image.Exposure` 2078 Exposure to process. 2079 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2080 `lsst.afw.image.DefectBase`. 2081 List of defects to mask and interpolate. 2085 Call this after CCD assembly, since defects may cross amplifier boundaries. 2087 maskedImage = exposure.getMaskedImage()
2088 if not isinstance(defectBaseList, Defects):
2090 defectList = Defects(defectBaseList)
2092 defectList = defectBaseList
2093 defectList.maskPixels(maskedImage, maskName=
"BAD")
2095 if self.config.numEdgeSuspect > 0:
2096 goodBBox = maskedImage.getBBox()
2098 goodBBox.grow(-self.config.numEdgeSuspect)
2100 SourceDetectionTask.setEdgeBits(
2103 maskedImage.getMask().getPlaneBitMask(
"SUSPECT")
2107 """Mask and interpolate defects using mask plane "BAD", in place. 2111 exposure : `lsst.afw.image.Exposure` 2112 Exposure to process. 2113 defectBaseList : `List` of `Defects` 2116 self.maskDefects(exposure, defectBaseList)
2117 isrFunctions.interpolateFromMask(
2118 maskedImage=exposure.getMaskedImage(),
2119 fwhm=self.config.fwhm,
2120 growSaturatedFootprints=0,
2121 maskNameList=[
"BAD"],
2125 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2129 exposure : `lsst.afw.image.Exposure` 2130 Exposure to process. 2134 We mask over all NaNs, including those that are masked with 2135 other bits (because those may or may not be interpolated over 2136 later, and we want to remove all NaNs). Despite this 2137 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2138 the historical name. 2140 maskedImage = exposure.getMaskedImage()
2143 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2144 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2145 numNans =
maskNans(maskedImage, maskVal)
2146 self.metadata.set(
"NUMNANS", numNans)
2148 self.log.warn(f
"There were {numNans} unmasked NaNs.")
2151 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2155 exposure : `lsst.afw.image.Exposure` 2156 Exposure to process. 2160 lsst.ip.isr.isrTask.maskNan() 2163 isrFunctions.interpolateFromMask(
2164 maskedImage=exposure.getMaskedImage(),
2165 fwhm=self.config.fwhm,
2166 growSaturatedFootprints=0,
2167 maskNameList=[
"UNMASKEDNAN"],
2171 """Measure the image background in subgrids, for quality control purposes. 2175 exposure : `lsst.afw.image.Exposure` 2176 Exposure to process. 2177 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2178 Configuration object containing parameters on which background 2179 statistics and subgrids to use. 2181 if IsrQaConfig
is not None:
2182 statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
2183 IsrQaConfig.flatness.nIter)
2184 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2185 statsControl.setAndMask(maskVal)
2186 maskedImage = exposure.getMaskedImage()
2187 stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
2188 skyLevel = stats.getValue(afwMath.MEDIAN)
2189 skySigma = stats.getValue(afwMath.STDEVCLIP)
2190 self.log.info(
"Flattened sky level: %f +/- %f." % (skyLevel, skySigma))
2191 metadata = exposure.getMetadata()
2192 metadata.set(
'SKYLEVEL', skyLevel)
2193 metadata.set(
'SKYSIGMA', skySigma)
2196 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2197 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2198 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2199 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2200 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2201 skyLevels = numpy.zeros((nX, nY))
2204 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2206 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2208 xLLC = xc - meshXHalf
2209 yLLC = yc - meshYHalf
2210 xURC = xc + meshXHalf - 1
2211 yURC = yc + meshYHalf - 1
2214 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2216 skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2218 good = numpy.where(numpy.isfinite(skyLevels))
2219 skyMedian = numpy.median(skyLevels[good])
2220 flatness = (skyLevels[good] - skyMedian) / skyMedian
2221 flatness_rms = numpy.std(flatness)
2222 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2224 self.log.info(
"Measuring sky levels in %dx%d grids: %f." % (nX, nY, skyMedian))
2225 self.log.info(
"Sky flatness in %dx%d grids - pp: %f rms: %f." %
2226 (nX, nY, flatness_pp, flatness_rms))
2228 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2229 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2230 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2231 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2232 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2235 """Set an approximate magnitude zero point for the exposure. 2239 exposure : `lsst.afw.image.Exposure` 2240 Exposure to process. 2242 filterName = afwImage.Filter(exposure.getFilter().getId()).getName()
2243 if filterName
in self.config.fluxMag0T1:
2244 fluxMag0 = self.config.fluxMag0T1[filterName]
2246 self.log.warn(
"No rough magnitude zero point set for filter %s." % filterName)
2247 fluxMag0 = self.config.defaultFluxMag0T1
2249 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2251 self.log.warn(
"Non-positive exposure time; skipping rough zero point.")
2254 self.log.info(
"Setting rough magnitude zero point: %f" % (2.5*math.log10(fluxMag0*expTime),))
2255 exposure.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(fluxMag0*expTime, 0.0))
2258 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2262 ccdExposure : `lsst.afw.image.Exposure` 2263 Exposure to process. 2264 fpPolygon : `lsst.afw.geom.Polygon` 2265 Polygon in focal plane coordinates. 2268 ccd = ccdExposure.getDetector()
2269 fpCorners = ccd.getCorners(FOCAL_PLANE)
2270 ccdPolygon = Polygon(fpCorners)
2273 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2276 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2277 validPolygon = Polygon(ccdPoints)
2278 ccdExposure.getInfo().setValidPolygon(validPolygon)
2282 """Context manager that applies and removes flats and darks, 2283 if the task is configured to apply them. 2287 exp : `lsst.afw.image.Exposure` 2288 Exposure to process. 2289 flat : `lsst.afw.image.Exposure` 2290 Flat exposure the same size as ``exp``. 2291 dark : `lsst.afw.image.Exposure`, optional 2292 Dark exposure the same size as ``exp``. 2296 exp : `lsst.afw.image.Exposure` 2297 The flat and dark corrected exposure. 2299 if self.config.doDark
and dark
is not None:
2301 if self.config.doFlat:
2306 if self.config.doFlat:
2308 if self.config.doDark
and dark
is not None:
2312 """Utility function to examine ISR exposure at different stages. 2316 exposure : `lsst.afw.image.Exposure` 2319 State of processing to view. 2321 frame = getDebugFrame(self._display, stepname)
2323 display = getDisplay(frame)
2324 display.scale(
'asinh',
'zscale')
2325 display.mtv(exposure)
2326 prompt =
"Press Enter to continue [c]... " 2328 ans = input(prompt).lower()
2329 if ans
in (
"",
"c",):
2334 """A Detector-like object that supports returning gain and saturation level 2336 This is used when the input exposure does not have a detector. 2340 exposure : `lsst.afw.image.Exposure` 2341 Exposure to generate a fake amplifier for. 2342 config : `lsst.ip.isr.isrTaskConfig` 2343 Configuration to apply to the fake amplifier. 2347 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2349 self.
_gain = config.gain
2379 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2383 """Task to wrap the default IsrTask to allow it to be retargeted. 2385 The standard IsrTask can be called directly from a command line 2386 program, but doing so removes the ability of the task to be 2387 retargeted. As most cameras override some set of the IsrTask 2388 methods, this would remove those data-specific methods in the 2389 output post-ISR images. This wrapping class fixes the issue, 2390 allowing identical post-ISR images to be generated by both the 2391 processCcd and isrTask code. 2393 ConfigClass = RunIsrConfig
2394 _DefaultName =
"runIsr" 2398 self.makeSubtask(
"isr")
2404 dataRef : `lsst.daf.persistence.ButlerDataRef` 2405 data reference of the detector data to be processed 2409 result : `pipeBase.Struct` 2410 Result struct with component: 2412 - exposure : `lsst.afw.image.Exposure` 2413 Post-ISR processed exposure. def getInputDatasetTypes(cls, config)
def runDataRef(self, sensorRef)
def measureBackground(self, exposure, IsrQaConfig=None)
def debugView(self, exposure, stepname)
def __init__(self, kwargs)
def ensureExposure(self, inputExp, camera, detectorNum)
def readIsrData(self, dataRef, rawExposure)
Retrieve necessary frames for instrument signature removal.
def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler)
def runDataRef(self, dataRef)
def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None, dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=None), opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None, detectorNum=None, strayLightData=None, illumMaskedImage=None, isGen3=False)
Perform instrument signature removal on an exposure.
def __init__(self, args, kwargs)
def getPrerequisiteDatasetTypes(cls, config)
def roughZeroPoint(self, exposure)
def maskAndInterpolateDefects(self, exposure, defectBaseList)
def getRawHorizontalOverscanBBox(self)
def maskNan(self, exposure)
def getSuspectLevel(self)
def getOutputDatasetTypes(cls, config)
def maskDefect(self, exposure, defectBaseList)
Mask defects using mask plane "BAD", in place.
def overscanCorrection(self, ccdExposure, amp)
def convertIntToFloat(self, exposure)
def flatCorrection(self, exposure, flatExposure, invert=False)
Apply flat correction in place.
def makeDatasetType(self, dsConfig)
def getIsrExposure(self, dataRef, datasetType, immediate=True)
Retrieve a calibration dataset for removing instrument signature.
_RawHorizontalOverscanBBox
def darkCorrection(self, exposure, darkExposure, invert=False)
Apply dark correction in place.
def doLinearize(self, detector)
Check if linearization is needed for the detector cameraGeom.
def setValidPolygonIntersect(self, ccdExposure, fpPolygon)
Set the valid polygon as the intersection of fpPolygon and the ccd corners.
def maskAmplifier(self, ccdExposure, amp, defects)
def getPerDatasetTypeDimensions(cls, config)
def flatContext(self, exp, flat, dark=None)
size_t maskNans(afw::image::MaskedImage< PixelT > const &mi, afw::image::MaskPixel maskVal, afw::image::MaskPixel allow=0)
Mask NANs in an image.
def updateVariance(self, ampExposure, amp, overscanImage=None)
def maskAndInterpolateNan(self, exposure)
def suspectDetection(self, exposure, amp)
Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place.
def saturationInterpolation(self, exposure)
Interpolate over saturated pixels, in place.
def saturationDetection(self, exposure, amp)
Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place...
doSaturationInterpolation
def __init__(self, exposure, config)