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 fringes = cT.PrerequisiteInput(
95 doc=
"Input fringe calibration.",
96 storageClass=
"ExposureF",
97 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
99 strayLightData = cT.PrerequisiteInput(
101 doc=
"Input stray light calibration.",
102 storageClass=
"StrayLightData",
103 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
105 bfKernel = cT.PrerequisiteInput(
107 doc=
"Input brighter-fatter kernel.",
108 storageClass=
"NumpyArray",
109 dimensions=[
"instrument",
"calibration_label"],
111 defects = cT.PrerequisiteInput(
113 doc=
"Input defect tables.",
114 storageClass=
"DefectsList",
115 dimensions=[
"instrument",
"calibration_label",
"detector"],
117 opticsTransmission = cT.PrerequisiteInput(
118 name=
"transmission_optics",
119 storageClass=
"TransmissionCurve",
120 doc=
"Transmission curve due to the optics.",
121 dimensions=[
"instrument",
"calibration_label"],
123 filterTransmission = cT.PrerequisiteInput(
124 name=
"transmission_filter",
125 storageClass=
"TransmissionCurve",
126 doc=
"Transmission curve due to the filter.",
127 dimensions=[
"instrument",
"physical_filter",
"calibration_label"],
129 sensorTransmission = cT.PrerequisiteInput(
130 name=
"transmission_sensor",
131 storageClass=
"TransmissionCurve",
132 doc=
"Transmission curve due to the sensor.",
133 dimensions=[
"instrument",
"calibration_label",
"detector"],
135 atmosphereTransmission = cT.PrerequisiteInput(
136 name=
"transmission_atmosphere",
137 storageClass=
"TransmissionCurve",
138 doc=
"Transmission curve due to the atmosphere.",
139 dimensions=[
"instrument"],
141 illumMaskedImage = cT.PrerequisiteInput(
143 doc=
"Input illumination correction.",
144 storageClass=
"MaskedImageF",
145 dimensions=[
"instrument",
"physical_filter",
"calibration_label",
"detector"],
148 outputExposure = cT.Output(
150 doc=
"Output ISR processed exposure.",
151 storageClass=
"ExposureF",
152 dimensions=[
"instrument",
"visit",
"detector"],
154 preInterpExposure = cT.Output(
155 name=
'preInterpISRCCD',
156 doc=
"Output ISR processed exposure, with pixels left uninterpolated.",
157 storageClass=
"ExposureF",
158 dimensions=[
"instrument",
"visit",
"detector"],
160 outputOssThumbnail = cT.Output(
162 doc=
"Output Overscan-subtracted thumbnail image.",
163 storageClass=
"Thumbnail",
164 dimensions=[
"instrument",
"visit",
"detector"],
166 outputFlattenedThumbnail = cT.Output(
167 name=
"FlattenedThumb",
168 doc=
"Output flat-corrected thumbnail image.",
169 storageClass=
"Thumbnail",
170 dimensions=[
"instrument",
"visit",
"detector"],
176 if config.doBias
is not True:
177 self.prerequisiteInputs.discard(
"bias")
178 if config.doLinearize
is not True:
179 self.prerequisiteInputs.discard(
"linearizer")
180 if config.doCrosstalk
is not True:
181 self.prerequisiteInputs.discard(
"crosstalkSources")
182 if config.doBrighterFatter
is not True:
183 self.prerequisiteInputs.discard(
"bfKernel")
184 if config.doDefect
is not True:
185 self.prerequisiteInputs.discard(
"defects")
186 if config.doDark
is not True:
187 self.prerequisiteInputs.discard(
"dark")
188 if config.doFlat
is not True:
189 self.prerequisiteInputs.discard(
"flat")
190 if config.doAttachTransmissionCurve
is not True:
191 self.prerequisiteInputs.discard(
"opticsTransmission")
192 self.prerequisiteInputs.discard(
"filterTransmission")
193 self.prerequisiteInputs.discard(
"sensorTransmission")
194 self.prerequisiteInputs.discard(
"atmosphereTransmission")
195 if config.doUseOpticsTransmission
is not True:
196 self.prerequisiteInputs.discard(
"opticsTransmission")
197 if config.doUseFilterTransmission
is not True:
198 self.prerequisiteInputs.discard(
"filterTransmission")
199 if config.doUseSensorTransmission
is not True:
200 self.prerequisiteInputs.discard(
"sensorTransmission")
201 if config.doUseAtmosphereTransmission
is not True:
202 self.prerequisiteInputs.discard(
"atmosphereTransmission")
203 if config.doIlluminationCorrection
is not True:
204 self.prerequisiteInputs.discard(
"illumMaskedImage")
206 if config.doWrite
is not True:
207 self.outputs.discard(
"outputExposure")
208 self.outputs.discard(
"preInterpExposure")
209 self.outputs.discard(
"outputFlattenedThumbnail")
210 self.outputs.discard(
"outputOssThumbnail")
211 if config.doSaveInterpPixels
is not True:
212 self.outputs.discard(
"preInterpExposure")
213 if config.qa.doThumbnailOss
is not True:
214 self.outputs.discard(
"outputOssThumbnail")
215 if config.qa.doThumbnailFlattened
is not True:
216 self.outputs.discard(
"outputFlattenedThumbnail")
220 pipelineConnections=IsrTaskConnections):
221 """Configuration parameters for IsrTask. 223 Items are grouped in the order in which they are executed by the task. 225 datasetType = pexConfig.Field(
227 doc=
"Dataset type for input data; users will typically leave this alone, " 228 "but camera-specific ISR tasks will override it",
232 fallbackFilterName = pexConfig.Field(
234 doc=
"Fallback default filter name for calibrations.",
237 expectWcs = pexConfig.Field(
240 doc=
"Expect input science images to have a WCS (set False for e.g. spectrographs)." 242 fwhm = pexConfig.Field(
244 doc=
"FWHM of PSF in arcseconds.",
247 qa = pexConfig.ConfigField(
249 doc=
"QA related configuration options.",
253 doConvertIntToFloat = pexConfig.Field(
255 doc=
"Convert integer raw images to floating point values?",
260 doSaturation = pexConfig.Field(
262 doc=
"Mask saturated pixels? NB: this is totally independent of the" 263 " interpolation option - this is ONLY setting the bits in the mask." 264 " To have them interpolated make sure doSaturationInterpolation=True",
267 saturatedMaskName = pexConfig.Field(
269 doc=
"Name of mask plane to use in saturation detection and interpolation",
272 saturation = pexConfig.Field(
274 doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
275 default=float(
"NaN"),
277 growSaturationFootprintSize = pexConfig.Field(
279 doc=
"Number of pixels by which to grow the saturation footprints",
284 doSuspect = pexConfig.Field(
286 doc=
"Mask suspect pixels?",
289 suspectMaskName = pexConfig.Field(
291 doc=
"Name of mask plane to use for suspect pixels",
294 numEdgeSuspect = pexConfig.Field(
296 doc=
"Number of edge pixels to be flagged as untrustworthy.",
301 doSetBadRegions = pexConfig.Field(
303 doc=
"Should we set the level of all BAD patches of the chip to the chip's average value?",
306 badStatistic = pexConfig.ChoiceField(
308 doc=
"How to estimate the average value for BAD regions.",
311 "MEANCLIP":
"Correct using the (clipped) mean of good data",
312 "MEDIAN":
"Correct using the median of the good data",
317 doOverscan = pexConfig.Field(
319 doc=
"Do overscan subtraction?",
322 overscanFitType = pexConfig.ChoiceField(
324 doc=
"The method for fitting the overscan bias level.",
327 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
328 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
329 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
330 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
331 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
332 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
333 "MEAN":
"Correct using the mean of the overscan region",
334 "MEANCLIP":
"Correct using a clipped mean of the overscan region",
335 "MEDIAN":
"Correct using the median of the overscan region",
338 overscanOrder = pexConfig.Field(
340 doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
341 "or number of spline knots if overscan fit type is a spline."),
344 overscanNumSigmaClip = pexConfig.Field(
346 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
349 overscanIsInt = pexConfig.Field(
351 doc=
"Treat overscan as an integer image for purposes of overscan.FitType=MEDIAN",
354 overscanNumLeadingColumnsToSkip = pexConfig.Field(
356 doc=
"Number of columns to skip in overscan, i.e. those closest to amplifier",
359 overscanNumTrailingColumnsToSkip = pexConfig.Field(
361 doc=
"Number of columns to skip in overscan, i.e. those farthest from amplifier",
364 overscanMaxDev = pexConfig.Field(
366 doc=
"Maximum deviation from the median for overscan",
367 default=1000.0, check=
lambda x: x > 0
369 overscanBiasJump = pexConfig.Field(
371 doc=
"Fit the overscan in a piecewise-fashion to correct for bias jumps?",
374 overscanBiasJumpKeyword = pexConfig.Field(
376 doc=
"Header keyword containing information about devices.",
377 default=
"NO_SUCH_KEY",
379 overscanBiasJumpDevices = pexConfig.ListField(
381 doc=
"List of devices that need piecewise overscan correction.",
384 overscanBiasJumpLocation = pexConfig.Field(
386 doc=
"Location of bias jump along y-axis.",
391 doAssembleCcd = pexConfig.Field(
394 doc=
"Assemble amp-level exposures into a ccd-level exposure?" 396 assembleCcd = pexConfig.ConfigurableField(
397 target=AssembleCcdTask,
398 doc=
"CCD assembly task",
402 doAssembleIsrExposures = pexConfig.Field(
405 doc=
"Assemble amp-level calibration exposures into ccd-level exposure?" 407 doTrimToMatchCalib = pexConfig.Field(
410 doc=
"Trim raw data to match calibration bounding boxes?" 414 doBias = pexConfig.Field(
416 doc=
"Apply bias frame correction?",
419 biasDataProductName = pexConfig.Field(
421 doc=
"Name of the bias data product",
426 doVariance = pexConfig.Field(
428 doc=
"Calculate variance?",
431 gain = pexConfig.Field(
433 doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
434 default=float(
"NaN"),
436 readNoise = pexConfig.Field(
438 doc=
"The read noise to use if no Detector is present in the Exposure",
441 doEmpiricalReadNoise = pexConfig.Field(
444 doc=
"Calculate empirical read noise instead of value from AmpInfo data?" 448 doLinearize = pexConfig.Field(
450 doc=
"Correct for nonlinearity of the detector's response?",
455 doCrosstalk = pexConfig.Field(
457 doc=
"Apply intra-CCD crosstalk correction?",
460 doCrosstalkBeforeAssemble = pexConfig.Field(
462 doc=
"Apply crosstalk correction before CCD assembly, and before trimming?",
465 crosstalk = pexConfig.ConfigurableField(
466 target=CrosstalkTask,
467 doc=
"Intra-CCD crosstalk correction",
471 doDefect = pexConfig.Field(
473 doc=
"Apply correction for CCD defects, e.g. hot pixels?",
476 doNanMasking = pexConfig.Field(
478 doc=
"Mask NAN pixels?",
481 doWidenSaturationTrails = pexConfig.Field(
483 doc=
"Widen bleed trails based on their width?",
488 doBrighterFatter = pexConfig.Field(
491 doc=
"Apply the brighter fatter correction" 493 brighterFatterLevel = pexConfig.ChoiceField(
496 doc=
"The level at which to correct for brighter-fatter.",
498 "AMP":
"Every amplifier treated separately.",
499 "DETECTOR":
"One kernel per detector",
502 brighterFatterMaxIter = pexConfig.Field(
505 doc=
"Maximum number of iterations for the brighter fatter correction" 507 brighterFatterThreshold = pexConfig.Field(
510 doc=
"Threshold used to stop iterating the brighter fatter correction. It is the " 511 " absolute value of the difference between the current corrected image and the one" 512 " from the previous iteration summed over all the pixels." 514 brighterFatterApplyGain = pexConfig.Field(
517 doc=
"Should the gain be applied when applying the brighter fatter correction?" 521 doDark = pexConfig.Field(
523 doc=
"Apply dark frame correction?",
526 darkDataProductName = pexConfig.Field(
528 doc=
"Name of the dark data product",
533 doStrayLight = pexConfig.Field(
535 doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
538 strayLight = pexConfig.ConfigurableField(
539 target=StrayLightTask,
540 doc=
"y-band stray light correction" 544 doFlat = pexConfig.Field(
546 doc=
"Apply flat field correction?",
549 flatDataProductName = pexConfig.Field(
551 doc=
"Name of the flat data product",
554 flatScalingType = pexConfig.ChoiceField(
556 doc=
"The method for scaling the flat on the fly.",
559 "USER":
"Scale by flatUserScale",
560 "MEAN":
"Scale by the inverse of the mean",
561 "MEDIAN":
"Scale by the inverse of the median",
564 flatUserScale = pexConfig.Field(
566 doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
569 doTweakFlat = pexConfig.Field(
571 doc=
"Tweak flats to match observed amplifier ratios?",
576 doApplyGains = pexConfig.Field(
578 doc=
"Correct the amplifiers for their gains instead of applying flat correction",
581 normalizeGains = pexConfig.Field(
583 doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
588 doFringe = pexConfig.Field(
590 doc=
"Apply fringe correction?",
593 fringe = pexConfig.ConfigurableField(
595 doc=
"Fringe subtraction task",
597 fringeAfterFlat = pexConfig.Field(
599 doc=
"Do fringe subtraction after flat-fielding?",
604 doAddDistortionModel = pexConfig.Field(
606 doc=
"Apply a distortion model based on camera geometry to the WCS?",
608 deprecated=(
"Camera geometry is incorporated when reading the raw files." 609 " This option no longer is used, and will be removed after v19.")
613 doMeasureBackground = pexConfig.Field(
615 doc=
"Measure the background level on the reduced image?",
620 doCameraSpecificMasking = pexConfig.Field(
622 doc=
"Mask camera-specific bad regions?",
625 masking = pexConfig.ConfigurableField(
632 doInterpolate = pexConfig.Field(
634 doc=
"Interpolate masked pixels?",
637 doSaturationInterpolation = pexConfig.Field(
639 doc=
"Perform interpolation over pixels masked as saturated?" 640 " NB: This is independent of doSaturation; if that is False this plane" 641 " will likely be blank, resulting in a no-op here.",
644 doNanInterpolation = pexConfig.Field(
646 doc=
"Perform interpolation over pixels masked as NaN?" 647 " NB: This is independent of doNanMasking; if that is False this plane" 648 " will likely be blank, resulting in a no-op here.",
651 doNanInterpAfterFlat = pexConfig.Field(
653 doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 654 "also have to interpolate them before flat-fielding."),
657 maskListToInterpolate = pexConfig.ListField(
659 doc=
"List of mask planes that should be interpolated.",
660 default=[
'SAT',
'BAD',
'UNMASKEDNAN'],
662 doSaveInterpPixels = pexConfig.Field(
664 doc=
"Save a copy of the pre-interpolated pixel values?",
669 fluxMag0T1 = pexConfig.DictField(
672 doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
673 default=dict((f, pow(10.0, 0.4*m))
for f, m
in ((
"Unknown", 28.0),
676 defaultFluxMag0T1 = pexConfig.Field(
678 doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
679 default=pow(10.0, 0.4*28.0)
683 doVignette = pexConfig.Field(
685 doc=
"Apply vignetting parameters?",
688 vignette = pexConfig.ConfigurableField(
690 doc=
"Vignetting task.",
694 doAttachTransmissionCurve = pexConfig.Field(
697 doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 699 doUseOpticsTransmission = pexConfig.Field(
702 doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 704 doUseFilterTransmission = pexConfig.Field(
707 doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 709 doUseSensorTransmission = pexConfig.Field(
712 doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 714 doUseAtmosphereTransmission = pexConfig.Field(
717 doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 721 doIlluminationCorrection = pexConfig.Field(
724 doc=
"Perform illumination correction?" 726 illuminationCorrectionDataProductName = pexConfig.Field(
728 doc=
"Name of the illumination correction data product.",
731 illumScale = pexConfig.Field(
733 doc=
"Scale factor for the illumination correction.",
736 illumFilters = pexConfig.ListField(
739 doc=
"Only perform illumination correction for these filters." 743 doWrite = pexConfig.Field(
745 doc=
"Persist postISRCCD?",
752 raise ValueError(
"You may not specify both doFlat and doApplyGains")
754 self.config.maskListToInterpolate.append(
"SAT")
756 self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
759 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
760 """Apply common instrument signature correction algorithms to a raw frame. 762 The process for correcting imaging data is very similar from 763 camera to camera. This task provides a vanilla implementation of 764 doing these corrections, including the ability to turn certain 765 corrections off if they are not needed. The inputs to the primary 766 method, `run()`, are a raw exposure to be corrected and the 767 calibration data products. The raw input is a single chip sized 768 mosaic of all amps including overscans and other non-science 769 pixels. The method `runDataRef()` identifies and defines the 770 calibration data products, and is intended for use by a 771 `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 772 `daf.persistence.butlerSubset.ButlerDataRef`. This task may be 773 subclassed for different camera, although the most camera specific 774 methods have been split into subtasks that can be redirected 777 The __init__ method sets up the subtasks for ISR processing, using 778 the defaults from `lsst.ip.isr`. 783 Positional arguments passed to the Task constructor. None used at this time. 784 kwargs : `dict`, optional 785 Keyword arguments passed on to the Task constructor. None used at this time. 787 ConfigClass = IsrTaskConfig
792 self.makeSubtask(
"assembleCcd")
793 self.makeSubtask(
"crosstalk")
794 self.makeSubtask(
"strayLight")
795 self.makeSubtask(
"fringe")
796 self.makeSubtask(
"masking")
797 self.makeSubtask(
"vignette")
800 inputs = butlerQC.get(inputRefs)
803 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
804 except Exception
as e:
805 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
808 inputs[
'isGen3'] =
True 810 if self.config.doLinearize
is True:
811 if 'linearizer' not in inputs:
812 detector = inputs[
'ccdExposure'].getDetector()
813 linearityName = detector.getAmplifiers()[0].getLinearityType()
814 inputs[
'linearizer'] = linearize.getLinearityTypeByName(linearityName)()
816 if self.config.doDefect
is True:
817 if "defects" in inputs
and inputs[
'defects']
is not None:
820 if not isinstance(inputs[
"defects"], Defects):
821 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
833 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
834 expId = inputs[
'ccdExposure'].
getInfo().getVisitInfo().getExposureId()
835 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
837 assembler=self.assembleCcd
838 if self.config.doAssembleIsrExposures
else None)
840 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
842 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
843 if 'strayLightData' not in inputs:
844 inputs[
'strayLightData'] =
None 846 outputs = self.
run(**inputs)
847 butlerQC.put(outputs, outputRefs)
850 """!Retrieve necessary frames for instrument signature removal. 852 Pre-fetching all required ISR data products limits the IO 853 required by the ISR. Any conflict between the calibration data 854 available and that needed for ISR is also detected prior to 855 doing processing, allowing it to fail quickly. 859 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 860 Butler reference of the detector data to be processed 861 rawExposure : `afw.image.Exposure` 862 The raw exposure that will later be corrected with the 863 retrieved calibration data; should not be modified in this 868 result : `lsst.pipe.base.Struct` 869 Result struct with components (which may be `None`): 870 - ``bias``: bias calibration frame (`afw.image.Exposure`) 871 - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 872 - ``crosstalkSources``: list of possible crosstalk sources (`list`) 873 - ``dark``: dark calibration frame (`afw.image.Exposure`) 874 - ``flat``: flat calibration frame (`afw.image.Exposure`) 875 - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 876 - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 877 - ``fringes``: `lsst.pipe.base.Struct` with components: 878 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 879 - ``seed``: random seed derived from the ccdExposureId for random 880 number generator (`uint32`). 881 - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 882 A ``TransmissionCurve`` that represents the throughput of the optics, 883 to be evaluated in focal-plane coordinates. 884 - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 885 A ``TransmissionCurve`` that represents the throughput of the filter 886 itself, to be evaluated in focal-plane coordinates. 887 - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 888 A ``TransmissionCurve`` that represents the throughput of the sensor 889 itself, to be evaluated in post-assembly trimmed detector coordinates. 890 - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 891 A ``TransmissionCurve`` that represents the throughput of the 892 atmosphere, assumed to be spatially constant. 893 - ``strayLightData`` : `object` 894 An opaque object containing calibration information for 895 stray-light correction. If `None`, no correction will be 897 - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 901 NotImplementedError : 902 Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 904 ccd = rawExposure.getDetector()
905 filterName =
afwImage.Filter(rawExposure.getFilter().getId()).getName()
906 rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")
907 biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
908 if self.config.doBias
else None)
910 linearizer = (dataRef.get(
"linearizer", immediate=
True)
912 crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
913 if self.config.doCrosstalk
else None)
914 darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
915 if self.config.doDark
else None)
916 flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName)
917 if self.config.doFlat
else None)
919 brighterFatterKernel =
None 920 brighterFatterGains =
None 921 if self.config.doBrighterFatter
is True:
926 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
927 brighterFatterGains = brighterFatterKernel.gain
928 self.log.
info(
"New style bright-fatter kernel (brighterFatterKernel) loaded")
931 brighterFatterKernel = dataRef.get(
"bfKernel")
932 self.log.
info(
"Old style bright-fatter kernel (np.array) loaded")
934 brighterFatterKernel =
None 935 if brighterFatterKernel
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
938 if self.config.brighterFatterLevel ==
'DETECTOR':
939 if brighterFatterKernel.detectorKernel:
940 brighterFatterKernel = brighterFatterKernel.detectorKernel[ccd.getId()]
941 elif brighterFatterKernel.detectorKernelFromAmpKernels:
942 brighterFatterKernel = brighterFatterKernel.detectorKernelFromAmpKernels[ccd.getId()]
944 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
947 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
949 defectList = (dataRef.get(
"defects")
950 if self.config.doDefect
else None)
951 fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
952 if self.config.doAssembleIsrExposures
else None)
953 if self.config.doFringe
and self.fringe.checkFilter(rawExposure)
954 else pipeBase.Struct(fringes=
None))
956 if self.config.doAttachTransmissionCurve:
957 opticsTransmission = (dataRef.get(
"transmission_optics")
958 if self.config.doUseOpticsTransmission
else None)
959 filterTransmission = (dataRef.get(
"transmission_filter")
960 if self.config.doUseFilterTransmission
else None)
961 sensorTransmission = (dataRef.get(
"transmission_sensor")
962 if self.config.doUseSensorTransmission
else None)
963 atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
964 if self.config.doUseAtmosphereTransmission
else None)
966 opticsTransmission =
None 967 filterTransmission =
None 968 sensorTransmission =
None 969 atmosphereTransmission =
None 971 if self.config.doStrayLight:
972 strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
974 strayLightData =
None 977 self.config.illuminationCorrectionDataProductName).getMaskedImage()
978 if (self.config.doIlluminationCorrection
and 979 filterName
in self.config.illumFilters)
983 return pipeBase.Struct(bias=biasExposure,
984 linearizer=linearizer,
985 crosstalkSources=crosstalkSources,
988 bfKernel=brighterFatterKernel,
989 bfGains=brighterFatterGains,
991 fringes=fringeStruct,
992 opticsTransmission=opticsTransmission,
993 filterTransmission=filterTransmission,
994 sensorTransmission=sensorTransmission,
995 atmosphereTransmission=atmosphereTransmission,
996 strayLightData=strayLightData,
997 illumMaskedImage=illumMaskedImage
1000 @pipeBase.timeMethod
1001 def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
1002 dark=None, flat=None, bfKernel=None, bfGains=None, defects=None,
1003 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1004 sensorTransmission=
None, atmosphereTransmission=
None,
1005 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1008 """!Perform instrument signature removal on an exposure. 1010 Steps included in the ISR processing, in order performed, are: 1011 - saturation and suspect pixel masking 1012 - overscan subtraction 1013 - CCD assembly of individual amplifiers 1015 - variance image construction 1016 - linearization of non-linear response 1018 - brighter-fatter correction 1021 - stray light subtraction 1023 - masking of known defects and camera specific features 1024 - vignette calculation 1025 - appending transmission curve and distortion model 1029 ccdExposure : `lsst.afw.image.Exposure` 1030 The raw exposure that is to be run through ISR. The 1031 exposure is modified by this method. 1032 camera : `lsst.afw.cameraGeom.Camera`, optional 1033 The camera geometry for this exposure. Used to select the 1034 distortion model appropriate for this data. 1035 bias : `lsst.afw.image.Exposure`, optional 1036 Bias calibration frame. 1037 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 1038 Functor for linearization. 1039 crosstalkSources : `list`, optional 1040 List of possible crosstalk sources. 1041 dark : `lsst.afw.image.Exposure`, optional 1042 Dark calibration frame. 1043 flat : `lsst.afw.image.Exposure`, optional 1044 Flat calibration frame. 1045 bfKernel : `numpy.ndarray`, optional 1046 Brighter-fatter kernel. 1047 bfGains : `dict` of `float`, optional 1048 Gains used to override the detector's nominal gains for the 1049 brighter-fatter correction. A dict keyed by amplifier name for 1050 the detector in question. 1051 defects : `lsst.meas.algorithms.Defects`, optional 1053 fringes : `lsst.pipe.base.Struct`, optional 1054 Struct containing the fringe correction data, with 1056 - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 1057 - ``seed``: random seed derived from the ccdExposureId for random 1058 number generator (`uint32`) 1059 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 1060 A ``TransmissionCurve`` that represents the throughput of the optics, 1061 to be evaluated in focal-plane coordinates. 1062 filterTransmission : `lsst.afw.image.TransmissionCurve` 1063 A ``TransmissionCurve`` that represents the throughput of the filter 1064 itself, to be evaluated in focal-plane coordinates. 1065 sensorTransmission : `lsst.afw.image.TransmissionCurve` 1066 A ``TransmissionCurve`` that represents the throughput of the sensor 1067 itself, to be evaluated in post-assembly trimmed detector coordinates. 1068 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 1069 A ``TransmissionCurve`` that represents the throughput of the 1070 atmosphere, assumed to be spatially constant. 1071 detectorNum : `int`, optional 1072 The integer number for the detector to process. 1073 isGen3 : bool, optional 1074 Flag this call to run() as using the Gen3 butler environment. 1075 strayLightData : `object`, optional 1076 Opaque object containing calibration information for stray-light 1077 correction. If `None`, no correction will be performed. 1078 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 1079 Illumination correction image. 1083 result : `lsst.pipe.base.Struct` 1084 Result struct with component: 1085 - ``exposure`` : `afw.image.Exposure` 1086 The fully ISR corrected exposure. 1087 - ``outputExposure`` : `afw.image.Exposure` 1088 An alias for `exposure` 1089 - ``ossThumb`` : `numpy.ndarray` 1090 Thumbnail image of the exposure after overscan subtraction. 1091 - ``flattenedThumb`` : `numpy.ndarray` 1092 Thumbnail image of the exposure after flat-field correction. 1097 Raised if a configuration option is set to True, but the 1098 required calibration data has not been specified. 1102 The current processed exposure can be viewed by setting the 1103 appropriate lsstDebug entries in the `debug.display` 1104 dictionary. The names of these entries correspond to some of 1105 the IsrTaskConfig Boolean options, with the value denoting the 1106 frame to use. The exposure is shown inside the matching 1107 option check and after the processing of that step has 1108 finished. The steps with debug points are: 1119 In addition, setting the "postISRCCD" entry displays the 1120 exposure after all ISR processing has finished. 1128 if detectorNum
is None:
1129 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
1131 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1136 if isinstance(ccdExposure, ButlerDataRef):
1139 ccd = ccdExposure.getDetector()
1140 filterName =
afwImage.Filter(ccdExposure.getFilter().getId()).getName()
1143 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd." 1144 ccd = [
FakeAmp(ccdExposure, self.config)]
1147 if self.config.doBias
and bias
is None:
1148 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1150 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1151 if self.config.doBrighterFatter
and bfKernel
is None:
1152 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1153 if self.config.doDark
and dark
is None:
1154 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1155 if self.config.doFlat
and flat
is None:
1156 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1157 if self.config.doDefect
and defects
is None:
1158 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1159 if (self.config.doFringe
and filterName
in self.fringe.config.filters
and 1160 fringes.fringes
is None):
1165 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1166 if (self.config.doIlluminationCorrection
and filterName
in self.config.illumFilters
and 1167 illumMaskedImage
is None):
1168 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1171 if self.config.doConvertIntToFloat:
1172 self.log.
info(
"Converting exposure to floating point values.")
1179 if ccdExposure.getBBox().
contains(amp.getBBox()):
1183 if self.config.doOverscan
and not badAmp:
1186 self.log.
debug(
"Corrected overscan for amplifier %s.", amp.getName())
1187 if overscanResults
is not None and \
1188 self.config.qa
is not None and self.config.qa.saveStats
is True:
1189 if isinstance(overscanResults.overscanFit, float):
1190 qaMedian = overscanResults.overscanFit
1191 qaStdev = float(
"NaN")
1194 afwMath.MEDIAN | afwMath.STDEVCLIP)
1195 qaMedian = qaStats.getValue(afwMath.MEDIAN)
1196 qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1198 self.metadata.
set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
1199 self.metadata.
set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
1200 self.log.
debug(
" Overscan stats for amplifer %s: %f +/- %f",
1201 amp.getName(), qaMedian, qaStdev)
1202 ccdExposure.getMetadata().
set(
'OVERSCAN',
"Overscan corrected")
1205 self.log.
warn(
"Amplifier %s is bad.", amp.getName())
1206 overscanResults =
None 1208 overscans.append(overscanResults
if overscanResults
is not None else None)
1210 self.log.
info(
"Skipped OSCAN for %s.", amp.getName())
1212 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1213 self.log.
info(
"Applying crosstalk correction.")
1214 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
1215 self.
debugView(ccdExposure,
"doCrosstalk")
1217 if self.config.doAssembleCcd:
1218 self.log.
info(
"Assembling CCD from amplifiers.")
1219 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1221 if self.config.expectWcs
and not ccdExposure.getWcs():
1222 self.log.
warn(
"No WCS found in input exposure.")
1223 self.
debugView(ccdExposure,
"doAssembleCcd")
1226 if self.config.qa.doThumbnailOss:
1227 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1229 if self.config.doBias:
1230 self.log.
info(
"Applying bias correction.")
1231 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1232 trimToFit=self.config.doTrimToMatchCalib)
1235 if self.config.doVariance:
1236 for amp, overscanResults
in zip(ccd, overscans):
1237 if ccdExposure.getBBox().
contains(amp.getBBox()):
1238 self.log.
debug(
"Constructing variance map for amplifer %s.", amp.getName())
1239 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1240 if overscanResults
is not None:
1242 overscanImage=overscanResults.overscanImage)
1246 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1248 afwMath.MEDIAN | afwMath.STDEVCLIP)
1249 self.metadata.
set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
1250 qaStats.getValue(afwMath.MEDIAN))
1251 self.metadata.
set(f
"ISR VARIANCE {amp.getName()} STDEV",
1252 qaStats.getValue(afwMath.STDEVCLIP))
1253 self.log.
debug(
" Variance stats for amplifer %s: %f +/- %f.",
1254 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1255 qaStats.getValue(afwMath.STDEVCLIP))
1258 self.log.
info(
"Applying linearizer.")
1259 linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1261 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1262 self.log.
info(
"Applying crosstalk correction.")
1263 self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
1264 self.
debugView(ccdExposure,
"doCrosstalk")
1268 if self.config.doDefect:
1269 self.log.
info(
"Masking defects.")
1272 if self.config.numEdgeSuspect > 0:
1273 self.log.
info(
"Masking edges as SUSPECT.")
1274 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1275 maskPlane=
"SUSPECT")
1277 if self.config.doNanMasking:
1278 self.log.
info(
"Masking NAN value pixels.")
1281 if self.config.doWidenSaturationTrails:
1282 self.log.
info(
"Widening saturation trails.")
1283 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1285 if self.config.doCameraSpecificMasking:
1286 self.log.
info(
"Masking regions for camera specific reasons.")
1287 self.masking.
run(ccdExposure)
1289 if self.config.doBrighterFatter:
1298 interpExp = ccdExposure.clone()
1300 isrFunctions.interpolateFromMask(
1301 maskedImage=interpExp.getMaskedImage(),
1302 fwhm=self.config.fwhm,
1303 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1304 maskNameList=self.config.maskListToInterpolate
1306 bfExp = interpExp.clone()
1308 self.log.
info(
"Applying brighter fatter correction.")
1309 bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
1310 self.config.brighterFatterMaxIter,
1311 self.config.brighterFatterThreshold,
1312 self.config.brighterFatterApplyGain,
1314 if bfResults[1] == self.config.brighterFatterMaxIter:
1315 self.log.
warn(
"Brighter fatter correction did not converge, final difference %f.",
1318 self.log.
info(
"Finished brighter fatter correction in %d iterations.",
1320 image = ccdExposure.getMaskedImage().getImage()
1321 bfCorr = bfExp.getMaskedImage().getImage()
1322 bfCorr -= interpExp.getMaskedImage().getImage()
1331 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1333 self.log.
warn(
"Ensuring image edges are masked as SUSPECT to the brighter-fatter kernel size.")
1335 self.
debugView(ccdExposure,
"doBrighterFatter")
1337 if self.config.doDark:
1338 self.log.
info(
"Applying dark correction.")
1342 if self.config.doFringe
and not self.config.fringeAfterFlat:
1343 self.log.
info(
"Applying fringe correction before flat.")
1344 self.fringe.
run(ccdExposure, **fringes.getDict())
1347 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1348 self.log.
info(
"Checking strayLight correction.")
1349 self.strayLight.
run(ccdExposure, strayLightData)
1350 self.
debugView(ccdExposure,
"doStrayLight")
1352 if self.config.doFlat:
1353 self.log.
info(
"Applying flat correction.")
1357 if self.config.doApplyGains:
1358 self.log.
info(
"Applying gain correction instead of flat.")
1359 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1361 if self.config.doFringe
and self.config.fringeAfterFlat:
1362 self.log.
info(
"Applying fringe correction after flat.")
1363 self.fringe.
run(ccdExposure, **fringes.getDict())
1365 if self.config.doVignette:
1366 self.log.
info(
"Constructing Vignette polygon.")
1369 if self.config.vignette.doWriteVignettePolygon:
1372 if self.config.doAttachTransmissionCurve:
1373 self.log.
info(
"Adding transmission curves.")
1374 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1375 filterTransmission=filterTransmission,
1376 sensorTransmission=sensorTransmission,
1377 atmosphereTransmission=atmosphereTransmission)
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())
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 isrData = self.
readIsrData(sensorRef, ccdExposure)
1491 result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
1493 if self.config.doWrite:
1494 sensorRef.put(result.exposure,
"postISRCCD")
1495 if result.preInterpolatedExposure
is not None:
1496 sensorRef.put(result.preInterpolatedExposure,
"postISRCCD_uninterpolated")
1497 if result.ossThumb
is not None:
1498 isrQa.writeThumbnail(sensorRef, result.ossThumb,
"ossThumb")
1499 if result.flattenedThumb
is not None:
1500 isrQa.writeThumbnail(sensorRef, result.flattenedThumb,
"flattenedThumb")
1505 """!Retrieve a calibration dataset for removing instrument signature. 1510 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 1511 DataRef of the detector data to find calibration datasets 1514 Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 1516 If True, disable butler proxies to enable error handling 1517 within this routine. 1521 exposure : `lsst.afw.image.Exposure` 1522 Requested calibration frame. 1527 Raised if no matching calibration frame can be found. 1530 exp = dataRef.get(datasetType, immediate=immediate)
1531 except Exception
as exc1:
1532 if not self.config.fallbackFilterName:
1533 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
1535 exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1536 except Exception
as exc2:
1537 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
1538 (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1539 self.log.
warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
1541 if self.config.doAssembleIsrExposures:
1542 exp = self.assembleCcd.assembleCcd(exp)
1546 """Ensure that the data returned by Butler is a fully constructed exposure. 1548 ISR requires exposure-level image data for historical reasons, so if we did 1549 not recieve that from Butler, construct it from what we have, modifying the 1554 inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 1555 `lsst.afw.image.ImageF` 1556 The input data structure obtained from Butler. 1557 camera : `lsst.afw.cameraGeom.camera` 1558 The camera associated with the image. Used to find the appropriate 1561 The detector this exposure should match. 1565 inputExp : `lsst.afw.image.Exposure` 1566 The re-constructed exposure, with appropriate detector parameters. 1571 Raised if the input data cannot be used to construct an exposure. 1573 if isinstance(inputExp, afwImage.DecoratedImageU):
1575 elif isinstance(inputExp, afwImage.ImageF):
1577 elif isinstance(inputExp, afwImage.MaskedImageF):
1581 elif inputExp
is None:
1585 raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
1588 if inputExp.getDetector()
is None:
1589 inputExp.setDetector(camera[detectorNum])
1594 """Convert exposure image from uint16 to float. 1596 If the exposure does not need to be converted, the input is 1597 immediately returned. For exposures that are converted to use 1598 floating point pixels, the variance is set to unity and the 1603 exposure : `lsst.afw.image.Exposure` 1604 The raw exposure to be converted. 1608 newexposure : `lsst.afw.image.Exposure` 1609 The input ``exposure``, converted to floating point pixels. 1614 Raised if the exposure type cannot be converted to float. 1617 if isinstance(exposure, afwImage.ExposureF):
1619 self.log.
debug(
"Exposure already of type float.")
1621 if not hasattr(exposure,
"convertF"):
1622 raise RuntimeError(
"Unable to convert exposure (%s) to float." %
type(exposure))
1624 newexposure = exposure.convertF()
1625 newexposure.variance[:] = 1
1626 newexposure.mask[:] = 0x0
1631 """Identify bad amplifiers, saturated and suspect pixels. 1635 ccdExposure : `lsst.afw.image.Exposure` 1636 Input exposure to be masked. 1637 amp : `lsst.afw.table.AmpInfoCatalog` 1638 Catalog of parameters defining the amplifier on this 1640 defects : `lsst.meas.algorithms.Defects` 1641 List of defects. Used to determine if the entire 1647 If this is true, the entire amplifier area is covered by 1648 defects and unusable. 1651 maskedImage = ccdExposure.getMaskedImage()
1657 if defects
is not None:
1658 badAmp = bool(sum([v.getBBox().
contains(amp.getBBox())
for v
in defects]))
1663 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1665 maskView = dataView.getMask()
1666 maskView |= maskView.getPlaneBitMask(
"BAD")
1673 if self.config.doSaturation
and not badAmp:
1674 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1675 if self.config.doSuspect
and not badAmp:
1676 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1677 if math.isfinite(self.config.saturation):
1678 limits.update({self.config.saturatedMaskName: self.config.saturation})
1680 for maskName, maskThreshold
in limits.items():
1681 if not math.isnan(maskThreshold):
1682 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1683 isrFunctions.makeThresholdMask(
1684 maskedImage=dataView,
1685 threshold=maskThreshold,
1691 maskView =
afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1693 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1694 self.config.suspectMaskName])
1695 if numpy.all(maskView.getArray() & maskVal > 0):
1697 maskView |= maskView.getPlaneBitMask(
"BAD")
1702 """Apply overscan correction in place. 1704 This method does initial pixel rejection of the overscan 1705 region. The overscan can also be optionally segmented to 1706 allow for discontinuous overscan responses to be fit 1707 separately. The actual overscan subtraction is performed by 1708 the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 1709 which is called here after the amplifier is preprocessed. 1713 ccdExposure : `lsst.afw.image.Exposure` 1714 Exposure to have overscan correction performed. 1715 amp : `lsst.afw.table.AmpInfoCatalog` 1716 The amplifier to consider while correcting the overscan. 1720 overscanResults : `lsst.pipe.base.Struct` 1721 Result struct with components: 1722 - ``imageFit`` : scalar or `lsst.afw.image.Image` 1723 Value or fit subtracted from the amplifier image data. 1724 - ``overscanFit`` : scalar or `lsst.afw.image.Image` 1725 Value or fit subtracted from the overscan image data. 1726 - ``overscanImage`` : `lsst.afw.image.Image` 1727 Image of the overscan region with the overscan 1728 correction applied. This quantity is used to estimate 1729 the amplifier read noise empirically. 1734 Raised if the ``amp`` does not contain raw pixel information. 1738 lsst.ip.isr.isrFunctions.overscanCorrection 1740 if not amp.getHasRawInfo():
1741 raise RuntimeError(
"This method must be executed on an amp with raw information.")
1743 if amp.getRawHorizontalOverscanBBox().isEmpty():
1744 self.log.
info(
"ISR_OSCAN: No overscan region. Not performing overscan correction.")
1748 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1751 dataBBox = amp.getRawDataBBox()
1752 oscanBBox = amp.getRawHorizontalOverscanBBox()
1756 prescanBBox = amp.getRawPrescanBBox()
1757 if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):
1758 dx0 += self.config.overscanNumLeadingColumnsToSkip
1759 dx1 -= self.config.overscanNumTrailingColumnsToSkip
1761 dx0 += self.config.overscanNumTrailingColumnsToSkip
1762 dx1 -= self.config.overscanNumLeadingColumnsToSkip
1768 if ((self.config.overscanBiasJump
and 1769 self.config.overscanBiasJumpLocation)
and 1770 (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword)
and 1771 ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword)
in 1772 self.config.overscanBiasJumpDevices)):
1773 if amp.getReadoutCorner()
in (ReadoutCorner.LL, ReadoutCorner.LR):
1774 yLower = self.config.overscanBiasJumpLocation
1775 yUpper = dataBBox.getHeight() - yLower
1777 yUpper = self.config.overscanBiasJumpLocation
1778 yLower = dataBBox.getHeight() - yUpper
1797 oscanBBox.getHeight())))
1800 for imageBBox, overscanBBox
in zip(imageBBoxes, overscanBBoxes):
1801 ampImage = ccdExposure.maskedImage[imageBBox]
1802 overscanImage = ccdExposure.maskedImage[overscanBBox]
1804 overscanArray = overscanImage.image.array
1805 median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1806 bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1807 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
1810 statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
1812 overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1813 overscanImage=overscanImage,
1814 fitType=self.config.overscanFitType,
1815 order=self.config.overscanOrder,
1816 collapseRej=self.config.overscanNumSigmaClip,
1817 statControl=statControl,
1818 overscanIsInt=self.config.overscanIsInt
1822 levelStat = afwMath.MEDIAN
1823 sigmaStat = afwMath.STDEVCLIP
1826 self.config.qa.flatness.nIter)
1827 metadata = ccdExposure.getMetadata()
1828 ampNum = amp.getName()
1829 if self.config.overscanFitType
in (
"MEDIAN",
"MEAN",
"MEANCLIP"):
1830 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1831 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1834 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1835 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1837 return overscanResults
1840 """Set the variance plane using the amplifier gain and read noise 1842 The read noise is calculated from the ``overscanImage`` if the 1843 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 1844 the value from the amplifier data is used. 1848 ampExposure : `lsst.afw.image.Exposure` 1849 Exposure to process. 1850 amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 1851 Amplifier detector data. 1852 overscanImage : `lsst.afw.image.MaskedImage`, optional. 1853 Image of overscan, required only for empirical read noise. 1857 lsst.ip.isr.isrFunctions.updateVariance 1859 maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1860 gain = amp.getGain()
1862 if math.isnan(gain):
1864 self.log.
warn(
"Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1867 self.log.
warn(
"Gain for amp %s == %g <= 0; setting to %f.",
1868 amp.getName(), gain, patchedGain)
1871 if self.config.doEmpiricalReadNoise
and overscanImage
is None:
1872 self.log.
info(
"Overscan is none for EmpiricalReadNoise.")
1874 if self.config.doEmpiricalReadNoise
and overscanImage
is not None:
1876 stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1878 self.log.
info(
"Calculated empirical read noise for amp %s: %f.",
1879 amp.getName(), readNoise)
1881 readNoise = amp.getReadNoise()
1883 isrFunctions.updateVariance(
1884 maskedImage=ampExposure.getMaskedImage(),
1886 readNoise=readNoise,
1890 """!Apply dark correction in place. 1894 exposure : `lsst.afw.image.Exposure` 1895 Exposure to process. 1896 darkExposure : `lsst.afw.image.Exposure` 1897 Dark exposure of the same size as ``exposure``. 1898 invert : `Bool`, optional 1899 If True, re-add the dark to an already corrected image. 1904 Raised if either ``exposure`` or ``darkExposure`` do not 1905 have their dark time defined. 1909 lsst.ip.isr.isrFunctions.darkCorrection 1911 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1912 if math.isnan(expScale):
1913 raise RuntimeError(
"Exposure darktime is NAN.")
1914 if darkExposure.getInfo().getVisitInfo()
is not None:
1915 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1919 self.log.
warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
1922 if math.isnan(darkScale):
1923 raise RuntimeError(
"Dark calib darktime is NAN.")
1924 isrFunctions.darkCorrection(
1925 maskedImage=exposure.getMaskedImage(),
1926 darkMaskedImage=darkExposure.getMaskedImage(),
1928 darkScale=darkScale,
1930 trimToFit=self.config.doTrimToMatchCalib
1934 """!Check if linearization is needed for the detector cameraGeom. 1936 Checks config.doLinearize and the linearity type of the first 1941 detector : `lsst.afw.cameraGeom.Detector` 1942 Detector to get linearity type from. 1946 doLinearize : `Bool` 1947 If True, linearization should be performed. 1949 return self.config.doLinearize
and \
1950 detector.getAmplifiers()[0].getLinearityType() != NullLinearityType
1953 """!Apply flat correction in place. 1957 exposure : `lsst.afw.image.Exposure` 1958 Exposure to process. 1959 flatExposure : `lsst.afw.image.Exposure` 1960 Flat exposure of the same size as ``exposure``. 1961 invert : `Bool`, optional 1962 If True, unflatten an already flattened image. 1966 lsst.ip.isr.isrFunctions.flatCorrection 1968 isrFunctions.flatCorrection(
1969 maskedImage=exposure.getMaskedImage(),
1970 flatMaskedImage=flatExposure.getMaskedImage(),
1971 scalingType=self.config.flatScalingType,
1972 userScale=self.config.flatUserScale,
1974 trimToFit=self.config.doTrimToMatchCalib
1978 """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 1982 exposure : `lsst.afw.image.Exposure` 1983 Exposure to process. Only the amplifier DataSec is processed. 1984 amp : `lsst.afw.table.AmpInfoCatalog` 1985 Amplifier detector data. 1989 lsst.ip.isr.isrFunctions.makeThresholdMask 1991 if not math.isnan(amp.getSaturation()):
1992 maskedImage = exposure.getMaskedImage()
1993 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1994 isrFunctions.makeThresholdMask(
1995 maskedImage=dataView,
1996 threshold=amp.getSaturation(),
1998 maskName=self.config.saturatedMaskName,
2002 """!Interpolate over saturated pixels, in place. 2004 This method should be called after `saturationDetection`, to 2005 ensure that the saturated pixels have been identified in the 2006 SAT mask. It should also be called after `assembleCcd`, since 2007 saturated regions may cross amplifier boundaries. 2011 exposure : `lsst.afw.image.Exposure` 2012 Exposure to process. 2016 lsst.ip.isr.isrTask.saturationDetection 2017 lsst.ip.isr.isrFunctions.interpolateFromMask 2019 isrFunctions.interpolateFromMask(
2020 maskedImage=exposure.getMaskedImage(),
2021 fwhm=self.config.fwhm,
2022 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2023 maskNameList=
list(self.config.saturatedMaskName),
2027 """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 2031 exposure : `lsst.afw.image.Exposure` 2032 Exposure to process. Only the amplifier DataSec is processed. 2033 amp : `lsst.afw.table.AmpInfoCatalog` 2034 Amplifier detector data. 2038 lsst.ip.isr.isrFunctions.makeThresholdMask 2042 Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 2043 This is intended to indicate pixels that may be affected by unknown systematics; 2044 for example if non-linearity corrections above a certain level are unstable 2045 then that would be a useful value for suspectLevel. A value of `nan` indicates 2046 that no such level exists and no pixels are to be masked as suspicious. 2048 suspectLevel = amp.getSuspectLevel()
2049 if math.isnan(suspectLevel):
2052 maskedImage = exposure.getMaskedImage()
2053 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2054 isrFunctions.makeThresholdMask(
2055 maskedImage=dataView,
2056 threshold=suspectLevel,
2058 maskName=self.config.suspectMaskName,
2062 """!Mask defects using mask plane "BAD", in place. 2066 exposure : `lsst.afw.image.Exposure` 2067 Exposure to process. 2068 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2069 `lsst.afw.image.DefectBase`. 2070 List of defects to mask. 2074 Call this after CCD assembly, since defects may cross amplifier boundaries. 2076 maskedImage = exposure.getMaskedImage()
2077 if not isinstance(defectBaseList, Defects):
2079 defectList =
Defects(defectBaseList)
2081 defectList = defectBaseList
2082 defectList.maskPixels(maskedImage, maskName=
"BAD")
2084 def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT"):
2085 """!Mask edge pixels with applicable mask plane. 2089 exposure : `lsst.afw.image.Exposure` 2090 Exposure to process. 2091 numEdgePixels : `int`, optional 2092 Number of edge pixels to mask. 2093 maskPlane : `str`, optional 2094 Mask plane name to use. 2096 maskedImage = exposure.getMaskedImage()
2097 maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
2099 if numEdgePixels > 0:
2100 goodBBox = maskedImage.getBBox()
2102 goodBBox.grow(-numEdgePixels)
2104 SourceDetectionTask.setEdgeBits(
2111 """Mask and interpolate defects using mask plane "BAD", in place. 2115 exposure : `lsst.afw.image.Exposure` 2116 Exposure to process. 2117 defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 2118 `lsst.afw.image.DefectBase`. 2119 List of defects to mask and interpolate. 2123 lsst.ip.isr.isrTask.maskDefect() 2126 self.
maskEdges(exposure, numEdgePixels=self.config.numEdgeSuspect,
2127 maskPlane=
"SUSPECT")
2128 isrFunctions.interpolateFromMask(
2129 maskedImage=exposure.getMaskedImage(),
2130 fwhm=self.config.fwhm,
2131 growSaturatedFootprints=0,
2132 maskNameList=[
"BAD"],
2136 """Mask NaNs using mask plane "UNMASKEDNAN", in place. 2140 exposure : `lsst.afw.image.Exposure` 2141 Exposure to process. 2145 We mask over all NaNs, including those that are masked with 2146 other bits (because those may or may not be interpolated over 2147 later, and we want to remove all NaNs). Despite this 2148 behaviour, the "UNMASKEDNAN" mask plane is used to preserve 2149 the historical name. 2151 maskedImage = exposure.getMaskedImage()
2154 maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
2155 maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
2156 numNans =
maskNans(maskedImage, maskVal)
2157 self.metadata.
set(
"NUMNANS", numNans)
2159 self.log.
warn(
"There were %d unmasked NaNs.", numNans)
2162 """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 2166 exposure : `lsst.afw.image.Exposure` 2167 Exposure to process. 2171 lsst.ip.isr.isrTask.maskNan() 2174 isrFunctions.interpolateFromMask(
2175 maskedImage=exposure.getMaskedImage(),
2176 fwhm=self.config.fwhm,
2177 growSaturatedFootprints=0,
2178 maskNameList=[
"UNMASKEDNAN"],
2182 """Measure the image background in subgrids, for quality control purposes. 2186 exposure : `lsst.afw.image.Exposure` 2187 Exposure to process. 2188 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 2189 Configuration object containing parameters on which background 2190 statistics and subgrids to use. 2192 if IsrQaConfig
is not None:
2194 IsrQaConfig.flatness.nIter)
2195 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD",
"SAT",
"DETECTED"])
2196 statsControl.setAndMask(maskVal)
2197 maskedImage = exposure.getMaskedImage()
2199 skyLevel = stats.getValue(afwMath.MEDIAN)
2200 skySigma = stats.getValue(afwMath.STDEVCLIP)
2201 self.log.
info(
"Flattened sky level: %f +/- %f.", skyLevel, skySigma)
2202 metadata = exposure.getMetadata()
2203 metadata.set(
'SKYLEVEL', skyLevel)
2204 metadata.set(
'SKYSIGMA', skySigma)
2207 stat = afwMath.MEANCLIP
if IsrQaConfig.flatness.doClip
else afwMath.MEAN
2208 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2209 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2210 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2211 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2212 skyLevels = numpy.zeros((nX, nY))
2215 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2217 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2219 xLLC = xc - meshXHalf
2220 yLLC = yc - meshYHalf
2221 xURC = xc + meshXHalf - 1
2222 yURC = yc + meshYHalf - 1
2225 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2229 good = numpy.where(numpy.isfinite(skyLevels))
2230 skyMedian = numpy.median(skyLevels[good])
2231 flatness = (skyLevels[good] - skyMedian) / skyMedian
2232 flatness_rms = numpy.std(flatness)
2233 flatness_pp = flatness.max() - flatness.min()
if len(flatness) > 0
else numpy.nan
2235 self.log.
info(
"Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
2236 self.log.
info(
"Sky flatness in %dx%d grids - pp: %f rms: %f.",
2237 nX, nY, flatness_pp, flatness_rms)
2239 metadata.set(
'FLATNESS_PP', float(flatness_pp))
2240 metadata.set(
'FLATNESS_RMS', float(flatness_rms))
2241 metadata.set(
'FLATNESS_NGRIDS',
'%dx%d' % (nX, nY))
2242 metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2243 metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2246 """Set an approximate magnitude zero point for the exposure. 2250 exposure : `lsst.afw.image.Exposure` 2251 Exposure to process. 2254 if filterName
in self.config.fluxMag0T1:
2255 fluxMag0 = self.config.fluxMag0T1[filterName]
2257 self.log.
warn(
"No rough magnitude zero point set for filter %s.", filterName)
2258 fluxMag0 = self.config.defaultFluxMag0T1
2260 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2262 self.log.
warn(
"Non-positive exposure time; skipping rough zero point.")
2265 self.log.
info(
"Setting rough magnitude zero point: %f", 2.5*math.log10(fluxMag0*expTime))
2269 """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 2273 ccdExposure : `lsst.afw.image.Exposure` 2274 Exposure to process. 2275 fpPolygon : `lsst.afw.geom.Polygon` 2276 Polygon in focal plane coordinates. 2279 ccd = ccdExposure.getDetector()
2280 fpCorners = ccd.getCorners(FOCAL_PLANE)
2281 ccdPolygon =
Polygon(fpCorners)
2284 intersect = ccdPolygon.intersectionSingle(fpPolygon)
2287 ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2288 validPolygon =
Polygon(ccdPoints)
2289 ccdExposure.getInfo().setValidPolygon(validPolygon)
2293 """Context manager that applies and removes flats and darks, 2294 if the task is configured to apply them. 2298 exp : `lsst.afw.image.Exposure` 2299 Exposure to process. 2300 flat : `lsst.afw.image.Exposure` 2301 Flat exposure the same size as ``exp``. 2302 dark : `lsst.afw.image.Exposure`, optional 2303 Dark exposure the same size as ``exp``. 2307 exp : `lsst.afw.image.Exposure` 2308 The flat and dark corrected exposure. 2310 if self.config.doDark
and dark
is not None:
2312 if self.config.doFlat:
2317 if self.config.doFlat:
2319 if self.config.doDark
and dark
is not None:
2323 """Utility function to examine ISR exposure at different stages. 2327 exposure : `lsst.afw.image.Exposure` 2330 State of processing to view. 2335 display.scale(
'asinh',
'zscale')
2336 display.mtv(exposure)
2337 prompt =
"Press Enter to continue [c]... " 2339 ans = input(prompt).lower()
2340 if ans
in (
"",
"c",):
2345 """A Detector-like object that supports returning gain and saturation level 2347 This is used when the input exposure does not have a detector. 2351 exposure : `lsst.afw.image.Exposure` 2352 Exposure to generate a fake amplifier for. 2353 config : `lsst.ip.isr.isrTaskConfig` 2354 Configuration to apply to the fake amplifier. 2358 self.
_bbox = exposure.getBBox(afwImage.LOCAL)
2360 self.
_gain = config.gain
2390 isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
2394 """Task to wrap the default IsrTask to allow it to be retargeted. 2396 The standard IsrTask can be called directly from a command line 2397 program, but doing so removes the ability of the task to be 2398 retargeted. As most cameras override some set of the IsrTask 2399 methods, this would remove those data-specific methods in the 2400 output post-ISR images. This wrapping class fixes the issue, 2401 allowing identical post-ISR images to be generated by both the 2402 processCcd and isrTask code. 2404 ConfigClass = RunIsrConfig
2405 _DefaultName =
"runIsr" 2409 self.makeSubtask(
"isr")
2415 dataRef : `lsst.daf.persistence.ButlerDataRef` 2416 data reference of the detector data to be processed 2420 result : `pipeBase.Struct` 2421 Result struct with component: 2423 - exposure : `lsst.afw.image.Exposure` 2424 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 __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.
def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None, dark=None, flat=None, bfKernel=None, bfGains=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.
_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)