29 import lsst.pex.config
as pexConfig
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
56 __all__ = [
"IsrTask",
"IsrTaskConfig",
"RunIsrTask",
"RunIsrConfig"]
60 """Configuration parameters for IsrTask. 62 Items are grouped in the order in which they are executed by the task. 67 isrName = pexConfig.Field(
74 ccdExposure = pipeBase.InputDatasetField(
75 doc=
"Input exposure to process",
78 storageClass=
"Exposure",
79 dimensions=[
"instrument",
"exposure",
"detector"],
81 camera = pipeBase.InputDatasetField(
82 doc=
"Input camera to construct complete exposures.",
85 storageClass=
"Camera",
86 dimensions=[
"instrument",
"calibration_label"],
88 bias = pipeBase.InputDatasetField(
89 doc=
"Input bias calibration.",
92 storageClass=
"ImageF",
93 dimensions=[
"instrument",
"calibration_label",
"detector"],
95 dark = pipeBase.InputDatasetField(
96 doc=
"Input dark calibration.",
99 storageClass=
"ImageF",
100 dimensions=[
"instrument",
"calibration_label",
"detector"],
102 flat = pipeBase.InputDatasetField(
103 doc=
"Input flat calibration.",
106 storageClass=
"MaskedImageF",
107 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
109 bfKernel = pipeBase.InputDatasetField(
110 doc=
"Input brighter-fatter kernel.",
113 storageClass=
"NumpyArray",
114 dimensions=[
"instrument",
"calibration_label"],
116 defects = pipeBase.InputDatasetField(
117 doc=
"Input defect tables.",
120 storageClass=
"DefectsList",
121 dimensions=[
"instrument",
"calibration_label",
"detector"],
123 opticsTransmission = pipeBase.InputDatasetField(
124 doc=
"Transmission curve due to the optics.",
125 name=
"transmission_optics",
127 storageClass=
"TransmissionCurve",
128 dimensions=[
"instrument",
"calibration_label"],
130 filterTransmission = pipeBase.InputDatasetField(
131 doc=
"Transmission curve due to the filter.",
132 name=
"transmission_filter",
134 storageClass=
"TransmissionCurve",
135 dimensions=[
"instrument",
"physical_filter",
"calibration_label"],
137 sensorTransmission = pipeBase.InputDatasetField(
138 doc=
"Transmission curve due to the sensor.",
139 name=
"transmission_sensor",
141 storageClass=
"TransmissionCurve",
142 dimensions=[
"instrument",
"calibration_label",
"detector"],
144 atmosphereTransmission = pipeBase.InputDatasetField(
145 doc=
"Transmission curve due to the atmosphere.",
146 name=
"transmission_atmosphere",
148 storageClass=
"TransmissionCurve",
149 dimensions=[
"instrument"],
151 illumMaskedImage = pipeBase.InputDatasetField(
152 doc=
"Input illumination correction.",
155 storageClass=
"MaskedImageF",
156 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
160 outputExposure = pipeBase.OutputDatasetField(
161 doc=
"Output ISR processed exposure.",
164 storageClass=
"ExposureF",
165 dimensions=[
"instrument",
"visit",
"detector"],
167 outputOssThumbnail = pipeBase.OutputDatasetField(
168 doc=
"Output Overscan-subtracted thumbnail image.",
171 storageClass=
"Thumbnail",
172 dimensions=[
"instrument",
"visit",
"detector"],
174 outputFlattenedThumbnail = pipeBase.OutputDatasetField(
175 doc=
"Output flat-corrected thumbnail image.",
176 name=
"FlattenedThumb",
178 storageClass=
"TextStorage",
179 dimensions=[
"instrument",
"visit",
"detector"],
182 quantum = pipeBase.QuantumConfig(
183 dimensions=[
"visit",
"detector",
"instrument"],
187 datasetType = pexConfig.Field(
189 doc=
"Dataset type for input data; users will typically leave this alone, " 190 "but camera-specific ISR tasks will override it",
194 fallbackFilterName = pexConfig.Field(
196 doc=
"Fallback default filter name for calibrations.",
199 expectWcs = pexConfig.Field(
202 doc=
"Expect input science images to have a WCS (set False for e.g. spectrographs)." 204 fwhm = pexConfig.Field(
206 doc=
"FWHM of PSF in arcseconds.",
209 qa = pexConfig.ConfigField(
211 doc=
"QA related configuration options.",
215 doConvertIntToFloat = pexConfig.Field(
217 doc=
"Convert integer raw images to floating point values?",
222 doSaturation = pexConfig.Field(
224 doc=
"Mask saturated pixels? NB: this is totally independent of the" 225 " interpolation option - this is ONLY setting the bits in the mask." 226 " To have them interpolated make sure doSaturationInterpolation=True",
229 saturatedMaskName = pexConfig.Field(
231 doc=
"Name of mask plane to use in saturation detection and interpolation",
234 saturation = pexConfig.Field(
236 doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
237 default=float(
"NaN"),
239 growSaturationFootprintSize = pexConfig.Field(
241 doc=
"Number of pixels by which to grow the saturation footprints",
246 doSuspect = pexConfig.Field(
248 doc=
"Mask suspect pixels?",
251 suspectMaskName = pexConfig.Field(
253 doc=
"Name of mask plane to use for suspect pixels",
256 numEdgeSuspect = pexConfig.Field(
258 doc=
"Number of edge pixels to be flagged as untrustworthy.",
263 doSetBadRegions = pexConfig.Field(
265 doc=
"Should we set the level of all BAD patches of the chip to the chip's average value?",
268 badStatistic = pexConfig.ChoiceField(
270 doc=
"How to estimate the average value for BAD regions.",
273 "MEANCLIP":
"Correct using the (clipped) mean of good data",
274 "MEDIAN":
"Correct using the median of the good data",
279 doOverscan = pexConfig.Field(
281 doc=
"Do overscan subtraction?",
284 overscanFitType = pexConfig.ChoiceField(
286 doc=
"The method for fitting the overscan bias level.",
289 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
290 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
291 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
292 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
293 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
294 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
295 "MEAN":
"Correct using the mean of the overscan region",
296 "MEANCLIP":
"Correct using a clipped mean of the overscan region",
297 "MEDIAN":
"Correct using the median of the overscan region",
300 overscanOrder = pexConfig.Field(
302 doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
303 "or number of spline knots if overscan fit type is a spline."),
306 overscanNumSigmaClip = pexConfig.Field(
308 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
311 overscanIsInt = pexConfig.Field(
313 doc=
"Treat overscan as an integer image for purposes of overscan.FitType=MEDIAN",
316 overscanNumLeadingColumnsToSkip = pexConfig.Field(
318 doc=
"Number of columns to skip in overscan, i.e. those closest to amplifier",
321 overscanNumTrailingColumnsToSkip = pexConfig.Field(
323 doc=
"Number of columns to skip in overscan, i.e. those farthest from amplifier",
326 overscanMaxDev = pexConfig.Field(
328 doc=
"Maximum deviation from the median for overscan",
329 default=1000.0, check=
lambda x: x > 0
331 overscanBiasJump = pexConfig.Field(
333 doc=
"Fit the overscan in a piecewise-fashion to correct for bias jumps?",
336 overscanBiasJumpKeyword = pexConfig.Field(
338 doc=
"Header keyword containing information about devices.",
339 default=
"NO_SUCH_KEY",
341 overscanBiasJumpDevices = pexConfig.ListField(
343 doc=
"List of devices that need piecewise overscan correction.",
346 overscanBiasJumpLocation = pexConfig.Field(
348 doc=
"Location of bias jump along y-axis.",
353 doAssembleCcd = pexConfig.Field(
356 doc=
"Assemble amp-level exposures into a ccd-level exposure?" 358 assembleCcd = pexConfig.ConfigurableField(
359 target=AssembleCcdTask,
360 doc=
"CCD assembly task",
364 doAssembleIsrExposures = pexConfig.Field(
367 doc=
"Assemble amp-level calibration exposures into ccd-level exposure?" 369 doTrimToMatchCalib = pexConfig.Field(
372 doc=
"Trim raw data to match calibration bounding boxes?" 376 doBias = pexConfig.Field(
378 doc=
"Apply bias frame correction?",
381 biasDataProductName = pexConfig.Field(
383 doc=
"Name of the bias data product",
388 doVariance = pexConfig.Field(
390 doc=
"Calculate variance?",
393 gain = pexConfig.Field(
395 doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
396 default=float(
"NaN"),
398 readNoise = pexConfig.Field(
400 doc=
"The read noise to use if no Detector is present in the Exposure",
403 doEmpiricalReadNoise = pexConfig.Field(
406 doc=
"Calculate empirical read noise instead of value from AmpInfo data?" 410 doLinearize = pexConfig.Field(
412 doc=
"Correct for nonlinearity of the detector's response?",
417 doCrosstalk = pexConfig.Field(
419 doc=
"Apply intra-CCD crosstalk correction?",
422 doCrosstalkBeforeAssemble = pexConfig.Field(
424 doc=
"Apply crosstalk correction before CCD assembly, and before trimming?",
427 crosstalk = pexConfig.ConfigurableField(
428 target=CrosstalkTask,
429 doc=
"Intra-CCD crosstalk correction",
433 doDefect = pexConfig.Field(
435 doc=
"Apply correction for CCD defects, e.g. hot pixels?",
438 doNanMasking = pexConfig.Field(
440 doc=
"Mask NAN pixels?",
443 doWidenSaturationTrails = pexConfig.Field(
445 doc=
"Widen bleed trails based on their width?",
450 doBrighterFatter = pexConfig.Field(
453 doc=
"Apply the brighter fatter correction" 455 brighterFatterLevel = pexConfig.ChoiceField(
458 doc=
"The level at which to correct for brighter-fatter.",
460 "AMP":
"Every amplifier treated separately.",
461 "DETECTOR":
"One kernel per detector",
464 brighterFatterKernelFile = pexConfig.Field(
467 doc=
"Kernel file used for the brighter fatter correction" 469 brighterFatterMaxIter = pexConfig.Field(
472 doc=
"Maximum number of iterations for the brighter fatter correction" 474 brighterFatterThreshold = pexConfig.Field(
477 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the " 478 " absolute value of the difference between the current corrected image and the one" 479 " from the previous iteration summed over all the pixels." 481 brighterFatterApplyGain = pexConfig.Field(
484 doc=
"Should the gain be applied when applying the brighter fatter correction?" 488 doDark = pexConfig.Field(
490 doc=
"Apply dark frame correction?",
493 darkDataProductName = pexConfig.Field(
495 doc=
"Name of the dark data product",
500 doStrayLight = pexConfig.Field(
502 doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
505 strayLight = pexConfig.ConfigurableField(
506 target=StrayLightTask,
507 doc=
"y-band stray light correction" 511 doFlat = pexConfig.Field(
513 doc=
"Apply flat field correction?",
516 flatDataProductName = pexConfig.Field(
518 doc=
"Name of the flat data product",
521 flatScalingType = pexConfig.ChoiceField(
523 doc=
"The method for scaling the flat on the fly.",
526 "USER":
"Scale by flatUserScale",
527 "MEAN":
"Scale by the inverse of the mean",
528 "MEDIAN":
"Scale by the inverse of the median",
531 flatUserScale = pexConfig.Field(
533 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
536 doTweakFlat = pexConfig.Field(
538 doc=
"Tweak flats to match observed amplifier ratios?",
543 doApplyGains = pexConfig.Field(
545 doc=
"Correct the amplifiers for their gains instead of applying flat correction",
548 normalizeGains = pexConfig.Field(
550 doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
555 doFringe = pexConfig.Field(
557 doc=
"Apply fringe correction?",
560 fringe = pexConfig.ConfigurableField(
562 doc=
"Fringe subtraction task",
564 fringeAfterFlat = pexConfig.Field(
566 doc=
"Do fringe subtraction after flat-fielding?",
571 doAddDistortionModel = pexConfig.Field(
573 doc=
"Apply a distortion model based on camera geometry to the WCS?",
575 deprecated=(
"Camera geometry is incorporated when reading the raw files." 576 " This option no longer is used, and will be removed after v19.")
580 doMeasureBackground = pexConfig.Field(
582 doc=
"Measure the background level on the reduced image?",
587 doCameraSpecificMasking = pexConfig.Field(
589 doc=
"Mask camera-specific bad regions?",
592 masking = pexConfig.ConfigurableField(
599 doInterpolate = pexConfig.Field(
601 doc=
"Interpolate masked pixels?",
604 doSaturationInterpolation = pexConfig.Field(
606 doc=
"Perform interpolation over pixels masked as saturated?" 607 " NB: This is independent of doSaturation; if that is False this plane" 608 " will likely be blank, resulting in a no-op here.",
611 doNanInterpolation = pexConfig.Field(
613 doc=
"Perform interpolation over pixels masked as NaN?" 614 " NB: This is independent of doNanMasking; if that is False this plane" 615 " will likely be blank, resulting in a no-op here.",
618 doNanInterpAfterFlat = pexConfig.Field(
620 doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 621 "also have to interpolate them before flat-fielding."),
624 maskListToInterpolate = pexConfig.ListField(
626 doc=
"List of mask planes that should be interpolated.",
627 default=[
'SAT',
'BAD',
'UNMASKEDNAN'],
629 doSaveInterpPixels = pexConfig.Field(
631 doc=
"Save a copy of the pre-interpolated pixel values?",
636 fluxMag0T1 = pexConfig.DictField(
639 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
640 default=dict((f, pow(10.0, 0.4*m))
for f, m
in ((
"Unknown", 28.0),
643 defaultFluxMag0T1 = pexConfig.Field(
645 doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
646 default=pow(10.0, 0.4*28.0)
650 doVignette = pexConfig.Field(
652 doc=
"Apply vignetting parameters?",
655 vignette = pexConfig.ConfigurableField(
657 doc=
"Vignetting task.",
661 doAttachTransmissionCurve = pexConfig.Field(
664 doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 666 doUseOpticsTransmission = pexConfig.Field(
669 doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 671 doUseFilterTransmission = pexConfig.Field(
674 doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 676 doUseSensorTransmission = pexConfig.Field(
679 doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 681 doUseAtmosphereTransmission = pexConfig.Field(
684 doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 688 doIlluminationCorrection = pexConfig.Field(
691 doc=
"Perform illumination correction?" 693 illuminationCorrectionDataProductName = pexConfig.Field(
695 doc=
"Name of the illumination correction data product.",
698 illumScale = pexConfig.Field(
700 doc=
"Scale factor for the illumination correction.",
703 illumFilters = pexConfig.ListField(
706 doc=
"Only perform illumination correction for these filters." 710 doWrite = pexConfig.Field(
712 doc=
"Persist postISRCCD?",
719 raise ValueError(
"You may not specify both doFlat and doApplyGains")
721 self.config.maskListToInterpolate.append(
"SAT")
723 self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
726 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
727 """Apply common instrument signature correction algorithms to a raw frame. 729 The process for correcting imaging data is very similar from 730 camera to camera. This task provides a vanilla implementation of 731 doing these corrections, including the ability to turn certain 732 corrections off if they are not needed. The inputs to the primary 733 method, `run()`, are a raw exposure to be corrected and the 734 calibration data products. The raw input is a single chip sized 735 mosaic of all amps including overscans and other non-science 736 pixels. The method `runDataRef()` identifies and defines the 737 calibration data products, and is intended for use by a 738 `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 739 `daf.persistence.butlerSubset.ButlerDataRef`. This task may be 740 subclassed for different camera, although the most camera specific 741 methods have been split into subtasks that can be redirected 744 The __init__ method sets up the subtasks for ISR processing, using 745 the defaults from `lsst.ip.isr`. 750 Positional arguments passed to the Task constructor. None used at this time. 751 kwargs : `dict`, optional 752 Keyword arguments passed on to the Task constructor. None used at this time. 754 ConfigClass = IsrTaskConfig
759 self.makeSubtask(
"assembleCcd")
760 self.makeSubtask(
"crosstalk")
761 self.makeSubtask(
"strayLight")
762 self.makeSubtask(
"fringe")
763 self.makeSubtask(
"masking")
764 self.makeSubtask(
"vignette")
773 if config.doBias
is not True:
774 inputTypeDict.pop(
"bias",
None)
775 if config.doLinearize
is not True:
776 inputTypeDict.pop(
"linearizer",
None)
777 if config.doCrosstalk
is not True:
778 inputTypeDict.pop(
"crosstalkSources",
None)
779 if config.doBrighterFatter
is not True:
780 inputTypeDict.pop(
"bfKernel",
None)
781 if config.doDefect
is not True:
782 inputTypeDict.pop(
"defects",
None)
783 if config.doDark
is not True:
784 inputTypeDict.pop(
"dark",
None)
785 if config.doFlat
is not True:
786 inputTypeDict.pop(
"flat",
None)
787 if config.doAttachTransmissionCurve
is not True:
788 inputTypeDict.pop(
"opticsTransmission",
None)
789 inputTypeDict.pop(
"filterTransmission",
None)
790 inputTypeDict.pop(
"sensorTransmission",
None)
791 inputTypeDict.pop(
"atmosphereTransmission",
None)
792 if config.doUseOpticsTransmission
is not True:
793 inputTypeDict.pop(
"opticsTransmission",
None)
794 if config.doUseFilterTransmission
is not True:
795 inputTypeDict.pop(
"filterTransmission",
None)
796 if config.doUseSensorTransmission
is not True:
797 inputTypeDict.pop(
"sensorTransmission",
None)
798 if config.doUseAtmosphereTransmission
is not True:
799 inputTypeDict.pop(
"atmosphereTransmission",
None)
800 if config.doIlluminationCorrection
is not True:
801 inputTypeDict.pop(
"illumMaskedImage",
None)
809 if config.qa.doThumbnailOss
is not True:
810 outputTypeDict.pop(
"outputOssThumbnail",
None)
811 if config.qa.doThumbnailFlattened
is not True:
812 outputTypeDict.pop(
"outputFlattenedThumbnail",
None)
813 if config.doWrite
is not True:
814 outputTypeDict.pop(
"outputExposure",
None)
816 return outputTypeDict
825 names.remove(
"ccdExposure")
830 inputData[
'detectorNum'] = int(inputDataIds[
'ccdExposure'][
'detector'])
831 except Exception
as e:
832 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
835 inputData[
'isGen3'] =
True 837 if self.config.doLinearize
is True:
838 if 'linearizer' not in inputData.keys():
839 detector = inputData[
'camera'][inputData[
'detectorNum']]
840 linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
841 inputData[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
843 if inputData[
'defects']
is not None:
846 if not isinstance(inputData[
"defects"], Defects):
847 inputData[
"defects"] = Defects.fromTable(inputData[
"defects"])
864 return super().
adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
870 """!Retrieve necessary frames for instrument signature removal. 872 Pre-fetching all required ISR data products limits the IO 873 required by the ISR. Any conflict between the calibration data 874 available and that needed for ISR is also detected prior to 875 doing processing, allowing it to fail quickly. 879 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 880 Butler reference of the detector data to be processed 881 rawExposure : `afw.image.Exposure` 882 The raw exposure that will later be corrected with the 883 retrieved calibration data; should not be modified in this 888 result : `lsst.pipe.base.Struct` 889 Result struct with components (which may be `None`): 890 - ``bias``: bias calibration frame (`afw.image.Exposure`) 891 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 892 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 893 - ``dark``: dark calibration frame (`afw.image.Exposure`) 894 - ``flat``: flat calibration frame (`afw.image.Exposure`) 895 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 896 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 897 - ``fringes``: `lsst.pipe.base.Struct` with components: 898 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 899 - ``seed``: random seed derived from the ccdExposureId for random 900 number generator (`uint32`). 901 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 902 A ``TransmissionCurve`` that represents the throughput of the optics, 903 to be evaluated in focal-plane coordinates. 904 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 905 A ``TransmissionCurve`` that represents the throughput of the filter 906 itself, to be evaluated in focal-plane coordinates. 907 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 908 A ``TransmissionCurve`` that represents the throughput of the sensor 909 itself, to be evaluated in post-assembly trimmed detector coordinates. 910 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 911 A ``TransmissionCurve`` that represents the throughput of the 912 atmosphere, assumed to be spatially constant. 913 - ``strayLightData`` : `object` 914 An opaque object containing calibration information for 915 stray-light correction. If `None`, no correction will be 917 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 921 NotImplementedError : 922 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 924 ccd = rawExposure.getDetector()
925 filterName = afwImage.Filter(rawExposure.getFilter().getId()).getName()
926 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
927 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
928 if self.config.doBias
else None)
930 linearizer = (dataRef.get(
"linearizer", immediate=
True)
932 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
933 if self.config.doCrosstalk
else None)
934 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
935 if self.config.doDark
else None)
936 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
937 if self.config.doFlat
else None)
939 brighterFatterKernel =
None 940 if self.config.doBrighterFatter
is True:
944 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
948 brighterFatterKernel = dataRef.get(
"bfKernel")
950 brighterFatterKernel =
None 951 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
954 if self.config.brighterFatterLevel ==
'DETECTOR':
955 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
958 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
960 defectList = (dataRef.get(
"defects")
961 if self.config.doDefect
else None)
962 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
963 if self.config.doAssembleIsrExposures
else None)
964 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
965 else pipeBase.Struct(fringes=
None))
967 if self.config.doAttachTransmissionCurve:
968 opticsTransmission = (dataRef.get(
"transmission_optics")
969 if self.config.doUseOpticsTransmission
else None)
970 filterTransmission = (dataRef.get(
"transmission_filter")
971 if self.config.doUseFilterTransmission
else None)
972 sensorTransmission = (dataRef.get(
"transmission_sensor")
973 if self.config.doUseSensorTransmission
else None)
974 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
975 if self.config.doUseAtmosphereTransmission
else None)
977 opticsTransmission =
None 978 filterTransmission =
None 979 sensorTransmission =
None 980 atmosphereTransmission =
None 982 if self.config.doStrayLight:
983 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
985 strayLightData =
None 988 self.config.illuminationCorrectionDataProductName).getMaskedImage()
989 if (self.config.doIlluminationCorrection
and 990 filterName
in self.config.illumFilters)
994 return pipeBase.Struct(bias=biasExposure,
995 linearizer=linearizer,
996 crosstalkSources=crosstalkSources,
999 bfKernel=brighterFatterKernel,
1001 fringes=fringeStruct,
1002 opticsTransmission=opticsTransmission,
1003 filterTransmission=filterTransmission,
1004 sensorTransmission=sensorTransmission,
1005 atmosphereTransmission=atmosphereTransmission,
1006 strayLightData=strayLightData,
1007 illumMaskedImage=illumMaskedImage
1010 @pipeBase.timeMethod
1011 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1012 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
1013 opticsTransmission=
None, filterTransmission=
None,
1014 sensorTransmission=
None, atmosphereTransmission=
None,
1015 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1018 """!Perform instrument signature removal on an exposure. 1020 Steps included in the ISR processing, in order performed, are: 1021 - saturation and suspect pixel masking 1022 - overscan subtraction 1023 - CCD assembly of individual amplifiers 1025 - variance image construction 1026 - linearization of non-linear response 1028 - brighter-fatter correction 1031 - stray light subtraction 1033 - masking of known defects and camera specific features 1034 - vignette calculation 1035 - appending transmission curve and distortion model 1039 ccdExposure : `lsst.afw.image.Exposure` 1040 The raw exposure that is to be run through ISR. The 1041 exposure is modified by this method. 1042 camera : `lsst.afw.cameraGeom.Camera`, optional 1043 The camera geometry for this exposure. Used to select the 1044 distortion model appropriate for this data. 1045 bias : `lsst.afw.image.Exposure`, optional 1046 Bias calibration frame. 1047 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1048 Functor for linearization. 1049 crosstalkSources : `list`, optional 1050 List of possible crosstalk sources. 1051 dark : `lsst.afw.image.Exposure`, optional 1052 Dark calibration frame. 1053 flat : `lsst.afw.image.Exposure`, optional 1054 Flat calibration frame. 1055 bfKernel : `numpy.ndarray`, optional 1056 Brighter-fatter kernel. 1057 defects : `lsst.meas.algorithms.Defects`, optional 1059 fringes : `lsst.pipe.base.Struct`, optional 1060 Struct containing the fringe correction data, with 1062 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1063 - ``seed``: random seed derived from the ccdExposureId for random 1064 number generator (`uint32`) 1065 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1066 A ``TransmissionCurve`` that represents the throughput of the optics, 1067 to be evaluated in focal-plane coordinates. 1068 filterTransmission : `lsst.afw.image.TransmissionCurve` 1069 A ``TransmissionCurve`` that represents the throughput of the filter 1070 itself, to be evaluated in focal-plane coordinates. 1071 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1072 A ``TransmissionCurve`` that represents the throughput of the sensor 1073 itself, to be evaluated in post-assembly trimmed detector coordinates. 1074 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1075 A ``TransmissionCurve`` that represents the throughput of the 1076 atmosphere, assumed to be spatially constant. 1077 detectorNum : `int`, optional 1078 The integer number for the detector to process. 1079 isGen3 : bool, optional 1080 Flag this call to run() as using the Gen3 butler environment. 1081 strayLightData : `object`, optional 1082 Opaque object containing calibration information for stray-light 1083 correction. If `None`, no correction will be performed. 1084 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1085 Illumination correction image. 1089 result : `lsst.pipe.base.Struct` 1090 Result struct with component: 1091 - ``exposure`` : `afw.image.Exposure` 1092 The fully ISR corrected exposure. 1093 - ``outputExposure`` : `afw.image.Exposure` 1094 An alias for `exposure` 1095 - ``ossThumb`` : `numpy.ndarray` 1096 Thumbnail image of the exposure after overscan subtraction. 1097 - ``flattenedThumb`` : `numpy.ndarray` 1098 Thumbnail image of the exposure after flat-field correction. 1103 Raised if a configuration option is set to True, but the 1104 required calibration data has not been specified. 1108 The current processed exposure can be viewed by setting the 1109 appropriate lsstDebug entries in the `debug.display` 1110 dictionary. The names of these entries correspond to some of 1111 the IsrTaskConfig Boolean options, with the value denoting the 1112 frame to use. The exposure is shown inside the matching 1113 option check and after the processing of that step has 1114 finished. The steps with debug points are: 1125 In addition, setting the "postISRCCD" entry displays the 1126 exposure after all ISR processing has finished. 1134 self.config.doFringe =
False 1137 if detectorNum
is None:
1138 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1140 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1145 if isinstance(ccdExposure, ButlerDataRef):
1148 ccd = ccdExposure.getDetector()
1149 filterName = afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1152 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1153 ccd = [
FakeAmp(ccdExposure, self.config)]
1156 if self.config.doBias
and bias
is None:
1157 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1159 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1160 if self.config.doBrighterFatter
and bfKernel
is None:
1161 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1162 if self.config.doDark
and dark
is None:
1163 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1164 if self.config.doFlat
and flat
is None:
1165 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1166 if self.config.doDefect
and defects
is None:
1167 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1168 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1169 fringes.fringes
is None):
1174 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1175 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1176 illumMaskedImage
is None):
1177 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1180 if self.config.doConvertIntToFloat:
1181 self.log.info(
"Converting exposure to floating point values.")
1188 if ccdExposure.getBBox().contains(amp.getBBox()):
1192 if self.config.doOverscan
and not badAmp:
1195 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1196 if overscanResults
is not None and \
1197 self.config.qa
is not None and self.config.qa.saveStats
is True:
1198 if isinstance(overscanResults.overscanFit, float):
1199 qaMedian = overscanResults.overscanFit
1200 qaStdev = float(
"NaN")
1202 qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1203 afwMath.MEDIAN | afwMath.STDEVCLIP)
1204 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1205 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1207 self.metadata.set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1208 self.metadata.set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1209 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1210 amp.getName(), qaMedian, qaStdev)
1211 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1214 self.log.warn(
"Amplifier %s is bad.", amp.getName())
1215 overscanResults =
None 1217 overscans.append(overscanResults
if overscanResults
is not None else None)
1219 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1221 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1222 self.log.info(
"Applying crosstalk correction.")
1223 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1224 self.
debugView(ccdExposure,
"doCrosstalk")
1226 if self.config.doAssembleCcd:
1227 self.log.info(
"Assembling CCD from amplifiers.")
1228 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1230 if self.config.expectWcs
and not ccdExposure.getWcs():
1231 self.log.warn(
"No WCS found in input exposure.")
1232 self.
debugView(ccdExposure,
"doAssembleCcd")
1235 if self.config.qa.doThumbnailOss:
1236 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1238 if self.config.doBias:
1239 self.log.info(
"Applying bias correction.")
1240 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1241 trimToFit=self.config.doTrimToMatchCalib)
1244 if self.config.doVariance:
1245 for amp, overscanResults
in zip(ccd, overscans):
1246 if ccdExposure.getBBox().contains(amp.getBBox()):
1247 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1248 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1249 if overscanResults
is not None:
1251 overscanImage=overscanResults.overscanImage)
1255 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1256 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1257 afwMath.MEDIAN | afwMath.STDEVCLIP)
1258 self.metadata.set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1259 qaStats.getValue(afwMath.MEDIAN))
1260 self.metadata.set(f
"ISR VARIANCE {amp.getName()} STDEV",
1261 qaStats.getValue(afwMath.STDEVCLIP))
1262 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1263 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1264 qaStats.getValue(afwMath.STDEVCLIP))
1267 self.log.info(
"Applying linearizer.")
1268 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1270 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1271 self.log.info(
"Applying crosstalk correction.")
1272 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1273 self.
debugView(ccdExposure,
"doCrosstalk")
1277 if self.config.doDefect:
1278 self.log.info(
"Masking defects.")
1281 if self.config.numEdgeSuspect > 0:
1282 self.log.info(
"Masking edges as SUSPECT.")
1283 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1284 maskPlane=
"SUSPECT")
1286 if self.config.doNanMasking:
1287 self.log.info(
"Masking NAN value pixels.")
1290 if self.config.doWidenSaturationTrails:
1291 self.log.info(
"Widening saturation trails.")
1292 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1294 if self.config.doCameraSpecificMasking:
1295 self.log.info(
"Masking regions for camera specific reasons.")
1296 self.masking.
run(ccdExposure)
1298 if self.config.doBrighterFatter:
1307 interpExp = ccdExposure.clone()
1309 isrFunctions.interpolateFromMask(
1310 maskedImage=interpExp.getMaskedImage(),
1311 fwhm=self.config.fwhm,
1312 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1313 maskNameList=self.config.maskListToInterpolate
1315 bfExp = interpExp.clone()
1317 self.log.info(
"Applying brighter fatter correction.")
1318 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1319 self.config.brighterFatterMaxIter,
1320 self.config.brighterFatterThreshold,
1321 self.config.brighterFatterApplyGain
1323 if bfResults[1] == self.config.brighterFatterMaxIter:
1324 self.log.warn(
"Brighter fatter correction did not converge, final difference %f.",
1327 self.log.info(
"Finished brighter fatter correction in %d iterations.",
1329 image = ccdExposure.getMaskedImage().getImage()
1330 bfCorr = bfExp.getMaskedImage().getImage()
1331 bfCorr -= interpExp.getMaskedImage().getImage()
1340 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1342 self.log.warn(
"Ensuring image edges are masked as SUSPECT to the brighter-fatter kernel size.")
1344 self.
debugView(ccdExposure,
"doBrighterFatter")
1346 if self.config.doDark:
1347 self.log.info(
"Applying dark correction.")
1351 if self.config.doFringe
and not self.config.fringeAfterFlat:
1352 self.log.info(
"Applying fringe correction before flat.")
1353 self.fringe.
run(ccdExposure, **fringes.getDict())
1356 if self.config.doStrayLight:
1357 if strayLightData
is not None:
1358 self.log.info(
"Applying stray light correction.")
1359 self.strayLight.
run(ccdExposure, strayLightData)
1360 self.
debugView(ccdExposure,
"doStrayLight")
1362 self.log.debug(
"Skipping stray light correction: no data found for this image.")
1364 if self.config.doFlat:
1365 self.log.info(
"Applying flat correction.")
1369 if self.config.doApplyGains:
1370 self.log.info(
"Applying gain correction instead of flat.")
1371 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1373 if self.config.doFringe
and self.config.fringeAfterFlat:
1374 self.log.info(
"Applying fringe correction after flat.")
1375 self.fringe.
run(ccdExposure, **fringes.getDict())
1377 if self.config.doVignette:
1378 self.log.info(
"Constructing Vignette polygon.")
1381 if self.config.vignette.doWriteVignettePolygon:
1384 if self.config.doAttachTransmissionCurve:
1385 self.log.info(
"Adding transmission curves.")
1386 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1387 filterTransmission=filterTransmission,
1388 sensorTransmission=sensorTransmission,
1389 atmosphereTransmission=atmosphereTransmission)
1391 flattenedThumb =
None 1392 if self.config.qa.doThumbnailFlattened:
1393 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1395 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1396 self.log.info(
"Performing illumination correction.")
1397 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1398 illumMaskedImage, illumScale=self.config.illumScale,
1399 trimToFit=self.config.doTrimToMatchCalib)
1402 if self.config.doSaveInterpPixels:
1403 preInterpExp = ccdExposure.clone()
1418 if self.config.doSetBadRegions:
1419 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1420 if badPixelCount > 0:
1421 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1423 if self.config.doInterpolate:
1424 self.log.info(
"Interpolating masked pixels.")
1425 isrFunctions.interpolateFromMask(
1426 maskedImage=ccdExposure.getMaskedImage(),
1427 fwhm=self.config.fwhm,
1428 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1429 maskNameList=list(self.config.maskListToInterpolate)
1434 if self.config.doMeasureBackground:
1435 self.log.info(
"Measuring background level.")
1438 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1440 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1441 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1442 afwMath.MEDIAN | afwMath.STDEVCLIP)
1443 self.metadata.set(
"ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1444 qaStats.getValue(afwMath.MEDIAN))
1445 self.metadata.set(
"ISR BACKGROUND {} STDEV".format(amp.getName()),
1446 qaStats.getValue(afwMath.STDEVCLIP))
1447 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1448 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1449 qaStats.getValue(afwMath.STDEVCLIP))
1451 self.
debugView(ccdExposure,
"postISRCCD")
1453 return pipeBase.Struct(
1454 exposure=ccdExposure,
1456 flattenedThumb=flattenedThumb,
1458 preInterpolatedExposure=preInterpExp,
1459 outputExposure=ccdExposure,
1460 outputOssThumbnail=ossThumb,
1461 outputFlattenedThumbnail=flattenedThumb,
1464 @pipeBase.timeMethod
1466 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1468 This method contains the `CmdLineTask` interface to the ISR 1469 processing. All IO is handled here, freeing the `run()` method 1470 to manage only pixel-level calculations. The steps performed 1472 - Read in necessary detrending/isr/calibration data. 1473 - Process raw exposure in `run()`. 1474 - Persist the ISR-corrected exposure as "postISRCCD" if 1475 config.doWrite=True. 1479 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1480 DataRef of the detector data to be processed 1484 result : `lsst.pipe.base.Struct` 1485 Result struct with component: 1486 - ``exposure`` : `afw.image.Exposure` 1487 The fully ISR corrected exposure. 1492 Raised if a configuration option is set to True, but the 1493 required calibration data does not exist. 1496 self.log.info(
"Performing ISR on sensor %s.", sensorRef.dataId)
1498 ccdExposure = sensorRef.get(self.config.datasetType)
1500 camera = sensorRef.get(
"camera")
1501 isrData = self.
readIsrData(sensorRef, ccdExposure)
1503 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1505 if self.config.doWrite:
1506 sensorRef.put(result.exposure,
"postISRCCD")
1507 if result.preInterpolatedExposure
is not None:
1508 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1509 if result.ossThumb
is not None:
1510 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1511 if result.flattenedThumb
is not None:
1512 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1517 """!Retrieve a calibration dataset for removing instrument signature. 1522 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1523 DataRef of the detector data to find calibration datasets 1526 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1528 If True, disable butler proxies to enable error handling 1529 within this routine. 1533 exposure : `lsst.afw.image.Exposure` 1534 Requested calibration frame. 1539 Raised if no matching calibration frame can be found. 1542 exp = dataRef.get(datasetType, immediate=immediate)
1543 except Exception
as exc1:
1544 if not self.config.fallbackFilterName:
1545 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1547 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1548 except Exception
as exc2:
1549 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1550 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1551 self.log.warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
1553 if self.config.doAssembleIsrExposures:
1554 exp = self.assembleCcd.assembleCcd(exp)
1558 """Ensure that the data returned by Butler is a fully constructed exposure. 1560 ISR requires exposure-level image data for historical reasons, so if we did 1561 not recieve that from Butler, construct it from what we have, modifying the 1566 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1567 `lsst.afw.image.ImageF` 1568 The input data structure obtained from Butler. 1569 camera : `lsst.afw.cameraGeom.camera` 1570 The camera associated with the image. Used to find the appropriate 1573 The detector this exposure should match. 1577 inputExp : `lsst.afw.image.Exposure` 1578 The re-constructed exposure, with appropriate detector parameters. 1583 Raised if the input data cannot be used to construct an exposure. 1585 if isinstance(inputExp, afwImage.DecoratedImageU):
1586 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1587 elif isinstance(inputExp, afwImage.ImageF):
1588 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1589 elif isinstance(inputExp, afwImage.MaskedImageF):
1590 inputExp = afwImage.makeExposure(inputExp)
1591 elif isinstance(inputExp, afwImage.Exposure):
1593 elif inputExp
is None:
1597 raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
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.",
1891 amp.getName(), readNoise)
1893 readNoise = amp.getReadNoise()
1895 isrFunctions.updateVariance(
1896 maskedImage=ampExposure.getMaskedImage(),
1898 readNoise=readNoise,
1902 """!Apply dark correction in place. 1906 exposure : `lsst.afw.image.Exposure` 1907 Exposure to process. 1908 darkExposure : `lsst.afw.image.Exposure` 1909 Dark exposure of the same size as ``exposure``. 1910 invert : `Bool`, optional 1911 If True, re-add the dark to an already corrected image. 1916 Raised if either ``exposure`` or ``darkExposure`` do not 1917 have their dark time defined. 1921 lsst.ip.isr.isrFunctions.darkCorrection 1923 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1924 if math.isnan(expScale):
1925 raise RuntimeError(
"Exposure darktime is NAN.")
1926 if darkExposure.getInfo().getVisitInfo()
is not None:
1927 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1931 self.log.warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1934 if math.isnan(darkScale):
1935 raise RuntimeError(
"Dark calib darktime is NAN.")
1936 isrFunctions.darkCorrection(
1937 maskedImage=exposure.getMaskedImage(),
1938 darkMaskedImage=darkExposure.getMaskedImage(),
1940 darkScale=darkScale,
1942 trimToFit=self.config.doTrimToMatchCalib
1946 """!Check if linearization is needed for the detector cameraGeom. 1948 Checks config.doLinearize and the linearity type of the first 1953 detector : `lsst.afw.cameraGeom.Detector` 1954 Detector to get linearity type from. 1958 doLinearize : `Bool` 1959 If True, linearization should be performed. 1961 return self.config.doLinearize
and \
1962 detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1965 """!Apply flat correction in place. 1969 exposure : `lsst.afw.image.Exposure` 1970 Exposure to process. 1971 flatExposure : `lsst.afw.image.Exposure` 1972 Flat exposure of the same size as ``exposure``. 1973 invert : `Bool`, optional 1974 If True, unflatten an already flattened image. 1978 lsst.ip.isr.isrFunctions.flatCorrection 1980 isrFunctions.flatCorrection(
1981 maskedImage=exposure.getMaskedImage(),
1982 flatMaskedImage=flatExposure.getMaskedImage(),
1983 scalingType=self.config.flatScalingType,
1984 userScale=self.config.flatUserScale,
1986 trimToFit=self.config.doTrimToMatchCalib
1990 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1994 exposure : `lsst.afw.image.Exposure` 1995 Exposure to process. Only the amplifier DataSec is processed. 1996 amp : `lsst.afw.table.AmpInfoCatalog` 1997 Amplifier detector data. 2001 lsst.ip.isr.isrFunctions.makeThresholdMask 2003 if not math.isnan(amp.getSaturation()):
2004 maskedImage = exposure.getMaskedImage()
2005 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2006 isrFunctions.makeThresholdMask(
2007 maskedImage=dataView,
2008 threshold=amp.getSaturation(),
2010 maskName=self.config.saturatedMaskName,
2014 """!Interpolate over saturated pixels, in place. 2016 This method should be called after `saturationDetection`, to 2017 ensure that the saturated pixels have been identified in the 2018 SAT mask. It should also be called after `assembleCcd`, since 2019 saturated regions may cross amplifier boundaries. 2023 exposure : `lsst.afw.image.Exposure` 2024 Exposure to process. 2028 lsst.ip.isr.isrTask.saturationDetection 2029 lsst.ip.isr.isrFunctions.interpolateFromMask 2031 isrFunctions.interpolateFromMask(
2032 maskedImage=exposure.getMaskedImage(),
2033 fwhm=self.config.fwhm,
2034 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2035 maskNameList=list(self.config.saturatedMaskName),
2039 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2043 exposure : `lsst.afw.image.Exposure` 2044 Exposure to process. Only the amplifier DataSec is processed. 2045 amp : `lsst.afw.table.AmpInfoCatalog` 2046 Amplifier detector data. 2050 lsst.ip.isr.isrFunctions.makeThresholdMask 2054 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2055 This is intended to indicate pixels that may be affected by unknown systematics; 2056 for example if non-linearity corrections above a certain level are unstable 2057 then that would be a useful value for suspectLevel. A value of `nan` indicates 2058 that no such level exists and no pixels are to be masked as suspicious. 2060 suspectLevel = amp.getSuspectLevel()
2061 if math.isnan(suspectLevel):
2064 maskedImage = exposure.getMaskedImage()
2065 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2066 isrFunctions.makeThresholdMask(
2067 maskedImage=dataView,
2068 threshold=suspectLevel,
2070 maskName=self.config.suspectMaskName,
2074 """!Mask defects using mask plane "BAD", in place. 2078 exposure : `lsst.afw.image.Exposure` 2079 Exposure to process. 2080 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2081 `lsst.afw.image.DefectBase`. 2082 List of defects to mask. 2086 Call this after CCD assembly, since defects may cross amplifier boundaries. 2088 maskedImage = exposure.getMaskedImage()
2089 if not isinstance(defectBaseList, Defects):
2091 defectList = Defects(defectBaseList)
2093 defectList = defectBaseList
2094 defectList.maskPixels(maskedImage, maskName=
"BAD")
2096 def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT"):
2097 """!Mask edge pixels with applicable mask plane. 2101 exposure : `lsst.afw.image.Exposure` 2102 Exposure to process. 2103 numEdgePixels : `int`, optional 2104 Number of edge pixels to mask. 2105 maskPlane : `str`, optional 2106 Mask plane name to use. 2108 maskedImage = exposure.getMaskedImage()
2109 maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
2111 if numEdgePixels > 0:
2112 goodBBox = maskedImage.getBBox()
2114 goodBBox.grow(-numEdgePixels)
2116 SourceDetectionTask.setEdgeBits(
2123 """Mask and interpolate defects using mask plane "BAD", in place. 2127 exposure : `lsst.afw.image.Exposure` 2128 Exposure to process. 2129 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2130 `lsst.afw.image.DefectBase`. 2131 List of defects to mask and interpolate. 2135 lsst.ip.isr.isrTask.maskDefect() 2138 self.
maskEdges(exposure, numEdgePixels=self.config.numEdgeSuspect,
2139 maskPlane=
"SUSPECT")
2140 isrFunctions.interpolateFromMask(
2141 maskedImage=exposure.getMaskedImage(),
2142 fwhm=self.config.fwhm,
2143 growSaturatedFootprints=0,
2144 maskNameList=[
"BAD"],
2148 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2152 exposure : `lsst.afw.image.Exposure` 2153 Exposure to process. 2157 We mask over all NaNs, including those that are masked with 2158 other bits (because those may or may not be interpolated over 2159 later, and we want to remove all NaNs). Despite this 2160 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2161 the historical name. 2163 maskedImage = exposure.getMaskedImage()
2166 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2167 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2168 numNans =
maskNans(maskedImage, maskVal)
2169 self.metadata.set(
"NUMNANS", numNans)
2171 self.log.warn(
"There were %d unmasked NaNs.", numNans)
2174 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2178 exposure : `lsst.afw.image.Exposure` 2179 Exposure to process. 2183 lsst.ip.isr.isrTask.maskNan() 2186 isrFunctions.interpolateFromMask(
2187 maskedImage=exposure.getMaskedImage(),
2188 fwhm=self.config.fwhm,
2189 growSaturatedFootprints=0,
2190 maskNameList=[
"UNMASKEDNAN"],
2194 """Measure the image background in subgrids, for quality control purposes. 2198 exposure : `lsst.afw.image.Exposure` 2199 Exposure to process. 2200 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2201 Configuration object containing parameters on which background 2202 statistics and subgrids to use. 2204 if IsrQaConfig
is not None:
2205 statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
2206 IsrQaConfig.flatness.nIter)
2207 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2208 statsControl.setAndMask(maskVal)
2209 maskedImage = exposure.getMaskedImage()
2210 stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
2211 skyLevel = stats.getValue(afwMath.MEDIAN)
2212 skySigma = stats.getValue(afwMath.STDEVCLIP)
2213 self.log.info(
"Flattened sky level: %f +/- %f.", skyLevel, skySigma)
2214 metadata = exposure.getMetadata()
2215 metadata.set(
'SKYLEVEL', skyLevel)
2216 metadata.set(
'SKYSIGMA', skySigma)
2219 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2220 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2221 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2222 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2223 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2224 skyLevels = numpy.zeros((nX, nY))
2227 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2229 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2231 xLLC = xc - meshXHalf
2232 yLLC = yc - meshYHalf
2233 xURC = xc + meshXHalf - 1
2234 yURC = yc + meshYHalf - 1
2237 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2239 skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2241 good = numpy.where(numpy.isfinite(skyLevels))
2242 skyMedian = numpy.median(skyLevels[good])
2243 flatness = (skyLevels[good] - skyMedian) / skyMedian
2244 flatness_rms = numpy.std(flatness)
2245 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2247 self.log.info(
"Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
2248 self.log.info(
"Sky flatness in %dx%d grids - pp: %f rms: %f.",
2249 nX, nY, flatness_pp, flatness_rms)
2251 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2252 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2253 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2254 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2255 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2258 """Set an approximate magnitude zero point for the exposure. 2262 exposure : `lsst.afw.image.Exposure` 2263 Exposure to process. 2265 filterName = afwImage.Filter(exposure.getFilter().getId()).getName()
2266 if filterName
in self.config.fluxMag0T1:
2267 fluxMag0 = self.config.fluxMag0T1[filterName]
2269 self.log.warn(
"No rough magnitude zero point set for filter %s.", filterName)
2270 fluxMag0 = self.config.defaultFluxMag0T1
2272 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2274 self.log.warn(
"Non-positive exposure time; skipping rough zero point.")
2277 self.log.info(
"Setting rough magnitude zero point: %f", 2.5*math.log10(fluxMag0*expTime))
2278 exposure.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(fluxMag0*expTime, 0.0))
2281 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2285 ccdExposure : `lsst.afw.image.Exposure` 2286 Exposure to process. 2287 fpPolygon : `lsst.afw.geom.Polygon` 2288 Polygon in focal plane coordinates. 2291 ccd = ccdExposure.getDetector()
2292 fpCorners = ccd.getCorners(FOCAL_PLANE)
2293 ccdPolygon = Polygon(fpCorners)
2296 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2299 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2300 validPolygon = Polygon(ccdPoints)
2301 ccdExposure.getInfo().setValidPolygon(validPolygon)
2305 """Context manager that applies and removes flats and darks, 2306 if the task is configured to apply them. 2310 exp : `lsst.afw.image.Exposure` 2311 Exposure to process. 2312 flat : `lsst.afw.image.Exposure` 2313 Flat exposure the same size as ``exp``. 2314 dark : `lsst.afw.image.Exposure`, optional 2315 Dark exposure the same size as ``exp``. 2319 exp : `lsst.afw.image.Exposure` 2320 The flat and dark corrected exposure. 2322 if self.config.doDark
and dark
is not None:
2324 if self.config.doFlat:
2329 if self.config.doFlat:
2331 if self.config.doDark
and dark
is not None:
2335 """Utility function to examine ISR exposure at different stages. 2339 exposure : `lsst.afw.image.Exposure` 2342 State of processing to view. 2344 frame = getDebugFrame(self._display, stepname)
2346 display = getDisplay(frame)
2347 display.scale(
'asinh',
'zscale')
2348 display.mtv(exposure)
2349 prompt =
"Press Enter to continue [c]... " 2351 ans = input(prompt).lower()
2352 if ans
in (
"",
"c",):
2357 """A Detector-like object that supports returning gain and saturation level 2359 This is used when the input exposure does not have a detector. 2363 exposure : `lsst.afw.image.Exposure` 2364 Exposure to generate a fake amplifier for. 2365 config : `lsst.ip.isr.isrTaskConfig` 2366 Configuration to apply to the fake amplifier. 2370 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2372 self.
_gain = config.gain
2402 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2406 """Task to wrap the default IsrTask to allow it to be retargeted. 2408 The standard IsrTask can be called directly from a command line 2409 program, but doing so removes the ability of the task to be 2410 retargeted. As most cameras override some set of the IsrTask 2411 methods, this would remove those data-specific methods in the 2412 output post-ISR images. This wrapping class fixes the issue, 2413 allowing identical post-ISR images to be generated by both the 2414 processCcd and isrTask code. 2416 ConfigClass = RunIsrConfig
2417 _DefaultName =
"runIsr" 2421 self.makeSubtask(
"isr")
2427 dataRef : `lsst.daf.persistence.ButlerDataRef` 2428 data reference of the detector data to be processed 2432 result : `pipeBase.Struct` 2433 Result struct with component: 2435 - exposure : `lsst.afw.image.Exposure` 2436 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 maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT")
Mask edge pixels with applicable mask plane.
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)