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 doNanMasking = pexConfig.Field(
439 doc=
"Mask NAN pixels?",
442 doWidenSaturationTrails = pexConfig.Field(
444 doc=
"Widen bleed trails based on their width?",
449 doBrighterFatter = pexConfig.Field(
452 doc=
"Apply the brighter fatter correction" 454 brighterFatterLevel = pexConfig.ChoiceField(
457 doc=
"The level at which to correct for brighter-fatter.",
459 "AMP":
"Every amplifier treated separately.",
460 "DETECTOR":
"One kernel per detector",
463 brighterFatterKernelFile = pexConfig.Field(
466 doc=
"Kernel file used for the brighter fatter correction" 468 brighterFatterMaxIter = pexConfig.Field(
471 doc=
"Maximum number of iterations for the brighter fatter correction" 473 brighterFatterThreshold = pexConfig.Field(
476 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the " 477 " absolute value of the difference between the current corrected image and the one" 478 " from the previous iteration summed over all the pixels." 480 brighterFatterApplyGain = pexConfig.Field(
483 doc=
"Should the gain be applied when applying the brighter fatter correction?" 487 doDark = pexConfig.Field(
489 doc=
"Apply dark frame correction?",
492 darkDataProductName = pexConfig.Field(
494 doc=
"Name of the dark data product",
499 doStrayLight = pexConfig.Field(
501 doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
504 strayLight = pexConfig.ConfigurableField(
505 target=StrayLightTask,
506 doc=
"y-band stray light correction" 510 doFlat = pexConfig.Field(
512 doc=
"Apply flat field correction?",
515 flatDataProductName = pexConfig.Field(
517 doc=
"Name of the flat data product",
520 flatScalingType = pexConfig.ChoiceField(
522 doc=
"The method for scaling the flat on the fly.",
525 "USER":
"Scale by flatUserScale",
526 "MEAN":
"Scale by the inverse of the mean",
527 "MEDIAN":
"Scale by the inverse of the median",
530 flatUserScale = pexConfig.Field(
532 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
535 doTweakFlat = pexConfig.Field(
537 doc=
"Tweak flats to match observed amplifier ratios?",
542 doApplyGains = pexConfig.Field(
544 doc=
"Correct the amplifiers for their gains instead of applying flat correction",
547 normalizeGains = pexConfig.Field(
549 doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
554 doFringe = pexConfig.Field(
556 doc=
"Apply fringe correction?",
559 fringe = pexConfig.ConfigurableField(
561 doc=
"Fringe subtraction task",
563 fringeAfterFlat = pexConfig.Field(
565 doc=
"Do fringe subtraction after flat-fielding?",
570 doAddDistortionModel = pexConfig.Field(
572 doc=
"Apply a distortion model based on camera geometry to the WCS?",
577 doMeasureBackground = pexConfig.Field(
579 doc=
"Measure the background level on the reduced image?",
584 doCameraSpecificMasking = pexConfig.Field(
586 doc=
"Mask camera-specific bad regions?",
589 masking = pexConfig.ConfigurableField(
596 doInterpolate = pexConfig.Field(
598 doc=
"Interpolate masked pixels?",
601 doSaturationInterpolation = pexConfig.Field(
603 doc=
"Perform interpolation over pixels masked as saturated?" 604 " NB: This is independent of doSaturation; if that is False this plane" 605 " will likely be blank, resulting in a no-op here.",
608 doNanInterpolation = pexConfig.Field(
610 doc=
"Perform interpolation over pixels masked as NaN?" 611 " NB: This is independent of doNanMasking; if that is False this plane" 612 " will likely be blank, resulting in a no-op here.",
615 doNanInterpAfterFlat = pexConfig.Field(
617 doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 618 "also have to interpolate them before flat-fielding."),
621 maskListToInterpolate = pexConfig.ListField(
623 doc=
"List of mask planes that should be interpolated.",
624 default=[
'SAT',
'BAD',
'UNMASKEDNAN'],
626 doSaveInterpPixels = pexConfig.Field(
628 doc=
"Save a copy of the pre-interpolated pixel values?",
633 fluxMag0T1 = pexConfig.DictField(
636 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
637 default=dict((f, pow(10.0, 0.4*m))
for f, m
in ((
"Unknown", 28.0),
640 defaultFluxMag0T1 = pexConfig.Field(
642 doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
643 default=pow(10.0, 0.4*28.0)
647 doVignette = pexConfig.Field(
649 doc=
"Apply vignetting parameters?",
652 vignette = pexConfig.ConfigurableField(
654 doc=
"Vignetting task.",
658 doAttachTransmissionCurve = pexConfig.Field(
661 doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 663 doUseOpticsTransmission = pexConfig.Field(
666 doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 668 doUseFilterTransmission = pexConfig.Field(
671 doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 673 doUseSensorTransmission = pexConfig.Field(
676 doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 678 doUseAtmosphereTransmission = pexConfig.Field(
681 doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 685 doIlluminationCorrection = pexConfig.Field(
688 doc=
"Perform illumination correction?" 690 illuminationCorrectionDataProductName = pexConfig.Field(
692 doc=
"Name of the illumination correction data product.",
695 illumScale = pexConfig.Field(
697 doc=
"Scale factor for the illumination correction.",
700 illumFilters = pexConfig.ListField(
703 doc=
"Only perform illumination correction for these filters." 707 doWrite = pexConfig.Field(
709 doc=
"Persist postISRCCD?",
716 raise ValueError(
"You may not specify both doFlat and doApplyGains")
718 self.config.maskListToInterpolate.append(
"SAT")
720 self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
723 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
724 """Apply common instrument signature correction algorithms to a raw frame. 726 The process for correcting imaging data is very similar from 727 camera to camera. This task provides a vanilla implementation of 728 doing these corrections, including the ability to turn certain 729 corrections off if they are not needed. The inputs to the primary 730 method, `run()`, are a raw exposure to be corrected and the 731 calibration data products. The raw input is a single chip sized 732 mosaic of all amps including overscans and other non-science 733 pixels. The method `runDataRef()` identifies and defines the 734 calibration data products, and is intended for use by a 735 `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 736 `daf.persistence.butlerSubset.ButlerDataRef`. This task may be 737 subclassed for different camera, although the most camera specific 738 methods have been split into subtasks that can be redirected 741 The __init__ method sets up the subtasks for ISR processing, using 742 the defaults from `lsst.ip.isr`. 747 Positional arguments passed to the Task constructor. None used at this time. 748 kwargs : `dict`, optional 749 Keyword arguments passed on to the Task constructor. None used at this time. 751 ConfigClass = IsrTaskConfig
756 self.makeSubtask(
"assembleCcd")
757 self.makeSubtask(
"crosstalk")
758 self.makeSubtask(
"strayLight")
759 self.makeSubtask(
"fringe")
760 self.makeSubtask(
"masking")
761 self.makeSubtask(
"vignette")
770 if config.doBias
is not True:
771 inputTypeDict.pop(
"bias",
None)
772 if config.doLinearize
is not True:
773 inputTypeDict.pop(
"linearizer",
None)
774 if config.doCrosstalk
is not True:
775 inputTypeDict.pop(
"crosstalkSources",
None)
776 if config.doBrighterFatter
is not True:
777 inputTypeDict.pop(
"bfKernel",
None)
778 if config.doDefect
is not True:
779 inputTypeDict.pop(
"defects",
None)
780 if config.doDark
is not True:
781 inputTypeDict.pop(
"dark",
None)
782 if config.doFlat
is not True:
783 inputTypeDict.pop(
"flat",
None)
784 if config.doAttachTransmissionCurve
is not True:
785 inputTypeDict.pop(
"opticsTransmission",
None)
786 inputTypeDict.pop(
"filterTransmission",
None)
787 inputTypeDict.pop(
"sensorTransmission",
None)
788 inputTypeDict.pop(
"atmosphereTransmission",
None)
789 if config.doUseOpticsTransmission
is not True:
790 inputTypeDict.pop(
"opticsTransmission",
None)
791 if config.doUseFilterTransmission
is not True:
792 inputTypeDict.pop(
"filterTransmission",
None)
793 if config.doUseSensorTransmission
is not True:
794 inputTypeDict.pop(
"sensorTransmission",
None)
795 if config.doUseAtmosphereTransmission
is not True:
796 inputTypeDict.pop(
"atmosphereTransmission",
None)
797 if config.doIlluminationCorrection
is not True:
798 inputTypeDict.pop(
"illumMaskedImage",
None)
806 if config.qa.doThumbnailOss
is not True:
807 outputTypeDict.pop(
"outputOssThumbnail",
None)
808 if config.qa.doThumbnailFlattened
is not True:
809 outputTypeDict.pop(
"outputFlattenedThumbnail",
None)
810 if config.doWrite
is not True:
811 outputTypeDict.pop(
"outputExposure",
None)
813 return outputTypeDict
822 names.remove(
"ccdExposure")
827 inputData[
'detectorNum'] = int(inputDataIds[
'ccdExposure'][
'detector'])
828 except Exception
as e:
829 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
832 inputData[
'isGen3'] =
True 834 if self.config.doLinearize
is True:
835 if 'linearizer' not in inputData.keys():
836 detector = inputData[
'camera'][inputData[
'detectorNum']]
837 linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
838 inputData[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
840 if inputData[
'defects']
is not None:
843 if not isinstance(inputData[
"defects"], Defects):
844 inputData[
"defects"] = Defects.fromTable(inputData[
"defects"])
861 return super().
adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
867 """!Retrieve necessary frames for instrument signature removal. 869 Pre-fetching all required ISR data products limits the IO 870 required by the ISR. Any conflict between the calibration data 871 available and that needed for ISR is also detected prior to 872 doing processing, allowing it to fail quickly. 876 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 877 Butler reference of the detector data to be processed 878 rawExposure : `afw.image.Exposure` 879 The raw exposure that will later be corrected with the 880 retrieved calibration data; should not be modified in this 885 result : `lsst.pipe.base.Struct` 886 Result struct with components (which may be `None`): 887 - ``bias``: bias calibration frame (`afw.image.Exposure`) 888 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 889 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 890 - ``dark``: dark calibration frame (`afw.image.Exposure`) 891 - ``flat``: flat calibration frame (`afw.image.Exposure`) 892 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 893 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 894 - ``fringes``: `lsst.pipe.base.Struct` with components: 895 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 896 - ``seed``: random seed derived from the ccdExposureId for random 897 number generator (`uint32`). 898 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 899 A ``TransmissionCurve`` that represents the throughput of the optics, 900 to be evaluated in focal-plane coordinates. 901 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 902 A ``TransmissionCurve`` that represents the throughput of the filter 903 itself, to be evaluated in focal-plane coordinates. 904 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 905 A ``TransmissionCurve`` that represents the throughput of the sensor 906 itself, to be evaluated in post-assembly trimmed detector coordinates. 907 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 908 A ``TransmissionCurve`` that represents the throughput of the 909 atmosphere, assumed to be spatially constant. 910 - ``strayLightData`` : `object` 911 An opaque object containing calibration information for 912 stray-light correction. If `None`, no correction will be 914 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 918 NotImplementedError : 919 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 921 ccd = rawExposure.getDetector()
922 filterName = afwImage.Filter(rawExposure.getFilter().getId()).getName()
923 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
924 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
925 if self.config.doBias
else None)
927 linearizer = (dataRef.get(
"linearizer", immediate=
True)
929 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
930 if self.config.doCrosstalk
else None)
931 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
932 if self.config.doDark
else None)
933 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
934 if self.config.doFlat
else None)
936 brighterFatterKernel =
None 937 if self.config.doBrighterFatter
is True:
941 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
945 brighterFatterKernel = dataRef.get(
"bfKernel")
947 brighterFatterKernel =
None 948 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
951 if self.config.brighterFatterLevel ==
'DETECTOR':
952 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
955 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
957 defectList = (dataRef.get(
"defects")
958 if self.config.doDefect
else None)
959 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
960 if self.config.doAssembleIsrExposures
else None)
961 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
962 else pipeBase.Struct(fringes=
None))
964 if self.config.doAttachTransmissionCurve:
965 opticsTransmission = (dataRef.get(
"transmission_optics")
966 if self.config.doUseOpticsTransmission
else None)
967 filterTransmission = (dataRef.get(
"transmission_filter")
968 if self.config.doUseFilterTransmission
else None)
969 sensorTransmission = (dataRef.get(
"transmission_sensor")
970 if self.config.doUseSensorTransmission
else None)
971 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
972 if self.config.doUseAtmosphereTransmission
else None)
974 opticsTransmission =
None 975 filterTransmission =
None 976 sensorTransmission =
None 977 atmosphereTransmission =
None 979 if self.config.doStrayLight:
980 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
982 strayLightData =
None 985 self.config.illuminationCorrectionDataProductName).getMaskedImage()
986 if (self.config.doIlluminationCorrection
and 987 filterName
in self.config.illumFilters)
991 return pipeBase.Struct(bias=biasExposure,
992 linearizer=linearizer,
993 crosstalkSources=crosstalkSources,
996 bfKernel=brighterFatterKernel,
998 fringes=fringeStruct,
999 opticsTransmission=opticsTransmission,
1000 filterTransmission=filterTransmission,
1001 sensorTransmission=sensorTransmission,
1002 atmosphereTransmission=atmosphereTransmission,
1003 strayLightData=strayLightData,
1004 illumMaskedImage=illumMaskedImage
1007 @pipeBase.timeMethod
1008 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1009 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
1010 opticsTransmission=
None, filterTransmission=
None,
1011 sensorTransmission=
None, atmosphereTransmission=
None,
1012 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1015 """!Perform instrument signature removal on an exposure. 1017 Steps included in the ISR processing, in order performed, are: 1018 - saturation and suspect pixel masking 1019 - overscan subtraction 1020 - CCD assembly of individual amplifiers 1022 - variance image construction 1023 - linearization of non-linear response 1025 - brighter-fatter correction 1028 - stray light subtraction 1030 - masking of known defects and camera specific features 1031 - vignette calculation 1032 - appending transmission curve and distortion model 1036 ccdExposure : `lsst.afw.image.Exposure` 1037 The raw exposure that is to be run through ISR. The 1038 exposure is modified by this method. 1039 camera : `lsst.afw.cameraGeom.Camera`, optional 1040 The camera geometry for this exposure. Used to select the 1041 distortion model appropriate for this data. 1042 bias : `lsst.afw.image.Exposure`, optional 1043 Bias calibration frame. 1044 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1045 Functor for linearization. 1046 crosstalkSources : `list`, optional 1047 List of possible crosstalk sources. 1048 dark : `lsst.afw.image.Exposure`, optional 1049 Dark calibration frame. 1050 flat : `lsst.afw.image.Exposure`, optional 1051 Flat calibration frame. 1052 bfKernel : `numpy.ndarray`, optional 1053 Brighter-fatter kernel. 1054 defects : `lsst.meas.algorithms.Defects`, optional 1056 fringes : `lsst.pipe.base.Struct`, optional 1057 Struct containing the fringe correction data, with 1059 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1060 - ``seed``: random seed derived from the ccdExposureId for random 1061 number generator (`uint32`) 1062 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1063 A ``TransmissionCurve`` that represents the throughput of the optics, 1064 to be evaluated in focal-plane coordinates. 1065 filterTransmission : `lsst.afw.image.TransmissionCurve` 1066 A ``TransmissionCurve`` that represents the throughput of the filter 1067 itself, to be evaluated in focal-plane coordinates. 1068 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1069 A ``TransmissionCurve`` that represents the throughput of the sensor 1070 itself, to be evaluated in post-assembly trimmed detector coordinates. 1071 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1072 A ``TransmissionCurve`` that represents the throughput of the 1073 atmosphere, assumed to be spatially constant. 1074 detectorNum : `int`, optional 1075 The integer number for the detector to process. 1076 isGen3 : bool, optional 1077 Flag this call to run() as using the Gen3 butler environment. 1078 strayLightData : `object`, optional 1079 Opaque object containing calibration information for stray-light 1080 correction. If `None`, no correction will be performed. 1081 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1082 Illumination correction image. 1086 result : `lsst.pipe.base.Struct` 1087 Result struct with component: 1088 - ``exposure`` : `afw.image.Exposure` 1089 The fully ISR corrected exposure. 1090 - ``outputExposure`` : `afw.image.Exposure` 1091 An alias for `exposure` 1092 - ``ossThumb`` : `numpy.ndarray` 1093 Thumbnail image of the exposure after overscan subtraction. 1094 - ``flattenedThumb`` : `numpy.ndarray` 1095 Thumbnail image of the exposure after flat-field correction. 1100 Raised if a configuration option is set to True, but the 1101 required calibration data has not been specified. 1105 The current processed exposure can be viewed by setting the 1106 appropriate lsstDebug entries in the `debug.display` 1107 dictionary. The names of these entries correspond to some of 1108 the IsrTaskConfig Boolean options, with the value denoting the 1109 frame to use. The exposure is shown inside the matching 1110 option check and after the processing of that step has 1111 finished. The steps with debug points are: 1122 In addition, setting the "postISRCCD" entry displays the 1123 exposure after all ISR processing has finished. 1131 self.config.doFringe =
False 1134 if detectorNum
is None:
1135 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1137 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1142 if isinstance(ccdExposure, ButlerDataRef):
1145 ccd = ccdExposure.getDetector()
1146 filterName = afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1149 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1150 ccd = [
FakeAmp(ccdExposure, self.config)]
1153 if self.config.doBias
and bias
is None:
1154 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1156 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1157 if self.config.doBrighterFatter
and bfKernel
is None:
1158 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1159 if self.config.doDark
and dark
is None:
1160 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1161 if self.config.doFlat
and flat
is None:
1162 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1163 if self.config.doDefect
and defects
is None:
1164 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1165 if self.config.doAddDistortionModel
and camera
is None:
1166 raise RuntimeError(
"Must supply camera if config.doAddDistortionModel=True.")
1167 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1168 fringes.fringes
is None):
1173 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1174 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1175 illumMaskedImage
is None):
1176 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1179 if self.config.doConvertIntToFloat:
1180 self.log.info(
"Converting exposure to floating point values.")
1187 if ccdExposure.getBBox().contains(amp.getBBox()):
1191 if self.config.doOverscan
and not badAmp:
1194 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1195 if overscanResults
is not None and \
1196 self.config.qa
is not None and self.config.qa.saveStats
is True:
1197 if isinstance(overscanResults.overscanFit, float):
1198 qaMedian = overscanResults.overscanFit
1199 qaStdev = float(
"NaN")
1201 qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1202 afwMath.MEDIAN | afwMath.STDEVCLIP)
1203 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1204 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1206 self.metadata.set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1207 self.metadata.set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1208 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1209 amp.getName(), qaMedian, qaStdev)
1210 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1213 self.log.warn(
"Amplifier %s is bad.", amp.getName())
1214 overscanResults =
None 1216 overscans.append(overscanResults
if overscanResults
is not None else None)
1218 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1220 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1221 self.log.info(
"Applying crosstalk correction.")
1222 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1223 self.
debugView(ccdExposure,
"doCrosstalk")
1225 if self.config.doAssembleCcd:
1226 self.log.info(
"Assembling CCD from amplifiers.")
1227 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1229 if self.config.expectWcs
and not ccdExposure.getWcs():
1230 self.log.warn(
"No WCS found in input exposure.")
1231 self.
debugView(ccdExposure,
"doAssembleCcd")
1234 if self.config.qa.doThumbnailOss:
1235 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1237 if self.config.doBias:
1238 self.log.info(
"Applying bias correction.")
1239 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1240 trimToFit=self.config.doTrimToMatchCalib)
1243 if self.config.doVariance:
1244 for amp, overscanResults
in zip(ccd, overscans):
1245 if ccdExposure.getBBox().contains(amp.getBBox()):
1246 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1247 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1248 if overscanResults
is not None:
1250 overscanImage=overscanResults.overscanImage)
1254 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1255 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1256 afwMath.MEDIAN | afwMath.STDEVCLIP)
1257 self.metadata.set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1258 qaStats.getValue(afwMath.MEDIAN))
1259 self.metadata.set(f
"ISR VARIANCE {amp.getName()} STDEV",
1260 qaStats.getValue(afwMath.STDEVCLIP))
1261 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1262 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1263 qaStats.getValue(afwMath.STDEVCLIP))
1266 self.log.info(
"Applying linearizer.")
1267 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1269 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1270 self.log.info(
"Applying crosstalk correction.")
1271 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1272 self.
debugView(ccdExposure,
"doCrosstalk")
1276 if self.config.doDefect:
1277 self.log.info(
"Masking defects.")
1280 if self.config.doNanMasking:
1281 self.log.info(
"Masking NAN value pixels.")
1284 if self.config.doWidenSaturationTrails:
1285 self.log.info(
"Widening saturation trails.")
1286 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1288 if self.config.doCameraSpecificMasking:
1289 self.log.info(
"Masking regions for camera specific reasons.")
1290 self.masking.
run(ccdExposure)
1292 if self.config.doBrighterFatter:
1301 interpExp = ccdExposure.clone()
1303 isrFunctions.interpolateFromMask(
1304 maskedImage=interpExp.getMaskedImage(),
1305 fwhm=self.config.fwhm,
1306 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1307 maskNameList=self.config.maskListToInterpolate
1309 bfExp = interpExp.clone()
1311 self.log.info(
"Applying brighter fatter correction.")
1312 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1313 self.config.brighterFatterMaxIter,
1314 self.config.brighterFatterThreshold,
1315 self.config.brighterFatterApplyGain
1317 if bfResults[1] == self.config.brighterFatterMaxIter:
1318 self.log.warn(
"Brighter fatter correction did not converge, final difference %f.",
1321 self.log.info(
"Finished brighter fatter correction in %d iterations.",
1323 image = ccdExposure.getMaskedImage().getImage()
1324 bfCorr = bfExp.getMaskedImage().getImage()
1325 bfCorr -= interpExp.getMaskedImage().getImage()
1328 self.
debugView(ccdExposure,
"doBrighterFatter")
1330 if self.config.doDark:
1331 self.log.info(
"Applying dark correction.")
1335 if self.config.doFringe
and not self.config.fringeAfterFlat:
1336 self.log.info(
"Applying fringe correction before flat.")
1337 self.fringe.
run(ccdExposure, **fringes.getDict())
1340 if self.config.doStrayLight:
1341 if strayLightData
is not None:
1342 self.log.info(
"Applying stray light correction.")
1343 self.strayLight.
run(ccdExposure, strayLightData)
1344 self.
debugView(ccdExposure,
"doStrayLight")
1346 self.log.debug(
"Skipping stray light correction: no data found for this image.")
1348 if self.config.doFlat:
1349 self.log.info(
"Applying flat correction.")
1353 if self.config.doApplyGains:
1354 self.log.info(
"Applying gain correction instead of flat.")
1355 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1357 if self.config.doFringe
and self.config.fringeAfterFlat:
1358 self.log.info(
"Applying fringe correction after flat.")
1359 self.fringe.
run(ccdExposure, **fringes.getDict())
1361 if self.config.doVignette:
1362 self.log.info(
"Constructing Vignette polygon.")
1365 if self.config.vignette.doWriteVignettePolygon:
1368 if self.config.doAttachTransmissionCurve:
1369 self.log.info(
"Adding transmission curves.")
1370 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1371 filterTransmission=filterTransmission,
1372 sensorTransmission=sensorTransmission,
1373 atmosphereTransmission=atmosphereTransmission)
1375 if self.config.doAddDistortionModel:
1376 self.log.info(
"Adding a distortion model to the WCS.")
1377 isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1379 flattenedThumb =
None 1380 if self.config.qa.doThumbnailFlattened:
1381 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1383 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1384 self.log.info(
"Performing illumination correction.")
1385 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1386 illumMaskedImage, illumScale=self.config.illumScale,
1387 trimToFit=self.config.doTrimToMatchCalib)
1390 if self.config.doSaveInterpPixels:
1391 preInterpExp = ccdExposure.clone()
1406 if self.config.doSetBadRegions:
1407 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1408 if badPixelCount > 0:
1409 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1411 if self.config.doInterpolate:
1412 self.log.info(
"Interpolating masked pixels.")
1413 isrFunctions.interpolateFromMask(
1414 maskedImage=ccdExposure.getMaskedImage(),
1415 fwhm=self.config.fwhm,
1416 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1417 maskNameList=list(self.config.maskListToInterpolate)
1422 if self.config.doMeasureBackground:
1423 self.log.info(
"Measuring background level.")
1426 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1428 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1429 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1430 afwMath.MEDIAN | afwMath.STDEVCLIP)
1431 self.metadata.set(
"ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1432 qaStats.getValue(afwMath.MEDIAN))
1433 self.metadata.set(
"ISR BACKGROUND {} STDEV".format(amp.getName()),
1434 qaStats.getValue(afwMath.STDEVCLIP))
1435 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1436 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1437 qaStats.getValue(afwMath.STDEVCLIP))
1439 self.
debugView(ccdExposure,
"postISRCCD")
1441 return pipeBase.Struct(
1442 exposure=ccdExposure,
1444 flattenedThumb=flattenedThumb,
1446 preInterpolatedExposure=preInterpExp,
1447 outputExposure=ccdExposure,
1448 outputOssThumbnail=ossThumb,
1449 outputFlattenedThumbnail=flattenedThumb,
1452 @pipeBase.timeMethod
1454 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1456 This method contains the `CmdLineTask` interface to the ISR 1457 processing. All IO is handled here, freeing the `run()` method 1458 to manage only pixel-level calculations. The steps performed 1460 - Read in necessary detrending/isr/calibration data. 1461 - Process raw exposure in `run()`. 1462 - Persist the ISR-corrected exposure as "postISRCCD" if 1463 config.doWrite=True. 1467 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1468 DataRef of the detector data to be processed 1472 result : `lsst.pipe.base.Struct` 1473 Result struct with component: 1474 - ``exposure`` : `afw.image.Exposure` 1475 The fully ISR corrected exposure. 1480 Raised if a configuration option is set to True, but the 1481 required calibration data does not exist. 1484 self.log.info(
"Performing ISR on sensor %s.", sensorRef.dataId)
1486 ccdExposure = sensorRef.get(self.config.datasetType)
1488 camera = sensorRef.get(
"camera")
1489 if camera
is None and self.config.doAddDistortionModel:
1490 raise RuntimeError(
"config.doAddDistortionModel is True " 1491 "but could not get a camera from the butler.")
1492 isrData = self.
readIsrData(sensorRef, ccdExposure)
1494 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1496 if self.config.doWrite:
1497 sensorRef.put(result.exposure,
"postISRCCD")
1498 if result.preInterpolatedExposure
is not None:
1499 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1500 if result.ossThumb
is not None:
1501 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1502 if result.flattenedThumb
is not None:
1503 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1508 """!Retrieve a calibration dataset for removing instrument signature. 1513 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1514 DataRef of the detector data to find calibration datasets 1517 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1519 If True, disable butler proxies to enable error handling 1520 within this routine. 1524 exposure : `lsst.afw.image.Exposure` 1525 Requested calibration frame. 1530 Raised if no matching calibration frame can be found. 1533 exp = dataRef.get(datasetType, immediate=immediate)
1534 except Exception
as exc1:
1535 if not self.config.fallbackFilterName:
1536 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1538 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1539 except Exception
as exc2:
1540 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1541 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1542 self.log.warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
1544 if self.config.doAssembleIsrExposures:
1545 exp = self.assembleCcd.assembleCcd(exp)
1549 """Ensure that the data returned by Butler is a fully constructed exposure. 1551 ISR requires exposure-level image data for historical reasons, so if we did 1552 not recieve that from Butler, construct it from what we have, modifying the 1557 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1558 `lsst.afw.image.ImageF` 1559 The input data structure obtained from Butler. 1560 camera : `lsst.afw.cameraGeom.camera` 1561 The camera associated with the image. Used to find the appropriate 1564 The detector this exposure should match. 1568 inputExp : `lsst.afw.image.Exposure` 1569 The re-constructed exposure, with appropriate detector parameters. 1574 Raised if the input data cannot be used to construct an exposure. 1576 if isinstance(inputExp, afwImage.DecoratedImageU):
1577 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1578 elif isinstance(inputExp, afwImage.ImageF):
1579 inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1580 elif isinstance(inputExp, afwImage.MaskedImageF):
1581 inputExp = afwImage.makeExposure(inputExp)
1582 elif isinstance(inputExp, afwImage.Exposure):
1584 elif inputExp
is None:
1588 raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
1591 if inputExp.getDetector()
is None:
1592 inputExp.setDetector(camera[detectorNum])
1597 """Convert exposure image from uint16 to float. 1599 If the exposure does not need to be converted, the input is 1600 immediately returned. For exposures that are converted to use 1601 floating point pixels, the variance is set to unity and the 1606 exposure : `lsst.afw.image.Exposure` 1607 The raw exposure to be converted. 1611 newexposure : `lsst.afw.image.Exposure` 1612 The input ``exposure``, converted to floating point pixels. 1617 Raised if the exposure type cannot be converted to float. 1620 if isinstance(exposure, afwImage.ExposureF):
1622 self.log.debug(
"Exposure already of type float.")
1624 if not hasattr(exposure,
"convertF"):
1625 raise RuntimeError(
"Unable to convert exposure (%s) to float." % type(exposure))
1627 newexposure = exposure.convertF()
1628 newexposure.variance[:] = 1
1629 newexposure.mask[:] = 0x0
1634 """Identify bad amplifiers, saturated and suspect pixels. 1638 ccdExposure : `lsst.afw.image.Exposure` 1639 Input exposure to be masked. 1640 amp : `lsst.afw.table.AmpInfoCatalog` 1641 Catalog of parameters defining the amplifier on this 1643 defects : `lsst.meas.algorithms.Defects` 1644 List of defects. Used to determine if the entire 1650 If this is true, the entire amplifier area is covered by 1651 defects and unusable. 1654 maskedImage = ccdExposure.getMaskedImage()
1660 if defects
is not None:
1661 badAmp = bool(sum([v.getBBox().contains(amp.getBBox())
for v
in defects]))
1666 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1668 maskView = dataView.getMask()
1669 maskView |= maskView.getPlaneBitMask(
"BAD")
1676 if self.config.doSaturation
and not badAmp:
1677 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1678 if self.config.doSuspect
and not badAmp:
1679 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1680 if math.isfinite(self.config.saturation):
1681 limits.update({self.config.saturatedMaskName: self.config.saturation})
1683 for maskName, maskThreshold
in limits.items():
1684 if not math.isnan(maskThreshold):
1685 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1686 isrFunctions.makeThresholdMask(
1687 maskedImage=dataView,
1688 threshold=maskThreshold,
1694 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1696 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1697 self.config.suspectMaskName])
1698 if numpy.all(maskView.getArray() & maskVal > 0):
1700 maskView |= maskView.getPlaneBitMask(
"BAD")
1705 """Apply overscan correction in place. 1707 This method does initial pixel rejection of the overscan 1708 region. The overscan can also be optionally segmented to 1709 allow for discontinuous overscan responses to be fit 1710 separately. The actual overscan subtraction is performed by 1711 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1712 which is called here after the amplifier is preprocessed. 1716 ccdExposure : `lsst.afw.image.Exposure` 1717 Exposure to have overscan correction performed. 1718 amp : `lsst.afw.table.AmpInfoCatalog` 1719 The amplifier to consider while correcting the overscan. 1723 overscanResults : `lsst.pipe.base.Struct` 1724 Result struct with components: 1725 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1726 Value or fit subtracted from the amplifier image data. 1727 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1728 Value or fit subtracted from the overscan image data. 1729 - ``overscanImage`` : `lsst.afw.image.Image` 1730 Image of the overscan region with the overscan 1731 correction applied. This quantity is used to estimate 1732 the amplifier read noise empirically. 1737 Raised if the ``amp`` does not contain raw pixel information. 1741 lsst.ip.isr.isrFunctions.overscanCorrection 1743 if not amp.getHasRawInfo():
1744 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1746 if amp.getRawHorizontalOverscanBBox().isEmpty():
1747 self.log.info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1750 statControl = afwMath.StatisticsControl()
1751 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1754 dataBBox = amp.getRawDataBBox()
1755 oscanBBox = amp.getRawHorizontalOverscanBBox()
1759 prescanBBox = amp.getRawPrescanBBox()
1760 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1761 dx0 += self.config.overscanNumLeadingColumnsToSkip
1762 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1764 dx0 += self.config.overscanNumTrailingColumnsToSkip
1765 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1771 if ((self.config.overscanBiasJump
and 1772 self.config.overscanBiasJumpLocation)
and 1773 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1774 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1775 self.config.overscanBiasJumpDevices)):
1776 if amp.getReadoutCorner()
in (afwTable.LL, afwTable.LR):
1777 yLower = self.config.overscanBiasJumpLocation
1778 yUpper = dataBBox.getHeight() - yLower
1780 yUpper = self.config.overscanBiasJumpLocation
1781 yLower = dataBBox.getHeight() - yUpper
1800 oscanBBox.getHeight())))
1803 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1804 ampImage = ccdExposure.maskedImage[imageBBox]
1805 overscanImage = ccdExposure.maskedImage[overscanBBox]
1807 overscanArray = overscanImage.image.array
1808 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1809 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1810 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1812 statControl = afwMath.StatisticsControl()
1813 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1815 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1816 overscanImage=overscanImage,
1817 fitType=self.config.overscanFitType,
1818 order=self.config.overscanOrder,
1819 collapseRej=self.config.overscanNumSigmaClip,
1820 statControl=statControl,
1821 overscanIsInt=self.config.overscanIsInt
1825 levelStat = afwMath.MEDIAN
1826 sigmaStat = afwMath.STDEVCLIP
1828 sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1829 self.config.qa.flatness.nIter)
1830 metadata = ccdExposure.getMetadata()
1831 ampNum = amp.getName()
1832 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1833 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1834 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1836 stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1837 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1838 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1840 return overscanResults
1843 """Set the variance plane using the amplifier gain and read noise 1845 The read noise is calculated from the ``overscanImage`` if the 1846 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1847 the value from the amplifier data is used. 1851 ampExposure : `lsst.afw.image.Exposure` 1852 Exposure to process. 1853 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1854 Amplifier detector data. 1855 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1856 Image of overscan, required only for empirical read noise. 1860 lsst.ip.isr.isrFunctions.updateVariance 1862 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1863 gain = amp.getGain()
1865 if math.isnan(gain):
1867 self.log.warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1870 self.log.warn(
"Gain for amp %s == %g <= 0; setting to %f.",
1871 amp.getName(), gain, patchedGain)
1874 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1875 self.log.info(
"Overscan is none for EmpiricalReadNoise.")
1877 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1878 stats = afwMath.StatisticsControl()
1879 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1880 readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1881 self.log.info(
"Calculated empirical read noise for amp %s: %f.",
1882 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(
"There were %d unmasked NaNs.", numNans)
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)