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(f
"Failure to find valid detectorNum value for Dataset {inputDataIds}: {e}")
836 inputData[
'isGen3'] =
True 838 if self.config.doLinearize
is True:
839 if 'linearizer' not in inputData.keys():
840 detector = inputData[
'camera'][inputData[
'detectorNum']]
841 linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
842 inputData[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
844 if inputData[
'defects']
is not None:
847 if not isinstance(inputData[
"defects"], Defects):
848 inputData[
"defects"] = Defects.fromTable(inputData[
"defects"])
865 return super().
adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
871 """!Retrieve necessary frames for instrument signature removal. 873 Pre-fetching all required ISR data products limits the IO 874 required by the ISR. Any conflict between the calibration data 875 available and that needed for ISR is also detected prior to 876 doing processing, allowing it to fail quickly. 880 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 881 Butler reference of the detector data to be processed 882 rawExposure : `afw.image.Exposure` 883 The raw exposure that will later be corrected with the 884 retrieved calibration data; should not be modified in this 889 result : `lsst.pipe.base.Struct` 890 Result struct with components (which may be `None`): 891 - ``bias``: bias calibration frame (`afw.image.Exposure`) 892 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 893 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 894 - ``dark``: dark calibration frame (`afw.image.Exposure`) 895 - ``flat``: flat calibration frame (`afw.image.Exposure`) 896 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 897 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 898 - ``fringes``: `lsst.pipe.base.Struct` with components: 899 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 900 - ``seed``: random seed derived from the ccdExposureId for random 901 number generator (`uint32`). 902 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 903 A ``TransmissionCurve`` that represents the throughput of the optics, 904 to be evaluated in focal-plane coordinates. 905 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 906 A ``TransmissionCurve`` that represents the throughput of the filter 907 itself, to be evaluated in focal-plane coordinates. 908 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 909 A ``TransmissionCurve`` that represents the throughput of the sensor 910 itself, to be evaluated in post-assembly trimmed detector coordinates. 911 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 912 A ``TransmissionCurve`` that represents the throughput of the 913 atmosphere, assumed to be spatially constant. 914 - ``strayLightData`` : `object` 915 An opaque object containing calibration information for 916 stray-light correction. If `None`, no correction will be 918 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 922 NotImplementedError : 923 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 925 ccd = rawExposure.getDetector()
926 filterName = afwImage.Filter(rawExposure.getFilter().getId()).getName()
927 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
928 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
929 if self.config.doBias
else None)
931 linearizer = (dataRef.get(
"linearizer", immediate=
True)
933 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
934 if self.config.doCrosstalk
else None)
935 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
936 if self.config.doDark
else None)
937 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
938 if self.config.doFlat
else None)
940 brighterFatterKernel =
None 941 if self.config.doBrighterFatter
is True:
945 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
949 brighterFatterKernel = dataRef.get(
"bfKernel")
951 brighterFatterKernel =
None 952 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
955 if self.config.brighterFatterLevel ==
'DETECTOR':
956 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
959 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
961 defectList = (dataRef.get(
"defects")
962 if self.config.doDefect
else None)
963 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
964 if self.config.doAssembleIsrExposures
else None)
965 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
966 else pipeBase.Struct(fringes=
None))
968 if self.config.doAttachTransmissionCurve:
969 opticsTransmission = (dataRef.get(
"transmission_optics")
970 if self.config.doUseOpticsTransmission
else None)
971 filterTransmission = (dataRef.get(
"transmission_filter")
972 if self.config.doUseFilterTransmission
else None)
973 sensorTransmission = (dataRef.get(
"transmission_sensor")
974 if self.config.doUseSensorTransmission
else None)
975 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
976 if self.config.doUseAtmosphereTransmission
else None)
978 opticsTransmission =
None 979 filterTransmission =
None 980 sensorTransmission =
None 981 atmosphereTransmission =
None 983 if self.config.doStrayLight:
984 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
986 strayLightData =
None 989 self.config.illuminationCorrectionDataProductName).getMaskedImage()
990 if (self.config.doIlluminationCorrection
and 991 filterName
in self.config.illumFilters)
995 return pipeBase.Struct(bias=biasExposure,
996 linearizer=linearizer,
997 crosstalkSources=crosstalkSources,
1000 bfKernel=brighterFatterKernel,
1002 fringes=fringeStruct,
1003 opticsTransmission=opticsTransmission,
1004 filterTransmission=filterTransmission,
1005 sensorTransmission=sensorTransmission,
1006 atmosphereTransmission=atmosphereTransmission,
1007 strayLightData=strayLightData,
1008 illumMaskedImage=illumMaskedImage
1011 @pipeBase.timeMethod
1012 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1013 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
1014 opticsTransmission=
None, filterTransmission=
None,
1015 sensorTransmission=
None, atmosphereTransmission=
None,
1016 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1019 """!Perform instrument signature removal on an exposure. 1021 Steps included in the ISR processing, in order performed, are: 1022 - saturation and suspect pixel masking 1023 - overscan subtraction 1024 - CCD assembly of individual amplifiers 1026 - variance image construction 1027 - linearization of non-linear response 1029 - brighter-fatter correction 1032 - stray light subtraction 1034 - masking of known defects and camera specific features 1035 - vignette calculation 1036 - appending transmission curve and distortion model 1040 ccdExposure : `lsst.afw.image.Exposure` 1041 The raw exposure that is to be run through ISR. The 1042 exposure is modified by this method. 1043 camera : `lsst.afw.cameraGeom.Camera`, optional 1044 The camera geometry for this exposure. Used to select the 1045 distortion model appropriate for this data. 1046 bias : `lsst.afw.image.Exposure`, optional 1047 Bias calibration frame. 1048 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1049 Functor for linearization. 1050 crosstalkSources : `list`, optional 1051 List of possible crosstalk sources. 1052 dark : `lsst.afw.image.Exposure`, optional 1053 Dark calibration frame. 1054 flat : `lsst.afw.image.Exposure`, optional 1055 Flat calibration frame. 1056 bfKernel : `numpy.ndarray`, optional 1057 Brighter-fatter kernel. 1058 defects : `lsst.meas.algorithms.Defects`, optional 1060 fringes : `lsst.pipe.base.Struct`, optional 1061 Struct containing the fringe correction data, with 1063 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1064 - ``seed``: random seed derived from the ccdExposureId for random 1065 number generator (`uint32`) 1066 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1067 A ``TransmissionCurve`` that represents the throughput of the optics, 1068 to be evaluated in focal-plane coordinates. 1069 filterTransmission : `lsst.afw.image.TransmissionCurve` 1070 A ``TransmissionCurve`` that represents the throughput of the filter 1071 itself, to be evaluated in focal-plane coordinates. 1072 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1073 A ``TransmissionCurve`` that represents the throughput of the sensor 1074 itself, to be evaluated in post-assembly trimmed detector coordinates. 1075 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1076 A ``TransmissionCurve`` that represents the throughput of the 1077 atmosphere, assumed to be spatially constant. 1078 detectorNum : `int`, optional 1079 The integer number for the detector to process. 1080 isGen3 : bool, optional 1081 Flag this call to run() as using the Gen3 butler environment. 1082 strayLightData : `object`, optional 1083 Opaque object containing calibration information for stray-light 1084 correction. If `None`, no correction will be performed. 1085 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1086 Illumination correction image. 1090 result : `lsst.pipe.base.Struct` 1091 Result struct with component: 1092 - ``exposure`` : `afw.image.Exposure` 1093 The fully ISR corrected exposure. 1094 - ``outputExposure`` : `afw.image.Exposure` 1095 An alias for `exposure` 1096 - ``ossThumb`` : `numpy.ndarray` 1097 Thumbnail image of the exposure after overscan subtraction. 1098 - ``flattenedThumb`` : `numpy.ndarray` 1099 Thumbnail image of the exposure after flat-field correction. 1104 Raised if a configuration option is set to True, but the 1105 required calibration data has not been specified. 1109 The current processed exposure can be viewed by setting the 1110 appropriate lsstDebug entries in the `debug.display` 1111 dictionary. The names of these entries correspond to some of 1112 the IsrTaskConfig Boolean options, with the value denoting the 1113 frame to use. The exposure is shown inside the matching 1114 option check and after the processing of that step has 1115 finished. The steps with debug points are: 1126 In addition, setting the "postISRCCD" entry displays the 1127 exposure after all ISR processing has finished. 1135 self.config.doFringe =
False 1138 if detectorNum
is None:
1139 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1141 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1146 if isinstance(ccdExposure, ButlerDataRef):
1149 ccd = ccdExposure.getDetector()
1150 filterName = afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1153 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1154 ccd = [
FakeAmp(ccdExposure, self.config)]
1157 if self.config.doBias
and bias
is None:
1158 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1160 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1161 if self.config.doBrighterFatter
and bfKernel
is None:
1162 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1163 if self.config.doDark
and dark
is None:
1164 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1165 if self.config.doFlat
and flat
is None:
1166 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1167 if self.config.doDefect
and defects
is None:
1168 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1169 if self.config.doAddDistortionModel
and camera
is None:
1170 raise RuntimeError(
"Must supply camera if config.doAddDistortionModel=True.")
1171 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1172 fringes.fringes
is None):
1177 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1178 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1179 illumMaskedImage
is None):
1180 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1183 if self.config.doConvertIntToFloat:
1184 self.log.info(
"Converting exposure to floating point values.")
1191 if ccdExposure.getBBox().contains(amp.getBBox()):
1195 if self.config.doOverscan
and not badAmp:
1198 self.log.debug(f
"Corrected overscan for amplifier {amp.getName()}.")
1199 if overscanResults
is not None and \
1200 self.config.qa
is not None and self.config.qa.saveStats
is True:
1201 if isinstance(overscanResults.overscanFit, float):
1202 qaMedian = overscanResults.overscanFit
1203 qaStdev = float(
"NaN")
1205 qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1206 afwMath.MEDIAN | afwMath.STDEVCLIP)
1207 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1208 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1210 self.metadata.set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1211 self.metadata.set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1212 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f" %
1213 (amp.getName(), qaMedian, qaStdev))
1214 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1217 self.log.warn(f
"Amplifier {amp.getName()} is bad.")
1218 overscanResults =
None 1220 overscans.append(overscanResults
if overscanResults
is not None else None)
1222 self.log.info(f
"Skipped OSCAN for {amp.getName()}.")
1224 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1225 self.log.info(
"Applying crosstalk correction.")
1226 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1227 self.
debugView(ccdExposure,
"doCrosstalk")
1229 if self.config.doAssembleCcd:
1230 self.log.info(
"Assembling CCD from amplifiers.")
1231 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1233 if self.config.expectWcs
and not ccdExposure.getWcs():
1234 self.log.warn(
"No WCS found in input exposure.")
1235 self.
debugView(ccdExposure,
"doAssembleCcd")
1238 if self.config.qa.doThumbnailOss:
1239 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1241 if self.config.doBias:
1242 self.log.info(
"Applying bias correction.")
1243 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1244 trimToFit=self.config.doTrimToMatchCalib)
1247 if self.config.doVariance:
1248 for amp, overscanResults
in zip(ccd, overscans):
1249 if ccdExposure.getBBox().contains(amp.getBBox()):
1250 self.log.debug(f
"Constructing variance map for amplifer {amp.getName()}.")
1251 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1252 if overscanResults
is not None:
1254 overscanImage=overscanResults.overscanImage)
1258 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1259 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1260 afwMath.MEDIAN | afwMath.STDEVCLIP)
1261 self.metadata.set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1262 qaStats.getValue(afwMath.MEDIAN))
1263 self.metadata.set(f
"ISR VARIANCE {amp.getName()} STDEV",
1264 qaStats.getValue(afwMath.STDEVCLIP))
1265 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f." %
1266 (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1267 qaStats.getValue(afwMath.STDEVCLIP)))
1270 self.log.info(
"Applying linearizer.")
1271 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1273 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1274 self.log.info(
"Applying crosstalk correction.")
1275 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1276 self.
debugView(ccdExposure,
"doCrosstalk")
1280 if self.config.doDefect:
1281 self.log.info(
"Masking defects.")
1284 if self.config.doNanMasking:
1285 self.log.info(
"Masking NAN value pixels.")
1288 if self.config.doWidenSaturationTrails:
1289 self.log.info(
"Widening saturation trails.")
1290 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1292 if self.config.doCameraSpecificMasking:
1293 self.log.info(
"Masking regions for camera specific reasons.")
1294 self.masking.
run(ccdExposure)
1296 if self.config.doBrighterFatter:
1305 interpExp = ccdExposure.clone()
1307 isrFunctions.interpolateFromMask(
1308 maskedImage=interpExp.getMaskedImage(),
1309 fwhm=self.config.fwhm,
1310 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1311 maskNameList=self.config.maskListToInterpolate
1313 bfExp = interpExp.clone()
1315 self.log.info(
"Applying brighter fatter correction.")
1316 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1317 self.config.brighterFatterMaxIter,
1318 self.config.brighterFatterThreshold,
1319 self.config.brighterFatterApplyGain
1321 if bfResults[1] == self.config.brighterFatterMaxIter:
1322 self.log.warn(
"Brighter fatter correction did not converge, final difference {bfResults[0]}.")
1324 self.log.info(
"Finished brighter fatter correction in {bfResults[1]} iterations.")
1325 image = ccdExposure.getMaskedImage().getImage()
1326 bfCorr = bfExp.getMaskedImage().getImage()
1327 bfCorr -= interpExp.getMaskedImage().getImage()
1330 self.
debugView(ccdExposure,
"doBrighterFatter")
1332 if self.config.doDark:
1333 self.log.info(
"Applying dark correction.")
1337 if self.config.doFringe
and not self.config.fringeAfterFlat:
1338 self.log.info(
"Applying fringe correction before flat.")
1339 self.fringe.
run(ccdExposure, **fringes.getDict())
1342 if self.config.doStrayLight:
1343 if strayLightData
is not None:
1344 self.log.info(
"Applying stray light correction.")
1345 self.strayLight.
run(ccdExposure, strayLightData)
1346 self.
debugView(ccdExposure,
"doStrayLight")
1348 self.log.debug(
"Skipping stray light correction: no data found for this image.")
1350 if self.config.doFlat:
1351 self.log.info(
"Applying flat correction.")
1355 if self.config.doApplyGains:
1356 self.log.info(
"Applying gain correction instead of flat.")
1357 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1359 if self.config.doFringe
and self.config.fringeAfterFlat:
1360 self.log.info(
"Applying fringe correction after flat.")
1361 self.fringe.
run(ccdExposure, **fringes.getDict())
1363 if self.config.doVignette:
1364 self.log.info(
"Constructing Vignette polygon.")
1367 if self.config.vignette.doWriteVignettePolygon:
1370 if self.config.doAttachTransmissionCurve:
1371 self.log.info(
"Adding transmission curves.")
1372 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1373 filterTransmission=filterTransmission,
1374 sensorTransmission=sensorTransmission,
1375 atmosphereTransmission=atmosphereTransmission)
1377 if self.config.doAddDistortionModel:
1378 self.log.info(
"Adding a distortion model to the WCS.")
1379 isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1381 flattenedThumb =
None 1382 if self.config.qa.doThumbnailFlattened:
1383 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1385 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1386 self.log.info(
"Performing illumination correction.")
1387 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1388 illumMaskedImage, illumScale=self.config.illumScale,
1389 trimToFit=self.config.doTrimToMatchCalib)
1392 if self.config.doSaveInterpPixels:
1393 preInterpExp = ccdExposure.clone()
1408 if self.config.doSetBadRegions:
1409 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1410 if badPixelCount > 0:
1411 self.log.info(
"Set %d BAD pixels to %f." % (badPixelCount, badPixelValue))
1413 if self.config.doInterpolate:
1414 self.log.info(
"Interpolating masked pixels.")
1415 isrFunctions.interpolateFromMask(
1416 maskedImage=ccdExposure.getMaskedImage(),
1417 fwhm=self.config.fwhm,
1418 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1419 maskNameList=list(self.config.maskListToInterpolate)
1424 if self.config.doMeasureBackground:
1425 self.log.info(
"Measuring background level.")
1428 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1430 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1431 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1432 afwMath.MEDIAN | afwMath.STDEVCLIP)
1433 self.metadata.set(
"ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1434 qaStats.getValue(afwMath.MEDIAN))
1435 self.metadata.set(
"ISR BACKGROUND {} STDEV".format(amp.getName()),
1436 qaStats.getValue(afwMath.STDEVCLIP))
1437 self.log.debug(
" Background stats for amplifer %s: %f +/- %f" %
1438 (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1439 qaStats.getValue(afwMath.STDEVCLIP)))
1441 self.
debugView(ccdExposure,
"postISRCCD")
1443 return pipeBase.Struct(
1444 exposure=ccdExposure,
1446 flattenedThumb=flattenedThumb,
1448 preInterpolatedExposure=preInterpExp,
1449 outputExposure=ccdExposure,
1450 outputOssThumbnail=ossThumb,
1451 outputFlattenedThumbnail=flattenedThumb,
1454 @pipeBase.timeMethod
1456 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1458 This method contains the `CmdLineTask` interface to the ISR 1459 processing. All IO is handled here, freeing the `run()` method 1460 to manage only pixel-level calculations. The steps performed 1462 - Read in necessary detrending/isr/calibration data. 1463 - Process raw exposure in `run()`. 1464 - Persist the ISR-corrected exposure as "postISRCCD" if 1465 config.doWrite=True. 1469 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1470 DataRef of the detector data to be processed 1474 result : `lsst.pipe.base.Struct` 1475 Result struct with component: 1476 - ``exposure`` : `afw.image.Exposure` 1477 The fully ISR corrected exposure. 1482 Raised if a configuration option is set to True, but the 1483 required calibration data does not exist. 1486 self.log.info(
"Performing ISR on sensor %s." % (sensorRef.dataId))
1488 ccdExposure = sensorRef.get(self.config.datasetType)
1490 camera = sensorRef.get(
"camera")
1491 if camera
is None and self.config.doAddDistortionModel:
1492 raise RuntimeError(
"config.doAddDistortionModel is True " 1493 "but could not get a camera from the butler.")
1494 isrData = self.
readIsrData(sensorRef, ccdExposure)
1496 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1498 if self.config.doWrite:
1499 sensorRef.put(result.exposure,
"postISRCCD")
1500 if result.preInterpolatedExposure
is not None:
1501 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1502 if result.ossThumb
is not None:
1503 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1504 if result.flattenedThumb
is not None:
1505 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1510 """!Retrieve a calibration dataset for removing instrument signature. 1515 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1516 DataRef of the detector data to find calibration datasets 1519 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1521 If True, disable butler proxies to enable error handling 1522 within this routine. 1526 exposure : `lsst.afw.image.Exposure` 1527 Requested calibration frame. 1532 Raised if no matching calibration frame can be found. 1535 exp = dataRef.get(datasetType, immediate=immediate)
1536 except Exception
as exc1:
1537 if not self.config.fallbackFilterName:
1538 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1540 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1541 except Exception
as exc2:
1542 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1543 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1544 self.log.warn(
"Using fallback calibration from filter %s." % self.config.fallbackFilterName)
1546 if self.config.doAssembleIsrExposures:
1547 exp = self.assembleCcd.assembleCcd(exp)
1551 """Ensure that the data returned by Butler is a fully constructed exposure. 1553 ISR requires exposure-level image data for historical reasons, so if we did 1554 not recieve that from Butler, construct it from what we have, modifying the 1559 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1560 `lsst.afw.image.ImageF` 1561 The input data structure obtained from Butler. 1562 camera : `lsst.afw.cameraGeom.camera` 1563 The camera associated with the image. Used to find the appropriate 1566 The detector this exposure should match. 1570 inputExp : `lsst.afw.image.Exposure` 1571 The re-constructed exposure, with appropriate detector parameters. 1576 Raised if the input data cannot be used to construct an exposure. 1578 if isinstance(inputExp, afwImage.DecoratedImageU):
1579 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1580 elif isinstance(inputExp, afwImage.ImageF):
1581 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1582 elif isinstance(inputExp, afwImage.MaskedImageF):
1583 inputExp = afwImage.makeExposure(inputExp)
1584 elif isinstance(inputExp, afwImage.Exposure):
1586 elif inputExp
is None:
1590 raise TypeError(f
"Input Exposure is not known type in isrTask.ensureExposure: {type(inputExp)}.")
1592 if inputExp.getDetector()
is None:
1593 inputExp.setDetector(camera[detectorNum])
1598 """Convert exposure image from uint16 to float. 1600 If the exposure does not need to be converted, the input is 1601 immediately returned. For exposures that are converted to use 1602 floating point pixels, the variance is set to unity and the 1607 exposure : `lsst.afw.image.Exposure` 1608 The raw exposure to be converted. 1612 newexposure : `lsst.afw.image.Exposure` 1613 The input ``exposure``, converted to floating point pixels. 1618 Raised if the exposure type cannot be converted to float. 1621 if isinstance(exposure, afwImage.ExposureF):
1623 self.log.debug(
"Exposure already of type float.")
1625 if not hasattr(exposure,
"convertF"):
1626 raise RuntimeError(
"Unable to convert exposure (%s) to float." % type(exposure))
1628 newexposure = exposure.convertF()
1629 newexposure.variance[:] = 1
1630 newexposure.mask[:] = 0x0
1635 """Identify bad amplifiers, saturated and suspect pixels. 1639 ccdExposure : `lsst.afw.image.Exposure` 1640 Input exposure to be masked. 1641 amp : `lsst.afw.table.AmpInfoCatalog` 1642 Catalog of parameters defining the amplifier on this 1644 defects : `lsst.meas.algorithms.Defects` 1645 List of defects. Used to determine if the entire 1651 If this is true, the entire amplifier area is covered by 1652 defects and unusable. 1655 maskedImage = ccdExposure.getMaskedImage()
1661 if defects
is not None:
1662 badAmp = bool(sum([v.getBBox().contains(amp.getBBox())
for v
in defects]))
1667 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1669 maskView = dataView.getMask()
1670 maskView |= maskView.getPlaneBitMask(
"BAD")
1677 if self.config.doSaturation
and not badAmp:
1678 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1679 if self.config.doSuspect
and not badAmp:
1680 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1681 if math.isfinite(self.config.saturation):
1682 limits.update({self.config.saturatedMaskName: self.config.saturation})
1684 for maskName, maskThreshold
in limits.items():
1685 if not math.isnan(maskThreshold):
1686 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1687 isrFunctions.makeThresholdMask(
1688 maskedImage=dataView,
1689 threshold=maskThreshold,
1695 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1697 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1698 self.config.suspectMaskName])
1699 if numpy.all(maskView.getArray() & maskVal > 0):
1701 maskView |= maskView.getPlaneBitMask(
"BAD")
1706 """Apply overscan correction in place. 1708 This method does initial pixel rejection of the overscan 1709 region. The overscan can also be optionally segmented to 1710 allow for discontinuous overscan responses to be fit 1711 separately. The actual overscan subtraction is performed by 1712 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1713 which is called here after the amplifier is preprocessed. 1717 ccdExposure : `lsst.afw.image.Exposure` 1718 Exposure to have overscan correction performed. 1719 amp : `lsst.afw.table.AmpInfoCatalog` 1720 The amplifier to consider while correcting the overscan. 1724 overscanResults : `lsst.pipe.base.Struct` 1725 Result struct with components: 1726 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1727 Value or fit subtracted from the amplifier image data. 1728 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1729 Value or fit subtracted from the overscan image data. 1730 - ``overscanImage`` : `lsst.afw.image.Image` 1731 Image of the overscan region with the overscan 1732 correction applied. This quantity is used to estimate 1733 the amplifier read noise empirically. 1738 Raised if the ``amp`` does not contain raw pixel information. 1742 lsst.ip.isr.isrFunctions.overscanCorrection 1744 if not amp.getHasRawInfo():
1745 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1747 if amp.getRawHorizontalOverscanBBox().isEmpty():
1748 self.log.info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1751 statControl = afwMath.StatisticsControl()
1752 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1755 dataBBox = amp.getRawDataBBox()
1756 oscanBBox = amp.getRawHorizontalOverscanBBox()
1760 prescanBBox = amp.getRawPrescanBBox()
1761 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1762 dx0 += self.config.overscanNumLeadingColumnsToSkip
1763 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1765 dx0 += self.config.overscanNumTrailingColumnsToSkip
1766 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1772 if ((self.config.overscanBiasJump
and 1773 self.config.overscanBiasJumpLocation)
and 1774 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1775 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1776 self.config.overscanBiasJumpDevices)):
1777 if amp.getReadoutCorner()
in (afwTable.LL, afwTable.LR):
1778 yLower = self.config.overscanBiasJumpLocation
1779 yUpper = dataBBox.getHeight() - yLower
1781 yUpper = self.config.overscanBiasJumpLocation
1782 yLower = dataBBox.getHeight() - yUpper
1801 oscanBBox.getHeight())))
1804 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1805 ampImage = ccdExposure.maskedImage[imageBBox]
1806 overscanImage = ccdExposure.maskedImage[overscanBBox]
1808 overscanArray = overscanImage.image.array
1809 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1810 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1811 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1813 statControl = afwMath.StatisticsControl()
1814 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1816 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1817 overscanImage=overscanImage,
1818 fitType=self.config.overscanFitType,
1819 order=self.config.overscanOrder,
1820 collapseRej=self.config.overscanNumSigmaClip,
1821 statControl=statControl,
1822 overscanIsInt=self.config.overscanIsInt
1826 levelStat = afwMath.MEDIAN
1827 sigmaStat = afwMath.STDEVCLIP
1829 sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1830 self.config.qa.flatness.nIter)
1831 metadata = ccdExposure.getMetadata()
1832 ampNum = amp.getName()
1833 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1834 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1835 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1837 stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1838 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1839 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1841 return overscanResults
1844 """Set the variance plane using the amplifier gain and read noise 1846 The read noise is calculated from the ``overscanImage`` if the 1847 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1848 the value from the amplifier data is used. 1852 ampExposure : `lsst.afw.image.Exposure` 1853 Exposure to process. 1854 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1855 Amplifier detector data. 1856 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1857 Image of overscan, required only for empirical read noise. 1861 lsst.ip.isr.isrFunctions.updateVariance 1863 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1864 gain = amp.getGain()
1866 if math.isnan(gain):
1868 self.log.warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1871 self.log.warn(
"Gain for amp %s == %g <= 0; setting to %f." %
1872 (amp.getName(), gain, patchedGain))
1875 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1876 self.log.info(
"Overscan is none for EmpiricalReadNoise.")
1878 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1879 stats = afwMath.StatisticsControl()
1880 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1881 readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1882 self.log.info(
"Calculated empirical read noise for amp %s: %f.", amp.getName(), readNoise)
1884 readNoise = amp.getReadNoise()
1886 isrFunctions.updateVariance(
1887 maskedImage=ampExposure.getMaskedImage(),
1889 readNoise=readNoise,
1893 """!Apply dark correction in place. 1897 exposure : `lsst.afw.image.Exposure` 1898 Exposure to process. 1899 darkExposure : `lsst.afw.image.Exposure` 1900 Dark exposure of the same size as ``exposure``. 1901 invert : `Bool`, optional 1902 If True, re-add the dark to an already corrected image. 1907 Raised if either ``exposure`` or ``darkExposure`` do not 1908 have their dark time defined. 1912 lsst.ip.isr.isrFunctions.darkCorrection 1914 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1915 if math.isnan(expScale):
1916 raise RuntimeError(
"Exposure darktime is NAN.")
1917 if darkExposure.getInfo().getVisitInfo()
is not None:
1918 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1922 self.log.warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1925 if math.isnan(darkScale):
1926 raise RuntimeError(
"Dark calib darktime is NAN.")
1927 isrFunctions.darkCorrection(
1928 maskedImage=exposure.getMaskedImage(),
1929 darkMaskedImage=darkExposure.getMaskedImage(),
1931 darkScale=darkScale,
1933 trimToFit=self.config.doTrimToMatchCalib
1937 """!Check if linearization is needed for the detector cameraGeom. 1939 Checks config.doLinearize and the linearity type of the first 1944 detector : `lsst.afw.cameraGeom.Detector` 1945 Detector to get linearity type from. 1949 doLinearize : `Bool` 1950 If True, linearization should be performed. 1952 return self.config.doLinearize
and \
1953 detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1956 """!Apply flat correction in place. 1960 exposure : `lsst.afw.image.Exposure` 1961 Exposure to process. 1962 flatExposure : `lsst.afw.image.Exposure` 1963 Flat exposure of the same size as ``exposure``. 1964 invert : `Bool`, optional 1965 If True, unflatten an already flattened image. 1969 lsst.ip.isr.isrFunctions.flatCorrection 1971 isrFunctions.flatCorrection(
1972 maskedImage=exposure.getMaskedImage(),
1973 flatMaskedImage=flatExposure.getMaskedImage(),
1974 scalingType=self.config.flatScalingType,
1975 userScale=self.config.flatUserScale,
1977 trimToFit=self.config.doTrimToMatchCalib
1981 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1985 exposure : `lsst.afw.image.Exposure` 1986 Exposure to process. Only the amplifier DataSec is processed. 1987 amp : `lsst.afw.table.AmpInfoCatalog` 1988 Amplifier detector data. 1992 lsst.ip.isr.isrFunctions.makeThresholdMask 1994 if not math.isnan(amp.getSaturation()):
1995 maskedImage = exposure.getMaskedImage()
1996 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1997 isrFunctions.makeThresholdMask(
1998 maskedImage=dataView,
1999 threshold=amp.getSaturation(),
2001 maskName=self.config.saturatedMaskName,
2005 """!Interpolate over saturated pixels, in place. 2007 This method should be called after `saturationDetection`, to 2008 ensure that the saturated pixels have been identified in the 2009 SAT mask. It should also be called after `assembleCcd`, since 2010 saturated regions may cross amplifier boundaries. 2014 exposure : `lsst.afw.image.Exposure` 2015 Exposure to process. 2019 lsst.ip.isr.isrTask.saturationDetection 2020 lsst.ip.isr.isrFunctions.interpolateFromMask 2022 isrFunctions.interpolateFromMask(
2023 maskedImage=exposure.getMaskedImage(),
2024 fwhm=self.config.fwhm,
2025 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2026 maskNameList=list(self.config.saturatedMaskName),
2030 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2034 exposure : `lsst.afw.image.Exposure` 2035 Exposure to process. Only the amplifier DataSec is processed. 2036 amp : `lsst.afw.table.AmpInfoCatalog` 2037 Amplifier detector data. 2041 lsst.ip.isr.isrFunctions.makeThresholdMask 2045 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2046 This is intended to indicate pixels that may be affected by unknown systematics; 2047 for example if non-linearity corrections above a certain level are unstable 2048 then that would be a useful value for suspectLevel. A value of `nan` indicates 2049 that no such level exists and no pixels are to be masked as suspicious. 2051 suspectLevel = amp.getSuspectLevel()
2052 if math.isnan(suspectLevel):
2055 maskedImage = exposure.getMaskedImage()
2056 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2057 isrFunctions.makeThresholdMask(
2058 maskedImage=dataView,
2059 threshold=suspectLevel,
2061 maskName=self.config.suspectMaskName,
2065 """!Mask defects using mask plane "BAD", in place. 2069 exposure : `lsst.afw.image.Exposure` 2070 Exposure to process. 2071 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2072 `lsst.afw.image.DefectBase`. 2073 List of defects to mask and interpolate. 2077 Call this after CCD assembly, since defects may cross amplifier boundaries. 2079 maskedImage = exposure.getMaskedImage()
2080 if not isinstance(defectBaseList, Defects):
2082 defectList = Defects(defectBaseList)
2084 defectList = defectBaseList
2085 defectList.maskPixels(maskedImage, maskName=
"BAD")
2087 if self.config.numEdgeSuspect > 0:
2088 goodBBox = maskedImage.getBBox()
2090 goodBBox.grow(-self.config.numEdgeSuspect)
2092 SourceDetectionTask.setEdgeBits(
2095 maskedImage.getMask().getPlaneBitMask(
"SUSPECT")
2099 """Mask and interpolate defects using mask plane "BAD", in place. 2103 exposure : `lsst.afw.image.Exposure` 2104 Exposure to process. 2105 defectBaseList : `List` of `Defects` 2108 self.maskDefects(exposure, defectBaseList)
2109 isrFunctions.interpolateFromMask(
2110 maskedImage=exposure.getMaskedImage(),
2111 fwhm=self.config.fwhm,
2112 growSaturatedFootprints=0,
2113 maskNameList=[
"BAD"],
2117 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2121 exposure : `lsst.afw.image.Exposure` 2122 Exposure to process. 2126 We mask over all NaNs, including those that are masked with 2127 other bits (because those may or may not be interpolated over 2128 later, and we want to remove all NaNs). Despite this 2129 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2130 the historical name. 2132 maskedImage = exposure.getMaskedImage()
2135 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2136 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2137 numNans =
maskNans(maskedImage, maskVal)
2138 self.metadata.set(
"NUMNANS", numNans)
2140 self.log.warn(f
"There were {numNans} unmasked NaNs.")
2143 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2147 exposure : `lsst.afw.image.Exposure` 2148 Exposure to process. 2152 lsst.ip.isr.isrTask.maskNan() 2155 isrFunctions.interpolateFromMask(
2156 maskedImage=exposure.getMaskedImage(),
2157 fwhm=self.config.fwhm,
2158 growSaturatedFootprints=0,
2159 maskNameList=[
"UNMASKEDNAN"],
2163 """Measure the image background in subgrids, for quality control purposes. 2167 exposure : `lsst.afw.image.Exposure` 2168 Exposure to process. 2169 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2170 Configuration object containing parameters on which background 2171 statistics and subgrids to use. 2173 if IsrQaConfig
is not None:
2174 statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
2175 IsrQaConfig.flatness.nIter)
2176 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2177 statsControl.setAndMask(maskVal)
2178 maskedImage = exposure.getMaskedImage()
2179 stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
2180 skyLevel = stats.getValue(afwMath.MEDIAN)
2181 skySigma = stats.getValue(afwMath.STDEVCLIP)
2182 self.log.info(
"Flattened sky level: %f +/- %f." % (skyLevel, skySigma))
2183 metadata = exposure.getMetadata()
2184 metadata.set(
'SKYLEVEL', skyLevel)
2185 metadata.set(
'SKYSIGMA', skySigma)
2188 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2189 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2190 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2191 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2192 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2193 skyLevels = numpy.zeros((nX, nY))
2196 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2198 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2200 xLLC = xc - meshXHalf
2201 yLLC = yc - meshYHalf
2202 xURC = xc + meshXHalf - 1
2203 yURC = yc + meshYHalf - 1
2206 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2208 skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2210 good = numpy.where(numpy.isfinite(skyLevels))
2211 skyMedian = numpy.median(skyLevels[good])
2212 flatness = (skyLevels[good] - skyMedian) / skyMedian
2213 flatness_rms = numpy.std(flatness)
2214 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2216 self.log.info(
"Measuring sky levels in %dx%d grids: %f." % (nX, nY, skyMedian))
2217 self.log.info(
"Sky flatness in %dx%d grids - pp: %f rms: %f." %
2218 (nX, nY, flatness_pp, flatness_rms))
2220 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2221 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2222 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2223 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2224 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2227 """Set an approximate magnitude zero point for the exposure. 2231 exposure : `lsst.afw.image.Exposure` 2232 Exposure to process. 2234 filterName = afwImage.Filter(exposure.getFilter().getId()).getName()
2235 if filterName
in self.config.fluxMag0T1:
2236 fluxMag0 = self.config.fluxMag0T1[filterName]
2238 self.log.warn(
"No rough magnitude zero point set for filter %s." % filterName)
2239 fluxMag0 = self.config.defaultFluxMag0T1
2241 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2243 self.log.warn(
"Non-positive exposure time; skipping rough zero point.")
2246 self.log.info(
"Setting rough magnitude zero point: %f" % (2.5*math.log10(fluxMag0*expTime),))
2247 exposure.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(fluxMag0*expTime, 0.0))
2250 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2254 ccdExposure : `lsst.afw.image.Exposure` 2255 Exposure to process. 2256 fpPolygon : `lsst.afw.geom.Polygon` 2257 Polygon in focal plane coordinates. 2260 ccd = ccdExposure.getDetector()
2261 fpCorners = ccd.getCorners(FOCAL_PLANE)
2262 ccdPolygon = Polygon(fpCorners)
2265 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2268 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2269 validPolygon = Polygon(ccdPoints)
2270 ccdExposure.getInfo().setValidPolygon(validPolygon)
2274 """Context manager that applies and removes flats and darks, 2275 if the task is configured to apply them. 2279 exp : `lsst.afw.image.Exposure` 2280 Exposure to process. 2281 flat : `lsst.afw.image.Exposure` 2282 Flat exposure the same size as ``exp``. 2283 dark : `lsst.afw.image.Exposure`, optional 2284 Dark exposure the same size as ``exp``. 2288 exp : `lsst.afw.image.Exposure` 2289 The flat and dark corrected exposure. 2291 if self.config.doDark
and dark
is not None:
2293 if self.config.doFlat:
2298 if self.config.doFlat:
2300 if self.config.doDark
and dark
is not None:
2304 """Utility function to examine ISR exposure at different stages. 2308 exposure : `lsst.afw.image.Exposure` 2311 State of processing to view. 2313 frame = getDebugFrame(self._display, stepname)
2315 display = getDisplay(frame)
2316 display.scale(
'asinh',
'zscale')
2317 display.mtv(exposure)
2318 prompt =
"Press Enter to continue [c]... " 2320 ans = input(prompt).lower()
2321 if ans
in (
"",
"c",):
2326 """A Detector-like object that supports returning gain and saturation level 2328 This is used when the input exposure does not have a detector. 2332 exposure : `lsst.afw.image.Exposure` 2333 Exposure to generate a fake amplifier for. 2334 config : `lsst.ip.isr.isrTaskConfig` 2335 Configuration to apply to the fake amplifier. 2339 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2341 self.
_gain = config.gain
2371 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2375 """Task to wrap the default IsrTask to allow it to be retargeted. 2377 The standard IsrTask can be called directly from a command line 2378 program, but doing so removes the ability of the task to be 2379 retargeted. As most cameras override some set of the IsrTask 2380 methods, this would remove those data-specific methods in the 2381 output post-ISR images. This wrapping class fixes the issue, 2382 allowing identical post-ISR images to be generated by both the 2383 processCcd and isrTask code. 2385 ConfigClass = RunIsrConfig
2386 _DefaultName =
"runIsr" 2390 self.makeSubtask(
"isr")
2396 dataRef : `lsst.daf.persistence.ButlerDataRef` 2397 data reference of the detector data to be processed 2401 result : `pipeBase.Struct` 2402 Result struct with component: 2404 - exposure : `lsst.afw.image.Exposure` 2405 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)