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 .overscan 
import OverscanCorrectionTask
 
   54 from .straylight 
import StrayLightTask
 
   55 from .vignette 
import VignetteTask
 
   58 __all__ = [
"IsrTask", 
"IsrTaskConfig", 
"RunIsrTask", 
"RunIsrConfig"]
 
   62                          dimensions={
"instrument", 
"exposure", 
"detector"},
 
   64     ccdExposure = cT.Input(
 
   66         doc=
"Input exposure to process.",
 
   67         storageClass=
"Exposure",
 
   68         dimensions=[
"instrument", 
"detector", 
"exposure"],
 
   70     camera = cT.PrerequisiteInput(
 
   72         storageClass=
"Camera",
 
   73         doc=
"Input camera to construct complete exposures.",
 
   74         dimensions=[
"instrument", 
"calibration_label"],
 
   76     bias = cT.PrerequisiteInput(
 
   78         doc=
"Input bias calibration.",
 
   79         storageClass=
"ExposureF",
 
   80         dimensions=[
"instrument", 
"calibration_label", 
"detector"],
 
   82     dark = cT.PrerequisiteInput(
 
   84         doc=
"Input dark calibration.",
 
   85         storageClass=
"ExposureF",
 
   86         dimensions=[
"instrument", 
"calibration_label", 
"detector"],
 
   88     flat = cT.PrerequisiteInput(
 
   90         doc=
"Input flat calibration.",
 
   91         storageClass=
"ExposureF",
 
   92         dimensions=[
"instrument", 
"physical_filter", 
"calibration_label", 
"detector"],
 
   94     fringes = cT.PrerequisiteInput(
 
   96         doc=
"Input fringe calibration.",
 
   97         storageClass=
"ExposureF",
 
   98         dimensions=[
"instrument", 
"physical_filter", 
"calibration_label", 
"detector"],
 
  100     strayLightData = cT.PrerequisiteInput(
 
  102         doc=
"Input stray light calibration.",
 
  103         storageClass=
"StrayLightData",
 
  104         dimensions=[
"instrument", 
"physical_filter", 
"calibration_label", 
"detector"],
 
  106     bfKernel = cT.PrerequisiteInput(
 
  108         doc=
"Input brighter-fatter kernel.",
 
  109         storageClass=
"NumpyArray",
 
  110         dimensions=[
"instrument", 
"calibration_label"],
 
  112     newBFKernel = cT.PrerequisiteInput(
 
  113         name=
'brighterFatterKernel',
 
  114         doc=
"Newer complete kernel + gain solutions.",
 
  115         storageClass=
"BrighterFatterKernel",
 
  116         dimensions=[
"instrument", 
"calibration_label", 
"detector"],
 
  118     defects = cT.PrerequisiteInput(
 
  120         doc=
"Input defect tables.",
 
  121         storageClass=
"Defects",
 
  122         dimensions=[
"instrument", 
"calibration_label", 
"detector"],
 
  124     opticsTransmission = cT.PrerequisiteInput(
 
  125         name=
"transmission_optics",
 
  126         storageClass=
"TransmissionCurve",
 
  127         doc=
"Transmission curve due to the optics.",
 
  128         dimensions=[
"instrument", 
"calibration_label"],
 
  130     filterTransmission = cT.PrerequisiteInput(
 
  131         name=
"transmission_filter",
 
  132         storageClass=
"TransmissionCurve",
 
  133         doc=
"Transmission curve due to the filter.",
 
  134         dimensions=[
"instrument", 
"physical_filter", 
"calibration_label"],
 
  136     sensorTransmission = cT.PrerequisiteInput(
 
  137         name=
"transmission_sensor",
 
  138         storageClass=
"TransmissionCurve",
 
  139         doc=
"Transmission curve due to the sensor.",
 
  140         dimensions=[
"instrument", 
"calibration_label", 
"detector"],
 
  142     atmosphereTransmission = cT.PrerequisiteInput(
 
  143         name=
"transmission_atmosphere",
 
  144         storageClass=
"TransmissionCurve",
 
  145         doc=
"Transmission curve due to the atmosphere.",
 
  146         dimensions=[
"instrument"],
 
  148     illumMaskedImage = cT.PrerequisiteInput(
 
  150         doc=
"Input illumination correction.",
 
  151         storageClass=
"MaskedImageF",
 
  152         dimensions=[
"instrument", 
"physical_filter", 
"calibration_label", 
"detector"],
 
  155     outputExposure = cT.Output(
 
  157         doc=
"Output ISR processed exposure.",
 
  158         storageClass=
"ExposureF",
 
  159         dimensions=[
"instrument", 
"exposure", 
"detector"],
 
  161     preInterpExposure = cT.Output(
 
  162         name=
'preInterpISRCCD',
 
  163         doc=
"Output ISR processed exposure, with pixels left uninterpolated.",
 
  164         storageClass=
"ExposureF",
 
  165         dimensions=[
"instrument", 
"exposure", 
"detector"],
 
  167     outputOssThumbnail = cT.Output(
 
  169         doc=
"Output Overscan-subtracted thumbnail image.",
 
  170         storageClass=
"Thumbnail",
 
  171         dimensions=[
"instrument", 
"exposure", 
"detector"],
 
  173     outputFlattenedThumbnail = cT.Output(
 
  174         name=
"FlattenedThumb",
 
  175         doc=
"Output flat-corrected thumbnail image.",
 
  176         storageClass=
"Thumbnail",
 
  177         dimensions=[
"instrument", 
"exposure", 
"detector"],
 
  183         if config.doBias 
is not True:
 
  184             self.prerequisiteInputs.discard(
"bias")
 
  185         if config.doLinearize 
is not True:
 
  186             self.prerequisiteInputs.discard(
"linearizer")
 
  187         if config.doCrosstalk 
is not True:
 
  188             self.prerequisiteInputs.discard(
"crosstalkSources")
 
  189         if config.doBrighterFatter 
is not True:
 
  190             self.prerequisiteInputs.discard(
"bfKernel")
 
  191             self.prerequisiteInputs.discard(
"newBFKernel")
 
  192         if config.doDefect 
is not True:
 
  193             self.prerequisiteInputs.discard(
"defects")
 
  194         if config.doDark 
is not True:
 
  195             self.prerequisiteInputs.discard(
"dark")
 
  196         if config.doFlat 
is not True:
 
  197             self.prerequisiteInputs.discard(
"flat")
 
  198         if config.doAttachTransmissionCurve 
is not True:
 
  199             self.prerequisiteInputs.discard(
"opticsTransmission")
 
  200             self.prerequisiteInputs.discard(
"filterTransmission")
 
  201             self.prerequisiteInputs.discard(
"sensorTransmission")
 
  202             self.prerequisiteInputs.discard(
"atmosphereTransmission")
 
  203         if config.doUseOpticsTransmission 
is not True:
 
  204             self.prerequisiteInputs.discard(
"opticsTransmission")
 
  205         if config.doUseFilterTransmission 
is not True:
 
  206             self.prerequisiteInputs.discard(
"filterTransmission")
 
  207         if config.doUseSensorTransmission 
is not True:
 
  208             self.prerequisiteInputs.discard(
"sensorTransmission")
 
  209         if config.doUseAtmosphereTransmission 
is not True:
 
  210             self.prerequisiteInputs.discard(
"atmosphereTransmission")
 
  211         if config.doIlluminationCorrection 
is not True:
 
  212             self.prerequisiteInputs.discard(
"illumMaskedImage")
 
  214         if config.doWrite 
is not True:
 
  215             self.outputs.discard(
"outputExposure")
 
  216             self.outputs.discard(
"preInterpExposure")
 
  217             self.outputs.discard(
"outputFlattenedThumbnail")
 
  218             self.outputs.discard(
"outputOssThumbnail")
 
  219         if config.doSaveInterpPixels 
is not True:
 
  220             self.outputs.discard(
"preInterpExposure")
 
  221         if config.qa.doThumbnailOss 
is not True:
 
  222             self.outputs.discard(
"outputOssThumbnail")
 
  223         if config.qa.doThumbnailFlattened 
is not True:
 
  224             self.outputs.discard(
"outputFlattenedThumbnail")
 
  228                     pipelineConnections=IsrTaskConnections):
 
  229     """Configuration parameters for IsrTask. 
  231     Items are grouped in the order in which they are executed by the task. 
  233     datasetType = pexConfig.Field(
 
  235         doc=
"Dataset type for input data; users will typically leave this alone, " 
  236         "but camera-specific ISR tasks will override it",
 
  240     fallbackFilterName = pexConfig.Field(
 
  242         doc=
"Fallback default filter name for calibrations.",
 
  245     useFallbackDate = pexConfig.Field(
 
  247         doc=
"Pass observation date when using fallback filter.",
 
  250     expectWcs = pexConfig.Field(
 
  253         doc=
"Expect input science images to have a WCS (set False for e.g. spectrographs)." 
  255     fwhm = pexConfig.Field(
 
  257         doc=
"FWHM of PSF in arcseconds.",
 
  260     qa = pexConfig.ConfigField(
 
  262         doc=
"QA related configuration options.",
 
  266     doConvertIntToFloat = pexConfig.Field(
 
  268         doc=
"Convert integer raw images to floating point values?",
 
  273     doSaturation = pexConfig.Field(
 
  275         doc=
"Mask saturated pixels? NB: this is totally independent of the" 
  276         " interpolation option - this is ONLY setting the bits in the mask." 
  277         " To have them interpolated make sure doSaturationInterpolation=True",
 
  280     saturatedMaskName = pexConfig.Field(
 
  282         doc=
"Name of mask plane to use in saturation detection and interpolation",
 
  285     saturation = pexConfig.Field(
 
  287         doc=
"The saturation level to use if no Detector is present in the Exposure (ignored if NaN)",
 
  288         default=float(
"NaN"),
 
  290     growSaturationFootprintSize = pexConfig.Field(
 
  292         doc=
"Number of pixels by which to grow the saturation footprints",
 
  297     doSuspect = pexConfig.Field(
 
  299         doc=
"Mask suspect pixels?",
 
  302     suspectMaskName = pexConfig.Field(
 
  304         doc=
"Name of mask plane to use for suspect pixels",
 
  307     numEdgeSuspect = pexConfig.Field(
 
  309         doc=
"Number of edge pixels to be flagged as untrustworthy.",
 
  314     doSetBadRegions = pexConfig.Field(
 
  316         doc=
"Should we set the level of all BAD patches of the chip to the chip's average value?",
 
  319     badStatistic = pexConfig.ChoiceField(
 
  321         doc=
"How to estimate the average value for BAD regions.",
 
  324             "MEANCLIP": 
"Correct using the (clipped) mean of good data",
 
  325             "MEDIAN": 
"Correct using the median of the good data",
 
  330     doOverscan = pexConfig.Field(
 
  332         doc=
"Do overscan subtraction?",
 
  335     overscan = pexConfig.ConfigurableField(
 
  336         target=OverscanCorrectionTask,
 
  337         doc=
"Overscan subtraction task for image segments.",
 
  340     overscanFitType = pexConfig.ChoiceField(
 
  342         doc=
"The method for fitting the overscan bias level.",
 
  345             "POLY": 
"Fit ordinary polynomial to the longest axis of the overscan region",
 
  346             "CHEB": 
"Fit Chebyshev polynomial to the longest axis of the overscan region",
 
  347             "LEG": 
"Fit Legendre polynomial to the longest axis of the overscan region",
 
  348             "NATURAL_SPLINE": 
"Fit natural spline to the longest axis of the overscan region",
 
  349             "CUBIC_SPLINE": 
"Fit cubic spline to the longest axis of the overscan region",
 
  350             "AKIMA_SPLINE": 
"Fit Akima spline to the longest axis of the overscan region",
 
  351             "MEAN": 
"Correct using the mean of the overscan region",
 
  352             "MEANCLIP": 
"Correct using a clipped mean of the overscan region",
 
  353             "MEDIAN": 
"Correct using the median of the overscan region",
 
  354             "MEDIAN_PER_ROW": 
"Correct using the median per row of the overscan region",
 
  356         deprecated=(
"Please configure overscan via the OverscanCorrectionConfig interface." +
 
  357                     " This option will no longer be used, and will be removed after v20.")
 
  359     overscanOrder = pexConfig.Field(
 
  361         doc=(
"Order of polynomial or to fit if overscan fit type is a polynomial, " +
 
  362              "or number of spline knots if overscan fit type is a spline."),
 
  364         deprecated=(
"Please configure overscan via the OverscanCorrectionConfig interface." +
 
  365                     " This option will no longer be used, and will be removed after v20.")
 
  367     overscanNumSigmaClip = pexConfig.Field(
 
  369         doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
 
  371         deprecated=(
"Please configure overscan via the OverscanCorrectionConfig interface." +
 
  372                     " This option will no longer be used, and will be removed after v20.")
 
  374     overscanIsInt = pexConfig.Field(
 
  376         doc=
"Treat overscan as an integer image for purposes of overscan.FitType=MEDIAN" +
 
  377             " and overscan.FitType=MEDIAN_PER_ROW.",
 
  379         deprecated=(
"Please configure overscan via the OverscanCorrectionConfig interface." +
 
  380                     " This option will no longer be used, and will be removed after v20.")
 
  383     overscanNumLeadingColumnsToSkip = pexConfig.Field(
 
  385         doc=
"Number of columns to skip in overscan, i.e. those closest to amplifier",
 
  388     overscanNumTrailingColumnsToSkip = pexConfig.Field(
 
  390         doc=
"Number of columns to skip in overscan, i.e. those farthest from amplifier",
 
  393     overscanMaxDev = pexConfig.Field(
 
  395         doc=
"Maximum deviation from the median for overscan",
 
  396         default=1000.0, check=
lambda x: x > 0
 
  398     overscanBiasJump = pexConfig.Field(
 
  400         doc=
"Fit the overscan in a piecewise-fashion to correct for bias jumps?",
 
  403     overscanBiasJumpKeyword = pexConfig.Field(
 
  405         doc=
"Header keyword containing information about devices.",
 
  406         default=
"NO_SUCH_KEY",
 
  408     overscanBiasJumpDevices = pexConfig.ListField(
 
  410         doc=
"List of devices that need piecewise overscan correction.",
 
  413     overscanBiasJumpLocation = pexConfig.Field(
 
  415         doc=
"Location of bias jump along y-axis.",
 
  420     doAssembleCcd = pexConfig.Field(
 
  423         doc=
"Assemble amp-level exposures into a ccd-level exposure?" 
  425     assembleCcd = pexConfig.ConfigurableField(
 
  426         target=AssembleCcdTask,
 
  427         doc=
"CCD assembly task",
 
  431     doAssembleIsrExposures = pexConfig.Field(
 
  434         doc=
"Assemble amp-level calibration exposures into ccd-level exposure?" 
  436     doTrimToMatchCalib = pexConfig.Field(
 
  439         doc=
"Trim raw data to match calibration bounding boxes?" 
  443     doBias = pexConfig.Field(
 
  445         doc=
"Apply bias frame correction?",
 
  448     biasDataProductName = pexConfig.Field(
 
  450         doc=
"Name of the bias data product",
 
  455     doVariance = pexConfig.Field(
 
  457         doc=
"Calculate variance?",
 
  460     gain = pexConfig.Field(
 
  462         doc=
"The gain to use if no Detector is present in the Exposure (ignored if NaN)",
 
  463         default=float(
"NaN"),
 
  465     readNoise = pexConfig.Field(
 
  467         doc=
"The read noise to use if no Detector is present in the Exposure",
 
  470     doEmpiricalReadNoise = pexConfig.Field(
 
  473         doc=
"Calculate empirical read noise instead of value from AmpInfo data?" 
  477     doLinearize = pexConfig.Field(
 
  479         doc=
"Correct for nonlinearity of the detector's response?",
 
  484     doCrosstalk = pexConfig.Field(
 
  486         doc=
"Apply intra-CCD crosstalk correction?",
 
  489     doCrosstalkBeforeAssemble = pexConfig.Field(
 
  491         doc=
"Apply crosstalk correction before CCD assembly, and before trimming?",
 
  494     crosstalk = pexConfig.ConfigurableField(
 
  495         target=CrosstalkTask,
 
  496         doc=
"Intra-CCD crosstalk correction",
 
  500     doDefect = pexConfig.Field(
 
  502         doc=
"Apply correction for CCD defects, e.g. hot pixels?",
 
  505     doNanMasking = pexConfig.Field(
 
  507         doc=
"Mask NAN pixels?",
 
  510     doWidenSaturationTrails = pexConfig.Field(
 
  512         doc=
"Widen bleed trails based on their width?",
 
  517     doBrighterFatter = pexConfig.Field(
 
  520         doc=
"Apply the brighter fatter correction" 
  522     brighterFatterLevel = pexConfig.ChoiceField(
 
  525         doc=
"The level at which to correct for brighter-fatter.",
 
  527             "AMP": 
"Every amplifier treated separately.",
 
  528             "DETECTOR": 
"One kernel per detector",
 
  531     brighterFatterMaxIter = pexConfig.Field(
 
  534         doc=
"Maximum number of iterations for the brighter fatter correction" 
  536     brighterFatterThreshold = pexConfig.Field(
 
  539         doc=
"Threshold used to stop iterating the brighter fatter correction.  It is the " 
  540         " absolute value of the difference between the current corrected image and the one" 
  541         " from the previous iteration summed over all the pixels." 
  543     brighterFatterApplyGain = pexConfig.Field(
 
  546         doc=
"Should the gain be applied when applying the brighter fatter correction?" 
  548     brighterFatterMaskGrowSize = pexConfig.Field(
 
  551         doc=
"Number of pixels to grow the masks listed in config.maskListToInterpolate " 
  552         " when brighter-fatter correction is applied." 
  556     doDark = pexConfig.Field(
 
  558         doc=
"Apply dark frame correction?",
 
  561     darkDataProductName = pexConfig.Field(
 
  563         doc=
"Name of the dark data product",
 
  568     doStrayLight = pexConfig.Field(
 
  570         doc=
"Subtract stray light in the y-band (due to encoder LEDs)?",
 
  573     strayLight = pexConfig.ConfigurableField(
 
  574         target=StrayLightTask,
 
  575         doc=
"y-band stray light correction" 
  579     doFlat = pexConfig.Field(
 
  581         doc=
"Apply flat field correction?",
 
  584     flatDataProductName = pexConfig.Field(
 
  586         doc=
"Name of the flat data product",
 
  589     flatScalingType = pexConfig.ChoiceField(
 
  591         doc=
"The method for scaling the flat on the fly.",
 
  594             "USER": 
"Scale by flatUserScale",
 
  595             "MEAN": 
"Scale by the inverse of the mean",
 
  596             "MEDIAN": 
"Scale by the inverse of the median",
 
  599     flatUserScale = pexConfig.Field(
 
  601         doc=
"If flatScalingType is 'USER' then scale flat by this amount; ignored otherwise",
 
  604     doTweakFlat = pexConfig.Field(
 
  606         doc=
"Tweak flats to match observed amplifier ratios?",
 
  611     doApplyGains = pexConfig.Field(
 
  613         doc=
"Correct the amplifiers for their gains instead of applying flat correction",
 
  616     normalizeGains = pexConfig.Field(
 
  618         doc=
"Normalize all the amplifiers in each CCD to have the same median value.",
 
  623     doFringe = pexConfig.Field(
 
  625         doc=
"Apply fringe correction?",
 
  628     fringe = pexConfig.ConfigurableField(
 
  630         doc=
"Fringe subtraction task",
 
  632     fringeAfterFlat = pexConfig.Field(
 
  634         doc=
"Do fringe subtraction after flat-fielding?",
 
  639     doMeasureBackground = pexConfig.Field(
 
  641         doc=
"Measure the background level on the reduced image?",
 
  646     doCameraSpecificMasking = pexConfig.Field(
 
  648         doc=
"Mask camera-specific bad regions?",
 
  651     masking = pexConfig.ConfigurableField(
 
  658     doInterpolate = pexConfig.Field(
 
  660         doc=
"Interpolate masked pixels?",
 
  663     doSaturationInterpolation = pexConfig.Field(
 
  665         doc=
"Perform interpolation over pixels masked as saturated?" 
  666         " NB: This is independent of doSaturation; if that is False this plane" 
  667         " will likely be blank, resulting in a no-op here.",
 
  670     doNanInterpolation = pexConfig.Field(
 
  672         doc=
"Perform interpolation over pixels masked as NaN?" 
  673         " NB: This is independent of doNanMasking; if that is False this plane" 
  674         " will likely be blank, resulting in a no-op here.",
 
  677     doNanInterpAfterFlat = pexConfig.Field(
 
  679         doc=(
"If True, ensure we interpolate NaNs after flat-fielding, even if we " 
  680              "also have to interpolate them before flat-fielding."),
 
  683     maskListToInterpolate = pexConfig.ListField(
 
  685         doc=
"List of mask planes that should be interpolated.",
 
  686         default=[
'SAT', 
'BAD', 
'UNMASKEDNAN'],
 
  688     doSaveInterpPixels = pexConfig.Field(
 
  690         doc=
"Save a copy of the pre-interpolated pixel values?",
 
  695     fluxMag0T1 = pexConfig.DictField(
 
  698         doc=
"The approximate flux of a zero-magnitude object in a one-second exposure, per filter.",
 
  699         default=dict((f, pow(10.0, 0.4*m)) 
for f, m 
in ((
"Unknown", 28.0),
 
  702     defaultFluxMag0T1 = pexConfig.Field(
 
  704         doc=
"Default value for fluxMag0T1 (for an unrecognized filter).",
 
  705         default=pow(10.0, 0.4*28.0)
 
  709     doVignette = pexConfig.Field(
 
  711         doc=
"Apply vignetting parameters?",
 
  714     vignette = pexConfig.ConfigurableField(
 
  716         doc=
"Vignetting task.",
 
  720     doAttachTransmissionCurve = pexConfig.Field(
 
  723         doc=
"Construct and attach a wavelength-dependent throughput curve for this CCD image?" 
  725     doUseOpticsTransmission = pexConfig.Field(
 
  728         doc=
"Load and use transmission_optics (if doAttachTransmissionCurve is True)?" 
  730     doUseFilterTransmission = pexConfig.Field(
 
  733         doc=
"Load and use transmission_filter (if doAttachTransmissionCurve is True)?" 
  735     doUseSensorTransmission = pexConfig.Field(
 
  738         doc=
"Load and use transmission_sensor (if doAttachTransmissionCurve is True)?" 
  740     doUseAtmosphereTransmission = pexConfig.Field(
 
  743         doc=
"Load and use transmission_atmosphere (if doAttachTransmissionCurve is True)?" 
  747     doIlluminationCorrection = pexConfig.Field(
 
  750         doc=
"Perform illumination correction?" 
  752     illuminationCorrectionDataProductName = pexConfig.Field(
 
  754         doc=
"Name of the illumination correction data product.",
 
  757     illumScale = pexConfig.Field(
 
  759         doc=
"Scale factor for the illumination correction.",
 
  762     illumFilters = pexConfig.ListField(
 
  765         doc=
"Only perform illumination correction for these filters." 
  769     doWrite = pexConfig.Field(
 
  771         doc=
"Persist postISRCCD?",
 
  778             raise ValueError(
"You may not specify both doFlat and doApplyGains")
 
  780             self.config.maskListToInterpolate.append(
"SAT")
 
  782             self.config.maskListToInterpolate.append(
"UNMASKEDNAN")
 
  785 class IsrTask(pipeBase.PipelineTask, pipeBase.CmdLineTask):
 
  786     """Apply common instrument signature correction algorithms to a raw frame. 
  788     The process for correcting imaging data is very similar from 
  789     camera to camera.  This task provides a vanilla implementation of 
  790     doing these corrections, including the ability to turn certain 
  791     corrections off if they are not needed.  The inputs to the primary 
  792     method, `run()`, are a raw exposure to be corrected and the 
  793     calibration data products. The raw input is a single chip sized 
  794     mosaic of all amps including overscans and other non-science 
  795     pixels.  The method `runDataRef()` identifies and defines the 
  796     calibration data products, and is intended for use by a 
  797     `lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a 
  798     `daf.persistence.butlerSubset.ButlerDataRef`.  This task may be 
  799     subclassed for different camera, although the most camera specific 
  800     methods have been split into subtasks that can be redirected 
  803     The __init__ method sets up the subtasks for ISR processing, using 
  804     the defaults from `lsst.ip.isr`. 
  809         Positional arguments passed to the Task constructor. None used at this time. 
  810     kwargs : `dict`, optional 
  811         Keyword arguments passed on to the Task constructor. None used at this time. 
  813     ConfigClass = IsrTaskConfig
 
  818         self.makeSubtask(
"assembleCcd")
 
  819         self.makeSubtask(
"crosstalk")
 
  820         self.makeSubtask(
"strayLight")
 
  821         self.makeSubtask(
"fringe")
 
  822         self.makeSubtask(
"masking")
 
  823         self.makeSubtask(
"overscan")
 
  824         self.makeSubtask(
"vignette")
 
  827         inputs = butlerQC.get(inputRefs)
 
  830             inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
 
  831         except Exception 
as e:
 
  832             raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
 
  835         inputs[
'isGen3'] = 
True 
  837         detector = inputs[
'ccdExposure'].getDetector()
 
  840             if 'linearizer' in inputs 
and isinstance(inputs[
'linearizer'], dict):
 
  842                 linearizer.fromYaml(inputs[
'linearizer'])
 
  846             inputs[
'linearizer'] = linearizer
 
  848         if self.config.doDefect 
is True:
 
  849             if "defects" in inputs 
and inputs[
'defects'] 
is not None:
 
  852                 if not isinstance(inputs[
"defects"], Defects):
 
  853                     inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
 
  857         if self.config.doBrighterFatter:
 
  858             brighterFatterKernel = inputs.pop(
'newBFKernel', 
None)
 
  859             if brighterFatterKernel 
is None:
 
  860                 brighterFatterKernel = inputs.get(
'bfKernel', 
None)
 
  862             if brighterFatterKernel 
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
 
  863                 detId = detector.getId()
 
  864                 inputs[
'bfGains'] = brighterFatterKernel.gain
 
  867                 if self.config.brighterFatterLevel == 
'DETECTOR':
 
  868                     if brighterFatterKernel.detectorKernel:
 
  869                         inputs[
'bfKernel'] = brighterFatterKernel.detectorKernel[detId]
 
  870                     elif brighterFatterKernel.detectorKernelFromAmpKernels:
 
  871                         inputs[
'bfKernel'] = brighterFatterKernel.detectorKernelFromAmpKernels[detId]
 
  873                         raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
 
  876                     raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
 
  884         if self.config.doFringe 
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
 
  885             expId = inputs[
'ccdExposure'].
getInfo().getVisitInfo().getExposureId()
 
  886             inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
 
  888                                                         assembler=self.assembleCcd
 
  889                                                         if self.config.doAssembleIsrExposures 
else None)
 
  891             inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
 
  893         if self.config.doStrayLight 
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
 
  894             if 'strayLightData' not in inputs:
 
  895                 inputs[
'strayLightData'] = 
None 
  897         outputs = self.
run(**inputs)
 
  898         butlerQC.put(outputs, outputRefs)
 
  901         """!Retrieve necessary frames for instrument signature removal. 
  903         Pre-fetching all required ISR data products limits the IO 
  904         required by the ISR. Any conflict between the calibration data 
  905         available and that needed for ISR is also detected prior to 
  906         doing processing, allowing it to fail quickly. 
  910         dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 
  911             Butler reference of the detector data to be processed 
  912         rawExposure : `afw.image.Exposure` 
  913             The raw exposure that will later be corrected with the 
  914             retrieved calibration data; should not be modified in this 
  919         result : `lsst.pipe.base.Struct` 
  920             Result struct with components (which may be `None`): 
  921             - ``bias``: bias calibration frame (`afw.image.Exposure`) 
  922             - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`) 
  923             - ``crosstalkSources``: list of possible crosstalk sources (`list`) 
  924             - ``dark``: dark calibration frame (`afw.image.Exposure`) 
  925             - ``flat``: flat calibration frame (`afw.image.Exposure`) 
  926             - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`) 
  927             - ``defects``: list of defects (`lsst.meas.algorithms.Defects`) 
  928             - ``fringes``: `lsst.pipe.base.Struct` with components: 
  929               - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 
  930               - ``seed``: random seed derived from the ccdExposureId for random 
  931                   number generator (`uint32`). 
  932             - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve` 
  933                 A ``TransmissionCurve`` that represents the throughput of the optics, 
  934                 to be evaluated in focal-plane coordinates. 
  935             - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve` 
  936                 A ``TransmissionCurve`` that represents the throughput of the filter 
  937                 itself, to be evaluated in focal-plane coordinates. 
  938             - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve` 
  939                 A ``TransmissionCurve`` that represents the throughput of the sensor 
  940                 itself, to be evaluated in post-assembly trimmed detector coordinates. 
  941             - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve` 
  942                 A ``TransmissionCurve`` that represents the throughput of the 
  943                 atmosphere, assumed to be spatially constant. 
  944             - ``strayLightData`` : `object` 
  945                 An opaque object containing calibration information for 
  946                 stray-light correction.  If `None`, no correction will be 
  948             - ``illumMaskedImage`` : illumination correction image (`lsst.afw.image.MaskedImage`) 
  952         NotImplementedError : 
  953             Raised if a per-amplifier brighter-fatter kernel is requested by the configuration. 
  956             dateObs = rawExposure.getInfo().getVisitInfo().getDate()
 
  957             dateObs = dateObs.toPython().isoformat()
 
  959             self.log.
warn(
"Unable to identify dateObs for rawExposure.")
 
  962         ccd = rawExposure.getDetector()
 
  963         filterName = 
afwImage.Filter(rawExposure.getFilter().getId()).getName()  
 
  964         rawExposure.mask.addMaskPlane(
"UNMASKEDNAN")  
 
  965         biasExposure = (self.
getIsrExposure(dataRef, self.config.biasDataProductName)
 
  966                         if self.config.doBias 
else None)
 
  968         linearizer = (dataRef.get(
"linearizer", immediate=
True)
 
  970         if linearizer 
is not None and not isinstance(linearizer, numpy.ndarray):
 
  971             linearizer.log = self.log
 
  972         if isinstance(linearizer, numpy.ndarray):
 
  974         crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
 
  975                             if self.config.doCrosstalk 
else None)
 
  976         darkExposure = (self.
getIsrExposure(dataRef, self.config.darkDataProductName)
 
  977                         if self.config.doDark 
else None)
 
  978         flatExposure = (self.
getIsrExposure(dataRef, self.config.flatDataProductName,
 
  980                         if self.config.doFlat 
else None)
 
  982         brighterFatterKernel = 
None 
  983         brighterFatterGains = 
None 
  984         if self.config.doBrighterFatter 
is True:
 
  989                 brighterFatterKernel = dataRef.get(
"brighterFatterKernel")
 
  990                 brighterFatterGains = brighterFatterKernel.gain
 
  991                 self.log.
info(
"New style bright-fatter kernel (brighterFatterKernel) loaded")
 
  994                     brighterFatterKernel = dataRef.get(
"bfKernel")
 
  995                     self.log.
info(
"Old style bright-fatter kernel (np.array) loaded")
 
  997                     brighterFatterKernel = 
None 
  998             if brighterFatterKernel 
is not None and not isinstance(brighterFatterKernel, numpy.ndarray):
 
 1001                 if self.config.brighterFatterLevel == 
'DETECTOR':
 
 1002                     if brighterFatterKernel.detectorKernel:
 
 1003                         brighterFatterKernel = brighterFatterKernel.detectorKernel[ccd.getId()]
 
 1004                     elif brighterFatterKernel.detectorKernelFromAmpKernels:
 
 1005                         brighterFatterKernel = brighterFatterKernel.detectorKernelFromAmpKernels[ccd.getId()]
 
 1007                         raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
 
 1010                     raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
 
 1012         defectList = (dataRef.get(
"defects")
 
 1013                       if self.config.doDefect 
else None)
 
 1014         fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
 
 1015                                                 if self.config.doAssembleIsrExposures 
else None)
 
 1016                         if self.config.doFringe 
and self.fringe.checkFilter(rawExposure)
 
 1017                         else pipeBase.Struct(fringes=
None))
 
 1019         if self.config.doAttachTransmissionCurve:
 
 1020             opticsTransmission = (dataRef.get(
"transmission_optics")
 
 1021                                   if self.config.doUseOpticsTransmission 
else None)
 
 1022             filterTransmission = (dataRef.get(
"transmission_filter")
 
 1023                                   if self.config.doUseFilterTransmission 
else None)
 
 1024             sensorTransmission = (dataRef.get(
"transmission_sensor")
 
 1025                                   if self.config.doUseSensorTransmission 
else None)
 
 1026             atmosphereTransmission = (dataRef.get(
"transmission_atmosphere")
 
 1027                                       if self.config.doUseAtmosphereTransmission 
else None)
 
 1029             opticsTransmission = 
None 
 1030             filterTransmission = 
None 
 1031             sensorTransmission = 
None 
 1032             atmosphereTransmission = 
None 
 1034         if self.config.doStrayLight:
 
 1035             strayLightData = self.strayLight.
readIsrData(dataRef, rawExposure)
 
 1037             strayLightData = 
None 
 1040                             self.config.illuminationCorrectionDataProductName).getMaskedImage()
 
 1041                             if (self.config.doIlluminationCorrection 
and 
 1042                             filterName 
in self.config.illumFilters)
 
 1046         return pipeBase.Struct(bias=biasExposure,
 
 1047                                linearizer=linearizer,
 
 1048                                crosstalkSources=crosstalkSources,
 
 1051                                bfKernel=brighterFatterKernel,
 
 1052                                bfGains=brighterFatterGains,
 
 1054                                fringes=fringeStruct,
 
 1055                                opticsTransmission=opticsTransmission,
 
 1056                                filterTransmission=filterTransmission,
 
 1057                                sensorTransmission=sensorTransmission,
 
 1058                                atmosphereTransmission=atmosphereTransmission,
 
 1059                                strayLightData=strayLightData,
 
 1060                                illumMaskedImage=illumMaskedImage
 
 1063     @pipeBase.timeMethod
 
 1064     def run(self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None,
 
 1065             dark=None, flat=None, bfKernel=None, bfGains=None, defects=None,
 
 1066             fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
 
 1067             sensorTransmission=
None, atmosphereTransmission=
None,
 
 1068             detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
 
 1071         """!Perform instrument signature removal on an exposure. 
 1073         Steps included in the ISR processing, in order performed, are: 
 1074         - saturation and suspect pixel masking 
 1075         - overscan subtraction 
 1076         - CCD assembly of individual amplifiers 
 1078         - variance image construction 
 1079         - linearization of non-linear response 
 1081         - brighter-fatter correction 
 1084         - stray light subtraction 
 1086         - masking of known defects and camera specific features 
 1087         - vignette calculation 
 1088         - appending transmission curve and distortion model 
 1092         ccdExposure : `lsst.afw.image.Exposure` 
 1093             The raw exposure that is to be run through ISR.  The 
 1094             exposure is modified by this method. 
 1095         camera : `lsst.afw.cameraGeom.Camera`, optional 
 1096             The camera geometry for this exposure. Required if ``isGen3`` is 
 1097             `True` and one or more of ``ccdExposure``, ``bias``, ``dark``, or 
 1098             ``flat`` does not have an associated detector. 
 1099         bias : `lsst.afw.image.Exposure`, optional 
 1100             Bias calibration frame. 
 1101         linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional 
 1102             Functor for linearization. 
 1103         crosstalkSources : `list`, optional 
 1104             List of possible crosstalk sources. 
 1105         dark : `lsst.afw.image.Exposure`, optional 
 1106             Dark calibration frame. 
 1107         flat : `lsst.afw.image.Exposure`, optional 
 1108             Flat calibration frame. 
 1109         bfKernel : `numpy.ndarray`, optional 
 1110             Brighter-fatter kernel. 
 1111         bfGains : `dict` of `float`, optional 
 1112             Gains used to override the detector's nominal gains for the 
 1113             brighter-fatter correction. A dict keyed by amplifier name for 
 1114             the detector in question. 
 1115         defects : `lsst.meas.algorithms.Defects`, optional 
 1117         fringes : `lsst.pipe.base.Struct`, optional 
 1118             Struct containing the fringe correction data, with 
 1120             - ``fringes``: fringe calibration frame (`afw.image.Exposure`) 
 1121             - ``seed``: random seed derived from the ccdExposureId for random 
 1122                 number generator (`uint32`) 
 1123         opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional 
 1124             A ``TransmissionCurve`` that represents the throughput of the optics, 
 1125             to be evaluated in focal-plane coordinates. 
 1126         filterTransmission : `lsst.afw.image.TransmissionCurve` 
 1127             A ``TransmissionCurve`` that represents the throughput of the filter 
 1128             itself, to be evaluated in focal-plane coordinates. 
 1129         sensorTransmission : `lsst.afw.image.TransmissionCurve` 
 1130             A ``TransmissionCurve`` that represents the throughput of the sensor 
 1131             itself, to be evaluated in post-assembly trimmed detector coordinates. 
 1132         atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 
 1133             A ``TransmissionCurve`` that represents the throughput of the 
 1134             atmosphere, assumed to be spatially constant. 
 1135         detectorNum : `int`, optional 
 1136             The integer number for the detector to process. 
 1137         isGen3 : bool, optional 
 1138             Flag this call to run() as using the Gen3 butler environment. 
 1139         strayLightData : `object`, optional 
 1140             Opaque object containing calibration information for stray-light 
 1141             correction.  If `None`, no correction will be performed. 
 1142         illumMaskedImage : `lsst.afw.image.MaskedImage`, optional 
 1143             Illumination correction image. 
 1147         result : `lsst.pipe.base.Struct` 
 1148             Result struct with component: 
 1149             - ``exposure`` : `afw.image.Exposure` 
 1150                 The fully ISR corrected exposure. 
 1151             - ``outputExposure`` : `afw.image.Exposure` 
 1152                 An alias for `exposure` 
 1153             - ``ossThumb`` : `numpy.ndarray` 
 1154                 Thumbnail image of the exposure after overscan subtraction. 
 1155             - ``flattenedThumb`` : `numpy.ndarray` 
 1156                 Thumbnail image of the exposure after flat-field correction. 
 1161             Raised if a configuration option is set to True, but the 
 1162             required calibration data has not been specified. 
 1166         The current processed exposure can be viewed by setting the 
 1167         appropriate lsstDebug entries in the `debug.display` 
 1168         dictionary.  The names of these entries correspond to some of 
 1169         the IsrTaskConfig Boolean options, with the value denoting the 
 1170         frame to use.  The exposure is shown inside the matching 
 1171         option check and after the processing of that step has 
 1172         finished.  The steps with debug points are: 
 1183         In addition, setting the "postISRCCD" entry displays the 
 1184         exposure after all ISR processing has finished. 
 1192             if detectorNum 
is None:
 
 1193                 raise RuntimeError(
"Must supply the detectorNum if running as Gen3.")
 
 1195             ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
 
 1200             if isinstance(ccdExposure, ButlerDataRef):
 
 1203         ccd = ccdExposure.getDetector()
 
 1204         filterName = 
afwImage.Filter(ccdExposure.getFilter().getId()).getName()  
 
 1207             assert not self.config.doAssembleCcd, 
"You need a Detector to run assembleCcd." 
 1208             ccd = [
FakeAmp(ccdExposure, self.config)]
 
 1211         if self.config.doBias 
and bias 
is None:
 
 1212             raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
 
 1214             raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
 
 1215         if self.config.doBrighterFatter 
and bfKernel 
is None:
 
 1216             raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
 
 1217         if self.config.doDark 
and dark 
is None:
 
 1218             raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
 
 1219         if self.config.doFlat 
and flat 
is None:
 
 1220             raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
 
 1221         if self.config.doDefect 
and defects 
is None:
 
 1222             raise RuntimeError(
"Must supply defects if config.doDefect=True.")
 
 1223         if (self.config.doFringe 
and filterName 
in self.fringe.config.filters 
and 
 1224                 fringes.fringes 
is None):
 
 1229             raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
 
 1230         if (self.config.doIlluminationCorrection 
and filterName 
in self.config.illumFilters 
and 
 1231                 illumMaskedImage 
is None):
 
 1232             raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
 
 1235         if self.config.doConvertIntToFloat:
 
 1236             self.log.
info(
"Converting exposure to floating point values.")
 
 1243             if ccdExposure.getBBox().
contains(amp.getBBox()):
 
 1247                 if self.config.doOverscan 
and not badAmp:
 
 1250                     self.log.
debug(
"Corrected overscan for amplifier %s.", amp.getName())
 
 1251                     if overscanResults 
is not None and \
 
 1252                        self.config.qa 
is not None and self.config.qa.saveStats 
is True:
 
 1253                         if isinstance(overscanResults.overscanFit, float):
 
 1254                             qaMedian = overscanResults.overscanFit
 
 1255                             qaStdev = float(
"NaN")
 
 1258                                                              afwMath.MEDIAN | afwMath.STDEVCLIP)
 
 1259                             qaMedian = qaStats.getValue(afwMath.MEDIAN)
 
 1260                             qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
 
 1262                         self.metadata.
set(f
"ISR OSCAN {amp.getName()} MEDIAN", qaMedian)
 
 1263                         self.metadata.
set(f
"ISR OSCAN {amp.getName()} STDEV", qaStdev)
 
 1264                         self.log.
debug(
"  Overscan stats for amplifer %s: %f +/- %f",
 
 1265                                        amp.getName(), qaMedian, qaStdev)
 
 1266                         ccdExposure.getMetadata().
set(
'OVERSCAN', 
"Overscan corrected")
 
 1269                         self.log.
warn(
"Amplifier %s is bad.", amp.getName())
 
 1270                     overscanResults = 
None 
 1272                 overscans.append(overscanResults 
if overscanResults 
is not None else None)
 
 1274                 self.log.
info(
"Skipped OSCAN for %s.", amp.getName())
 
 1276         if self.config.doCrosstalk 
and self.config.doCrosstalkBeforeAssemble:
 
 1277             self.log.
info(
"Applying crosstalk correction.")
 
 1278             self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources)
 
 1279             self.
debugView(ccdExposure, 
"doCrosstalk")
 
 1281         if self.config.doAssembleCcd:
 
 1282             self.log.
info(
"Assembling CCD from amplifiers.")
 
 1283             ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
 
 1285             if self.config.expectWcs 
and not ccdExposure.getWcs():
 
 1286                 self.log.
warn(
"No WCS found in input exposure.")
 
 1287             self.
debugView(ccdExposure, 
"doAssembleCcd")
 
 1290         if self.config.qa.doThumbnailOss:
 
 1291             ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
 
 1293         if self.config.doBias:
 
 1294             self.log.
info(
"Applying bias correction.")
 
 1295             isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
 
 1296                                         trimToFit=self.config.doTrimToMatchCalib)
 
 1299         if self.config.doVariance:
 
 1300             for amp, overscanResults 
in zip(ccd, overscans):
 
 1301                 if ccdExposure.getBBox().
contains(amp.getBBox()):
 
 1302                     self.log.
debug(
"Constructing variance map for amplifer %s.", amp.getName())
 
 1303                     ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
 
 1304                     if overscanResults 
is not None:
 
 1306                                             overscanImage=overscanResults.overscanImage)
 
 1310                     if self.config.qa 
is not None and self.config.qa.saveStats 
is True:
 
 1312                                                          afwMath.MEDIAN | afwMath.STDEVCLIP)
 
 1313                         self.metadata.
set(f
"ISR VARIANCE {amp.getName()} MEDIAN",
 
 1314                                           qaStats.getValue(afwMath.MEDIAN))
 
 1315                         self.metadata.
set(f
"ISR VARIANCE {amp.getName()} STDEV",
 
 1316                                           qaStats.getValue(afwMath.STDEVCLIP))
 
 1317                         self.log.
debug(
"  Variance stats for amplifer %s: %f +/- %f.",
 
 1318                                        amp.getName(), qaStats.getValue(afwMath.MEDIAN),
 
 1319                                        qaStats.getValue(afwMath.STDEVCLIP))
 
 1322             self.log.
info(
"Applying linearizer.")
 
 1323             linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
 
 1324                                       detector=ccd, log=self.log)
 
 1326         if self.config.doCrosstalk 
and not self.config.doCrosstalkBeforeAssemble:
 
 1327             self.log.
info(
"Applying crosstalk correction.")
 
 1328             self.crosstalk.
run(ccdExposure, crosstalkSources=crosstalkSources, isTrimmed=
True)
 
 1329             self.
debugView(ccdExposure, 
"doCrosstalk")
 
 1333         if self.config.doDefect:
 
 1334             self.log.
info(
"Masking defects.")
 
 1337             if self.config.numEdgeSuspect > 0:
 
 1338                 self.log.
info(
"Masking edges as SUSPECT.")
 
 1339                 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
 
 1340                                maskPlane=
"SUSPECT")
 
 1342         if self.config.doNanMasking:
 
 1343             self.log.
info(
"Masking NAN value pixels.")
 
 1346         if self.config.doWidenSaturationTrails:
 
 1347             self.log.
info(
"Widening saturation trails.")
 
 1348             isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
 
 1350         if self.config.doCameraSpecificMasking:
 
 1351             self.log.
info(
"Masking regions for camera specific reasons.")
 
 1352             self.masking.
run(ccdExposure)
 
 1354         if self.config.doBrighterFatter:
 
 1363             interpExp = ccdExposure.clone()
 
 1365                 isrFunctions.interpolateFromMask(
 
 1366                     maskedImage=interpExp.getMaskedImage(),
 
 1367                     fwhm=self.config.fwhm,
 
 1368                     growSaturatedFootprints=self.config.growSaturationFootprintSize,
 
 1369                     maskNameList=self.config.maskListToInterpolate
 
 1371             bfExp = interpExp.clone()
 
 1373             self.log.
info(
"Applying brighter fatter correction using kernel type %s / gains %s.",
 
 1375             bfResults = isrFunctions.brighterFatterCorrection(bfExp, bfKernel,
 
 1376                                                               self.config.brighterFatterMaxIter,
 
 1377                                                               self.config.brighterFatterThreshold,
 
 1378                                                               self.config.brighterFatterApplyGain,
 
 1380             if bfResults[1] == self.config.brighterFatterMaxIter:
 
 1381                 self.log.
warn(
"Brighter fatter correction did not converge, final difference %f.",
 
 1384                 self.log.
info(
"Finished brighter fatter correction in %d iterations.",
 
 1386             image = ccdExposure.getMaskedImage().getImage()
 
 1387             bfCorr = bfExp.getMaskedImage().getImage()
 
 1388             bfCorr -= interpExp.getMaskedImage().getImage()
 
 1397             self.log.
info(
"Ensuring image edges are masked as SUSPECT to the brighter-fatter kernel size.")
 
 1398             self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
 
 1401             if self.config.brighterFatterMaskGrowSize > 0:
 
 1402                 self.log.
info(
"Growing masks to account for brighter-fatter kernel convolution.")
 
 1403                 for maskPlane 
in self.config.maskListToInterpolate:
 
 1404                     isrFunctions.growMasks(ccdExposure.getMask(),
 
 1405                                            radius=self.config.brighterFatterMaskGrowSize,
 
 1406                                            maskNameList=maskPlane,
 
 1407                                            maskValue=maskPlane)
 
 1409             self.
debugView(ccdExposure, 
"doBrighterFatter")
 
 1411         if self.config.doDark:
 
 1412             self.log.
info(
"Applying dark correction.")
 
 1416         if self.config.doFringe 
and not self.config.fringeAfterFlat:
 
 1417             self.log.
info(
"Applying fringe correction before flat.")
 
 1418             self.fringe.
run(ccdExposure, **fringes.getDict())
 
 1421         if self.config.doStrayLight 
and self.strayLight.check(ccdExposure):
 
 1422             self.log.
info(
"Checking strayLight correction.")
 
 1423             self.strayLight.
run(ccdExposure, strayLightData)
 
 1424             self.
debugView(ccdExposure, 
"doStrayLight")
 
 1426         if self.config.doFlat:
 
 1427             self.log.
info(
"Applying flat correction.")
 
 1431         if self.config.doApplyGains:
 
 1432             self.log.
info(
"Applying gain correction instead of flat.")
 
 1433             isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
 
 1435         if self.config.doFringe 
and self.config.fringeAfterFlat:
 
 1436             self.log.
info(
"Applying fringe correction after flat.")
 
 1437             self.fringe.
run(ccdExposure, **fringes.getDict())
 
 1439         if self.config.doVignette:
 
 1440             self.log.
info(
"Constructing Vignette polygon.")
 
 1443             if self.config.vignette.doWriteVignettePolygon:
 
 1446         if self.config.doAttachTransmissionCurve:
 
 1447             self.log.
info(
"Adding transmission curves.")
 
 1448             isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
 
 1449                                                  filterTransmission=filterTransmission,
 
 1450                                                  sensorTransmission=sensorTransmission,
 
 1451                                                  atmosphereTransmission=atmosphereTransmission)
 
 1453         flattenedThumb = 
None 
 1454         if self.config.qa.doThumbnailFlattened:
 
 1455             flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
 
 1457         if self.config.doIlluminationCorrection 
and filterName 
in self.config.illumFilters:
 
 1458             self.log.
info(
"Performing illumination correction.")
 
 1459             isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
 
 1460                                                 illumMaskedImage, illumScale=self.config.illumScale,
 
 1461                                                 trimToFit=self.config.doTrimToMatchCalib)
 
 1464         if self.config.doSaveInterpPixels:
 
 1465             preInterpExp = ccdExposure.clone()
 
 1480         if self.config.doSetBadRegions:
 
 1481             badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
 
 1482             if badPixelCount > 0:
 
 1483                 self.log.
info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
 
 1485         if self.config.doInterpolate:
 
 1486             self.log.
info(
"Interpolating masked pixels.")
 
 1487             isrFunctions.interpolateFromMask(
 
 1488                 maskedImage=ccdExposure.getMaskedImage(),
 
 1489                 fwhm=self.config.fwhm,
 
 1490                 growSaturatedFootprints=self.config.growSaturationFootprintSize,
 
 1491                 maskNameList=
list(self.config.maskListToInterpolate)
 
 1496         if self.config.doMeasureBackground:
 
 1497             self.log.
info(
"Measuring background level.")
 
 1500             if self.config.qa 
is not None and self.config.qa.saveStats 
is True:
 
 1502                     ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
 
 1504                                                      afwMath.MEDIAN | afwMath.STDEVCLIP)
 
 1505                     self.metadata.
set(
"ISR BACKGROUND {} MEDIAN".
format(amp.getName()),
 
 1506                                       qaStats.getValue(afwMath.MEDIAN))
 
 1507                     self.metadata.
set(
"ISR BACKGROUND {} STDEV".
format(amp.getName()),
 
 1508                                       qaStats.getValue(afwMath.STDEVCLIP))
 
 1509                     self.log.
debug(
"  Background stats for amplifer %s: %f +/- %f",
 
 1510                                    amp.getName(), qaStats.getValue(afwMath.MEDIAN),
 
 1511                                    qaStats.getValue(afwMath.STDEVCLIP))
 
 1513         self.
debugView(ccdExposure, 
"postISRCCD")
 
 1515         return pipeBase.Struct(
 
 1516             exposure=ccdExposure,
 
 1518             flattenedThumb=flattenedThumb,
 
 1520             preInterpolatedExposure=preInterpExp,
 
 1521             outputExposure=ccdExposure,
 
 1522             outputOssThumbnail=ossThumb,
 
 1523             outputFlattenedThumbnail=flattenedThumb,
 
 1526     @pipeBase.timeMethod
 
 1528         """Perform instrument signature removal on a ButlerDataRef of a Sensor. 
 1530         This method contains the `CmdLineTask` interface to the ISR 
 1531         processing.  All IO is handled here, freeing the `run()` method 
 1532         to manage only pixel-level calculations.  The steps performed 
 1534         - Read in necessary detrending/isr/calibration data. 
 1535         - Process raw exposure in `run()`. 
 1536         - Persist the ISR-corrected exposure as "postISRCCD" if 
 1537           config.doWrite=True. 
 1541         sensorRef : `daf.persistence.butlerSubset.ButlerDataRef` 
 1542             DataRef of the detector data to be processed 
 1546         result : `lsst.pipe.base.Struct` 
 1547             Result struct with component: 
 1548             - ``exposure`` : `afw.image.Exposure` 
 1549                 The fully ISR corrected exposure. 
 1554             Raised if a configuration option is set to True, but the 
 1555             required calibration data does not exist. 
 1558         self.log.
info(
"Performing ISR on sensor %s.", sensorRef.dataId)
 
 1560         ccdExposure = sensorRef.get(self.config.datasetType)
 
 1562         camera = sensorRef.get(
"camera")
 
 1563         isrData = self.
readIsrData(sensorRef, ccdExposure)
 
 1565         result = self.
run(ccdExposure, camera=camera, **isrData.getDict())
 
 1567         if self.config.doWrite:
 
 1568             sensorRef.put(result.exposure, 
"postISRCCD")
 
 1569             if result.preInterpolatedExposure 
is not None:
 
 1570                 sensorRef.put(result.preInterpolatedExposure, 
"postISRCCD_uninterpolated")
 
 1571         if result.ossThumb 
is not None:
 
 1572             isrQa.writeThumbnail(sensorRef, result.ossThumb, 
"ossThumb")
 
 1573         if result.flattenedThumb 
is not None:
 
 1574             isrQa.writeThumbnail(sensorRef, result.flattenedThumb, 
"flattenedThumb")
 
 1579         """!Retrieve a calibration dataset for removing instrument signature. 
 1584         dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 
 1585             DataRef of the detector data to find calibration datasets 
 1588             Type of dataset to retrieve (e.g. 'bias', 'flat', etc). 
 1589         dateObs : `str`, optional 
 1590             Date of the observation.  Used to correct butler failures 
 1591             when using fallback filters. 
 1593             If True, disable butler proxies to enable error handling 
 1594             within this routine. 
 1598         exposure : `lsst.afw.image.Exposure` 
 1599             Requested calibration frame. 
 1604             Raised if no matching calibration frame can be found. 
 1607             exp = dataRef.get(datasetType, immediate=immediate)
 
 1608         except Exception 
as exc1:
 
 1609             if not self.config.fallbackFilterName:
 
 1610                 raise RuntimeError(
"Unable to retrieve %s for %s: %s." % (datasetType, dataRef.dataId, exc1))
 
 1612                 if self.config.useFallbackDate 
and dateObs:
 
 1613                     exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName,
 
 1614                                       dateObs=dateObs, immediate=immediate)
 
 1616                     exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
 
 1617             except Exception 
as exc2:
 
 1618                 raise RuntimeError(
"Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s." %
 
 1619                                    (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
 
 1620             self.log.
warn(
"Using fallback calibration from filter %s.", self.config.fallbackFilterName)
 
 1622         if self.config.doAssembleIsrExposures:
 
 1623             exp = self.assembleCcd.assembleCcd(exp)
 
 1627         """Ensure that the data returned by Butler is a fully constructed exposure. 
 1629         ISR requires exposure-level image data for historical reasons, so if we did 
 1630         not recieve that from Butler, construct it from what we have, modifying the 
 1635         inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or 
 1636                    `lsst.afw.image.ImageF` 
 1637             The input data structure obtained from Butler. 
 1638         camera : `lsst.afw.cameraGeom.camera` 
 1639             The camera associated with the image.  Used to find the appropriate 
 1642             The detector this exposure should match. 
 1646         inputExp : `lsst.afw.image.Exposure` 
 1647             The re-constructed exposure, with appropriate detector parameters. 
 1652             Raised if the input data cannot be used to construct an exposure. 
 1654         if isinstance(inputExp, afwImage.DecoratedImageU):
 
 1656         elif isinstance(inputExp, afwImage.ImageF):
 
 1658         elif isinstance(inputExp, afwImage.MaskedImageF):
 
 1662         elif inputExp 
is None:
 
 1666             raise TypeError(
"Input Exposure is not known type in isrTask.ensureExposure: %s." %
 
 1669         if inputExp.getDetector() 
is None:
 
 1670             inputExp.setDetector(camera[detectorNum])
 
 1675         """Convert exposure image from uint16 to float. 
 1677         If the exposure does not need to be converted, the input is 
 1678         immediately returned.  For exposures that are converted to use 
 1679         floating point pixels, the variance is set to unity and the 
 1684         exposure : `lsst.afw.image.Exposure` 
 1685            The raw exposure to be converted. 
 1689         newexposure : `lsst.afw.image.Exposure` 
 1690            The input ``exposure``, converted to floating point pixels. 
 1695             Raised if the exposure type cannot be converted to float. 
 1698         if isinstance(exposure, afwImage.ExposureF):
 
 1700             self.log.
debug(
"Exposure already of type float.")
 
 1702         if not hasattr(exposure, 
"convertF"):
 
 1703             raise RuntimeError(
"Unable to convert exposure (%s) to float." % 
type(exposure))
 
 1705         newexposure = exposure.convertF()
 
 1706         newexposure.variance[:] = 1
 
 1707         newexposure.mask[:] = 0x0
 
 1712         """Identify bad amplifiers, saturated and suspect pixels. 
 1716         ccdExposure : `lsst.afw.image.Exposure` 
 1717             Input exposure to be masked. 
 1718         amp : `lsst.afw.table.AmpInfoCatalog` 
 1719             Catalog of parameters defining the amplifier on this 
 1721         defects : `lsst.meas.algorithms.Defects` 
 1722             List of defects.  Used to determine if the entire 
 1728             If this is true, the entire amplifier area is covered by 
 1729             defects and unusable. 
 1732         maskedImage = ccdExposure.getMaskedImage()
 
 1738         if defects 
is not None:
 
 1739             badAmp = bool(sum([v.getBBox().
contains(amp.getBBox()) 
for v 
in defects]))
 
 1744             dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
 
 1746             maskView = dataView.getMask()
 
 1747             maskView |= maskView.getPlaneBitMask(
"BAD")
 
 1754         if self.config.doSaturation 
and not badAmp:
 
 1755             limits.update({self.config.saturatedMaskName: amp.getSaturation()})
 
 1756         if self.config.doSuspect 
and not badAmp:
 
 1757             limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
 
 1758         if math.isfinite(self.config.saturation):
 
 1759             limits.update({self.config.saturatedMaskName: self.config.saturation})
 
 1761         for maskName, maskThreshold 
in limits.items():
 
 1762             if not math.isnan(maskThreshold):
 
 1763                 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
 
 1764                 isrFunctions.makeThresholdMask(
 
 1765                     maskedImage=dataView,
 
 1766                     threshold=maskThreshold,
 
 1772         maskView = 
afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
 
 1774         maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
 
 1775                                             self.config.suspectMaskName])
 
 1776         if numpy.all(maskView.getArray() & maskVal > 0):
 
 1778             maskView |= maskView.getPlaneBitMask(
"BAD")
 
 1783         """Apply overscan correction in place. 
 1785         This method does initial pixel rejection of the overscan 
 1786         region.  The overscan can also be optionally segmented to 
 1787         allow for discontinuous overscan responses to be fit 
 1788         separately.  The actual overscan subtraction is performed by 
 1789         the `lsst.ip.isr.isrFunctions.overscanCorrection` function, 
 1790         which is called here after the amplifier is preprocessed. 
 1794         ccdExposure : `lsst.afw.image.Exposure` 
 1795             Exposure to have overscan correction performed. 
 1796         amp : `lsst.afw.table.AmpInfoCatalog` 
 1797             The amplifier to consider while correcting the overscan. 
 1801         overscanResults : `lsst.pipe.base.Struct` 
 1802             Result struct with components: 
 1803             - ``imageFit`` : scalar or `lsst.afw.image.Image` 
 1804                 Value or fit subtracted from the amplifier image data. 
 1805             - ``overscanFit`` : scalar or `lsst.afw.image.Image` 
 1806                 Value or fit subtracted from the overscan image data. 
 1807             - ``overscanImage`` : `lsst.afw.image.Image` 
 1808                 Image of the overscan region with the overscan 
 1809                 correction applied. This quantity is used to estimate 
 1810                 the amplifier read noise empirically. 
 1815             Raised if the ``amp`` does not contain raw pixel information. 
 1819         lsst.ip.isr.isrFunctions.overscanCorrection 
 1821         if amp.getRawHorizontalOverscanBBox().isEmpty():
 
 1822             self.log.
info(
"ISR_OSCAN: No overscan region.  Not performing overscan correction.")
 
 1826         statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
 
 1829         dataBBox = amp.getRawDataBBox()
 
 1830         oscanBBox = amp.getRawHorizontalOverscanBBox()
 
 1834         prescanBBox = amp.getRawPrescanBBox()
 
 1835         if (oscanBBox.getBeginX() > prescanBBox.getBeginX()):  
 
 1836             dx0 += self.config.overscanNumLeadingColumnsToSkip
 
 1837             dx1 -= self.config.overscanNumTrailingColumnsToSkip
 
 1839             dx0 += self.config.overscanNumTrailingColumnsToSkip
 
 1840             dx1 -= self.config.overscanNumLeadingColumnsToSkip
 
 1846         if ((self.config.overscanBiasJump 
and 
 1847              self.config.overscanBiasJumpLocation) 
and 
 1848             (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword) 
and 
 1849              ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword) 
in 
 1850              self.config.overscanBiasJumpDevices)):
 
 1851             if amp.getReadoutCorner() 
in (ReadoutCorner.LL, ReadoutCorner.LR):
 
 1852                 yLower = self.config.overscanBiasJumpLocation
 
 1853                 yUpper = dataBBox.getHeight() - yLower
 
 1855                 yUpper = self.config.overscanBiasJumpLocation
 
 1856                 yLower = dataBBox.getHeight() - yUpper
 
 1875                                                                      oscanBBox.getHeight())))
 
 1878         for imageBBox, overscanBBox 
in zip(imageBBoxes, overscanBBoxes):
 
 1879             ampImage = ccdExposure.maskedImage[imageBBox]
 
 1880             overscanImage = ccdExposure.maskedImage[overscanBBox]
 
 1882             overscanArray = overscanImage.image.array
 
 1883             median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
 
 1884             bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
 
 1885             overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
 
 1888             statControl.setAndMask(ccdExposure.mask.getPlaneBitMask(
"SAT"))
 
 1890             overscanResults = self.overscan.
run(ampImage.getImage(), overscanImage)
 
 1893             levelStat = afwMath.MEDIAN
 
 1894             sigmaStat = afwMath.STDEVCLIP
 
 1897                                               self.config.qa.flatness.nIter)
 
 1898             metadata = ccdExposure.getMetadata()
 
 1899             ampNum = amp.getName()
 
 1901             if isinstance(overscanResults.overscanFit, float):
 
 1902                 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
 
 1903                 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
 
 1906                 metadata.set(
"ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
 
 1907                 metadata.set(
"ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
 
 1909         return overscanResults
 
 1912         """Set the variance plane using the amplifier gain and read noise 
 1914         The read noise is calculated from the ``overscanImage`` if the 
 1915         ``doEmpiricalReadNoise`` option is set in the configuration; otherwise 
 1916         the value from the amplifier data is used. 
 1920         ampExposure : `lsst.afw.image.Exposure` 
 1921             Exposure to process. 
 1922         amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp` 
 1923             Amplifier detector data. 
 1924         overscanImage : `lsst.afw.image.MaskedImage`, optional. 
 1925             Image of overscan, required only for empirical read noise. 
 1929         lsst.ip.isr.isrFunctions.updateVariance 
 1931         maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
 
 1932         gain = amp.getGain()
 
 1934         if math.isnan(gain):
 
 1936             self.log.
warn(
"Gain set to NAN!  Updating to 1.0 to generate Poisson variance.")
 
 1939             self.log.
warn(
"Gain for amp %s == %g <= 0; setting to %f.",
 
 1940                           amp.getName(), gain, patchedGain)
 
 1943         if self.config.doEmpiricalReadNoise 
and overscanImage 
is None:
 
 1944             self.log.
info(
"Overscan is none for EmpiricalReadNoise.")
 
 1946         if self.config.doEmpiricalReadNoise 
and overscanImage 
is not None:
 
 1948             stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
 
 1950             self.log.
info(
"Calculated empirical read noise for amp %s: %f.",
 
 1951                           amp.getName(), readNoise)
 
 1953             readNoise = amp.getReadNoise()
 
 1955         isrFunctions.updateVariance(
 
 1956             maskedImage=ampExposure.getMaskedImage(),
 
 1958             readNoise=readNoise,
 
 1962         """!Apply dark correction in place. 
 1966         exposure : `lsst.afw.image.Exposure` 
 1967             Exposure to process. 
 1968         darkExposure : `lsst.afw.image.Exposure` 
 1969             Dark exposure of the same size as ``exposure``. 
 1970         invert : `Bool`, optional 
 1971             If True, re-add the dark to an already corrected image. 
 1976             Raised if either ``exposure`` or ``darkExposure`` do not 
 1977             have their dark time defined. 
 1981         lsst.ip.isr.isrFunctions.darkCorrection 
 1983         expScale = exposure.getInfo().getVisitInfo().getDarkTime()
 
 1984         if math.isnan(expScale):
 
 1985             raise RuntimeError(
"Exposure darktime is NAN.")
 
 1986         if darkExposure.getInfo().getVisitInfo() 
is not None \
 
 1987                 and not math.isnan(darkExposure.getInfo().getVisitInfo().getDarkTime()):
 
 1988             darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
 
 1992             self.log.
warn(
"darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
 
 1995         isrFunctions.darkCorrection(
 
 1996             maskedImage=exposure.getMaskedImage(),
 
 1997             darkMaskedImage=darkExposure.getMaskedImage(),
 
 1999             darkScale=darkScale,
 
 2001             trimToFit=self.config.doTrimToMatchCalib
 
 2005         """!Check if linearization is needed for the detector cameraGeom. 
 2007         Checks config.doLinearize and the linearity type of the first 
 2012         detector : `lsst.afw.cameraGeom.Detector` 
 2013             Detector to get linearity type from. 
 2017         doLinearize : `Bool` 
 2018             If True, linearization should be performed. 
 2020         return self.config.doLinearize 
and \
 
 2021             detector.getAmplifiers()[0].getLinearityType() != NullLinearityType
 
 2024         """!Apply flat correction in place. 
 2028         exposure : `lsst.afw.image.Exposure` 
 2029             Exposure to process. 
 2030         flatExposure : `lsst.afw.image.Exposure` 
 2031             Flat exposure of the same size as ``exposure``. 
 2032         invert : `Bool`, optional 
 2033             If True, unflatten an already flattened image. 
 2037         lsst.ip.isr.isrFunctions.flatCorrection 
 2039         isrFunctions.flatCorrection(
 
 2040             maskedImage=exposure.getMaskedImage(),
 
 2041             flatMaskedImage=flatExposure.getMaskedImage(),
 
 2042             scalingType=self.config.flatScalingType,
 
 2043             userScale=self.config.flatUserScale,
 
 2045             trimToFit=self.config.doTrimToMatchCalib
 
 2049         """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. 
 2053         exposure : `lsst.afw.image.Exposure` 
 2054             Exposure to process.  Only the amplifier DataSec is processed. 
 2055         amp : `lsst.afw.table.AmpInfoCatalog` 
 2056             Amplifier detector data. 
 2060         lsst.ip.isr.isrFunctions.makeThresholdMask 
 2062         if not math.isnan(amp.getSaturation()):
 
 2063             maskedImage = exposure.getMaskedImage()
 
 2064             dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
 
 2065             isrFunctions.makeThresholdMask(
 
 2066                 maskedImage=dataView,
 
 2067                 threshold=amp.getSaturation(),
 
 2069                 maskName=self.config.saturatedMaskName,
 
 2073         """!Interpolate over saturated pixels, in place. 
 2075         This method should be called after `saturationDetection`, to 
 2076         ensure that the saturated pixels have been identified in the 
 2077         SAT mask.  It should also be called after `assembleCcd`, since 
 2078         saturated regions may cross amplifier boundaries. 
 2082         exposure : `lsst.afw.image.Exposure` 
 2083             Exposure to process. 
 2087         lsst.ip.isr.isrTask.saturationDetection 
 2088         lsst.ip.isr.isrFunctions.interpolateFromMask 
 2090         isrFunctions.interpolateFromMask(
 
 2091             maskedImage=exposure.getMaskedImage(),
 
 2092             fwhm=self.config.fwhm,
 
 2093             growSaturatedFootprints=self.config.growSaturationFootprintSize,
 
 2094             maskNameList=
list(self.config.saturatedMaskName),
 
 2098         """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. 
 2102         exposure : `lsst.afw.image.Exposure` 
 2103             Exposure to process.  Only the amplifier DataSec is processed. 
 2104         amp : `lsst.afw.table.AmpInfoCatalog` 
 2105             Amplifier detector data. 
 2109         lsst.ip.isr.isrFunctions.makeThresholdMask 
 2113         Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). 
 2114         This is intended to indicate pixels that may be affected by unknown systematics; 
 2115         for example if non-linearity corrections above a certain level are unstable 
 2116         then that would be a useful value for suspectLevel. A value of `nan` indicates 
 2117         that no such level exists and no pixels are to be masked as suspicious. 
 2119         suspectLevel = amp.getSuspectLevel()
 
 2120         if math.isnan(suspectLevel):
 
 2123         maskedImage = exposure.getMaskedImage()
 
 2124         dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
 
 2125         isrFunctions.makeThresholdMask(
 
 2126             maskedImage=dataView,
 
 2127             threshold=suspectLevel,
 
 2129             maskName=self.config.suspectMaskName,
 
 2133         """!Mask defects using mask plane "BAD", in place. 
 2137         exposure : `lsst.afw.image.Exposure` 
 2138             Exposure to process. 
 2139         defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 
 2140                          `lsst.afw.image.DefectBase`. 
 2141             List of defects to mask. 
 2145         Call this after CCD assembly, since defects may cross amplifier boundaries. 
 2147         maskedImage = exposure.getMaskedImage()
 
 2148         if not isinstance(defectBaseList, Defects):
 
 2150             defectList = 
Defects(defectBaseList)
 
 2152             defectList = defectBaseList
 
 2153         defectList.maskPixels(maskedImage, maskName=
"BAD")
 
 2155     def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT"):
 
 2156         """!Mask edge pixels with applicable mask plane. 
 2160         exposure : `lsst.afw.image.Exposure` 
 2161             Exposure to process. 
 2162         numEdgePixels : `int`, optional 
 2163             Number of edge pixels to mask. 
 2164         maskPlane : `str`, optional 
 2165             Mask plane name to use. 
 2167         maskedImage = exposure.getMaskedImage()
 
 2168         maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
 
 2170         if numEdgePixels > 0:
 
 2171             goodBBox = maskedImage.getBBox()
 
 2173             goodBBox.grow(-numEdgePixels)
 
 2175             SourceDetectionTask.setEdgeBits(
 
 2182         """Mask and interpolate defects using mask plane "BAD", in place. 
 2186         exposure : `lsst.afw.image.Exposure` 
 2187             Exposure to process. 
 2188         defectBaseList : `lsst.meas.algorithms.Defects` or `list` of 
 2189                          `lsst.afw.image.DefectBase`. 
 2190             List of defects to mask and interpolate. 
 2194         lsst.ip.isr.isrTask.maskDefect() 
 2197         self.
maskEdges(exposure, numEdgePixels=self.config.numEdgeSuspect,
 
 2198                        maskPlane=
"SUSPECT")
 
 2199         isrFunctions.interpolateFromMask(
 
 2200             maskedImage=exposure.getMaskedImage(),
 
 2201             fwhm=self.config.fwhm,
 
 2202             growSaturatedFootprints=0,
 
 2203             maskNameList=[
"BAD"],
 
 2207         """Mask NaNs using mask plane "UNMASKEDNAN", in place. 
 2211         exposure : `lsst.afw.image.Exposure` 
 2212             Exposure to process. 
 2216         We mask over all NaNs, including those that are masked with 
 2217         other bits (because those may or may not be interpolated over 
 2218         later, and we want to remove all NaNs).  Despite this 
 2219         behaviour, the "UNMASKEDNAN" mask plane is used to preserve 
 2220         the historical name. 
 2222         maskedImage = exposure.getMaskedImage()
 
 2225         maskedImage.getMask().addMaskPlane(
"UNMASKEDNAN")
 
 2226         maskVal = maskedImage.getMask().getPlaneBitMask(
"UNMASKEDNAN")
 
 2227         numNans = 
maskNans(maskedImage, maskVal)
 
 2228         self.metadata.
set(
"NUMNANS", numNans)
 
 2230             self.log.
warn(
"There were %d unmasked NaNs.", numNans)
 
 2233         """"Mask and interpolate NaNs using mask plane "UNMASKEDNAN", in place. 
 2237         exposure : `lsst.afw.image.Exposure` 
 2238             Exposure to process. 
 2242         lsst.ip.isr.isrTask.maskNan() 
 2245         isrFunctions.interpolateFromMask(
 
 2246             maskedImage=exposure.getMaskedImage(),
 
 2247             fwhm=self.config.fwhm,
 
 2248             growSaturatedFootprints=0,
 
 2249             maskNameList=[
"UNMASKEDNAN"],
 
 2253         """Measure the image background in subgrids, for quality control purposes. 
 2257         exposure : `lsst.afw.image.Exposure` 
 2258             Exposure to process. 
 2259         IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig` 
 2260             Configuration object containing parameters on which background 
 2261             statistics and subgrids to use. 
 2263         if IsrQaConfig 
is not None:
 
 2265                                                      IsrQaConfig.flatness.nIter)
 
 2266             maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask([
"BAD", 
"SAT", 
"DETECTED"])
 
 2267             statsControl.setAndMask(maskVal)
 
 2268             maskedImage = exposure.getMaskedImage()
 
 2270             skyLevel = stats.getValue(afwMath.MEDIAN)
 
 2271             skySigma = stats.getValue(afwMath.STDEVCLIP)
 
 2272             self.log.
info(
"Flattened sky level: %f +/- %f.", skyLevel, skySigma)
 
 2273             metadata = exposure.getMetadata()
 
 2274             metadata.set(
'SKYLEVEL', skyLevel)
 
 2275             metadata.set(
'SKYSIGMA', skySigma)
 
 2278             stat = afwMath.MEANCLIP 
if IsrQaConfig.flatness.doClip 
else afwMath.MEAN
 
 2279             meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
 
 2280             meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
 
 2281             nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
 
 2282             nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
 
 2283             skyLevels = numpy.zeros((nX, nY))
 
 2286                 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
 
 2288                     xc = meshXHalf + i * IsrQaConfig.flatness.meshX
 
 2290                     xLLC = xc - meshXHalf
 
 2291                     yLLC = yc - meshYHalf
 
 2292                     xURC = xc + meshXHalf - 1
 
 2293                     yURC = yc + meshYHalf - 1
 
 2296                     miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
 
 2300             good = numpy.where(numpy.isfinite(skyLevels))
 
 2301             skyMedian = numpy.median(skyLevels[good])
 
 2302             flatness = (skyLevels[good] - skyMedian) / skyMedian
 
 2303             flatness_rms = numpy.std(flatness)
 
 2304             flatness_pp = flatness.max() - flatness.min() 
if len(flatness) > 0 
else numpy.nan
 
 2306             self.log.
info(
"Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
 
 2307             self.log.
info(
"Sky flatness in %dx%d grids - pp: %f rms: %f.",
 
 2308                           nX, nY, flatness_pp, flatness_rms)
 
 2310             metadata.set(
'FLATNESS_PP', float(flatness_pp))
 
 2311             metadata.set(
'FLATNESS_RMS', float(flatness_rms))
 
 2312             metadata.set(
'FLATNESS_NGRIDS', 
'%dx%d' % (nX, nY))
 
 2313             metadata.set(
'FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
 
 2314             metadata.set(
'FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
 
 2317         """Set an approximate magnitude zero point for the exposure. 
 2321         exposure : `lsst.afw.image.Exposure` 
 2322             Exposure to process. 
 2325         if filterName 
in self.config.fluxMag0T1:
 
 2326             fluxMag0 = self.config.fluxMag0T1[filterName]
 
 2328             self.log.
warn(
"No rough magnitude zero point set for filter %s.", filterName)
 
 2329             fluxMag0 = self.config.defaultFluxMag0T1
 
 2331         expTime = exposure.getInfo().getVisitInfo().getExposureTime()
 
 2333             self.log.
warn(
"Non-positive exposure time; skipping rough zero point.")
 
 2336         self.log.
info(
"Setting rough magnitude zero point: %f", 2.5*math.log10(fluxMag0*expTime))
 
 2340         """!Set the valid polygon as the intersection of fpPolygon and the ccd corners. 
 2344         ccdExposure : `lsst.afw.image.Exposure` 
 2345             Exposure to process. 
 2346         fpPolygon : `lsst.afw.geom.Polygon` 
 2347             Polygon in focal plane coordinates. 
 2350         ccd = ccdExposure.getDetector()
 
 2351         fpCorners = ccd.getCorners(FOCAL_PLANE)
 
 2352         ccdPolygon = 
Polygon(fpCorners)
 
 2355         intersect = ccdPolygon.intersectionSingle(fpPolygon)
 
 2358         ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
 
 2359         validPolygon = 
Polygon(ccdPoints)
 
 2360         ccdExposure.getInfo().setValidPolygon(validPolygon)
 
 2364         """Context manager that applies and removes flats and darks, 
 2365         if the task is configured to apply them. 
 2369         exp : `lsst.afw.image.Exposure` 
 2370             Exposure to process. 
 2371         flat : `lsst.afw.image.Exposure` 
 2372             Flat exposure the same size as ``exp``. 
 2373         dark : `lsst.afw.image.Exposure`, optional 
 2374             Dark exposure the same size as ``exp``. 
 2378         exp : `lsst.afw.image.Exposure` 
 2379             The flat and dark corrected exposure. 
 2381         if self.config.doDark 
and dark 
is not None:
 
 2383         if self.config.doFlat:
 
 2388             if self.config.doFlat:
 
 2390             if self.config.doDark 
and dark 
is not None:
 
 2394         """Utility function to examine ISR exposure at different stages. 
 2398         exposure : `lsst.afw.image.Exposure` 
 2401             State of processing to view. 
 2406             display.scale(
'asinh', 
'zscale')
 
 2407             display.mtv(exposure)
 
 2408             prompt = 
"Press Enter to continue [c]... " 
 2410                 ans = input(prompt).lower()
 
 2411                 if ans 
in (
"", 
"c",):
 
 2416     """A Detector-like object that supports returning gain and saturation level 
 2418     This is used when the input exposure does not have a detector. 
 2422     exposure : `lsst.afw.image.Exposure` 
 2423         Exposure to generate a fake amplifier for. 
 2424     config : `lsst.ip.isr.isrTaskConfig` 
 2425         Configuration to apply to the fake amplifier. 
 2429         self.
_bbox = exposure.getBBox(afwImage.LOCAL)
 
 2431         self.
_gain = config.gain
 
 2458     isr = pexConfig.ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
 
 2462     """Task to wrap the default IsrTask to allow it to be retargeted. 
 2464     The standard IsrTask can be called directly from a command line 
 2465     program, but doing so removes the ability of the task to be 
 2466     retargeted.  As most cameras override some set of the IsrTask 
 2467     methods, this would remove those data-specific methods in the 
 2468     output post-ISR images.  This wrapping class fixes the issue, 
 2469     allowing identical post-ISR images to be generated by both the 
 2470     processCcd and isrTask code. 
 2472     ConfigClass = RunIsrConfig
 
 2473     _DefaultName = 
"runIsr" 
 2477         self.makeSubtask(
"isr")
 
 2483         dataRef : `lsst.daf.persistence.ButlerDataRef` 
 2484             data reference of the detector data to be processed 
 2488         result : `pipeBase.Struct` 
 2489             Result struct with component: 
 2491             - exposure : `lsst.afw.image.Exposure` 
 2492                 Post-ISR processed exposure.