28 import lsst.pex.config
as pexConfig
32 from contextlib
import contextmanager
33 from lsstDebug
import getDebugFrame
44 from .
import isrFunctions
46 from .
import linearize
48 from .assembleCcdTask
import AssembleCcdTask
49 from .crosstalk
import CrosstalkTask
50 from .fringe
import FringeTask
51 from .isr
import maskNans
52 from .masking
import MaskingTask
53 from .straylight
import StrayLightTask
54 from .vignette
import VignetteTask
57 __all__ = [
"IsrTask",
"IsrTaskConfig",
"RunIsrTask",
"RunIsrConfig"]
61 dimensions={
"instrument",
"visit",
"detector"},
63 ccdExposure = cT.PrerequisiteInput(
65 doc=
"Input exposure to process.",
66 storageClass=
"Exposure",
67 dimensions=[
"instrument",
"visit",
"detector"],
69 camera = cT.PrerequisiteInput(
71 storageClass=
"Camera",
72 doc=
"Input camera to construct complete exposures.",
73 dimensions=[
"instrument",
"calibration_label"],
75 bias = cT.PrerequisiteInput(
77 doc=
"Input bias calibration.",
78 storageClass=
"ImageF",
79 dimensions=[
"instrument",
"calibration_label",
"detector"],
81 dark = cT.PrerequisiteInput(
83 doc=
"Input dark calibration.",
84 storageClass=
"ImageF",
85 dimensions=[
"instrument",
"calibration_label",
"detector"],
87 flat = cT.PrerequisiteInput(
89 doc=
"Input flat calibration.",
90 storageClass=
"MaskedImageF",
91 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
93 bfKernel = cT.PrerequisiteInput(
95 doc=
"Input brighter-fatter kernel.",
96 storageClass=
"NumpyArray",
97 dimensions=[
"instrument",
"calibration_label"],
99 defects = cT.PrerequisiteInput(
101 doc=
"Input defect tables.",
102 storageClass=
"DefectsList",
103 dimensions=[
"instrument",
"calibration_label",
"detector"],
105 opticsTransmission = cT.PrerequisiteInput(
106 name=
"transmission_optics",
107 storageClass=
"TransmissionCurve",
108 doc=
"Transmission curve due to the optics.",
109 dimensions=[
"instrument",
"calibration_label"],
111 filterTransmission = cT.PrerequisiteInput(
112 name=
"transmission_filter",
113 storageClass=
"TransmissionCurve",
114 doc=
"Transmission curve due to the filter.",
115 dimensions=[
"instrument",
"physical_filter",
"calibration_label"],
117 sensorTransmission = cT.PrerequisiteInput(
118 name=
"transmission_sensor",
119 storageClass=
"TransmissionCurve",
120 doc=
"Transmission curve due to the sensor.",
121 dimensions=[
"instrument",
"calibration_label",
"detector"],
123 atmosphereTransmission = cT.PrerequisiteInput(
124 name=
"transmission_atmosphere",
125 storageClass=
"TransmissionCurve",
126 doc=
"Transmission curve due to the atmosphere.",
127 dimensions=[
"instrument"],
129 illumMaskedImage = cT.PrerequisiteInput(
131 doc=
"Input illumination correction.",
132 storageClass=
"MaskedImageF",
133 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
136 outputExposure = cT.Output(
138 doc=
"Output ISR processed exposure.",
139 storageClass=
"ExposureF",
140 dimensions=[
"instrument",
"visit",
"detector"],
142 preInterpExposure = cT.Output(
143 name=
'preInterpISRCCD',
144 doc=
"Output ISR processed exposure, with pixels left uninterpolated.",
145 storageClass=
"ExposureF",
146 dimensions=[
"instrument",
"visit",
"detector"],
148 outputOssThumbnail = cT.Output(
150 doc=
"Output Overscan-subtracted thumbnail image.",
151 storageClass=
"Thumbnail",
152 dimensions=[
"instrument",
"visit",
"detector"],
154 outputFlattenedThumbnail = cT.Output(
155 name=
"FlattenedThumb",
156 doc=
"Output flat-corrected thumbnail image.",
157 storageClass=
"Thumbnail",
158 dimensions=[
"instrument",
"visit",
"detector"],
164 if config.doBias
is not True:
165 self.prerequisiteInputs.discard(
"bias")
166 if config.doLinearize
is not True:
167 self.prerequisiteInputs.discard(
"linearizer")
168 if config.doCrosstalk
is not True:
169 self.prerequisiteInputs.discard(
"crosstalkSources")
170 if config.doBrighterFatter
is not True:
171 self.prerequisiteInputs.discard(
"bfKernel")
172 if config.doDefect
is not True:
173 self.prerequisiteInputs.discard(
"defects")
174 if config.doDark
is not True:
175 self.prerequisiteInputs.discard(
"dark")
176 if config.doFlat
is not True:
177 self.prerequisiteInputs.discard(
"flat")
178 if config.doAttachTransmissionCurve
is not True:
179 self.prerequisiteInputs.discard(
"opticsTransmission")
180 self.prerequisiteInputs.discard(
"filterTransmission")
181 self.prerequisiteInputs.discard(
"sensorTransmission")
182 self.prerequisiteInputs.discard(
"atmosphereTransmission")
183 if config.doUseOpticsTransmission
is not True:
184 self.prerequisiteInputs.discard(
"opticsTransmission")
185 if config.doUseFilterTransmission
is not True:
186 self.prerequisiteInputs.discard(
"filterTransmission")
187 if config.doUseSensorTransmission
is not True:
188 self.prerequisiteInputs.discard(
"sensorTransmission")
189 if config.doUseAtmosphereTransmission
is not True:
190 self.prerequisiteInputs.discard(
"atmosphereTransmission")
191 if config.doIlluminationCorrection
is not True:
192 self.prerequisiteInputs.discard(
"illumMaskedImage")
194 if config.doWrite
is not True:
195 self.outputs.discard(
"outputExposure")
196 self.outputs.discard(
"preInterpExposure")
197 self.outputs.discard(
"outputFlattenedThumbnail")
198 self.outputs.discard(
"outputOssThumbnail")
199 if config.doSaveInterpPixels
is not True:
200 self.outputs.discard(
"preInterpExposure")
201 if config.qa.doThumbnailOss
is not True:
202 self.outputs.discard(
"outputOssThumbnail")
203 if config.qa.doThumbnailFlattened
is not True:
204 self.outputs.discard(
"outputFlattenedThumbnail")
208 pipelineConnections=IsrTaskConnections):
209 """Configuration parameters for IsrTask. 211 Items are grouped in the order in which they are executed by the task. 213 datasetType = pexConfig.Field(
215 doc=
"Dataset type for input data; users will typically leave this alone, " 216 "but camera-specific ISR tasks will override it",
220 fallbackFilterName = pexConfig.Field(
222 doc=
"Fallback default filter name for calibrations.",
225 expectWcs = pexConfig.Field(
228 doc=
"Expect input science images to have a WCS (set False for e.g. spectrographs)." 230 fwhm = pexConfig.Field(
232 doc=
"FWHM of PSF in arcseconds.",
235 qa = pexConfig.ConfigField(
237 doc=
"QA related configuration options.",
241 doConvertIntToFloat = pexConfig.Field(
243 doc=
"Convert integer raw images to floating point values?",
248 doSaturation = pexConfig.Field(
250 doc=
"Mask saturated pixels? NB: this is totally independent of the" 251 " interpolation option - this is ONLY setting the bits in the mask." 252 " To have them interpolated make sure doSaturationInterpolation=True",
255 saturatedMaskName = pexConfig.Field(
257 doc=
"Name of mask plane to use in saturation detection and interpolation",
260 saturation = pexConfig.Field(
262 doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
263 default=float(
"NaN"),
265 growSaturationFootprintSize = pexConfig.Field(
267 doc=
"Number of pixels by which to grow the saturation footprints",
272 doSuspect = pexConfig.Field(
274 doc=
"Mask suspect pixels?",
277 suspectMaskName = pexConfig.Field(
279 doc=
"Name of mask plane to use for suspect pixels",
282 numEdgeSuspect = pexConfig.Field(
284 doc=
"Number of edge pixels to be flagged as untrustworthy.",
289 doSetBadRegions = pexConfig.Field(
291 doc=
"Should we set the level of all BAD patches of the chip to the chip's average value?",
294 badStatistic = pexConfig.ChoiceField(
296 doc=
"How to estimate the average value for BAD regions.",
299 "MEANCLIP":
"Correct using the (clipped) mean of good data",
300 "MEDIAN":
"Correct using the median of the good data",
305 doOverscan = pexConfig.Field(
307 doc=
"Do overscan subtraction?",
310 overscanFitType = pexConfig.ChoiceField(
312 doc=
"The method for fitting the overscan bias level.",
315 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
316 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
317 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
318 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
319 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
320 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
321 "MEAN":
"Correct using the mean of the overscan region",
322 "MEANCLIP":
"Correct using a clipped mean of the overscan region",
323 "MEDIAN":
"Correct using the median of the overscan region",
326 overscanOrder = pexConfig.Field(
328 doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
329 "or number of spline knots if overscan fit type is a spline."),
332 overscanNumSigmaClip = pexConfig.Field(
334 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
337 overscanIsInt = pexConfig.Field(
339 doc=
"Treat overscan as an integer image for purposes of overscan.FitType=MEDIAN",
342 overscanNumLeadingColumnsToSkip = pexConfig.Field(
344 doc=
"Number of columns to skip in overscan, i.e. those closest to amplifier",
347 overscanNumTrailingColumnsToSkip = pexConfig.Field(
349 doc=
"Number of columns to skip in overscan, i.e. those farthest from amplifier",
352 overscanMaxDev = pexConfig.Field(
354 doc=
"Maximum deviation from the median for overscan",
355 default=1000.0, check=
lambda x: x > 0
357 overscanBiasJump = pexConfig.Field(
359 doc=
"Fit the overscan in a piecewise-fashion to correct for bias jumps?",
362 overscanBiasJumpKeyword = pexConfig.Field(
364 doc=
"Header keyword containing information about devices.",
365 default=
"NO_SUCH_KEY",
367 overscanBiasJumpDevices = pexConfig.ListField(
369 doc=
"List of devices that need piecewise overscan correction.",
372 overscanBiasJumpLocation = pexConfig.Field(
374 doc=
"Location of bias jump along y-axis.",
379 doAssembleCcd = pexConfig.Field(
382 doc=
"Assemble amp-level exposures into a ccd-level exposure?" 384 assembleCcd = pexConfig.ConfigurableField(
385 target=AssembleCcdTask,
386 doc=
"CCD assembly task",
390 doAssembleIsrExposures = pexConfig.Field(
393 doc=
"Assemble amp-level calibration exposures into ccd-level exposure?" 395 doTrimToMatchCalib = pexConfig.Field(
398 doc=
"Trim raw data to match calibration bounding boxes?" 402 doBias = pexConfig.Field(
404 doc=
"Apply bias frame correction?",
407 biasDataProductName = pexConfig.Field(
409 doc=
"Name of the bias data product",
414 doVariance = pexConfig.Field(
416 doc=
"Calculate variance?",
419 gain = pexConfig.Field(
421 doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
422 default=float(
"NaN"),
424 readNoise = pexConfig.Field(
426 doc=
"The read noise to use if no Detector is present in the Exposure",
429 doEmpiricalReadNoise = pexConfig.Field(
432 doc=
"Calculate empirical read noise instead of value from AmpInfo data?" 436 doLinearize = pexConfig.Field(
438 doc=
"Correct for nonlinearity of the detector's response?",
443 doCrosstalk = pexConfig.Field(
445 doc=
"Apply intra-CCD crosstalk correction?",
448 doCrosstalkBeforeAssemble = pexConfig.Field(
450 doc=
"Apply crosstalk correction before CCD assembly, and before trimming?",
453 crosstalk = pexConfig.ConfigurableField(
454 target=CrosstalkTask,
455 doc=
"Intra-CCD crosstalk correction",
459 doDefect = pexConfig.Field(
461 doc=
"Apply correction for CCD defects, e.g. hot pixels?",
464 doNanMasking = pexConfig.Field(
466 doc=
"Mask NAN pixels?",
469 doWidenSaturationTrails = pexConfig.Field(
471 doc=
"Widen bleed trails based on their width?",
476 doBrighterFatter = pexConfig.Field(
479 doc=
"Apply the brighter fatter correction" 481 brighterFatterLevel = pexConfig.ChoiceField(
484 doc=
"The level at which to correct for brighter-fatter.",
486 "AMP":
"Every amplifier treated separately.",
487 "DETECTOR":
"One kernel per detector",
490 brighterFatterKernelFile = pexConfig.Field(
493 doc=
"Kernel file used for the brighter fatter correction" 495 brighterFatterMaxIter = pexConfig.Field(
498 doc=
"Maximum number of iterations for the brighter fatter correction" 500 brighterFatterThreshold = pexConfig.Field(
503 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the " 504 " absolute value of the difference between the current corrected image and the one" 505 " from the previous iteration summed over all the pixels." 507 brighterFatterApplyGain = pexConfig.Field(
510 doc=
"Should the gain be applied when applying the brighter fatter correction?" 514 doDark = pexConfig.Field(
516 doc=
"Apply dark frame correction?",
519 darkDataProductName = pexConfig.Field(
521 doc=
"Name of the dark data product",
526 doStrayLight = pexConfig.Field(
528 doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
531 strayLight = pexConfig.ConfigurableField(
532 target=StrayLightTask,
533 doc=
"y-band stray light correction" 537 doFlat = pexConfig.Field(
539 doc=
"Apply flat field correction?",
542 flatDataProductName = pexConfig.Field(
544 doc=
"Name of the flat data product",
547 flatScalingType = pexConfig.ChoiceField(
549 doc=
"The method for scaling the flat on the fly.",
552 "USER":
"Scale by flatUserScale",
553 "MEAN":
"Scale by the inverse of the mean",
554 "MEDIAN":
"Scale by the inverse of the median",
557 flatUserScale = pexConfig.Field(
559 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
562 doTweakFlat = pexConfig.Field(
564 doc=
"Tweak flats to match observed amplifier ratios?",
569 doApplyGains = pexConfig.Field(
571 doc=
"Correct the amplifiers for their gains instead of applying flat correction",
574 normalizeGains = pexConfig.Field(
576 doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
581 doFringe = pexConfig.Field(
583 doc=
"Apply fringe correction?",
586 fringe = pexConfig.ConfigurableField(
588 doc=
"Fringe subtraction task",
590 fringeAfterFlat = pexConfig.Field(
592 doc=
"Do fringe subtraction after flat-fielding?",
597 doAddDistortionModel = pexConfig.Field(
599 doc=
"Apply a distortion model based on camera geometry to the WCS?",
601 deprecated=(
"Camera geometry is incorporated when reading the raw files." 602 " This option no longer is used, and will be removed after v19.")
606 doMeasureBackground = pexConfig.Field(
608 doc=
"Measure the background level on the reduced image?",
613 doCameraSpecificMasking = pexConfig.Field(
615 doc=
"Mask camera-specific bad regions?",
618 masking = pexConfig.ConfigurableField(
625 doInterpolate = pexConfig.Field(
627 doc=
"Interpolate masked pixels?",
630 doSaturationInterpolation = pexConfig.Field(
632 doc=
"Perform interpolation over pixels masked as saturated?" 633 " NB: This is independent of doSaturation; if that is False this plane" 634 " will likely be blank, resulting in a no-op here.",
637 doNanInterpolation = pexConfig.Field(
639 doc=
"Perform interpolation over pixels masked as NaN?" 640 " NB: This is independent of doNanMasking; if that is False this plane" 641 " will likely be blank, resulting in a no-op here.",
644 doNanInterpAfterFlat = pexConfig.Field(
646 doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 647 "also have to interpolate them before flat-fielding."),
650 maskListToInterpolate = pexConfig.ListField(
652 doc=
"List of mask planes that should be interpolated.",
653 default=[
'SAT',
'BAD',
'UNMASKEDNAN'],
655 doSaveInterpPixels = pexConfig.Field(
657 doc=
"Save a copy of the pre-interpolated pixel values?",
662 fluxMag0T1 = pexConfig.DictField(
665 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
666 default=dict((f, pow(10.0, 0.4*m))
for f, m
in ((
"Unknown", 28.0),
669 defaultFluxMag0T1 = pexConfig.Field(
671 doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
672 default=pow(10.0, 0.4*28.0)
676 doVignette = pexConfig.Field(
678 doc=
"Apply vignetting parameters?",
681 vignette = pexConfig.ConfigurableField(
683 doc=
"Vignetting task.",
687 doAttachTransmissionCurve = pexConfig.Field(
690 doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 692 doUseOpticsTransmission = pexConfig.Field(
695 doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 697 doUseFilterTransmission = pexConfig.Field(
700 doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 702 doUseSensorTransmission = pexConfig.Field(
705 doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 707 doUseAtmosphereTransmission = pexConfig.Field(
710 doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 714 doIlluminationCorrection = pexConfig.Field(
717 doc=
"Perform illumination correction?" 719 illuminationCorrectionDataProductName = pexConfig.Field(
721 doc=
"Name of the illumination correction data product.",
724 illumScale = pexConfig.Field(
726 doc=
"Scale factor for the illumination correction.",
729 illumFilters = pexConfig.ListField(
732 doc=
"Only perform illumination correction for these filters." 736 doWrite = pexConfig.Field(
738 doc=
"Persist postISRCCD?",
745 raise ValueError(
"You may not specify both doFlat and doApplyGains")
747 self.config.maskListToInterpolate.append(
"SAT")
749 self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
752 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
753 """Apply common instrument signature correction algorithms to a raw frame. 755 The process for correcting imaging data is very similar from 756 camera to camera. This task provides a vanilla implementation of 757 doing these corrections, including the ability to turn certain 758 corrections off if they are not needed. The inputs to the primary 759 method, `run()`, are a raw exposure to be corrected and the 760 calibration data products. The raw input is a single chip sized 761 mosaic of all amps including overscans and other non-science 762 pixels. The method `runDataRef()` identifies and defines the 763 calibration data products, and is intended for use by a 764 `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 765 `daf.persistence.butlerSubset.ButlerDataRef`. This task may be 766 subclassed for different camera, although the most camera specific 767 methods have been split into subtasks that can be redirected 770 The __init__ method sets up the subtasks for ISR processing, using 771 the defaults from `lsst.ip.isr`. 776 Positional arguments passed to the Task constructor. None used at this time. 777 kwargs : `dict`, optional 778 Keyword arguments passed on to the Task constructor. None used at this time. 780 ConfigClass = IsrTaskConfig
785 self.makeSubtask(
"assembleCcd")
786 self.makeSubtask(
"crosstalk")
787 self.makeSubtask(
"strayLight")
788 self.makeSubtask(
"fringe")
789 self.makeSubtask(
"masking")
790 self.makeSubtask(
"vignette")
793 inputs = butlerQC.get(inputRefs)
796 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
797 except Exception
as e:
798 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
801 inputs[
'isGen3'] =
True 803 if self.config.doLinearize
is True:
804 if 'linearizer' not in inputs:
805 detector = inputs[
'ccdExposure'].getDetector()
806 linearityName = detector.getAmplifiers()[0].getLinearityType()
807 inputs[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
809 if self.config.doDefect
is True:
810 if "defects" in inputs
and inputs[
'defects']
is not None:
813 if not isinstance(inputs[
"defects"], Defects):
814 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
831 outputs = self.
run(**inputs)
832 butlerQC.put(outputs, outputRefs)
835 """!Retrieve necessary frames for instrument signature removal. 837 Pre-fetching all required ISR data products limits the IO 838 required by the ISR. Any conflict between the calibration data 839 available and that needed for ISR is also detected prior to 840 doing processing, allowing it to fail quickly. 844 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 845 Butler reference of the detector data to be processed 846 rawExposure : `afw.image.Exposure` 847 The raw exposure that will later be corrected with the 848 retrieved calibration data; should not be modified in this 853 result : `lsst.pipe.base.Struct` 854 Result struct with components (which may be `None`): 855 - ``bias``: bias calibration frame (`afw.image.Exposure`) 856 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 857 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 858 - ``dark``: dark calibration frame (`afw.image.Exposure`) 859 - ``flat``: flat calibration frame (`afw.image.Exposure`) 860 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 861 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 862 - ``fringes``: `lsst.pipe.base.Struct` with components: 863 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 864 - ``seed``: random seed derived from the ccdExposureId for random 865 number generator (`uint32`). 866 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 867 A ``TransmissionCurve`` that represents the throughput of the optics, 868 to be evaluated in focal-plane coordinates. 869 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 870 A ``TransmissionCurve`` that represents the throughput of the filter 871 itself, to be evaluated in focal-plane coordinates. 872 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 873 A ``TransmissionCurve`` that represents the throughput of the sensor 874 itself, to be evaluated in post-assembly trimmed detector coordinates. 875 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 876 A ``TransmissionCurve`` that represents the throughput of the 877 atmosphere, assumed to be spatially constant. 878 - ``strayLightData`` : `object` 879 An opaque object containing calibration information for 880 stray-light correction. If `None`, no correction will be 882 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 886 NotImplementedError : 887 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 889 ccd = rawExposure.getDetector()
890 filterName =
afwImage.Filter(rawExposure.getFilter().getId()).getName()
891 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
892 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
893 if self.config.doBias
else None)
895 linearizer = (dataRef.get(
"linearizer", immediate=
True)
897 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
898 if self.config.doCrosstalk
else None)
899 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
900 if self.config.doDark
else None)
901 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
902 if self.config.doFlat
else None)
904 brighterFatterKernel =
None 905 if self.config.doBrighterFatter
is True:
909 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
913 brighterFatterKernel = dataRef.get(
"bfKernel")
915 brighterFatterKernel =
None 916 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
919 if self.config.brighterFatterLevel ==
'DETECTOR':
920 brighterFatterKernel = brighterFatterKernel.kernel[ccd.getId()]
923 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
925 defectList = (dataRef.get(
"defects")
926 if self.config.doDefect
else None)
927 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
928 if self.config.doAssembleIsrExposures
else None)
929 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
930 else pipeBase.Struct(fringes=
None))
932 if self.config.doAttachTransmissionCurve:
933 opticsTransmission = (dataRef.get(
"transmission_optics")
934 if self.config.doUseOpticsTransmission
else None)
935 filterTransmission = (dataRef.get(
"transmission_filter")
936 if self.config.doUseFilterTransmission
else None)
937 sensorTransmission = (dataRef.get(
"transmission_sensor")
938 if self.config.doUseSensorTransmission
else None)
939 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
940 if self.config.doUseAtmosphereTransmission
else None)
942 opticsTransmission =
None 943 filterTransmission =
None 944 sensorTransmission =
None 945 atmosphereTransmission =
None 947 if self.config.doStrayLight:
948 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
950 strayLightData =
None 953 self.config.illuminationCorrectionDataProductName).getMaskedImage()
954 if (self.config.doIlluminationCorrection
and 955 filterName
in self.config.illumFilters)
959 return pipeBase.Struct(bias=biasExposure,
960 linearizer=linearizer,
961 crosstalkSources=crosstalkSources,
964 bfKernel=brighterFatterKernel,
966 fringes=fringeStruct,
967 opticsTransmission=opticsTransmission,
968 filterTransmission=filterTransmission,
969 sensorTransmission=sensorTransmission,
970 atmosphereTransmission=atmosphereTransmission,
971 strayLightData=strayLightData,
972 illumMaskedImage=illumMaskedImage
976 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
977 dark=None, flat=None, bfKernel=None, defects=None, fringes=pipeBase.Struct(fringes=
None),
978 opticsTransmission=
None, filterTransmission=
None,
979 sensorTransmission=
None, atmosphereTransmission=
None,
980 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
983 """!Perform instrument signature removal on an exposure. 985 Steps included in the ISR processing, in order performed, are: 986 - saturation and suspect pixel masking 987 - overscan subtraction 988 - CCD assembly of individual amplifiers 990 - variance image construction 991 - linearization of non-linear response 993 - brighter-fatter correction 996 - stray light subtraction 998 - masking of known defects and camera specific features 999 - vignette calculation 1000 - appending transmission curve and distortion model 1004 ccdExposure : `lsst.afw.image.Exposure` 1005 The raw exposure that is to be run through ISR. The 1006 exposure is modified by this method. 1007 camera : `lsst.afw.cameraGeom.Camera`, optional 1008 The camera geometry for this exposure. Used to select the 1009 distortion model appropriate for this data. 1010 bias : `lsst.afw.image.Exposure`, optional 1011 Bias calibration frame. 1012 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1013 Functor for linearization. 1014 crosstalkSources : `list`, optional 1015 List of possible crosstalk sources. 1016 dark : `lsst.afw.image.Exposure`, optional 1017 Dark calibration frame. 1018 flat : `lsst.afw.image.Exposure`, optional 1019 Flat calibration frame. 1020 bfKernel : `numpy.ndarray`, optional 1021 Brighter-fatter kernel. 1022 defects : `lsst.meas.algorithms.Defects`, optional 1024 fringes : `lsst.pipe.base.Struct`, optional 1025 Struct containing the fringe correction data, with 1027 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1028 - ``seed``: random seed derived from the ccdExposureId for random 1029 number generator (`uint32`) 1030 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1031 A ``TransmissionCurve`` that represents the throughput of the optics, 1032 to be evaluated in focal-plane coordinates. 1033 filterTransmission : `lsst.afw.image.TransmissionCurve` 1034 A ``TransmissionCurve`` that represents the throughput of the filter 1035 itself, to be evaluated in focal-plane coordinates. 1036 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1037 A ``TransmissionCurve`` that represents the throughput of the sensor 1038 itself, to be evaluated in post-assembly trimmed detector coordinates. 1039 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1040 A ``TransmissionCurve`` that represents the throughput of the 1041 atmosphere, assumed to be spatially constant. 1042 detectorNum : `int`, optional 1043 The integer number for the detector to process. 1044 isGen3 : bool, optional 1045 Flag this call to run() as using the Gen3 butler environment. 1046 strayLightData : `object`, optional 1047 Opaque object containing calibration information for stray-light 1048 correction. If `None`, no correction will be performed. 1049 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1050 Illumination correction image. 1054 result : `lsst.pipe.base.Struct` 1055 Result struct with component: 1056 - ``exposure`` : `afw.image.Exposure` 1057 The fully ISR corrected exposure. 1058 - ``outputExposure`` : `afw.image.Exposure` 1059 An alias for `exposure` 1060 - ``ossThumb`` : `numpy.ndarray` 1061 Thumbnail image of the exposure after overscan subtraction. 1062 - ``flattenedThumb`` : `numpy.ndarray` 1063 Thumbnail image of the exposure after flat-field correction. 1068 Raised if a configuration option is set to True, but the 1069 required calibration data has not been specified. 1073 The current processed exposure can be viewed by setting the 1074 appropriate lsstDebug entries in the `debug.display` 1075 dictionary. The names of these entries correspond to some of 1076 the IsrTaskConfig Boolean options, with the value denoting the 1077 frame to use. The exposure is shown inside the matching 1078 option check and after the processing of that step has 1079 finished. The steps with debug points are: 1090 In addition, setting the "postISRCCD" entry displays the 1091 exposure after all ISR processing has finished. 1099 self.config.doFringe =
False 1102 if detectorNum
is None:
1103 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1105 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1110 if isinstance(ccdExposure, ButlerDataRef):
1113 ccd = ccdExposure.getDetector()
1114 filterName =
afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1117 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1118 ccd = [
FakeAmp(ccdExposure, self.config)]
1121 if self.config.doBias
and bias
is None:
1122 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1124 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1125 if self.config.doBrighterFatter
and bfKernel
is None:
1126 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1127 if self.config.doDark
and dark
is None:
1128 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1129 if self.config.doFlat
and flat
is None:
1130 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1131 if self.config.doDefect
and defects
is None:
1132 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1133 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1134 fringes.fringes
is None):
1139 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1140 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1141 illumMaskedImage
is None):
1142 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1145 if self.config.doConvertIntToFloat:
1146 self.log.
info(
"Converting exposure to floating point values.")
1153 if ccdExposure.getBBox().
contains(amp.getBBox()):
1157 if self.config.doOverscan
and not badAmp:
1160 self.log.
debug(
"Corrected overscan for amplifier %s.", amp.getName())
1161 if overscanResults
is not None and \
1162 self.config.qa
is not None and self.config.qa.saveStats
is True:
1163 if isinstance(overscanResults.overscanFit, float):
1164 qaMedian = overscanResults.overscanFit
1165 qaStdev = float(
"NaN")
1168 afwMath.MEDIAN | afwMath.STDEVCLIP)
1169 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1170 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1172 self.metadata.
set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1173 self.metadata.
set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1174 self.log.
debug(
" Overscan stats for amplifer %s: %f +/- %f",
1175 amp.getName(), qaMedian, qaStdev)
1176 ccdExposure.getMetadata().
set(
'OVERSCAN',
"Overscan corrected")
1179 self.log.
warn(
"Amplifier %s is bad.", amp.getName())
1180 overscanResults =
None 1182 overscans.append(overscanResults
if overscanResults
is not None else None)
1184 self.log.
info(
"Skipped OSCAN for %s.", amp.getName())
1186 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1187 self.log.
info(
"Applying crosstalk correction.")
1188 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1189 self.
debugView(ccdExposure,
"doCrosstalk")
1191 if self.config.doAssembleCcd:
1192 self.log.
info(
"Assembling CCD from amplifiers.")
1193 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1195 if self.config.expectWcs
and not ccdExposure.getWcs():
1196 self.log.
warn(
"No WCS found in input exposure.")
1197 self.
debugView(ccdExposure,
"doAssembleCcd")
1200 if self.config.qa.doThumbnailOss:
1201 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1203 if self.config.doBias:
1204 self.log.
info(
"Applying bias correction.")
1205 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1206 trimToFit=self.config.doTrimToMatchCalib)
1209 if self.config.doVariance:
1210 for amp, overscanResults
in zip(ccd, overscans):
1211 if ccdExposure.getBBox().
contains(amp.getBBox()):
1212 self.log.
debug(
"Constructing variance map for amplifer %s.", amp.getName())
1213 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1214 if overscanResults
is not None:
1216 overscanImage=overscanResults.overscanImage)
1220 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1222 afwMath.MEDIAN | afwMath.STDEVCLIP)
1223 self.metadata.
set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1224 qaStats.getValue(afwMath.MEDIAN))
1225 self.metadata.
set(f
"ISR VARIANCE {amp.getName()} STDEV",
1226 qaStats.getValue(afwMath.STDEVCLIP))
1227 self.log.
debug(
" Variance stats for amplifer %s: %f +/- %f.",
1228 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1229 qaStats.getValue(afwMath.STDEVCLIP))
1232 self.log.
info(
"Applying linearizer.")
1233 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1235 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1236 self.log.
info(
"Applying crosstalk correction.")
1237 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1238 self.
debugView(ccdExposure,
"doCrosstalk")
1242 if self.config.doDefect:
1243 self.log.
info(
"Masking defects.")
1246 if self.config.numEdgeSuspect > 0:
1247 self.log.
info(
"Masking edges as SUSPECT.")
1248 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1249 maskPlane=
"SUSPECT")
1251 if self.config.doNanMasking:
1252 self.log.
info(
"Masking NAN value pixels.")
1255 if self.config.doWidenSaturationTrails:
1256 self.log.
info(
"Widening saturation trails.")
1257 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1259 if self.config.doCameraSpecificMasking:
1260 self.log.
info(
"Masking regions for camera specific reasons.")
1261 self.masking.
run(ccdExposure)
1263 if self.config.doBrighterFatter:
1272 interpExp = ccdExposure.clone()
1274 isrFunctions.interpolateFromMask(
1275 maskedImage=interpExp.getMaskedImage(),
1276 fwhm=self.config.fwhm,
1277 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1278 maskNameList=self.config.maskListToInterpolate
1280 bfExp = interpExp.clone()
1282 self.log.
info(
"Applying brighter fatter correction.")
1283 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1284 self.config.brighterFatterMaxIter,
1285 self.config.brighterFatterThreshold,
1286 self.config.brighterFatterApplyGain
1288 if bfResults[1] == self.config.brighterFatterMaxIter:
1289 self.log.
warn(
"Brighter fatter correction did not converge, final difference %f.",
1292 self.log.
info(
"Finished brighter fatter correction in %d iterations.",
1294 image = ccdExposure.getMaskedImage().getImage()
1295 bfCorr = bfExp.getMaskedImage().getImage()
1296 bfCorr -= interpExp.getMaskedImage().getImage()
1305 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1307 self.log.
warn(
"Ensuring image edges are masked as SUSPECT to the brighter-fatter kernel size.")
1309 self.
debugView(ccdExposure,
"doBrighterFatter")
1311 if self.config.doDark:
1312 self.log.
info(
"Applying dark correction.")
1316 if self.config.doFringe
and not self.config.fringeAfterFlat:
1317 self.log.
info(
"Applying fringe correction before flat.")
1318 self.fringe.
run(ccdExposure, **fringes.getDict())
1321 if self.config.doStrayLight:
1322 if strayLightData
is not None:
1323 self.log.
info(
"Applying stray light correction.")
1324 self.strayLight.
run(ccdExposure, strayLightData)
1325 self.
debugView(ccdExposure,
"doStrayLight")
1327 self.log.
debug(
"Skipping stray light correction: no data found for this image.")
1329 if self.config.doFlat:
1330 self.log.
info(
"Applying flat correction.")
1334 if self.config.doApplyGains:
1335 self.log.
info(
"Applying gain correction instead of flat.")
1336 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1338 if self.config.doFringe
and self.config.fringeAfterFlat:
1339 self.log.
info(
"Applying fringe correction after flat.")
1340 self.fringe.
run(ccdExposure, **fringes.getDict())
1342 if self.config.doVignette:
1343 self.log.
info(
"Constructing Vignette polygon.")
1346 if self.config.vignette.doWriteVignettePolygon:
1349 if self.config.doAttachTransmissionCurve:
1350 self.log.
info(
"Adding transmission curves.")
1351 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1352 filterTransmission=filterTransmission,
1353 sensorTransmission=sensorTransmission,
1354 atmosphereTransmission=atmosphereTransmission)
1356 flattenedThumb =
None 1357 if self.config.qa.doThumbnailFlattened:
1358 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1360 if self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters:
1361 self.log.
info(
"Performing illumination correction.")
1362 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1363 illumMaskedImage, illumScale=self.config.illumScale,
1364 trimToFit=self.config.doTrimToMatchCalib)
1367 if self.config.doSaveInterpPixels:
1368 preInterpExp = ccdExposure.clone()
1383 if self.config.doSetBadRegions:
1384 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1385 if badPixelCount > 0:
1386 self.log.
info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1388 if self.config.doInterpolate:
1389 self.log.
info(
"Interpolating masked pixels.")
1390 isrFunctions.interpolateFromMask(
1391 maskedImage=ccdExposure.getMaskedImage(),
1392 fwhm=self.config.fwhm,
1393 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1394 maskNameList=
list(self.config.maskListToInterpolate)
1399 if self.config.doMeasureBackground:
1400 self.log.
info(
"Measuring background level.")
1403 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1405 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1407 afwMath.MEDIAN | afwMath.STDEVCLIP)
1408 self.metadata.
set(
"ISR BACKGROUND {} MEDIAN".
format(amp.getName()),
1409 qaStats.getValue(afwMath.MEDIAN))
1410 self.metadata.
set(
"ISR BACKGROUND {} STDEV".
format(amp.getName()),
1411 qaStats.getValue(afwMath.STDEVCLIP))
1412 self.log.
debug(
" Background stats for amplifer %s: %f +/- %f",
1413 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1414 qaStats.getValue(afwMath.STDEVCLIP))
1416 self.
debugView(ccdExposure,
"postISRCCD")
1418 return pipeBase.Struct(
1419 exposure=ccdExposure,
1421 flattenedThumb=flattenedThumb,
1423 preInterpolatedExposure=preInterpExp,
1424 outputExposure=ccdExposure,
1425 outputOssThumbnail=ossThumb,
1426 outputFlattenedThumbnail=flattenedThumb,
1429 @pipeBase.timeMethod
1431 """Perform instrument signature removal on a ButlerDataRef of a Sensor. 1433 This method contains the `CmdLineTask` interface to the ISR 1434 processing. All IO is handled here, freeing the `run()` method 1435 to manage only pixel-level calculations. The steps performed 1437 - Read in necessary detrending/isr/calibration data. 1438 - Process raw exposure in `run()`. 1439 - Persist the ISR-corrected exposure as "postISRCCD" if 1440 config.doWrite=True. 1444 sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 1445 DataRef of the detector data to be processed 1449 result : `lsst.pipe.base.Struct` 1450 Result struct with component: 1451 - ``exposure`` : `afw.image.Exposure` 1452 The fully ISR corrected exposure. 1457 Raised if a configuration option is set to True, but the 1458 required calibration data does not exist. 1461 self.log.
info(
"Performing ISR on sensor %s.", sensorRef.dataId)
1463 ccdExposure = sensorRef.get(self.config.datasetType)
1465 camera = sensorRef.get(
"camera")
1466 isrData = self.
readIsrData(sensorRef, ccdExposure)
1468 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1470 if self.config.doWrite:
1471 sensorRef.put(result.exposure,
"postISRCCD")
1472 if result.preInterpolatedExposure
is not None:
1473 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1474 if result.ossThumb
is not None:
1475 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1476 if result.flattenedThumb
is not None:
1477 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1482 """!Retrieve a calibration dataset for removing instrument signature. 1487 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1488 DataRef of the detector data to find calibration datasets 1491 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1493 If True, disable butler proxies to enable error handling 1494 within this routine. 1498 exposure : `lsst.afw.image.Exposure` 1499 Requested calibration frame. 1504 Raised if no matching calibration frame can be found. 1507 exp = dataRef.get(datasetType, immediate=immediate)
1508 except Exception
as exc1:
1509 if not self.config.fallbackFilterName:
1510 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1512 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1513 except Exception
as exc2:
1514 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1515 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1516 self.log.
warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
1518 if self.config.doAssembleIsrExposures:
1519 exp = self.assembleCcd.assembleCcd(exp)
1523 """Ensure that the data returned by Butler is a fully constructed exposure. 1525 ISR requires exposure-level image data for historical reasons, so if we did 1526 not recieve that from Butler, construct it from what we have, modifying the 1531 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1532 `lsst.afw.image.ImageF` 1533 The input data structure obtained from Butler. 1534 camera : `lsst.afw.cameraGeom.camera` 1535 The camera associated with the image. Used to find the appropriate 1538 The detector this exposure should match. 1542 inputExp : `lsst.afw.image.Exposure` 1543 The re-constructed exposure, with appropriate detector parameters. 1548 Raised if the input data cannot be used to construct an exposure. 1550 if isinstance(inputExp, afwImage.DecoratedImageU):
1552 elif isinstance(inputExp, afwImage.ImageF):
1554 elif isinstance(inputExp, afwImage.MaskedImageF):
1558 elif inputExp
is None:
1562 raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
1565 if inputExp.getDetector()
is None:
1566 inputExp.setDetector(camera[detectorNum])
1571 """Convert exposure image from uint16 to float. 1573 If the exposure does not need to be converted, the input is 1574 immediately returned. For exposures that are converted to use 1575 floating point pixels, the variance is set to unity and the 1580 exposure : `lsst.afw.image.Exposure` 1581 The raw exposure to be converted. 1585 newexposure : `lsst.afw.image.Exposure` 1586 The input ``exposure``, converted to floating point pixels. 1591 Raised if the exposure type cannot be converted to float. 1594 if isinstance(exposure, afwImage.ExposureF):
1596 self.log.
debug(
"Exposure already of type float.")
1598 if not hasattr(exposure,
"convertF"):
1599 raise RuntimeError(
"Unable to convert exposure (%s) to float." %
type(exposure))
1601 newexposure = exposure.convertF()
1602 newexposure.variance[:] = 1
1603 newexposure.mask[:] = 0x0
1608 """Identify bad amplifiers, saturated and suspect pixels. 1612 ccdExposure : `lsst.afw.image.Exposure` 1613 Input exposure to be masked. 1614 amp : `lsst.afw.table.AmpInfoCatalog` 1615 Catalog of parameters defining the amplifier on this 1617 defects : `lsst.meas.algorithms.Defects` 1618 List of defects. Used to determine if the entire 1624 If this is true, the entire amplifier area is covered by 1625 defects and unusable. 1628 maskedImage = ccdExposure.getMaskedImage()
1634 if defects
is not None:
1635 badAmp = bool(sum([v.getBBox().
contains(amp.getBBox())
for v
in defects]))
1640 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1642 maskView = dataView.getMask()
1643 maskView |= maskView.getPlaneBitMask(
"BAD")
1650 if self.config.doSaturation
and not badAmp:
1651 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1652 if self.config.doSuspect
and not badAmp:
1653 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1654 if math.isfinite(self.config.saturation):
1655 limits.update({self.config.saturatedMaskName: self.config.saturation})
1657 for maskName, maskThreshold
in limits.items():
1658 if not math.isnan(maskThreshold):
1659 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1660 isrFunctions.makeThresholdMask(
1661 maskedImage=dataView,
1662 threshold=maskThreshold,
1668 maskView =
afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1670 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1671 self.config.suspectMaskName])
1672 if numpy.all(maskView.getArray() & maskVal > 0):
1674 maskView |= maskView.getPlaneBitMask(
"BAD")
1679 """Apply overscan correction in place. 1681 This method does initial pixel rejection of the overscan 1682 region. The overscan can also be optionally segmented to 1683 allow for discontinuous overscan responses to be fit 1684 separately. The actual overscan subtraction is performed by 1685 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1686 which is called here after the amplifier is preprocessed. 1690 ccdExposure : `lsst.afw.image.Exposure` 1691 Exposure to have overscan correction performed. 1692 amp : `lsst.afw.table.AmpInfoCatalog` 1693 The amplifier to consider while correcting the overscan. 1697 overscanResults : `lsst.pipe.base.Struct` 1698 Result struct with components: 1699 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1700 Value or fit subtracted from the amplifier image data. 1701 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1702 Value or fit subtracted from the overscan image data. 1703 - ``overscanImage`` : `lsst.afw.image.Image` 1704 Image of the overscan region with the overscan 1705 correction applied. This quantity is used to estimate 1706 the amplifier read noise empirically. 1711 Raised if the ``amp`` does not contain raw pixel information. 1715 lsst.ip.isr.isrFunctions.overscanCorrection 1717 if not amp.getHasRawInfo():
1718 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1720 if amp.getRawHorizontalOverscanBBox().isEmpty():
1721 self.log.
info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1725 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1728 dataBBox = amp.getRawDataBBox()
1729 oscanBBox = amp.getRawHorizontalOverscanBBox()
1733 prescanBBox = amp.getRawPrescanBBox()
1734 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1735 dx0 += self.config.overscanNumLeadingColumnsToSkip
1736 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1738 dx0 += self.config.overscanNumTrailingColumnsToSkip
1739 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1745 if ((self.config.overscanBiasJump
and 1746 self.config.overscanBiasJumpLocation)
and 1747 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1748 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1749 self.config.overscanBiasJumpDevices)):
1750 if amp.getReadoutCorner()
in (ReadoutCorner.LL, ReadoutCorner.LR):
1751 yLower = self.config.overscanBiasJumpLocation
1752 yUpper = dataBBox.getHeight() - yLower
1754 yUpper = self.config.overscanBiasJumpLocation
1755 yLower = dataBBox.getHeight() - yUpper
1774 oscanBBox.getHeight())))
1777 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1778 ampImage = ccdExposure.maskedImage[imageBBox]
1779 overscanImage = ccdExposure.maskedImage[overscanBBox]
1781 overscanArray = overscanImage.image.array
1782 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1783 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1784 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1787 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1789 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1790 overscanImage=overscanImage,
1791 fitType=self.config.overscanFitType,
1792 order=self.config.overscanOrder,
1793 collapseRej=self.config.overscanNumSigmaClip,
1794 statControl=statControl,
1795 overscanIsInt=self.config.overscanIsInt
1799 levelStat = afwMath.MEDIAN
1800 sigmaStat = afwMath.STDEVCLIP
1803 self.config.qa.flatness.nIter)
1804 metadata = ccdExposure.getMetadata()
1805 ampNum = amp.getName()
1806 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1807 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1808 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1811 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1812 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1814 return overscanResults
1817 """Set the variance plane using the amplifier gain and read noise 1819 The read noise is calculated from the ``overscanImage`` if the 1820 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1821 the value from the amplifier data is used. 1825 ampExposure : `lsst.afw.image.Exposure` 1826 Exposure to process. 1827 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1828 Amplifier detector data. 1829 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1830 Image of overscan, required only for empirical read noise. 1834 lsst.ip.isr.isrFunctions.updateVariance 1836 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1837 gain = amp.getGain()
1839 if math.isnan(gain):
1841 self.log.
warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1844 self.log.
warn(
"Gain for amp %s == %g <= 0; setting to %f.",
1845 amp.getName(), gain, patchedGain)
1848 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1849 self.log.
info(
"Overscan is none for EmpiricalReadNoise.")
1851 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1853 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1855 self.log.
info(
"Calculated empirical read noise for amp %s: %f.",
1856 amp.getName(), readNoise)
1858 readNoise = amp.getReadNoise()
1860 isrFunctions.updateVariance(
1861 maskedImage=ampExposure.getMaskedImage(),
1863 readNoise=readNoise,
1867 """!Apply dark correction in place. 1871 exposure : `lsst.afw.image.Exposure` 1872 Exposure to process. 1873 darkExposure : `lsst.afw.image.Exposure` 1874 Dark exposure of the same size as ``exposure``. 1875 invert : `Bool`, optional 1876 If True, re-add the dark to an already corrected image. 1881 Raised if either ``exposure`` or ``darkExposure`` do not 1882 have their dark time defined. 1886 lsst.ip.isr.isrFunctions.darkCorrection 1888 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1889 if math.isnan(expScale):
1890 raise RuntimeError(
"Exposure darktime is NAN.")
1891 if darkExposure.getInfo().getVisitInfo()
is not None:
1892 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1896 self.log.
warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1899 if math.isnan(darkScale):
1900 raise RuntimeError(
"Dark calib darktime is NAN.")
1901 isrFunctions.darkCorrection(
1902 maskedImage=exposure.getMaskedImage(),
1903 darkMaskedImage=darkExposure.getMaskedImage(),
1905 darkScale=darkScale,
1907 trimToFit=self.config.doTrimToMatchCalib
1911 """!Check if linearization is needed for the detector cameraGeom. 1913 Checks config.doLinearize and the linearity type of the first 1918 detector : `lsst.afw.cameraGeom.Detector` 1919 Detector to get linearity type from. 1923 doLinearize : `Bool` 1924 If True, linearization should be performed. 1926 return self.config.doLinearize
and \
1927 detector.getAmplifiers()[0].getLinearityType() != NullLinearityType
1930 """!Apply flat correction in place. 1934 exposure : `lsst.afw.image.Exposure` 1935 Exposure to process. 1936 flatExposure : `lsst.afw.image.Exposure` 1937 Flat exposure of the same size as ``exposure``. 1938 invert : `Bool`, optional 1939 If True, unflatten an already flattened image. 1943 lsst.ip.isr.isrFunctions.flatCorrection 1945 isrFunctions.flatCorrection(
1946 maskedImage=exposure.getMaskedImage(),
1947 flatMaskedImage=flatExposure.getMaskedImage(),
1948 scalingType=self.config.flatScalingType,
1949 userScale=self.config.flatUserScale,
1951 trimToFit=self.config.doTrimToMatchCalib
1955 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1959 exposure : `lsst.afw.image.Exposure` 1960 Exposure to process. Only the amplifier DataSec is processed. 1961 amp : `lsst.afw.table.AmpInfoCatalog` 1962 Amplifier detector data. 1966 lsst.ip.isr.isrFunctions.makeThresholdMask 1968 if not math.isnan(amp.getSaturation()):
1969 maskedImage = exposure.getMaskedImage()
1970 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1971 isrFunctions.makeThresholdMask(
1972 maskedImage=dataView,
1973 threshold=amp.getSaturation(),
1975 maskName=self.config.saturatedMaskName,
1979 """!Interpolate over saturated pixels, in place. 1981 This method should be called after `saturationDetection`, to 1982 ensure that the saturated pixels have been identified in the 1983 SAT mask. It should also be called after `assembleCcd`, since 1984 saturated regions may cross amplifier boundaries. 1988 exposure : `lsst.afw.image.Exposure` 1989 Exposure to process. 1993 lsst.ip.isr.isrTask.saturationDetection 1994 lsst.ip.isr.isrFunctions.interpolateFromMask 1996 isrFunctions.interpolateFromMask(
1997 maskedImage=exposure.getMaskedImage(),
1998 fwhm=self.config.fwhm,
1999 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2000 maskNameList=
list(self.config.saturatedMaskName),
2004 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2008 exposure : `lsst.afw.image.Exposure` 2009 Exposure to process. Only the amplifier DataSec is processed. 2010 amp : `lsst.afw.table.AmpInfoCatalog` 2011 Amplifier detector data. 2015 lsst.ip.isr.isrFunctions.makeThresholdMask 2019 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2020 This is intended to indicate pixels that may be affected by unknown systematics; 2021 for example if non-linearity corrections above a certain level are unstable 2022 then that would be a useful value for suspectLevel. A value of `nan` indicates 2023 that no such level exists and no pixels are to be masked as suspicious. 2025 suspectLevel = amp.getSuspectLevel()
2026 if math.isnan(suspectLevel):
2029 maskedImage = exposure.getMaskedImage()
2030 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2031 isrFunctions.makeThresholdMask(
2032 maskedImage=dataView,
2033 threshold=suspectLevel,
2035 maskName=self.config.suspectMaskName,
2039 """!Mask defects using mask plane "BAD", in place. 2043 exposure : `lsst.afw.image.Exposure` 2044 Exposure to process. 2045 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2046 `lsst.afw.image.DefectBase`. 2047 List of defects to mask. 2051 Call this after CCD assembly, since defects may cross amplifier boundaries. 2053 maskedImage = exposure.getMaskedImage()
2054 if not isinstance(defectBaseList, Defects):
2056 defectList =
Defects(defectBaseList)
2058 defectList = defectBaseList
2059 defectList.maskPixels(maskedImage, maskName=
"BAD")
2061 def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT"):
2062 """!Mask edge pixels with applicable mask plane. 2066 exposure : `lsst.afw.image.Exposure` 2067 Exposure to process. 2068 numEdgePixels : `int`, optional 2069 Number of edge pixels to mask. 2070 maskPlane : `str`, optional 2071 Mask plane name to use. 2073 maskedImage = exposure.getMaskedImage()
2074 maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
2076 if numEdgePixels > 0:
2077 goodBBox = maskedImage.getBBox()
2079 goodBBox.grow(-numEdgePixels)
2081 SourceDetectionTask.setEdgeBits(
2088 """Mask and interpolate defects using mask plane "BAD", in place. 2092 exposure : `lsst.afw.image.Exposure` 2093 Exposure to process. 2094 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2095 `lsst.afw.image.DefectBase`. 2096 List of defects to mask and interpolate. 2100 lsst.ip.isr.isrTask.maskDefect() 2103 self.
maskEdges(exposure, numEdgePixels=self.config.numEdgeSuspect,
2104 maskPlane=
"SUSPECT")
2105 isrFunctions.interpolateFromMask(
2106 maskedImage=exposure.getMaskedImage(),
2107 fwhm=self.config.fwhm,
2108 growSaturatedFootprints=0,
2109 maskNameList=[
"BAD"],
2113 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2117 exposure : `lsst.afw.image.Exposure` 2118 Exposure to process. 2122 We mask over all NaNs, including those that are masked with 2123 other bits (because those may or may not be interpolated over 2124 later, and we want to remove all NaNs). Despite this 2125 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2126 the historical name. 2128 maskedImage = exposure.getMaskedImage()
2131 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2132 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2133 numNans =
maskNans(maskedImage, maskVal)
2134 self.metadata.
set(
"NUMNANS", numNans)
2136 self.log.
warn(
"There were %d unmasked NaNs.", numNans)
2139 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2143 exposure : `lsst.afw.image.Exposure` 2144 Exposure to process. 2148 lsst.ip.isr.isrTask.maskNan() 2151 isrFunctions.interpolateFromMask(
2152 maskedImage=exposure.getMaskedImage(),
2153 fwhm=self.config.fwhm,
2154 growSaturatedFootprints=0,
2155 maskNameList=[
"UNMASKEDNAN"],
2159 """Measure the image background in subgrids, for quality control purposes. 2163 exposure : `lsst.afw.image.Exposure` 2164 Exposure to process. 2165 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2166 Configuration object containing parameters on which background 2167 statistics and subgrids to use. 2169 if IsrQaConfig
is not None:
2171 IsrQaConfig.flatness.nIter)
2172 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2173 statsControl.setAndMask(maskVal)
2174 maskedImage = exposure.getMaskedImage()
2176 skyLevel = stats.getValue(afwMath.MEDIAN)
2177 skySigma = stats.getValue(afwMath.STDEVCLIP)
2178 self.log.
info(
"Flattened sky level: %f +/- %f.", skyLevel, skySigma)
2179 metadata = exposure.getMetadata()
2180 metadata.set(
'SKYLEVEL', skyLevel)
2181 metadata.set(
'SKYSIGMA', skySigma)
2184 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2185 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2186 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2187 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2188 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2189 skyLevels = numpy.zeros((nX, nY))
2192 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2194 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2196 xLLC = xc - meshXHalf
2197 yLLC = yc - meshYHalf
2198 xURC = xc + meshXHalf - 1
2199 yURC = yc + meshYHalf - 1
2202 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2206 good = numpy.where(numpy.isfinite(skyLevels))
2207 skyMedian = numpy.median(skyLevels[good])
2208 flatness = (skyLevels[good] - skyMedian) / skyMedian
2209 flatness_rms = numpy.std(flatness)
2210 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2212 self.log.
info(
"Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
2213 self.log.
info(
"Sky flatness in %dx%d grids - pp: %f rms: %f.",
2214 nX, nY, flatness_pp, flatness_rms)
2216 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2217 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2218 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2219 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2220 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2223 """Set an approximate magnitude zero point for the exposure. 2227 exposure : `lsst.afw.image.Exposure` 2228 Exposure to process. 2231 if filterName
in self.config.fluxMag0T1:
2232 fluxMag0 = self.config.fluxMag0T1[filterName]
2234 self.log.
warn(
"No rough magnitude zero point set for filter %s.", filterName)
2235 fluxMag0 = self.config.defaultFluxMag0T1
2237 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2239 self.log.
warn(
"Non-positive exposure time; skipping rough zero point.")
2242 self.log.
info(
"Setting rough magnitude zero point: %f", 2.5*math.log10(fluxMag0*expTime))
2246 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2250 ccdExposure : `lsst.afw.image.Exposure` 2251 Exposure to process. 2252 fpPolygon : `lsst.afw.geom.Polygon` 2253 Polygon in focal plane coordinates. 2256 ccd = ccdExposure.getDetector()
2257 fpCorners = ccd.getCorners(FOCAL_PLANE)
2258 ccdPolygon =
Polygon(fpCorners)
2261 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2264 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2265 validPolygon =
Polygon(ccdPoints)
2266 ccdExposure.getInfo().setValidPolygon(validPolygon)
2270 """Context manager that applies and removes flats and darks, 2271 if the task is configured to apply them. 2275 exp : `lsst.afw.image.Exposure` 2276 Exposure to process. 2277 flat : `lsst.afw.image.Exposure` 2278 Flat exposure the same size as ``exp``. 2279 dark : `lsst.afw.image.Exposure`, optional 2280 Dark exposure the same size as ``exp``. 2284 exp : `lsst.afw.image.Exposure` 2285 The flat and dark corrected exposure. 2287 if self.config.doDark
and dark
is not None:
2289 if self.config.doFlat:
2294 if self.config.doFlat:
2296 if self.config.doDark
and dark
is not None:
2300 """Utility function to examine ISR exposure at different stages. 2304 exposure : `lsst.afw.image.Exposure` 2307 State of processing to view. 2312 display.scale(
'asinh',
'zscale')
2313 display.mtv(exposure)
2314 prompt =
"Press Enter to continue [c]... " 2316 ans = input(prompt).lower()
2317 if ans
in (
"",
"c",):
2322 """A Detector-like object that supports returning gain and saturation level 2324 This is used when the input exposure does not have a detector. 2328 exposure : `lsst.afw.image.Exposure` 2329 Exposure to generate a fake amplifier for. 2330 config : `lsst.ip.isr.isrTaskConfig` 2331 Configuration to apply to the fake amplifier. 2335 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2337 self.
_gain = config.gain
2367 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2371 """Task to wrap the default IsrTask to allow it to be retargeted. 2373 The standard IsrTask can be called directly from a command line 2374 program, but doing so removes the ability of the task to be 2375 retargeted. As most cameras override some set of the IsrTask 2376 methods, this would remove those data-specific methods in the 2377 output post-ISR images. This wrapping class fixes the issue, 2378 allowing identical post-ISR images to be generated by both the 2379 processCcd and isrTask code. 2381 ConfigClass = RunIsrConfig
2382 _DefaultName =
"runIsr" 2386 self.makeSubtask(
"isr")
2392 dataRef : `lsst.daf.persistence.ButlerDataRef` 2393 data reference of the detector data to be processed 2397 result : `pipeBase.Struct` 2398 Result struct with component: 2400 - exposure : `lsst.afw.image.Exposure` 2401 Post-ISR processed exposure.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
std::shared_ptr< PhotoCalib > makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err)
Construct a PhotoCalib from the deprecated Calib-style instFluxMag0/instFluxMag0Err values...
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)
A class to contain the data, WCS, and other information needed to describe an image of the sky...
def readIsrData(self, dataRef, rawExposure)
Retrieve necessary frames for instrument signature removal.
def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT")
Mask edge pixels with applicable mask plane.
def runQuantum(self, butlerQC, inputRefs, outputRefs)
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)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
daf::base::PropertySet * set
def roughZeroPoint(self, exposure)
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
def maskAndInterpolateDefects(self, exposure, defectBaseList)
def getRawHorizontalOverscanBBox(self)
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
def maskNan(self, exposure)
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
def getSuspectLevel(self)
Pass parameters to a Statistics object.
Represent a 2-dimensional array of bitmask pixels.
def maskDefect(self, exposure, defectBaseList)
Mask defects using mask plane "BAD", in place.
def overscanCorrection(self, ccdExposure, amp)
def convertIntToFloat(self, exposure)
Holds an integer identifier for an LSST filter.
def flatCorrection(self, exposure, flatExposure, invert=False)
Apply flat correction in place.
def getDebugFrame(debugDisplay, name)
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 __init__(self, config=None)
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.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def updateVariance(self, ampExposure, amp, overscanImage=None)
def maskAndInterpolateNan(self, exposure)
An integer coordinate rectangle.
daf::base::PropertyList * list
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)