1020 inputs = butlerQC.get(inputRefs)
1023 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
1024 except Exception
as e:
1025 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
1028 detector = inputs[
'ccdExposure'].getDetector()
1031 additionalInputDates = {}
1033 if self.config.doCrosstalk
is True:
1036 if 'crosstalk' in inputs
and inputs[
'crosstalk']
is not None:
1037 if not isinstance(inputs[
'crosstalk'], CrosstalkCalib):
1038 inputs[
'crosstalk'] = CrosstalkCalib.fromTable(inputs[
'crosstalk'])
1040 coeffVector = (self.config.crosstalk.crosstalkValues
1041 if self.config.crosstalk.useConfigCoefficients
else None)
1042 crosstalkCalib =
CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1043 inputs[
'crosstalk'] = crosstalkCalib
1044 if inputs[
'crosstalk'].interChip
and len(inputs[
'crosstalk'].interChip) > 0:
1045 if 'crosstalkSources' not in inputs:
1046 self.log.warning(
"No crosstalkSources found for chip with interChip terms!")
1049 if 'linearizer' in inputs:
1050 if isinstance(inputs[
'linearizer'], dict):
1052 linearizer.fromYaml(inputs[
'linearizer'])
1053 self.log.warning(
"Dictionary linearizers will be deprecated in DM-28741.")
1054 elif isinstance(inputs[
'linearizer'], numpy.ndarray):
1058 self.log.warning(
"Bare lookup table linearizers will be deprecated in DM-28741.")
1060 linearizer = inputs[
'linearizer']
1061 linearizer.log = self.log
1062 inputs[
'linearizer'] = linearizer
1065 self.log.warning(
"Constructing linearizer from cameraGeom information.")
1067 if self.config.doDefect
is True:
1068 if "defects" in inputs
and inputs[
'defects']
is not None:
1072 if not isinstance(inputs[
"defects"], Defects):
1073 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1077 brighterFatterSource =
None
1078 if self.config.doBrighterFatter:
1079 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1080 if brighterFatterKernel
is None:
1084 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1085 brighterFatterSource =
'bfKernel'
1086 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1088 if brighterFatterKernel
is None:
1090 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1091 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1098 brighterFatterSource =
'newBFKernel'
1099 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1101 detName = detector.getName()
1102 level = brighterFatterKernel.level
1105 inputs[
'bfGains'] = brighterFatterKernel.gain
1106 if self.config.brighterFatterLevel ==
'DETECTOR':
1108 if level ==
'DETECTOR':
1109 if detName
in brighterFatterKernel.detKernels:
1110 kernel = brighterFatterKernel.detKernels[detName]
1112 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1113 elif level ==
'AMP':
1114 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1116 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1117 kernel = brighterFatterKernel.detKernels[detName]
1119 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1123 inputs[
'bfKernel'] = numpy.transpose(kernel)
1124 elif self.config.brighterFatterLevel ==
'AMP':
1125 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1127 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1128 expId = inputs[
'ccdExposure'].info.id
1129 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1131 assembler=self.assembleCcd
1132 if self.config.doAssembleIsrExposures
else None)
1134 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1136 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1137 if 'strayLightData' not in inputs:
1138 inputs[
'strayLightData'] =
None
1140 if self.config.doHeaderProvenance:
1142 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1146 additionalInputs = []
1147 if self.config.doBrighterFatter:
1148 additionalInputs.append(brighterFatterSource)
1150 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1151 reference = getattr(inputRefs, inputName,
None)
1152 if reference
is not None and hasattr(reference,
"run"):
1153 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1154 runValue = reference.run
1155 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1156 idValue = str(reference.id)
1157 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1159 if inputName
in additionalInputDates:
1160 dateValue = additionalInputDates[inputName]
1164 exposureMetadata[runKey] = runValue
1165 exposureMetadata[idKey] = idValue
1166 exposureMetadata[dateKey] = dateValue
1168 outputs = self.
run(**inputs)
1169 butlerQC.put(outputs, outputRefs)
1172 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1173 crosstalk=None, crosstalkSources=None,
1174 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1175 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1176 sensorTransmission=
None, atmosphereTransmission=
None,
1177 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1178 deferredChargeCalib=
None,
1180 """Perform instrument signature removal on an exposure.
1182 Steps included in the ISR processing, in order performed, are:
1184 - saturation and suspect pixel masking
1185 - overscan subtraction
1186 - CCD assembly of individual amplifiers
1188 - variance image construction
1189 - linearization of non-linear response
1191 - brighter-fatter correction
1194 - stray light subtraction
1196 - masking of known defects and camera specific features
1197 - vignette calculation
1198 - appending transmission curve and distortion model
1202 ccdExposure : `lsst.afw.image.Exposure`
1203 The raw exposure that is to be run through ISR. The
1204 exposure is modified by this method.
1205 camera : `lsst.afw.cameraGeom.Camera`, optional
1206 The camera geometry for this exposure. Required if
1207 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1208 ``flat`` does not have an associated detector.
1209 bias : `lsst.afw.image.Exposure`, optional
1210 Bias calibration frame.
1211 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1212 Functor for linearization.
1213 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1214 Calibration for crosstalk.
1215 crosstalkSources : `list`, optional
1216 List of possible crosstalk sources.
1217 dark : `lsst.afw.image.Exposure`, optional
1218 Dark calibration frame.
1219 flat : `lsst.afw.image.Exposure`, optional
1220 Flat calibration frame.
1221 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1222 Photon transfer curve dataset, with, e.g., gains
1224 bfKernel : `numpy.ndarray`, optional
1225 Brighter-fatter kernel.
1226 bfGains : `dict` of `float`, optional
1227 Gains used to override the detector's nominal gains for the
1228 brighter-fatter correction. A dict keyed by amplifier name for
1229 the detector in question.
1230 defects : `lsst.ip.isr.Defects`, optional
1232 fringes : `lsst.pipe.base.Struct`, optional
1233 Struct containing the fringe correction data, with
1237 fringe calibration frame (`lsst.afw.image.Exposure`)
1239 random seed derived from the ``ccdExposureId`` for random
1240 number generator (`numpy.uint32`)
1241 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1242 A ``TransmissionCurve`` that represents the throughput of the,
1243 optics, to be evaluated in focal-plane coordinates.
1244 filterTransmission : `lsst.afw.image.TransmissionCurve`
1245 A ``TransmissionCurve`` that represents the throughput of the
1246 filter itself, to be evaluated in focal-plane coordinates.
1247 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1248 A ``TransmissionCurve`` that represents the throughput of the
1249 sensor itself, to be evaluated in post-assembly trimmed detector
1251 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1252 A ``TransmissionCurve`` that represents the throughput of the
1253 atmosphere, assumed to be spatially constant.
1254 detectorNum : `int`, optional
1255 The integer number for the detector to process.
1256 strayLightData : `object`, optional
1257 Opaque object containing calibration information for stray-light
1258 correction. If `None`, no correction will be performed.
1259 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1260 Illumination correction image.
1264 result : `lsst.pipe.base.Struct`
1265 Result struct with component:
1268 The fully ISR corrected exposure.
1269 (`lsst.afw.image.Exposure`)
1271 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1273 Thumbnail image of the exposure after overscan subtraction.
1276 Thumbnail image of the exposure after flat-field correction.
1278 ``outputStatistics``
1279 Values of the additional statistics calculated.
1284 Raised if a configuration option is set to `True`, but the
1285 required calibration data has not been specified.
1289 The current processed exposure can be viewed by setting the
1290 appropriate `lsstDebug` entries in the ``debug.display``
1291 dictionary. The names of these entries correspond to some of
1292 the `IsrTaskConfig` Boolean options, with the value denoting the
1293 frame to use. The exposure is shown inside the matching
1294 option check and after the processing of that step has
1295 finished. The steps with debug points are:
1306 In addition, setting the ``postISRCCD`` entry displays the
1307 exposure after all ISR processing has finished.
1310 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1315 ccd = ccdExposure.getDetector()
1316 filterLabel = ccdExposure.getFilter()
1317 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1320 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1321 ccd = [
FakeAmp(ccdExposure, self.config)]
1324 if self.config.doBias
and bias
is None:
1325 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1327 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1328 if self.config.doBrighterFatter
and bfKernel
is None:
1329 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1330 if self.config.doDark
and dark
is None:
1331 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1332 if self.config.doFlat
and flat
is None:
1333 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1334 if self.config.doDefect
and defects
is None:
1335 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1336 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1337 and fringes.fringes
is None):
1342 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1343 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1344 and illumMaskedImage
is None):
1345 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1346 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1347 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1350 exposureMetadata = ccdExposure.getMetadata()
1351 if self.config.doBias:
1353 if self.config.doBrighterFatter:
1355 if self.config.doCrosstalk:
1357 if self.config.doDark:
1359 if self.config.doDefect:
1361 if self.config.doDeferredCharge:
1363 if self.config.doFlat:
1365 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1367 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1371 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1373 if self.config.doStrayLight:
1377 if self.config.doConvertIntToFloat:
1378 self.log.info(
"Converting exposure to floating point values.")
1381 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1382 self.log.info(
"Applying bias correction.")
1383 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1384 trimToFit=self.config.doTrimToMatchCalib)
1390 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1392 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1397 if ccdExposure.getBBox().contains(amp.getBBox()):
1402 if self.config.doOverscan
and not badAmp:
1405 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1406 if overscanResults
is not None and \
1407 self.config.qa
is not None and self.config.qa.saveStats
is True:
1408 if isinstance(overscanResults.overscanMean, float):
1410 mean = overscanResults.overscanMean
1411 sigma = overscanResults.overscanSigma
1412 residMean = overscanResults.residualMean
1413 residSigma = overscanResults.residualSigma
1417 mean = overscanResults.overscanMean[0]
1418 sigma = overscanResults.overscanSigma[0]
1419 residMean = overscanResults.residualMean[0]
1420 residSigma = overscanResults.residualSigma[0]
1422 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = mean
1423 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1424 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1425 amp.getName(), mean, sigma)
1427 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1428 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1429 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1430 amp.getName(), residMean, residSigma)
1432 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1435 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1436 overscanResults =
None
1438 overscans.append(overscanResults
if overscanResults
is not None else None)
1440 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1442 if self.config.doDeferredCharge:
1443 self.log.info(
"Applying deferred charge/CTI correction.")
1444 self.deferredChargeCorrection.
run(ccdExposure, deferredChargeCalib)
1445 self.
debugView(ccdExposure,
"doDeferredCharge")
1447 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1448 self.log.info(
"Applying crosstalk correction.")
1449 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1450 crosstalkSources=crosstalkSources, camera=camera)
1451 self.
debugView(ccdExposure,
"doCrosstalk")
1453 if self.config.doAssembleCcd:
1454 self.log.info(
"Assembling CCD from amplifiers.")
1455 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1457 if self.config.expectWcs
and not ccdExposure.getWcs():
1458 self.log.warning(
"No WCS found in input exposure.")
1459 self.
debugView(ccdExposure,
"doAssembleCcd")
1462 if self.config.qa.doThumbnailOss:
1463 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1465 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1466 self.log.info(
"Applying bias correction.")
1467 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1468 trimToFit=self.config.doTrimToMatchCalib)
1471 if self.config.doVariance:
1472 for amp, overscanResults
in zip(ccd, overscans):
1473 if ccdExposure.getBBox().contains(amp.getBBox()):
1474 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1475 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1476 if overscanResults
is not None:
1478 overscanImage=overscanResults.overscanImage,
1484 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1485 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1486 afwMath.MEDIAN | afwMath.STDEVCLIP)
1487 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1488 qaStats.getValue(afwMath.MEDIAN)
1489 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1490 qaStats.getValue(afwMath.STDEVCLIP)
1491 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1492 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1493 qaStats.getValue(afwMath.STDEVCLIP))
1494 if self.config.maskNegativeVariance:
1498 self.log.info(
"Applying linearizer.")
1499 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1500 detector=ccd, log=self.log)
1502 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1503 self.log.info(
"Applying crosstalk correction.")
1504 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1505 crosstalkSources=crosstalkSources, isTrimmed=
True)
1506 self.
debugView(ccdExposure,
"doCrosstalk")
1511 if self.config.doDefect:
1512 self.log.info(
"Masking defects.")
1515 if self.config.numEdgeSuspect > 0:
1516 self.log.info(
"Masking edges as SUSPECT.")
1517 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1518 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1520 if self.config.doNanMasking:
1521 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1524 if self.config.doWidenSaturationTrails:
1525 self.log.info(
"Widening saturation trails.")
1526 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1528 if self.config.doCameraSpecificMasking:
1529 self.log.info(
"Masking regions for camera specific reasons.")
1530 self.masking.
run(ccdExposure)
1532 if self.config.doBrighterFatter:
1542 interpExp = ccdExposure.clone()
1544 isrFunctions.interpolateFromMask(
1545 maskedImage=interpExp.getMaskedImage(),
1546 fwhm=self.config.fwhm,
1547 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1548 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1550 bfExp = interpExp.clone()
1552 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1553 type(bfKernel), type(bfGains))
1554 if self.config.doFluxConservingBrighterFatterCorrection:
1555 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1558 self.config.brighterFatterMaxIter,
1559 self.config.brighterFatterThreshold,
1560 self.config.brighterFatterApplyGain,
1564 bfResults = isrFunctions.brighterFatterCorrection(
1567 self.config.brighterFatterMaxIter,
1568 self.config.brighterFatterThreshold,
1569 self.config.brighterFatterApplyGain,
1572 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1573 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1576 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1578 image = ccdExposure.getMaskedImage().getImage()
1579 bfCorr = bfExp.getMaskedImage().getImage()
1580 bfCorr -= interpExp.getMaskedImage().getImage()
1589 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1590 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1593 if self.config.brighterFatterMaskGrowSize > 0:
1594 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1595 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1596 isrFunctions.growMasks(ccdExposure.getMask(),
1597 radius=self.config.brighterFatterMaskGrowSize,
1598 maskNameList=maskPlane,
1599 maskValue=maskPlane)
1601 self.
debugView(ccdExposure,
"doBrighterFatter")
1603 if self.config.doDark:
1604 self.log.info(
"Applying dark correction.")
1608 if self.config.doFringe
and not self.config.fringeAfterFlat:
1609 self.log.info(
"Applying fringe correction before flat.")
1610 self.fringe.
run(ccdExposure, **fringes.getDict())
1613 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1614 self.log.info(
"Checking strayLight correction.")
1615 self.strayLight.
run(ccdExposure, strayLightData)
1616 self.
debugView(ccdExposure,
"doStrayLight")
1618 if self.config.doFlat:
1619 self.log.info(
"Applying flat correction.")
1623 if self.config.doApplyGains:
1624 self.log.info(
"Applying gain correction instead of flat.")
1625 if self.config.usePtcGains:
1626 self.log.info(
"Using gains from the Photon Transfer Curve.")
1627 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1630 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1632 if self.config.doFringe
and self.config.fringeAfterFlat:
1633 self.log.info(
"Applying fringe correction after flat.")
1634 self.fringe.
run(ccdExposure, **fringes.getDict())
1636 if self.config.doVignette:
1637 if self.config.doMaskVignettePolygon:
1638 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1640 self.log.info(
"Constructing and attaching vignette polygon.")
1642 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1643 vignetteValue=self.config.vignetteValue, log=self.log)
1645 if self.config.doAttachTransmissionCurve:
1646 self.log.info(
"Adding transmission curves.")
1647 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1648 filterTransmission=filterTransmission,
1649 sensorTransmission=sensorTransmission,
1650 atmosphereTransmission=atmosphereTransmission)
1652 flattenedThumb =
None
1653 if self.config.qa.doThumbnailFlattened:
1654 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1656 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1657 self.log.info(
"Performing illumination correction.")
1658 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1659 illumMaskedImage, illumScale=self.config.illumScale,
1660 trimToFit=self.config.doTrimToMatchCalib)
1663 if self.config.doSaveInterpPixels:
1664 preInterpExp = ccdExposure.clone()
1679 if self.config.doSetBadRegions:
1680 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1681 if badPixelCount > 0:
1682 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1684 if self.config.doInterpolate:
1685 self.log.info(
"Interpolating masked pixels.")
1686 isrFunctions.interpolateFromMask(
1687 maskedImage=ccdExposure.getMaskedImage(),
1688 fwhm=self.config.fwhm,
1689 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1690 maskNameList=list(self.config.maskListToInterpolate)
1696 if self.config.doAmpOffset:
1697 self.log.info(
"Correcting amp offsets.")
1698 self.ampOffset.
run(ccdExposure)
1700 if self.config.doMeasureBackground:
1701 self.log.info(
"Measuring background level.")
1704 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1706 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1707 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1708 afwMath.MEDIAN | afwMath.STDEVCLIP)
1709 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1710 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1711 qaStats.getValue(afwMath.STDEVCLIP)
1712 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1713 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1714 qaStats.getValue(afwMath.STDEVCLIP))
1717 if self.config.doStandardStatistics:
1718 metadata = ccdExposure.getMetadata()
1720 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1721 ampName = amp.getName()
1722 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1723 ampExposure.getMaskedImage(),
1724 [self.config.saturatedMaskName]
1726 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1727 ampExposure.getMaskedImage(),
1730 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1731 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1733 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1734 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1735 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1737 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1738 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1739 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1740 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1742 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1745 outputStatistics =
None
1746 if self.config.doCalculateStatistics:
1747 outputStatistics = self.isrStats.
run(ccdExposure, overscanResults=overscans,
1751 outputBin1Exposure =
None
1752 outputBin2Exposure =
None
1753 if self.config.doBinnedExposures:
1754 outputBin1Exposure, outputBin2Exposure = self.
makeBinnedImages(ccdExposure)
1756 self.
debugView(ccdExposure,
"postISRCCD")
1758 return pipeBase.Struct(
1759 exposure=ccdExposure,
1761 flattenedThumb=flattenedThumb,
1763 outputBin1Exposure=outputBin1Exposure,
1764 outputBin2Exposure=outputBin2Exposure,
1766 preInterpExposure=preInterpExp,
1767 outputExposure=ccdExposure,
1768 outputOssThumbnail=ossThumb,
1769 outputFlattenedThumbnail=flattenedThumb,
1770 outputStatistics=outputStatistics,