1024 inputs = butlerQC.get(inputRefs)
1027 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
1028 except Exception
as e:
1029 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
1032 detector = inputs[
'ccdExposure'].getDetector()
1035 additionalInputDates = {}
1037 if self.config.doCrosstalk
is True:
1040 if 'crosstalk' in inputs
and inputs[
'crosstalk']
is not None:
1041 if not isinstance(inputs[
'crosstalk'], CrosstalkCalib):
1042 inputs[
'crosstalk'] = CrosstalkCalib.fromTable(inputs[
'crosstalk'])
1044 coeffVector = (self.config.crosstalk.crosstalkValues
1045 if self.config.crosstalk.useConfigCoefficients
else None)
1046 crosstalkCalib =
CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1047 inputs[
'crosstalk'] = crosstalkCalib
1048 if inputs[
'crosstalk'].interChip
and len(inputs[
'crosstalk'].interChip) > 0:
1049 if 'crosstalkSources' not in inputs:
1050 self.log.warning(
"No crosstalkSources found for chip with interChip terms!")
1053 if 'linearizer' in inputs:
1054 if isinstance(inputs[
'linearizer'], dict):
1056 linearizer.fromYaml(inputs[
'linearizer'])
1057 self.log.warning(
"Dictionary linearizers will be deprecated in DM-28741.")
1058 elif isinstance(inputs[
'linearizer'], numpy.ndarray):
1062 self.log.warning(
"Bare lookup table linearizers will be deprecated in DM-28741.")
1064 linearizer = inputs[
'linearizer']
1065 self.log.info(
"Loading linearizer from the Butler.")
1066 linearizer.log = self.log
1067 inputs[
'linearizer'] = linearizer
1070 self.log.info(
"Constructing linearizer from cameraGeom information.")
1072 if self.config.doDefect
is True:
1073 if "defects" in inputs
and inputs[
'defects']
is not None:
1077 if not isinstance(inputs[
"defects"], Defects):
1078 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1082 brighterFatterSource =
None
1083 if self.config.doBrighterFatter:
1084 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1085 if brighterFatterKernel
is None:
1089 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1090 brighterFatterSource =
'bfKernel'
1091 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1093 if brighterFatterKernel
is None:
1095 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1096 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1103 brighterFatterSource =
'newBFKernel'
1104 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1106 detName = detector.getName()
1107 level = brighterFatterKernel.level
1110 inputs[
'bfGains'] = brighterFatterKernel.gain
1111 if self.config.brighterFatterLevel ==
'DETECTOR':
1113 if level ==
'DETECTOR':
1114 if detName
in brighterFatterKernel.detKernels:
1115 kernel = brighterFatterKernel.detKernels[detName]
1117 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1118 elif level ==
'AMP':
1119 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1121 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1122 kernel = brighterFatterKernel.detKernels[detName]
1124 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1128 inputs[
'bfKernel'] = numpy.transpose(kernel)
1129 elif self.config.brighterFatterLevel ==
'AMP':
1130 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1132 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1133 expId = inputs[
'ccdExposure'].info.id
1134 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1136 assembler=self.assembleCcd
1137 if self.config.doAssembleIsrExposures
else None)
1139 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1141 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1142 if 'strayLightData' not in inputs:
1143 inputs[
'strayLightData'] =
None
1145 if self.config.doHeaderProvenance:
1147 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1151 additionalInputs = []
1152 if self.config.doBrighterFatter:
1153 additionalInputs.append(brighterFatterSource)
1155 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1156 reference = getattr(inputRefs, inputName,
None)
1157 if reference
is not None and hasattr(reference,
"run"):
1158 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1159 runValue = reference.run
1160 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1161 idValue = str(reference.id)
1162 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1164 if inputName
in additionalInputDates:
1165 dateValue = additionalInputDates[inputName]
1169 exposureMetadata[runKey] = runValue
1170 exposureMetadata[idKey] = idValue
1171 exposureMetadata[dateKey] = dateValue
1173 outputs = self.
run(**inputs)
1174 butlerQC.put(outputs, outputRefs)
1177 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1178 crosstalk=None, crosstalkSources=None,
1179 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1180 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1181 sensorTransmission=
None, atmosphereTransmission=
None,
1182 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1183 deferredChargeCalib=
None,
1185 """Perform instrument signature removal on an exposure.
1187 Steps included in the ISR processing, in order performed, are:
1189 - saturation and suspect pixel masking
1190 - overscan subtraction
1191 - CCD assembly of individual amplifiers
1193 - variance image construction
1194 - linearization of non-linear response
1196 - brighter-fatter correction
1199 - stray light subtraction
1201 - masking of known defects and camera specific features
1202 - vignette calculation
1203 - appending transmission curve and distortion model
1207 ccdExposure : `lsst.afw.image.Exposure`
1208 The raw exposure that is to be run through ISR. The
1209 exposure is modified by this method.
1210 camera : `lsst.afw.cameraGeom.Camera`, optional
1211 The camera geometry for this exposure. Required if
1212 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1213 ``flat`` does not have an associated detector.
1214 bias : `lsst.afw.image.Exposure`, optional
1215 Bias calibration frame.
1216 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1217 Functor for linearization.
1218 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1219 Calibration for crosstalk.
1220 crosstalkSources : `list`, optional
1221 List of possible crosstalk sources.
1222 dark : `lsst.afw.image.Exposure`, optional
1223 Dark calibration frame.
1224 flat : `lsst.afw.image.Exposure`, optional
1225 Flat calibration frame.
1226 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1227 Photon transfer curve dataset, with, e.g., gains
1229 bfKernel : `numpy.ndarray`, optional
1230 Brighter-fatter kernel.
1231 bfGains : `dict` of `float`, optional
1232 Gains used to override the detector's nominal gains for the
1233 brighter-fatter correction. A dict keyed by amplifier name for
1234 the detector in question.
1235 defects : `lsst.ip.isr.Defects`, optional
1237 fringes : `lsst.pipe.base.Struct`, optional
1238 Struct containing the fringe correction data, with
1242 fringe calibration frame (`lsst.afw.image.Exposure`)
1244 random seed derived from the ``ccdExposureId`` for random
1245 number generator (`numpy.uint32`)
1246 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1247 A ``TransmissionCurve`` that represents the throughput of the,
1248 optics, to be evaluated in focal-plane coordinates.
1249 filterTransmission : `lsst.afw.image.TransmissionCurve`
1250 A ``TransmissionCurve`` that represents the throughput of the
1251 filter itself, to be evaluated in focal-plane coordinates.
1252 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1253 A ``TransmissionCurve`` that represents the throughput of the
1254 sensor itself, to be evaluated in post-assembly trimmed detector
1256 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1257 A ``TransmissionCurve`` that represents the throughput of the
1258 atmosphere, assumed to be spatially constant.
1259 detectorNum : `int`, optional
1260 The integer number for the detector to process.
1261 strayLightData : `object`, optional
1262 Opaque object containing calibration information for stray-light
1263 correction. If `None`, no correction will be performed.
1264 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1265 Illumination correction image.
1269 result : `lsst.pipe.base.Struct`
1270 Result struct with component:
1273 The fully ISR corrected exposure.
1274 (`lsst.afw.image.Exposure`)
1276 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1278 Thumbnail image of the exposure after overscan subtraction.
1281 Thumbnail image of the exposure after flat-field correction.
1283 ``outputStatistics``
1284 Values of the additional statistics calculated.
1289 Raised if a configuration option is set to `True`, but the
1290 required calibration data has not been specified.
1294 The current processed exposure can be viewed by setting the
1295 appropriate `lsstDebug` entries in the ``debug.display``
1296 dictionary. The names of these entries correspond to some of
1297 the `IsrTaskConfig` Boolean options, with the value denoting the
1298 frame to use. The exposure is shown inside the matching
1299 option check and after the processing of that step has
1300 finished. The steps with debug points are:
1311 In addition, setting the ``postISRCCD`` entry displays the
1312 exposure after all ISR processing has finished.
1315 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1320 ccd = ccdExposure.getDetector()
1321 filterLabel = ccdExposure.getFilter()
1322 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1325 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1326 ccd = [
FakeAmp(ccdExposure, self.config)]
1329 if self.config.doBias
and bias
is None:
1330 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1332 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1333 if self.config.doBrighterFatter
and bfKernel
is None:
1334 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1335 if self.config.doDark
and dark
is None:
1336 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1337 if self.config.doFlat
and flat
is None:
1338 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1339 if self.config.doDefect
and defects
is None:
1340 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1341 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1342 and fringes.fringes
is None):
1347 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1348 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1349 and illumMaskedImage
is None):
1350 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1351 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1352 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1353 if (self.config.usePtcGains
and ptc
is None):
1354 raise RuntimeError(
"No ptcDataset provided to use PTC gains.")
1355 if (self.config.usePtcReadNoise
and ptc
is None):
1356 raise RuntimeError(
"No ptcDataset provided to use PTC read noise.")
1359 exposureMetadata = ccdExposure.getMetadata()
1360 if self.config.doBias:
1362 if self.config.doBrighterFatter:
1364 if self.config.doCrosstalk:
1366 if self.config.doDark:
1368 if self.config.doDefect:
1370 if self.config.doDeferredCharge:
1372 if self.config.doFlat:
1374 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1376 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1380 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1382 if self.config.doStrayLight:
1388 exposureMetadata[
"LSST ISR UNITS"] =
"ADU"
1391 if self.config.doConvertIntToFloat:
1392 self.log.info(
"Converting exposure to floating point values.")
1395 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1396 self.log.info(
"Applying bias correction.")
1397 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1398 trimToFit=self.config.doTrimToMatchCalib)
1404 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1406 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1411 if ccdExposure.getBBox().contains(amp.getBBox()):
1416 if self.config.doOverscan
and not badAmp:
1419 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1420 if overscanResults
is not None and \
1421 self.config.qa
is not None and self.config.qa.saveStats
is True:
1422 if isinstance(overscanResults.overscanMean, float):
1424 mean = overscanResults.overscanMean
1425 sigma = overscanResults.overscanSigma
1426 residMean = overscanResults.residualMean
1427 residSigma = overscanResults.residualSigma
1431 mean = overscanResults.overscanMean[0]
1432 sigma = overscanResults.overscanSigma[0]
1433 residMean = overscanResults.residualMean[0]
1434 residSigma = overscanResults.residualSigma[0]
1436 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = mean
1437 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1438 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1439 amp.getName(), mean, sigma)
1441 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1442 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1443 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1444 amp.getName(), residMean, residSigma)
1446 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1449 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1450 overscanResults =
None
1452 overscans.append(overscanResults
if overscanResults
is not None else None)
1454 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1460 if self.config.doDeferredCharge:
1461 self.log.info(
"Applying deferred charge/CTI correction.")
1462 self.deferredChargeCorrection.
run(ccdExposure, deferredChargeCalib)
1463 self.
debugView(ccdExposure,
"doDeferredCharge")
1465 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1466 self.log.info(
"Applying crosstalk correction.")
1467 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1468 crosstalkSources=crosstalkSources, camera=camera)
1469 self.
debugView(ccdExposure,
"doCrosstalk")
1471 if self.config.doAssembleCcd:
1472 self.log.info(
"Assembling CCD from amplifiers.")
1473 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1475 if self.config.expectWcs
and not ccdExposure.getWcs():
1476 self.log.warning(
"No WCS found in input exposure.")
1477 self.
debugView(ccdExposure,
"doAssembleCcd")
1480 if self.config.qa.doThumbnailOss:
1481 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1483 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1484 self.log.info(
"Applying bias correction.")
1485 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1486 trimToFit=self.config.doTrimToMatchCalib)
1489 if self.config.doVariance:
1491 if ccdExposure.getBBox().contains(amp.getBBox()):
1492 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1493 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1496 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1497 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1498 afwMath.MEDIAN | afwMath.STDEVCLIP)
1499 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1500 qaStats.getValue(afwMath.MEDIAN)
1501 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1502 qaStats.getValue(afwMath.STDEVCLIP)
1503 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1504 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1505 qaStats.getValue(afwMath.STDEVCLIP))
1506 if self.config.maskNegativeVariance:
1510 self.log.info(
"Applying linearizer.")
1511 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1512 detector=ccd, log=self.log)
1514 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1515 self.log.info(
"Applying crosstalk correction.")
1516 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1517 crosstalkSources=crosstalkSources, isTrimmed=
True)
1518 self.
debugView(ccdExposure,
"doCrosstalk")
1523 if self.config.doDefect:
1524 self.log.info(
"Masking defects.")
1527 if self.config.numEdgeSuspect > 0:
1528 self.log.info(
"Masking edges as SUSPECT.")
1529 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1530 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1532 if self.config.doNanMasking:
1533 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1536 if self.config.doWidenSaturationTrails:
1537 self.log.info(
"Widening saturation trails.")
1538 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1540 if self.config.doCameraSpecificMasking:
1541 self.log.info(
"Masking regions for camera specific reasons.")
1542 self.masking.
run(ccdExposure)
1544 if self.config.doBrighterFatter:
1554 interpExp = ccdExposure.clone()
1556 isrFunctions.interpolateFromMask(
1557 maskedImage=interpExp.getMaskedImage(),
1558 fwhm=self.config.fwhm,
1559 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1560 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1562 bfExp = interpExp.clone()
1564 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1565 type(bfKernel), type(bfGains))
1566 if self.config.doFluxConservingBrighterFatterCorrection:
1567 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1570 self.config.brighterFatterMaxIter,
1571 self.config.brighterFatterThreshold,
1572 self.config.brighterFatterApplyGain,
1576 bfResults = isrFunctions.brighterFatterCorrection(
1579 self.config.brighterFatterMaxIter,
1580 self.config.brighterFatterThreshold,
1581 self.config.brighterFatterApplyGain,
1584 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1585 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1588 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1590 image = ccdExposure.getMaskedImage().getImage()
1591 bfCorr = bfExp.getMaskedImage().getImage()
1592 bfCorr -= interpExp.getMaskedImage().getImage()
1601 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1602 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1605 if self.config.brighterFatterMaskGrowSize > 0:
1606 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1607 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1608 isrFunctions.growMasks(ccdExposure.getMask(),
1609 radius=self.config.brighterFatterMaskGrowSize,
1610 maskNameList=maskPlane,
1611 maskValue=maskPlane)
1613 self.
debugView(ccdExposure,
"doBrighterFatter")
1615 if self.config.doDark:
1616 self.log.info(
"Applying dark correction.")
1620 if self.config.doFringe
and not self.config.fringeAfterFlat:
1621 self.log.info(
"Applying fringe correction before flat.")
1622 self.fringe.
run(ccdExposure, **fringes.getDict())
1625 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1626 self.log.info(
"Checking strayLight correction.")
1627 self.strayLight.
run(ccdExposure, strayLightData)
1628 self.
debugView(ccdExposure,
"doStrayLight")
1630 if self.config.doFlat:
1631 self.log.info(
"Applying flat correction.")
1635 if self.config.doApplyGains:
1636 self.log.info(
"Applying gain correction instead of flat.")
1637 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1639 exposureMetadata[
"LSST ISR UNITS"] =
"electrons"
1641 if self.config.doFringe
and self.config.fringeAfterFlat:
1642 self.log.info(
"Applying fringe correction after flat.")
1643 self.fringe.
run(ccdExposure, **fringes.getDict())
1645 if self.config.doVignette:
1646 if self.config.doMaskVignettePolygon:
1647 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1649 self.log.info(
"Constructing and attaching vignette polygon.")
1651 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1652 vignetteValue=self.config.vignetteValue, log=self.log)
1654 if self.config.doAttachTransmissionCurve:
1655 self.log.info(
"Adding transmission curves.")
1656 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1657 filterTransmission=filterTransmission,
1658 sensorTransmission=sensorTransmission,
1659 atmosphereTransmission=atmosphereTransmission)
1661 flattenedThumb =
None
1662 if self.config.qa.doThumbnailFlattened:
1663 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1665 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1666 self.log.info(
"Performing illumination correction.")
1667 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1668 illumMaskedImage, illumScale=self.config.illumScale,
1669 trimToFit=self.config.doTrimToMatchCalib)
1672 if self.config.doSaveInterpPixels:
1673 preInterpExp = ccdExposure.clone()
1688 if self.config.doSetBadRegions:
1689 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1690 if badPixelCount > 0:
1691 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1693 if self.config.doInterpolate:
1694 self.log.info(
"Interpolating masked pixels.")
1695 isrFunctions.interpolateFromMask(
1696 maskedImage=ccdExposure.getMaskedImage(),
1697 fwhm=self.config.fwhm,
1698 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1699 maskNameList=list(self.config.maskListToInterpolate)
1705 if self.config.doAmpOffset:
1706 self.log.info(
"Correcting amp offsets.")
1707 self.ampOffset.
run(ccdExposure)
1709 if self.config.doMeasureBackground:
1710 self.log.info(
"Measuring background level.")
1713 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1715 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1716 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1717 afwMath.MEDIAN | afwMath.STDEVCLIP)
1718 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1719 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1720 qaStats.getValue(afwMath.STDEVCLIP)
1721 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1722 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1723 qaStats.getValue(afwMath.STDEVCLIP))
1726 if self.config.doStandardStatistics:
1727 metadata = ccdExposure.getMetadata()
1729 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1730 ampName = amp.getName()
1731 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1732 ampExposure.getMaskedImage(),
1733 [self.config.saturatedMaskName]
1735 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1736 ampExposure.getMaskedImage(),
1739 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1740 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1742 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1743 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1744 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1746 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1747 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1748 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1749 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1751 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1754 outputStatistics =
None
1755 if self.config.doCalculateStatistics:
1756 outputStatistics = self.isrStats.
run(ccdExposure, overscanResults=overscans,
1757 bias=bias, dark=dark, flat=flat, ptc=ptc).results
1760 outputBin1Exposure =
None
1761 outputBin2Exposure =
None
1762 if self.config.doBinnedExposures:
1763 outputBin1Exposure, outputBin2Exposure = self.
makeBinnedImages(ccdExposure)
1765 self.
debugView(ccdExposure,
"postISRCCD")
1767 return pipeBase.Struct(
1768 exposure=ccdExposure,
1770 flattenedThumb=flattenedThumb,
1772 outputBin1Exposure=outputBin1Exposure,
1773 outputBin2Exposure=outputBin2Exposure,
1775 preInterpExposure=preInterpExp,
1776 outputExposure=ccdExposure,
1777 outputOssThumbnail=ossThumb,
1778 outputFlattenedThumbnail=flattenedThumb,
1779 outputStatistics=outputStatistics,