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 linearizer.log = self.log
1066 inputs[
'linearizer'] = linearizer
1069 self.log.warning(
"Constructing linearizer from cameraGeom information.")
1071 if self.config.doDefect
is True:
1072 if "defects" in inputs
and inputs[
'defects']
is not None:
1076 if not isinstance(inputs[
"defects"], Defects):
1077 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1081 brighterFatterSource =
None
1082 if self.config.doBrighterFatter:
1083 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1084 if brighterFatterKernel
is None:
1088 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1089 brighterFatterSource =
'bfKernel'
1090 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1092 if brighterFatterKernel
is None:
1094 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1095 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1102 brighterFatterSource =
'newBFKernel'
1103 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1105 detName = detector.getName()
1106 level = brighterFatterKernel.level
1109 inputs[
'bfGains'] = brighterFatterKernel.gain
1110 if self.config.brighterFatterLevel ==
'DETECTOR':
1112 if level ==
'DETECTOR':
1113 if detName
in brighterFatterKernel.detKernels:
1114 kernel = brighterFatterKernel.detKernels[detName]
1116 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1117 elif level ==
'AMP':
1118 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1120 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1121 kernel = brighterFatterKernel.detKernels[detName]
1123 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1127 inputs[
'bfKernel'] = numpy.transpose(kernel)
1128 elif self.config.brighterFatterLevel ==
'AMP':
1129 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1131 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1132 expId = inputs[
'ccdExposure'].info.id
1133 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1135 assembler=self.assembleCcd
1136 if self.config.doAssembleIsrExposures
else None)
1138 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1140 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1141 if 'strayLightData' not in inputs:
1142 inputs[
'strayLightData'] =
None
1144 if self.config.doHeaderProvenance:
1146 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1150 additionalInputs = []
1151 if self.config.doBrighterFatter:
1152 additionalInputs.append(brighterFatterSource)
1154 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1155 reference = getattr(inputRefs, inputName,
None)
1156 if reference
is not None and hasattr(reference,
"run"):
1157 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1158 runValue = reference.run
1159 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1160 idValue = str(reference.id)
1161 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1163 if inputName
in additionalInputDates:
1164 dateValue = additionalInputDates[inputName]
1168 exposureMetadata[runKey] = runValue
1169 exposureMetadata[idKey] = idValue
1170 exposureMetadata[dateKey] = dateValue
1172 outputs = self.
run(**inputs)
1173 butlerQC.put(outputs, outputRefs)
1176 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1177 crosstalk=None, crosstalkSources=None,
1178 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1179 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1180 sensorTransmission=
None, atmosphereTransmission=
None,
1181 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1182 deferredChargeCalib=
None,
1184 """Perform instrument signature removal on an exposure.
1186 Steps included in the ISR processing, in order performed, are:
1188 - saturation and suspect pixel masking
1189 - overscan subtraction
1190 - CCD assembly of individual amplifiers
1192 - variance image construction
1193 - linearization of non-linear response
1195 - brighter-fatter correction
1198 - stray light subtraction
1200 - masking of known defects and camera specific features
1201 - vignette calculation
1202 - appending transmission curve and distortion model
1206 ccdExposure : `lsst.afw.image.Exposure`
1207 The raw exposure that is to be run through ISR. The
1208 exposure is modified by this method.
1209 camera : `lsst.afw.cameraGeom.Camera`, optional
1210 The camera geometry for this exposure. Required if
1211 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1212 ``flat`` does not have an associated detector.
1213 bias : `lsst.afw.image.Exposure`, optional
1214 Bias calibration frame.
1215 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1216 Functor for linearization.
1217 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1218 Calibration for crosstalk.
1219 crosstalkSources : `list`, optional
1220 List of possible crosstalk sources.
1221 dark : `lsst.afw.image.Exposure`, optional
1222 Dark calibration frame.
1223 flat : `lsst.afw.image.Exposure`, optional
1224 Flat calibration frame.
1225 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1226 Photon transfer curve dataset, with, e.g., gains
1228 bfKernel : `numpy.ndarray`, optional
1229 Brighter-fatter kernel.
1230 bfGains : `dict` of `float`, optional
1231 Gains used to override the detector's nominal gains for the
1232 brighter-fatter correction. A dict keyed by amplifier name for
1233 the detector in question.
1234 defects : `lsst.ip.isr.Defects`, optional
1236 fringes : `lsst.pipe.base.Struct`, optional
1237 Struct containing the fringe correction data, with
1241 fringe calibration frame (`lsst.afw.image.Exposure`)
1243 random seed derived from the ``ccdExposureId`` for random
1244 number generator (`numpy.uint32`)
1245 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1246 A ``TransmissionCurve`` that represents the throughput of the,
1247 optics, to be evaluated in focal-plane coordinates.
1248 filterTransmission : `lsst.afw.image.TransmissionCurve`
1249 A ``TransmissionCurve`` that represents the throughput of the
1250 filter itself, to be evaluated in focal-plane coordinates.
1251 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1252 A ``TransmissionCurve`` that represents the throughput of the
1253 sensor itself, to be evaluated in post-assembly trimmed detector
1255 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1256 A ``TransmissionCurve`` that represents the throughput of the
1257 atmosphere, assumed to be spatially constant.
1258 detectorNum : `int`, optional
1259 The integer number for the detector to process.
1260 strayLightData : `object`, optional
1261 Opaque object containing calibration information for stray-light
1262 correction. If `None`, no correction will be performed.
1263 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1264 Illumination correction image.
1268 result : `lsst.pipe.base.Struct`
1269 Result struct with component:
1272 The fully ISR corrected exposure.
1273 (`lsst.afw.image.Exposure`)
1275 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1277 Thumbnail image of the exposure after overscan subtraction.
1280 Thumbnail image of the exposure after flat-field correction.
1282 ``outputStatistics``
1283 Values of the additional statistics calculated.
1288 Raised if a configuration option is set to `True`, but the
1289 required calibration data has not been specified.
1293 The current processed exposure can be viewed by setting the
1294 appropriate `lsstDebug` entries in the ``debug.display``
1295 dictionary. The names of these entries correspond to some of
1296 the `IsrTaskConfig` Boolean options, with the value denoting the
1297 frame to use. The exposure is shown inside the matching
1298 option check and after the processing of that step has
1299 finished. The steps with debug points are:
1310 In addition, setting the ``postISRCCD`` entry displays the
1311 exposure after all ISR processing has finished.
1314 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1319 ccd = ccdExposure.getDetector()
1320 filterLabel = ccdExposure.getFilter()
1321 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1324 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1325 ccd = [
FakeAmp(ccdExposure, self.config)]
1328 if self.config.doBias
and bias
is None:
1329 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1331 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1332 if self.config.doBrighterFatter
and bfKernel
is None:
1333 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1334 if self.config.doDark
and dark
is None:
1335 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1336 if self.config.doFlat
and flat
is None:
1337 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1338 if self.config.doDefect
and defects
is None:
1339 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1340 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1341 and fringes.fringes
is None):
1346 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1347 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1348 and illumMaskedImage
is None):
1349 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1350 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1351 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1352 if (self.config.usePtcGains
and ptc
is None):
1353 raise RuntimeError(
"No ptcDataset provided to use PTC gains.")
1354 if (self.config.usePtcReadNoise
and ptc
is None):
1355 raise RuntimeError(
"No ptcDataset provided to use PTC read noise.")
1358 exposureMetadata = ccdExposure.getMetadata()
1359 if self.config.doBias:
1361 if self.config.doBrighterFatter:
1363 if self.config.doCrosstalk:
1365 if self.config.doDark:
1367 if self.config.doDefect:
1369 if self.config.doDeferredCharge:
1371 if self.config.doFlat:
1373 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1375 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1379 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1381 if self.config.doStrayLight:
1387 exposureMetadata[
"LSST ISR UNITS"] =
"ADU"
1390 if self.config.doConvertIntToFloat:
1391 self.log.info(
"Converting exposure to floating point values.")
1394 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1395 self.log.info(
"Applying bias correction.")
1396 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1397 trimToFit=self.config.doTrimToMatchCalib)
1403 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1405 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1410 if ccdExposure.getBBox().contains(amp.getBBox()):
1415 if self.config.doOverscan
and not badAmp:
1418 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1419 if overscanResults
is not None and \
1420 self.config.qa
is not None and self.config.qa.saveStats
is True:
1421 if isinstance(overscanResults.overscanMean, float):
1423 mean = overscanResults.overscanMean
1424 sigma = overscanResults.overscanSigma
1425 residMean = overscanResults.residualMean
1426 residSigma = overscanResults.residualSigma
1430 mean = overscanResults.overscanMean[0]
1431 sigma = overscanResults.overscanSigma[0]
1432 residMean = overscanResults.residualMean[0]
1433 residSigma = overscanResults.residualSigma[0]
1435 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = mean
1436 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1437 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1438 amp.getName(), mean, sigma)
1440 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1441 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1442 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1443 amp.getName(), residMean, residSigma)
1445 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1448 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1449 overscanResults =
None
1451 overscans.append(overscanResults
if overscanResults
is not None else None)
1453 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1459 if self.config.doDeferredCharge:
1460 self.log.info(
"Applying deferred charge/CTI correction.")
1461 self.deferredChargeCorrection.
run(ccdExposure, deferredChargeCalib)
1462 self.
debugView(ccdExposure,
"doDeferredCharge")
1464 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1465 self.log.info(
"Applying crosstalk correction.")
1466 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1467 crosstalkSources=crosstalkSources, camera=camera)
1468 self.
debugView(ccdExposure,
"doCrosstalk")
1470 if self.config.doAssembleCcd:
1471 self.log.info(
"Assembling CCD from amplifiers.")
1472 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1474 if self.config.expectWcs
and not ccdExposure.getWcs():
1475 self.log.warning(
"No WCS found in input exposure.")
1476 self.
debugView(ccdExposure,
"doAssembleCcd")
1479 if self.config.qa.doThumbnailOss:
1480 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1482 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1483 self.log.info(
"Applying bias correction.")
1484 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1485 trimToFit=self.config.doTrimToMatchCalib)
1488 if self.config.doVariance:
1490 if ccdExposure.getBBox().contains(amp.getBBox()):
1491 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1492 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1495 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1496 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1497 afwMath.MEDIAN | afwMath.STDEVCLIP)
1498 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1499 qaStats.getValue(afwMath.MEDIAN)
1500 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1501 qaStats.getValue(afwMath.STDEVCLIP)
1502 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1503 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1504 qaStats.getValue(afwMath.STDEVCLIP))
1505 if self.config.maskNegativeVariance:
1509 self.log.info(
"Applying linearizer.")
1510 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1511 detector=ccd, log=self.log)
1513 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1514 self.log.info(
"Applying crosstalk correction.")
1515 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1516 crosstalkSources=crosstalkSources, isTrimmed=
True)
1517 self.
debugView(ccdExposure,
"doCrosstalk")
1522 if self.config.doDefect:
1523 self.log.info(
"Masking defects.")
1526 if self.config.numEdgeSuspect > 0:
1527 self.log.info(
"Masking edges as SUSPECT.")
1528 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1529 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1531 if self.config.doNanMasking:
1532 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1535 if self.config.doWidenSaturationTrails:
1536 self.log.info(
"Widening saturation trails.")
1537 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1539 if self.config.doCameraSpecificMasking:
1540 self.log.info(
"Masking regions for camera specific reasons.")
1541 self.masking.
run(ccdExposure)
1543 if self.config.doBrighterFatter:
1553 interpExp = ccdExposure.clone()
1555 isrFunctions.interpolateFromMask(
1556 maskedImage=interpExp.getMaskedImage(),
1557 fwhm=self.config.fwhm,
1558 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1559 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1561 bfExp = interpExp.clone()
1563 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1564 type(bfKernel), type(bfGains))
1565 if self.config.doFluxConservingBrighterFatterCorrection:
1566 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1569 self.config.brighterFatterMaxIter,
1570 self.config.brighterFatterThreshold,
1571 self.config.brighterFatterApplyGain,
1575 bfResults = isrFunctions.brighterFatterCorrection(
1578 self.config.brighterFatterMaxIter,
1579 self.config.brighterFatterThreshold,
1580 self.config.brighterFatterApplyGain,
1583 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1584 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1587 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1589 image = ccdExposure.getMaskedImage().getImage()
1590 bfCorr = bfExp.getMaskedImage().getImage()
1591 bfCorr -= interpExp.getMaskedImage().getImage()
1600 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1601 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1604 if self.config.brighterFatterMaskGrowSize > 0:
1605 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1606 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1607 isrFunctions.growMasks(ccdExposure.getMask(),
1608 radius=self.config.brighterFatterMaskGrowSize,
1609 maskNameList=maskPlane,
1610 maskValue=maskPlane)
1612 self.
debugView(ccdExposure,
"doBrighterFatter")
1614 if self.config.doDark:
1615 self.log.info(
"Applying dark correction.")
1619 if self.config.doFringe
and not self.config.fringeAfterFlat:
1620 self.log.info(
"Applying fringe correction before flat.")
1621 self.fringe.
run(ccdExposure, **fringes.getDict())
1624 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1625 self.log.info(
"Checking strayLight correction.")
1626 self.strayLight.
run(ccdExposure, strayLightData)
1627 self.
debugView(ccdExposure,
"doStrayLight")
1629 if self.config.doFlat:
1630 self.log.info(
"Applying flat correction.")
1634 if self.config.doApplyGains:
1635 self.log.info(
"Applying gain correction instead of flat.")
1636 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1638 exposureMetadata[
"LSST ISR UNITS"] =
"electrons"
1640 if self.config.doFringe
and self.config.fringeAfterFlat:
1641 self.log.info(
"Applying fringe correction after flat.")
1642 self.fringe.
run(ccdExposure, **fringes.getDict())
1644 if self.config.doVignette:
1645 if self.config.doMaskVignettePolygon:
1646 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1648 self.log.info(
"Constructing and attaching vignette polygon.")
1650 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1651 vignetteValue=self.config.vignetteValue, log=self.log)
1653 if self.config.doAttachTransmissionCurve:
1654 self.log.info(
"Adding transmission curves.")
1655 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1656 filterTransmission=filterTransmission,
1657 sensorTransmission=sensorTransmission,
1658 atmosphereTransmission=atmosphereTransmission)
1660 flattenedThumb =
None
1661 if self.config.qa.doThumbnailFlattened:
1662 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1664 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1665 self.log.info(
"Performing illumination correction.")
1666 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1667 illumMaskedImage, illumScale=self.config.illumScale,
1668 trimToFit=self.config.doTrimToMatchCalib)
1671 if self.config.doSaveInterpPixels:
1672 preInterpExp = ccdExposure.clone()
1687 if self.config.doSetBadRegions:
1688 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1689 if badPixelCount > 0:
1690 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1692 if self.config.doInterpolate:
1693 self.log.info(
"Interpolating masked pixels.")
1694 isrFunctions.interpolateFromMask(
1695 maskedImage=ccdExposure.getMaskedImage(),
1696 fwhm=self.config.fwhm,
1697 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1698 maskNameList=list(self.config.maskListToInterpolate)
1704 if self.config.doAmpOffset:
1705 self.log.info(
"Correcting amp offsets.")
1706 self.ampOffset.
run(ccdExposure)
1708 if self.config.doMeasureBackground:
1709 self.log.info(
"Measuring background level.")
1712 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1714 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1715 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1716 afwMath.MEDIAN | afwMath.STDEVCLIP)
1717 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1718 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1719 qaStats.getValue(afwMath.STDEVCLIP)
1720 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1721 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1722 qaStats.getValue(afwMath.STDEVCLIP))
1725 if self.config.doStandardStatistics:
1726 metadata = ccdExposure.getMetadata()
1728 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1729 ampName = amp.getName()
1730 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1731 ampExposure.getMaskedImage(),
1732 [self.config.saturatedMaskName]
1734 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1735 ampExposure.getMaskedImage(),
1738 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1739 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1741 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1742 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1743 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1745 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1746 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1747 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1748 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1750 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1753 outputStatistics =
None
1754 if self.config.doCalculateStatistics:
1755 outputStatistics = self.isrStats.
run(ccdExposure, overscanResults=overscans,
1756 bias=bias, dark=dark, flat=flat, ptc=ptc).results
1759 outputBin1Exposure =
None
1760 outputBin2Exposure =
None
1761 if self.config.doBinnedExposures:
1762 outputBin1Exposure, outputBin2Exposure = self.
makeBinnedImages(ccdExposure)
1764 self.
debugView(ccdExposure,
"postISRCCD")
1766 return pipeBase.Struct(
1767 exposure=ccdExposure,
1769 flattenedThumb=flattenedThumb,
1771 outputBin1Exposure=outputBin1Exposure,
1772 outputBin2Exposure=outputBin2Exposure,
1774 preInterpExposure=preInterpExp,
1775 outputExposure=ccdExposure,
1776 outputOssThumbnail=ossThumb,
1777 outputFlattenedThumbnail=flattenedThumb,
1778 outputStatistics=outputStatistics,