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")
832 inputData[
'detectorNum'] = int(inputDataIds[
'ccdExposure'][
'detector'])
833 except Exception
as e:
834 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
837 inputData[
'isGen3'] =
True 839 if self.config.doLinearize
is True:
840 if 'linearizer' not in inputData.keys():
841 detector = inputData[
'camera'][inputData[
'detectorNum']]
842 linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
843 inputData[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
845 if inputData[
'defects']
is not None:
848 if not isinstance(inputData[
"defects"], Defects):
849 inputData[
"defects"] = Defects.fromTable(inputData[
"defects"])
866 return super().
adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
872 """!Retrieve necessary frames for instrument signature removal. 874 Pre-fetching all required ISR data products limits the IO 875 required by the ISR. Any conflict between the calibration data 876 available and that needed for ISR is also detected prior to 877 doing processing, allowing it to fail quickly. 881 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 882 Butler reference of the detector data to be processed 883 rawExposure : `afw.image.Exposure` 884 The raw exposure that will later be corrected with the 885 retrieved calibration data; should not be modified in this 890 result : `lsst.pipe.base.Struct` 891 Result struct with components (which may be `None`): 892 - ``bias``: bias calibration frame (`afw.image.Exposure`) 893 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 894 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 895 - ``dark``: dark calibration frame (`afw.image.Exposure`) 896 - ``flat``: flat calibration frame (`afw.image.Exposure`) 897 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 898 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 899 - ``fringes``: `lsst.pipe.base.Struct` with components: 900 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 901 - ``seed``: random seed derived from the ccdExposureId for random 902 number generator (`uint32`). 903 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 904 A ``TransmissionCurve`` that represents the throughput of the optics, 905 to be evaluated in focal-plane coordinates. 906 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 907 A ``TransmissionCurve`` that represents the throughput of the filter 908 itself, to be evaluated in focal-plane coordinates. 909 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 910 A ``TransmissionCurve`` that represents the throughput of the sensor 911 itself, to be evaluated in post-assembly trimmed detector coordinates. 912 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 913 A ``TransmissionCurve`` that represents the throughput of the 914 atmosphere, assumed to be spatially constant. 915 - ``strayLightData`` : `object` 916 An opaque object containing calibration information for 917 stray-light correction. If `None`, no correction will be 919 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 923 NotImplementedError : 924 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 926 ccd = rawExposure.getDetector()
927 filterName = afwImage.Filter(rawExposure.getFilter().getId()).getName()
928 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
929 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
930 if self.config.doBias
else None)
932 linearizer = (dataRef.get(
"linearizer", immediate=
True)
934 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
935 if self.config.doCrosstalk
else None)
936 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
937 if self.config.doDark
else None)
938 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
939 if self.config.doFlat
else None)
941 brighterFatterKernel =
None 942 if self.config.doBrighterFatter
is True:
946 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
950 brighterFatterKernel = dataRef.get(
"bfKernel")
952 brighterFatterKernel =
None 953 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
956 if self.config.brighterFatterLevel ==
'DETECTOR':
957 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
960 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
962 defectList = (dataRef.get(
"defects")
963 if self.config.doDefect
else None)
964 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
965 if self.config.doAssembleIsrExposures
else None)
966 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
967 else pipeBase.Struct(fringes=
None))
969 if self.config.doAttachTransmissionCurve:
970 opticsTransmission = (dataRef.get(
"transmission_optics")
971 if self.config.doUseOpticsTransmission
else None)
972 filterTransmission = (dataRef.get(
"transmission_filter")
973 if self.config.doUseFilterTransmission
else None)
974 sensorTransmission = (dataRef.get(
"transmission_sensor")
975 if self.config.doUseSensorTransmission
else None)
976 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
977 if self.config.doUseAtmosphereTransmission
else None)
979 opticsTransmission =
None 980 filterTransmission =
None 981 sensorTransmission =
None 982 atmosphereTransmission =
None 984 if self.config.doStrayLight:
985 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
987 strayLightData =
None 990 self.config.illuminationCorrectionDataProductName).getMaskedImage()
991 if (self.config.doIlluminationCorrection
and 992 filterName
in self.config.illumFilters)
996 return pipeBase.Struct(bias=biasExposure,
997 linearizer=linearizer,
998 crosstalkSources=crosstalkSources,
1001 bfKernel=brighterFatterKernel,
1003 fringes=fringeStruct,
1004 opticsTransmission=opticsTransmission,
1005 filterTransmission=filterTransmission,
1006 sensorTransmission=sensorTransmission,
1007 atmosphereTransmission=atmosphereTransmission,
1008 strayLightData=strayLightData,
1009 illumMaskedImage=illumMaskedImage
1012 @pipeBase.timeMethod
1013 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1014 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
1015 opticsTransmission=
None, filterTransmission=
None,
1016 sensorTransmission=
None, atmosphereTransmission=
None,
1017 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1020 """!Perform instrument signature removal on an exposure. 1022 Steps included in the ISR processing, in order performed, are: 1023 - saturation and suspect pixel masking 1024 - overscan subtraction 1025 - CCD assembly of individual amplifiers 1027 - variance image construction 1028 - linearization of non-linear response 1030 - brighter-fatter correction 1033 - stray light subtraction 1035 - masking of known defects and camera specific features 1036 - vignette calculation 1037 - appending transmission curve and distortion model 1041 ccdExposure : `lsst.afw.image.Exposure` 1042 The raw exposure that is to be run through ISR. The 1043 exposure is modified by this method. 1044 camera : `lsst.afw.cameraGeom.Camera`, optional 1045 The camera geometry for this exposure. Used to select the 1046 distortion model appropriate for this data. 1047 bias : `lsst.afw.image.Exposure`, optional 1048 Bias calibration frame. 1049 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1050 Functor for linearization. 1051 crosstalkSources : `list`, optional 1052 List of possible crosstalk sources. 1053 dark : `lsst.afw.image.Exposure`, optional 1054 Dark calibration frame. 1055 flat : `lsst.afw.image.Exposure`, optional 1056 Flat calibration frame. 1057 bfKernel : `numpy.ndarray`, optional 1058 Brighter-fatter kernel. 1059 defects : `lsst.meas.algorithms.Defects`, optional 1061 fringes : `lsst.pipe.base.Struct`, optional 1062 Struct containing the fringe correction data, with 1064 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1065 - ``seed``: random seed derived from the ccdExposureId for random 1066 number generator (`uint32`) 1067 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1068 A ``TransmissionCurve`` that represents the throughput of the optics, 1069 to be evaluated in focal-plane coordinates. 1070 filterTransmission : `lsst.afw.image.TransmissionCurve` 1071 A ``TransmissionCurve`` that represents the throughput of the filter 1072 itself, to be evaluated in focal-plane coordinates. 1073 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1074 A ``TransmissionCurve`` that represents the throughput of the sensor 1075 itself, to be evaluated in post-assembly trimmed detector coordinates. 1076 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1077 A ``TransmissionCurve`` that represents the throughput of the 1078 atmosphere, assumed to be spatially constant. 1079 detectorNum : `int`, optional 1080 The integer number for the detector to process. 1081 isGen3 : bool, optional 1082 Flag this call to run() as using the Gen3 butler environment. 1083 strayLightData : `object`, optional 1084 Opaque object containing calibration information for stray-light 1085 correction. If `None`, no correction will be performed. 1086 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1087 Illumination correction image. 1091 result : `lsst.pipe.base.Struct` 1092 Result struct with component: 1093 - ``exposure`` : `afw.image.Exposure` 1094 The fully ISR corrected exposure. 1095 - ``outputExposure`` : `afw.image.Exposure` 1096 An alias for `exposure` 1097 - ``ossThumb`` : `numpy.ndarray` 1098 Thumbnail image of the exposure after overscan subtraction. 1099 - ``flattenedThumb`` : `numpy.ndarray` 1100 Thumbnail image of the exposure after flat-field correction. 1105 Raised if a configuration option is set to True, but the 1106 required calibration data has not been specified. 1110 The current processed exposure can be viewed by setting the 1111 appropriate lsstDebug entries in the `debug.display` 1112 dictionary. The names of these entries correspond to some of 1113 the IsrTaskConfig Boolean options, with the value denoting the 1114 frame to use. The exposure is shown inside the matching 1115 option check and after the processing of that step has 1116 finished. The steps with debug points are: 1127 In addition, setting the "postISRCCD" entry displays the 1128 exposure after all ISR processing has finished. 1136 self.config.doFringe =
False 1139 if detectorNum
is None:
1140 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1142 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1147 if isinstance(ccdExposure, ButlerDataRef):
1150 ccd = ccdExposure.getDetector()
1151 filterName = afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1154 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1155 ccd = [
FakeAmp(ccdExposure, self.config)]
1158 if self.config.doBias
and bias
is None:
1159 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1161 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1162 if self.config.doBrighterFatter
and bfKernel
is None:
1163 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1164 if self.config.doDark
and dark
is None:
1165 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1166 if self.config.doFlat
and flat
is None:
1167 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1168 if self.config.doDefect
and defects
is None:
1169 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1170 if self.config.doAddDistortionModel
and camera
is None:
1171 raise RuntimeError(
"Must supply camera if config.doAddDistortionModel=True.")
1172 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1173 fringes.fringes
is None):
1178 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1179 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1180 illumMaskedImage
is None):
1181 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1184 if self.config.doConvertIntToFloat:
1185 self.log.info(
"Converting exposure to floating point values.")
1192 if ccdExposure.getBBox().contains(amp.getBBox()):
1196 if self.config.doOverscan
and not badAmp:
1199 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1200 if overscanResults
is not None and \
1201 self.config.qa
is not None and self.config.qa.saveStats
is True:
1202 if isinstance(overscanResults.overscanFit, float):
1203 qaMedian = overscanResults.overscanFit
1204 qaStdev = float(
"NaN")
1206 qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1207 afwMath.MEDIAN | afwMath.STDEVCLIP)
1208 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1209 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1211 self.metadata.set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1212 self.metadata.set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1213 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1214 amp.getName(), qaMedian, qaStdev)
1215 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1218 self.log.warn(
"Amplifier %s is bad.", amp.getName())
1219 overscanResults =
None 1221 overscans.append(overscanResults
if overscanResults
is not None else None)
1223 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1225 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1226 self.log.info(
"Applying crosstalk correction.")
1227 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1228 self.
debugView(ccdExposure,
"doCrosstalk")
1230 if self.config.doAssembleCcd:
1231 self.log.info(
"Assembling CCD from amplifiers.")
1232 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1234 if self.config.expectWcs
and not ccdExposure.getWcs():
1235 self.log.warn(
"No WCS found in input exposure.")
1236 self.
debugView(ccdExposure,
"doAssembleCcd")
1239 if self.config.qa.doThumbnailOss:
1240 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1242 if self.config.doBias:
1243 self.log.info(
"Applying bias correction.")
1244 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1245 trimToFit=self.config.doTrimToMatchCalib)
1248 if self.config.doVariance:
1249 for amp, overscanResults
in zip(ccd, overscans):
1250 if ccdExposure.getBBox().contains(amp.getBBox()):
1251 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1252 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1253 if overscanResults
is not None:
1255 overscanImage=overscanResults.overscanImage)
1259 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1260 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1261 afwMath.MEDIAN | afwMath.STDEVCLIP)
1262 self.metadata.set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1263 qaStats.getValue(afwMath.MEDIAN))
1264 self.metadata.set(f
"ISR VARIANCE {amp.getName()} STDEV",
1265 qaStats.getValue(afwMath.STDEVCLIP))
1266 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1267 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1268 qaStats.getValue(afwMath.STDEVCLIP))
1271 self.log.info(
"Applying linearizer.")
1272 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1274 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1275 self.log.info(
"Applying crosstalk correction.")
1276 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1277 self.
debugView(ccdExposure,
"doCrosstalk")
1281 if self.config.doDefect:
1282 self.log.info(
"Masking defects.")
1285 if self.config.doNanMasking:
1286 self.log.info(
"Masking NAN value pixels.")
1289 if self.config.doWidenSaturationTrails:
1290 self.log.info(
"Widening saturation trails.")
1291 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1293 if self.config.doCameraSpecificMasking:
1294 self.log.info(
"Masking regions for camera specific reasons.")
1295 self.masking.
run(ccdExposure)
1297 if self.config.doBrighterFatter:
1306 interpExp = ccdExposure.clone()
1308 isrFunctions.interpolateFromMask(
1309 maskedImage=interpExp.getMaskedImage(),
1310 fwhm=self.config.fwhm,
1311 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1312 maskNameList=self.config.maskListToInterpolate
1314 bfExp = interpExp.clone()
1316 self.log.info(
"Applying brighter fatter correction.")
1317 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1318 self.config.brighterFatterMaxIter,
1319 self.config.brighterFatterThreshold,
1320 self.config.brighterFatterApplyGain
1322 if bfResults[1] == self.config.brighterFatterMaxIter:
1323 self.log.warn(
"Brighter fatter correction did not converge, final difference %f.",
1326 self.log.info(
"Finished brighter fatter correction in %d iterations.",
1328 image = ccdExposure.getMaskedImage().getImage()
1329 bfCorr = bfExp.getMaskedImage().getImage()
1330 bfCorr -= interpExp.getMaskedImage().getImage()
1333 self.
debugView(ccdExposure,
"doBrighterFatter")
1335 if self.config.doDark:
1336 self.log.info(
"Applying dark correction.")
1340 if self.config.doFringe
and not self.config.fringeAfterFlat:
1341 self.log.info(
"Applying fringe correction before flat.")
1342 self.fringe.
run(ccdExposure, **fringes.getDict())
1345 if self.config.doStrayLight:
1346 if strayLightData
is not None:
1347 self.log.info(
"Applying stray light correction.")
1348 self.strayLight.
run(ccdExposure, strayLightData)
1349 self.
debugView(ccdExposure,
"doStrayLight")
1351 self.log.debug(
"Skipping stray light correction: no data found for this image.")
1353 if self.config.doFlat:
1354 self.log.info(
"Applying flat correction.")
1358 if self.config.doApplyGains:
1359 self.log.info(
"Applying gain correction instead of flat.")
1360 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1362 if self.config.doFringe
and self.config.fringeAfterFlat:
1363 self.log.info(
"Applying fringe correction after flat.")
1364 self.fringe.
run(ccdExposure, **fringes.getDict())
1366 if self.config.doVignette:
1367 self.log.info(
"Constructing Vignette polygon.")
1370 if self.config.vignette.doWriteVignettePolygon:
1373 if self.config.doAttachTransmissionCurve:
1374 self.log.info(
"Adding transmission curves.")
1375 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1376 filterTransmission=filterTransmission,
1377 sensorTransmission=sensorTransmission,
1378 atmosphereTransmission=atmosphereTransmission)
1380 if self.config.doAddDistortionModel:
1381 self.log.info(
"Adding a distortion model to the WCS.")
1382 isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1384 flattenedThumb =
None 1385 if self.config.qa.doThumbnailFlattened:
1386 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1388 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1389 self.log.info(
"Performing illumination correction.")
1390 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1391 illumMaskedImage, illumScale=self.config.illumScale,
1392 trimToFit=self.config.doTrimToMatchCalib)
1395 if self.config.doSaveInterpPixels:
1396 preInterpExp = ccdExposure.clone()
1411 if self.config.doSetBadRegions:
1412 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1413 if badPixelCount > 0:
1414 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1416 if self.config.doInterpolate:
1417 self.log.info(
"Interpolating masked pixels.")
1418 isrFunctions.interpolateFromMask(
1419 maskedImage=ccdExposure.getMaskedImage(),
1420 fwhm=self.config.fwhm,
1421 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1422 maskNameList=list(self.config.maskListToInterpolate)
1427 if self.config.doMeasureBackground:
1428 self.log.info(
"Measuring background level.")
1431 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1433 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1434 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1435 afwMath.MEDIAN | afwMath.STDEVCLIP)
1436 self.metadata.set(
"ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1437 qaStats.getValue(afwMath.MEDIAN))
1438 self.metadata.set(
"ISR BACKGROUND {} STDEV".format(amp.getName()),
1439 qaStats.getValue(afwMath.STDEVCLIP))
1440 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1441 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1442 qaStats.getValue(afwMath.STDEVCLIP))
1444 self.
debugView(ccdExposure,
"postISRCCD")
1446 return pipeBase.Struct(
1447 exposure=ccdExposure,
1449 flattenedThumb=flattenedThumb,
1451 preInterpolatedExposure=preInterpExp,
1452 outputExposure=ccdExposure,
1453 outputOssThumbnail=ossThumb,
1454 outputFlattenedThumbnail=flattenedThumb,
1457 @pipeBase.timeMethod
1459 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1461 This method contains the `CmdLineTask` interface to the ISR 1462 processing. All IO is handled here, freeing the `run()` method 1463 to manage only pixel-level calculations. The steps performed 1465 - Read in necessary detrending/isr/calibration data. 1466 - Process raw exposure in `run()`. 1467 - Persist the ISR-corrected exposure as "postISRCCD" if 1468 config.doWrite=True. 1472 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1473 DataRef of the detector data to be processed 1477 result : `lsst.pipe.base.Struct` 1478 Result struct with component: 1479 - ``exposure`` : `afw.image.Exposure` 1480 The fully ISR corrected exposure. 1485 Raised if a configuration option is set to True, but the 1486 required calibration data does not exist. 1489 self.log.info(
"Performing ISR on sensor %s.", sensorRef.dataId)
1491 ccdExposure = sensorRef.get(self.config.datasetType)
1493 camera = sensorRef.get(
"camera")
1494 if camera
is None and self.config.doAddDistortionModel:
1495 raise RuntimeError(
"config.doAddDistortionModel is True " 1496 "but could not get a camera from the butler.")
1497 isrData = self.
readIsrData(sensorRef, ccdExposure)
1499 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1501 if self.config.doWrite:
1502 sensorRef.put(result.exposure,
"postISRCCD")
1503 if result.preInterpolatedExposure
is not None:
1504 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1505 if result.ossThumb
is not None:
1506 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1507 if result.flattenedThumb
is not None:
1508 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1513 """!Retrieve a calibration dataset for removing instrument signature. 1518 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1519 DataRef of the detector data to find calibration datasets 1522 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1524 If True, disable butler proxies to enable error handling 1525 within this routine. 1529 exposure : `lsst.afw.image.Exposure` 1530 Requested calibration frame. 1535 Raised if no matching calibration frame can be found. 1538 exp = dataRef.get(datasetType, immediate=immediate)
1539 except Exception
as exc1:
1540 if not self.config.fallbackFilterName:
1541 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1543 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1544 except Exception
as exc2:
1545 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1546 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1547 self.log.warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
1549 if self.config.doAssembleIsrExposures:
1550 exp = self.assembleCcd.assembleCcd(exp)
1554 """Ensure that the data returned by Butler is a fully constructed exposure. 1556 ISR requires exposure-level image data for historical reasons, so if we did 1557 not recieve that from Butler, construct it from what we have, modifying the 1562 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1563 `lsst.afw.image.ImageF` 1564 The input data structure obtained from Butler. 1565 camera : `lsst.afw.cameraGeom.camera` 1566 The camera associated with the image. Used to find the appropriate 1569 The detector this exposure should match. 1573 inputExp : `lsst.afw.image.Exposure` 1574 The re-constructed exposure, with appropriate detector parameters. 1579 Raised if the input data cannot be used to construct an exposure. 1581 if isinstance(inputExp, afwImage.DecoratedImageU):
1582 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1583 elif isinstance(inputExp, afwImage.ImageF):
1584 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1585 elif isinstance(inputExp, afwImage.MaskedImageF):
1586 inputExp = afwImage.makeExposure(inputExp)
1587 elif isinstance(inputExp, afwImage.Exposure):
1589 elif inputExp
is None:
1593 raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
1596 if inputExp.getDetector()
is None:
1597 inputExp.setDetector(camera[detectorNum])
1602 """Convert exposure image from uint16 to float. 1604 If the exposure does not need to be converted, the input is 1605 immediately returned. For exposures that are converted to use 1606 floating point pixels, the variance is set to unity and the 1611 exposure : `lsst.afw.image.Exposure` 1612 The raw exposure to be converted. 1616 newexposure : `lsst.afw.image.Exposure` 1617 The input ``exposure``, converted to floating point pixels. 1622 Raised if the exposure type cannot be converted to float. 1625 if isinstance(exposure, afwImage.ExposureF):
1627 self.log.debug(
"Exposure already of type float.")
1629 if not hasattr(exposure,
"convertF"):
1630 raise RuntimeError(
"Unable to convert exposure (%s) to float." % type(exposure))
1632 newexposure = exposure.convertF()
1633 newexposure.variance[:] = 1
1634 newexposure.mask[:] = 0x0
1639 """Identify bad amplifiers, saturated and suspect pixels. 1643 ccdExposure : `lsst.afw.image.Exposure` 1644 Input exposure to be masked. 1645 amp : `lsst.afw.table.AmpInfoCatalog` 1646 Catalog of parameters defining the amplifier on this 1648 defects : `lsst.meas.algorithms.Defects` 1649 List of defects. Used to determine if the entire 1655 If this is true, the entire amplifier area is covered by 1656 defects and unusable. 1659 maskedImage = ccdExposure.getMaskedImage()
1665 if defects
is not None:
1666 badAmp = bool(sum([v.getBBox().contains(amp.getBBox())
for v
in defects]))
1671 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1673 maskView = dataView.getMask()
1674 maskView |= maskView.getPlaneBitMask(
"BAD")
1681 if self.config.doSaturation
and not badAmp:
1682 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1683 if self.config.doSuspect
and not badAmp:
1684 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1685 if math.isfinite(self.config.saturation):
1686 limits.update({self.config.saturatedMaskName: self.config.saturation})
1688 for maskName, maskThreshold
in limits.items():
1689 if not math.isnan(maskThreshold):
1690 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1691 isrFunctions.makeThresholdMask(
1692 maskedImage=dataView,
1693 threshold=maskThreshold,
1699 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1701 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1702 self.config.suspectMaskName])
1703 if numpy.all(maskView.getArray() & maskVal > 0):
1705 maskView |= maskView.getPlaneBitMask(
"BAD")
1710 """Apply overscan correction in place. 1712 This method does initial pixel rejection of the overscan 1713 region. The overscan can also be optionally segmented to 1714 allow for discontinuous overscan responses to be fit 1715 separately. The actual overscan subtraction is performed by 1716 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1717 which is called here after the amplifier is preprocessed. 1721 ccdExposure : `lsst.afw.image.Exposure` 1722 Exposure to have overscan correction performed. 1723 amp : `lsst.afw.table.AmpInfoCatalog` 1724 The amplifier to consider while correcting the overscan. 1728 overscanResults : `lsst.pipe.base.Struct` 1729 Result struct with components: 1730 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1731 Value or fit subtracted from the amplifier image data. 1732 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1733 Value or fit subtracted from the overscan image data. 1734 - ``overscanImage`` : `lsst.afw.image.Image` 1735 Image of the overscan region with the overscan 1736 correction applied. This quantity is used to estimate 1737 the amplifier read noise empirically. 1742 Raised if the ``amp`` does not contain raw pixel information. 1746 lsst.ip.isr.isrFunctions.overscanCorrection 1748 if not amp.getHasRawInfo():
1749 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1751 if amp.getRawHorizontalOverscanBBox().isEmpty():
1752 self.log.info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1755 statControl = afwMath.StatisticsControl()
1756 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1759 dataBBox = amp.getRawDataBBox()
1760 oscanBBox = amp.getRawHorizontalOverscanBBox()
1764 prescanBBox = amp.getRawPrescanBBox()
1765 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1766 dx0 += self.config.overscanNumLeadingColumnsToSkip
1767 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1769 dx0 += self.config.overscanNumTrailingColumnsToSkip
1770 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1776 if ((self.config.overscanBiasJump
and 1777 self.config.overscanBiasJumpLocation)
and 1778 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1779 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1780 self.config.overscanBiasJumpDevices)):
1781 if amp.getReadoutCorner()
in (afwTable.LL, afwTable.LR):
1782 yLower = self.config.overscanBiasJumpLocation
1783 yUpper = dataBBox.getHeight() - yLower
1785 yUpper = self.config.overscanBiasJumpLocation
1786 yLower = dataBBox.getHeight() - yUpper
1805 oscanBBox.getHeight())))
1808 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1809 ampImage = ccdExposure.maskedImage[imageBBox]
1810 overscanImage = ccdExposure.maskedImage[overscanBBox]
1812 overscanArray = overscanImage.image.array
1813 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1814 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1815 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1817 statControl = afwMath.StatisticsControl()
1818 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1820 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1821 overscanImage=overscanImage,
1822 fitType=self.config.overscanFitType,
1823 order=self.config.overscanOrder,
1824 collapseRej=self.config.overscanNumSigmaClip,
1825 statControl=statControl,
1826 overscanIsInt=self.config.overscanIsInt
1830 levelStat = afwMath.MEDIAN
1831 sigmaStat = afwMath.STDEVCLIP
1833 sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1834 self.config.qa.flatness.nIter)
1835 metadata = ccdExposure.getMetadata()
1836 ampNum = amp.getName()
1837 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1838 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1839 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1841 stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1842 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1843 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1845 return overscanResults
1848 """Set the variance plane using the amplifier gain and read noise 1850 The read noise is calculated from the ``overscanImage`` if the 1851 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1852 the value from the amplifier data is used. 1856 ampExposure : `lsst.afw.image.Exposure` 1857 Exposure to process. 1858 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1859 Amplifier detector data. 1860 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1861 Image of overscan, required only for empirical read noise. 1865 lsst.ip.isr.isrFunctions.updateVariance 1867 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1868 gain = amp.getGain()
1870 if math.isnan(gain):
1872 self.log.warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1875 self.log.warn(
"Gain for amp %s == %g <= 0; setting to %f.",
1876 amp.getName(), gain, patchedGain)
1879 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1880 self.log.info(
"Overscan is none for EmpiricalReadNoise.")
1882 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1883 stats = afwMath.StatisticsControl()
1884 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1885 readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1886 self.log.info(
"Calculated empirical read noise for amp %s: %f.",
1887 amp.getName(), readNoise)
1889 readNoise = amp.getReadNoise()
1891 isrFunctions.updateVariance(
1892 maskedImage=ampExposure.getMaskedImage(),
1894 readNoise=readNoise,
1898 """!Apply dark correction in place. 1902 exposure : `lsst.afw.image.Exposure` 1903 Exposure to process. 1904 darkExposure : `lsst.afw.image.Exposure` 1905 Dark exposure of the same size as ``exposure``. 1906 invert : `Bool`, optional 1907 If True, re-add the dark to an already corrected image. 1912 Raised if either ``exposure`` or ``darkExposure`` do not 1913 have their dark time defined. 1917 lsst.ip.isr.isrFunctions.darkCorrection 1919 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1920 if math.isnan(expScale):
1921 raise RuntimeError(
"Exposure darktime is NAN.")
1922 if darkExposure.getInfo().getVisitInfo()
is not None:
1923 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1927 self.log.warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1930 if math.isnan(darkScale):
1931 raise RuntimeError(
"Dark calib darktime is NAN.")
1932 isrFunctions.darkCorrection(
1933 maskedImage=exposure.getMaskedImage(),
1934 darkMaskedImage=darkExposure.getMaskedImage(),
1936 darkScale=darkScale,
1938 trimToFit=self.config.doTrimToMatchCalib
1942 """!Check if linearization is needed for the detector cameraGeom. 1944 Checks config.doLinearize and the linearity type of the first 1949 detector : `lsst.afw.cameraGeom.Detector` 1950 Detector to get linearity type from. 1954 doLinearize : `Bool` 1955 If True, linearization should be performed. 1957 return self.config.doLinearize
and \
1958 detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1961 """!Apply flat correction in place. 1965 exposure : `lsst.afw.image.Exposure` 1966 Exposure to process. 1967 flatExposure : `lsst.afw.image.Exposure` 1968 Flat exposure of the same size as ``exposure``. 1969 invert : `Bool`, optional 1970 If True, unflatten an already flattened image. 1974 lsst.ip.isr.isrFunctions.flatCorrection 1976 isrFunctions.flatCorrection(
1977 maskedImage=exposure.getMaskedImage(),
1978 flatMaskedImage=flatExposure.getMaskedImage(),
1979 scalingType=self.config.flatScalingType,
1980 userScale=self.config.flatUserScale,
1982 trimToFit=self.config.doTrimToMatchCalib
1986 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1990 exposure : `lsst.afw.image.Exposure` 1991 Exposure to process. Only the amplifier DataSec is processed. 1992 amp : `lsst.afw.table.AmpInfoCatalog` 1993 Amplifier detector data. 1997 lsst.ip.isr.isrFunctions.makeThresholdMask 1999 if not math.isnan(amp.getSaturation()):
2000 maskedImage = exposure.getMaskedImage()
2001 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2002 isrFunctions.makeThresholdMask(
2003 maskedImage=dataView,
2004 threshold=amp.getSaturation(),
2006 maskName=self.config.saturatedMaskName,
2010 """!Interpolate over saturated pixels, in place. 2012 This method should be called after `saturationDetection`, to 2013 ensure that the saturated pixels have been identified in the 2014 SAT mask. It should also be called after `assembleCcd`, since 2015 saturated regions may cross amplifier boundaries. 2019 exposure : `lsst.afw.image.Exposure` 2020 Exposure to process. 2024 lsst.ip.isr.isrTask.saturationDetection 2025 lsst.ip.isr.isrFunctions.interpolateFromMask 2027 isrFunctions.interpolateFromMask(
2028 maskedImage=exposure.getMaskedImage(),
2029 fwhm=self.config.fwhm,
2030 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2031 maskNameList=list(self.config.saturatedMaskName),
2035 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2039 exposure : `lsst.afw.image.Exposure` 2040 Exposure to process. Only the amplifier DataSec is processed. 2041 amp : `lsst.afw.table.AmpInfoCatalog` 2042 Amplifier detector data. 2046 lsst.ip.isr.isrFunctions.makeThresholdMask 2050 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2051 This is intended to indicate pixels that may be affected by unknown systematics; 2052 for example if non-linearity corrections above a certain level are unstable 2053 then that would be a useful value for suspectLevel. A value of `nan` indicates 2054 that no such level exists and no pixels are to be masked as suspicious. 2056 suspectLevel = amp.getSuspectLevel()
2057 if math.isnan(suspectLevel):
2060 maskedImage = exposure.getMaskedImage()
2061 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2062 isrFunctions.makeThresholdMask(
2063 maskedImage=dataView,
2064 threshold=suspectLevel,
2066 maskName=self.config.suspectMaskName,
2070 """!Mask defects using mask plane "BAD", in place. 2074 exposure : `lsst.afw.image.Exposure` 2075 Exposure to process. 2076 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2077 `lsst.afw.image.DefectBase`. 2078 List of defects to mask and interpolate. 2082 Call this after CCD assembly, since defects may cross amplifier boundaries. 2084 maskedImage = exposure.getMaskedImage()
2085 if not isinstance(defectBaseList, Defects):
2087 defectList = Defects(defectBaseList)
2089 defectList = defectBaseList
2090 defectList.maskPixels(maskedImage, maskName=
"BAD")
2092 if self.config.numEdgeSuspect > 0:
2093 goodBBox = maskedImage.getBBox()
2095 goodBBox.grow(-self.config.numEdgeSuspect)
2097 SourceDetectionTask.setEdgeBits(
2100 maskedImage.getMask().getPlaneBitMask(
"SUSPECT")
2104 """Mask and interpolate defects using mask plane "BAD", in place. 2108 exposure : `lsst.afw.image.Exposure` 2109 Exposure to process. 2110 defectBaseList : `List` of `Defects` 2113 self.maskDefects(exposure, defectBaseList)
2114 isrFunctions.interpolateFromMask(
2115 maskedImage=exposure.getMaskedImage(),
2116 fwhm=self.config.fwhm,
2117 growSaturatedFootprints=0,
2118 maskNameList=[
"BAD"],
2122 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2126 exposure : `lsst.afw.image.Exposure` 2127 Exposure to process. 2131 We mask over all NaNs, including those that are masked with 2132 other bits (because those may or may not be interpolated over 2133 later, and we want to remove all NaNs). Despite this 2134 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2135 the historical name. 2137 maskedImage = exposure.getMaskedImage()
2140 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2141 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2142 numNans =
maskNans(maskedImage, maskVal)
2143 self.metadata.set(
"NUMNANS", numNans)
2145 self.log.warn(
"There were %d unmasked NaNs.", numNans)
2148 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2152 exposure : `lsst.afw.image.Exposure` 2153 Exposure to process. 2157 lsst.ip.isr.isrTask.maskNan() 2160 isrFunctions.interpolateFromMask(
2161 maskedImage=exposure.getMaskedImage(),
2162 fwhm=self.config.fwhm,
2163 growSaturatedFootprints=0,
2164 maskNameList=[
"UNMASKEDNAN"],
2168 """Measure the image background in subgrids, for quality control purposes. 2172 exposure : `lsst.afw.image.Exposure` 2173 Exposure to process. 2174 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2175 Configuration object containing parameters on which background 2176 statistics and subgrids to use. 2178 if IsrQaConfig
is not None:
2179 statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
2180 IsrQaConfig.flatness.nIter)
2181 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2182 statsControl.setAndMask(maskVal)
2183 maskedImage = exposure.getMaskedImage()
2184 stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
2185 skyLevel = stats.getValue(afwMath.MEDIAN)
2186 skySigma = stats.getValue(afwMath.STDEVCLIP)
2187 self.log.info(
"Flattened sky level: %f +/- %f.", skyLevel, skySigma)
2188 metadata = exposure.getMetadata()
2189 metadata.set(
'SKYLEVEL', skyLevel)
2190 metadata.set(
'SKYSIGMA', skySigma)
2193 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2194 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2195 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2196 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2197 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2198 skyLevels = numpy.zeros((nX, nY))
2201 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2203 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2205 xLLC = xc - meshXHalf
2206 yLLC = yc - meshYHalf
2207 xURC = xc + meshXHalf - 1
2208 yURC = yc + meshYHalf - 1
2211 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2213 skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2215 good = numpy.where(numpy.isfinite(skyLevels))
2216 skyMedian = numpy.median(skyLevels[good])
2217 flatness = (skyLevels[good] - skyMedian) / skyMedian
2218 flatness_rms = numpy.std(flatness)
2219 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2221 self.log.info(
"Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
2222 self.log.info(
"Sky flatness in %dx%d grids - pp: %f rms: %f.",
2223 nX, nY, flatness_pp, flatness_rms)
2225 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2226 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2227 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2228 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2229 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2232 """Set an approximate magnitude zero point for the exposure. 2236 exposure : `lsst.afw.image.Exposure` 2237 Exposure to process. 2239 filterName = afwImage.Filter(exposure.getFilter().getId()).getName()
2240 if filterName
in self.config.fluxMag0T1:
2241 fluxMag0 = self.config.fluxMag0T1[filterName]
2243 self.log.warn(
"No rough magnitude zero point set for filter %s.", filterName)
2244 fluxMag0 = self.config.defaultFluxMag0T1
2246 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2248 self.log.warn(
"Non-positive exposure time; skipping rough zero point.")
2251 self.log.info(
"Setting rough magnitude zero point: %f", 2.5*math.log10(fluxMag0*expTime))
2252 exposure.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(fluxMag0*expTime, 0.0))
2255 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2259 ccdExposure : `lsst.afw.image.Exposure` 2260 Exposure to process. 2261 fpPolygon : `lsst.afw.geom.Polygon` 2262 Polygon in focal plane coordinates. 2265 ccd = ccdExposure.getDetector()
2266 fpCorners = ccd.getCorners(FOCAL_PLANE)
2267 ccdPolygon = Polygon(fpCorners)
2270 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2273 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2274 validPolygon = Polygon(ccdPoints)
2275 ccdExposure.getInfo().setValidPolygon(validPolygon)
2279 """Context manager that applies and removes flats and darks, 2280 if the task is configured to apply them. 2284 exp : `lsst.afw.image.Exposure` 2285 Exposure to process. 2286 flat : `lsst.afw.image.Exposure` 2287 Flat exposure the same size as ``exp``. 2288 dark : `lsst.afw.image.Exposure`, optional 2289 Dark exposure the same size as ``exp``. 2293 exp : `lsst.afw.image.Exposure` 2294 The flat and dark corrected exposure. 2296 if self.config.doDark
and dark
is not None:
2298 if self.config.doFlat:
2303 if self.config.doFlat:
2305 if self.config.doDark
and dark
is not None:
2309 """Utility function to examine ISR exposure at different stages. 2313 exposure : `lsst.afw.image.Exposure` 2316 State of processing to view. 2318 frame = getDebugFrame(self._display, stepname)
2320 display = getDisplay(frame)
2321 display.scale(
'asinh',
'zscale')
2322 display.mtv(exposure)
2323 prompt =
"Press Enter to continue [c]... " 2325 ans = input(prompt).lower()
2326 if ans
in (
"",
"c",):
2331 """A Detector-like object that supports returning gain and saturation level 2333 This is used when the input exposure does not have a detector. 2337 exposure : `lsst.afw.image.Exposure` 2338 Exposure to generate a fake amplifier for. 2339 config : `lsst.ip.isr.isrTaskConfig` 2340 Configuration to apply to the fake amplifier. 2344 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2346 self.
_gain = config.gain
2376 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2380 """Task to wrap the default IsrTask to allow it to be retargeted. 2382 The standard IsrTask can be called directly from a command line 2383 program, but doing so removes the ability of the task to be 2384 retargeted. As most cameras override some set of the IsrTask 2385 methods, this would remove those data-specific methods in the 2386 output post-ISR images. This wrapping class fixes the issue, 2387 allowing identical post-ISR images to be generated by both the 2388 processCcd and isrTask code. 2390 ConfigClass = RunIsrConfig
2391 _DefaultName =
"runIsr" 2395 self.makeSubtask(
"isr")
2401 dataRef : `lsst.daf.persistence.ButlerDataRef` 2402 data reference of the detector data to be processed 2406 result : `pipeBase.Struct` 2407 Result struct with component: 2409 - exposure : `lsst.afw.image.Exposure` 2410 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 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)