1025 inputs = butlerQC.get(inputRefs)
1028 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
1029 except Exception
as e:
1030 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
1033 detector = inputs[
'ccdExposure'].getDetector()
1036 additionalInputDates = {}
1038 if self.config.doCrosstalk
is True:
1041 if 'crosstalk' in inputs
and inputs[
'crosstalk']
is not None:
1042 if not isinstance(inputs[
'crosstalk'], CrosstalkCalib):
1043 inputs[
'crosstalk'] = CrosstalkCalib.fromTable(inputs[
'crosstalk'])
1045 coeffVector = (self.config.crosstalk.crosstalkValues
1046 if self.config.crosstalk.useConfigCoefficients
else None)
1047 crosstalkCalib =
CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1048 inputs[
'crosstalk'] = crosstalkCalib
1049 if inputs[
'crosstalk'].interChip
and len(inputs[
'crosstalk'].interChip) > 0:
1050 if 'crosstalkSources' not in inputs:
1051 self.log.warning(
"No crosstalkSources found for chip with interChip terms!")
1054 if 'linearizer' in inputs:
1055 if isinstance(inputs[
'linearizer'], dict):
1057 linearizer.fromYaml(inputs[
'linearizer'])
1058 self.log.warning(
"Dictionary linearizers will be deprecated in DM-28741.")
1059 elif isinstance(inputs[
'linearizer'], numpy.ndarray):
1063 self.log.warning(
"Bare lookup table linearizers will be deprecated in DM-28741.")
1065 linearizer = inputs[
'linearizer']
1066 self.log.info(
"Loading linearizer from the Butler.")
1067 linearizer.log = self.log
1068 inputs[
'linearizer'] = linearizer
1071 self.log.info(
"Constructing linearizer from cameraGeom information.")
1073 if self.config.doDefect
is True:
1074 if "defects" in inputs
and inputs[
'defects']
is not None:
1078 if not isinstance(inputs[
"defects"], Defects):
1079 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1083 brighterFatterSource =
None
1084 if self.config.doBrighterFatter:
1085 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1086 if brighterFatterKernel
is None:
1090 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1091 brighterFatterSource =
'bfKernel'
1092 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1094 if brighterFatterKernel
is None:
1096 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1097 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1104 brighterFatterSource =
'newBFKernel'
1105 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1107 detName = detector.getName()
1108 level = brighterFatterKernel.level
1111 inputs[
'bfGains'] = brighterFatterKernel.gain
1112 if self.config.brighterFatterLevel ==
'DETECTOR':
1114 if level ==
'DETECTOR':
1115 if detName
in brighterFatterKernel.detKernels:
1116 kernel = brighterFatterKernel.detKernels[detName]
1118 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1119 elif level ==
'AMP':
1120 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1122 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1123 kernel = brighterFatterKernel.detKernels[detName]
1125 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1129 inputs[
'bfKernel'] = numpy.transpose(kernel)
1130 elif self.config.brighterFatterLevel ==
'AMP':
1131 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1133 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1134 expId = inputs[
'ccdExposure'].info.id
1135 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1137 assembler=self.assembleCcd
1138 if self.config.doAssembleIsrExposures
else None)
1140 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1142 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1143 if 'strayLightData' not in inputs:
1144 inputs[
'strayLightData'] =
None
1146 if self.config.doHeaderProvenance:
1148 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1152 additionalInputs = []
1153 if self.config.doBrighterFatter:
1154 additionalInputs.append(brighterFatterSource)
1156 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1157 reference = getattr(inputRefs, inputName,
None)
1158 if reference
is not None and hasattr(reference,
"run"):
1159 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1160 runValue = reference.run
1161 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1162 idValue = str(reference.id)
1163 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1165 if inputName
in additionalInputDates:
1166 dateValue = additionalInputDates[inputName]
1170 exposureMetadata[runKey] = runValue
1171 exposureMetadata[idKey] = idValue
1172 exposureMetadata[dateKey] = dateValue
1174 outputs = self.
run(**inputs)
1175 butlerQC.put(outputs, outputRefs)
1178 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1179 crosstalk=None, crosstalkSources=None,
1180 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1181 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1182 sensorTransmission=
None, atmosphereTransmission=
None,
1183 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1184 deferredChargeCalib=
None,
1186 """Perform instrument signature removal on an exposure.
1188 Steps included in the ISR processing, in order performed, are:
1190 - saturation and suspect pixel masking
1191 - overscan subtraction
1192 - CCD assembly of individual amplifiers
1194 - variance image construction
1195 - linearization of non-linear response
1197 - brighter-fatter correction
1200 - stray light subtraction
1202 - masking of known defects and camera specific features
1203 - vignette calculation
1204 - appending transmission curve and distortion model
1208 ccdExposure : `lsst.afw.image.Exposure`
1209 The raw exposure that is to be run through ISR. The
1210 exposure is modified by this method.
1211 camera : `lsst.afw.cameraGeom.Camera`, optional
1212 The camera geometry for this exposure. Required if
1213 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1214 ``flat`` does not have an associated detector.
1215 bias : `lsst.afw.image.Exposure`, optional
1216 Bias calibration frame.
1217 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1218 Functor for linearization.
1219 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1220 Calibration for crosstalk.
1221 crosstalkSources : `list`, optional
1222 List of possible crosstalk sources.
1223 dark : `lsst.afw.image.Exposure`, optional
1224 Dark calibration frame.
1225 flat : `lsst.afw.image.Exposure`, optional
1226 Flat calibration frame.
1227 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1228 Photon transfer curve dataset, with, e.g., gains
1230 bfKernel : `numpy.ndarray`, optional
1231 Brighter-fatter kernel.
1232 bfGains : `dict` of `float`, optional
1233 Gains used to override the detector's nominal gains for the
1234 brighter-fatter correction. A dict keyed by amplifier name for
1235 the detector in question.
1236 defects : `lsst.ip.isr.Defects`, optional
1238 fringes : `lsst.pipe.base.Struct`, optional
1239 Struct containing the fringe correction data, with
1243 fringe calibration frame (`lsst.afw.image.Exposure`)
1245 random seed derived from the ``ccdExposureId`` for random
1246 number generator (`numpy.uint32`)
1247 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1248 A ``TransmissionCurve`` that represents the throughput of the,
1249 optics, to be evaluated in focal-plane coordinates.
1250 filterTransmission : `lsst.afw.image.TransmissionCurve`
1251 A ``TransmissionCurve`` that represents the throughput of the
1252 filter itself, to be evaluated in focal-plane coordinates.
1253 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1254 A ``TransmissionCurve`` that represents the throughput of the
1255 sensor itself, to be evaluated in post-assembly trimmed detector
1257 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1258 A ``TransmissionCurve`` that represents the throughput of the
1259 atmosphere, assumed to be spatially constant.
1260 detectorNum : `int`, optional
1261 The integer number for the detector to process.
1262 strayLightData : `object`, optional
1263 Opaque object containing calibration information for stray-light
1264 correction. If `None`, no correction will be performed.
1265 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1266 Illumination correction image.
1270 result : `lsst.pipe.base.Struct`
1271 Result struct with component:
1274 The fully ISR corrected exposure.
1275 (`lsst.afw.image.Exposure`)
1277 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1279 Thumbnail image of the exposure after overscan subtraction.
1282 Thumbnail image of the exposure after flat-field correction.
1284 ``outputStatistics``
1285 Values of the additional statistics calculated.
1290 Raised if a configuration option is set to `True`, but the
1291 required calibration data has not been specified.
1295 The current processed exposure can be viewed by setting the
1296 appropriate `lsstDebug` entries in the ``debug.display``
1297 dictionary. The names of these entries correspond to some of
1298 the `IsrTaskConfig` Boolean options, with the value denoting the
1299 frame to use. The exposure is shown inside the matching
1300 option check and after the processing of that step has
1301 finished. The steps with debug points are:
1312 In addition, setting the ``postISRCCD`` entry displays the
1313 exposure after all ISR processing has finished.
1316 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1321 ccd = ccdExposure.getDetector()
1322 filterLabel = ccdExposure.getFilter()
1323 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1326 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1327 ccd = [
FakeAmp(ccdExposure, self.config)]
1330 if self.config.doBias
and bias
is None:
1331 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1333 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1334 if self.config.doBrighterFatter
and bfKernel
is None:
1335 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1336 if self.config.doDark
and dark
is None:
1337 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1338 if self.config.doFlat
and flat
is None:
1339 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1340 if self.config.doDefect
and defects
is None:
1341 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1342 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1343 and fringes.fringes
is None):
1348 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1349 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1350 and illumMaskedImage
is None):
1351 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1352 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1353 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1354 if (self.config.usePtcGains
and ptc
is None):
1355 raise RuntimeError(
"No ptcDataset provided to use PTC gains.")
1356 if (self.config.usePtcReadNoise
and ptc
is None):
1357 raise RuntimeError(
"No ptcDataset provided to use PTC read noise.")
1360 exposureMetadata = ccdExposure.getMetadata()
1361 if self.config.doBias:
1363 if self.config.doBrighterFatter:
1365 if self.config.doCrosstalk:
1367 if self.config.doDark:
1369 if self.config.doDefect:
1371 if self.config.doDeferredCharge:
1373 if self.config.doFlat:
1375 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1377 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1381 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1383 if self.config.doStrayLight:
1389 exposureMetadata[
"LSST ISR UNITS"] =
"ADU"
1392 if self.config.doConvertIntToFloat:
1393 self.log.info(
"Converting exposure to floating point values.")
1396 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1397 self.log.info(
"Applying bias correction.")
1398 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1399 trimToFit=self.config.doTrimToMatchCalib)
1405 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1407 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1412 if ccdExposure.getBBox().contains(amp.getBBox()):
1417 if self.config.doOverscan
and not badAmp:
1420 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1421 if overscanResults
is not None and \
1422 self.config.qa
is not None and self.config.qa.saveStats
is True:
1423 if isinstance(overscanResults.overscanMean, float):
1425 mean = overscanResults.overscanMean
1426 sigma = overscanResults.overscanSigma
1427 residMean = overscanResults.residualMean
1428 residSigma = overscanResults.residualSigma
1432 mean = overscanResults.overscanMean[0]
1433 sigma = overscanResults.overscanSigma[0]
1434 residMean = overscanResults.residualMean[0]
1435 residSigma = overscanResults.residualSigma[0]
1437 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = mean
1438 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1439 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1440 amp.getName(), mean, sigma)
1442 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1443 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1444 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1445 amp.getName(), residMean, residSigma)
1447 ccdExposure.getMetadata().
set(
'OVERSCAN',
"Overscan corrected")
1450 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1451 overscanResults =
None
1453 overscans.append(overscanResults
if overscanResults
is not None else None)
1455 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1461 if self.config.doDeferredCharge:
1462 self.log.info(
"Applying deferred charge/CTI correction.")
1463 self.deferredChargeCorrection.run(ccdExposure, deferredChargeCalib)
1464 self.
debugView(ccdExposure,
"doDeferredCharge")
1466 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1467 self.log.info(
"Applying crosstalk correction.")
1468 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1469 crosstalkSources=crosstalkSources, camera=camera)
1470 self.
debugView(ccdExposure,
"doCrosstalk")
1472 if self.config.doAssembleCcd:
1473 self.log.info(
"Assembling CCD from amplifiers.")
1474 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1476 if self.config.expectWcs
and not ccdExposure.getWcs():
1477 self.log.warning(
"No WCS found in input exposure.")
1478 self.
debugView(ccdExposure,
"doAssembleCcd")
1481 if self.config.qa.doThumbnailOss:
1482 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1484 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1485 self.log.info(
"Applying bias correction.")
1486 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1487 trimToFit=self.config.doTrimToMatchCalib)
1490 if self.config.doVariance:
1492 if ccdExposure.getBBox().contains(amp.getBBox()):
1493 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1494 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1497 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1499 afwMath.MEDIAN | afwMath.STDEVCLIP)
1500 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1501 qaStats.getValue(afwMath.MEDIAN)
1502 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1503 qaStats.getValue(afwMath.STDEVCLIP)
1504 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1505 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1506 qaStats.getValue(afwMath.STDEVCLIP))
1507 if self.config.maskNegativeVariance:
1511 self.log.info(
"Applying linearizer.")
1512 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1513 detector=ccd, log=self.log)
1515 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1516 self.log.info(
"Applying crosstalk correction.")
1517 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1518 crosstalkSources=crosstalkSources, isTrimmed=
True)
1519 self.
debugView(ccdExposure,
"doCrosstalk")
1524 if self.config.doDefect:
1525 self.log.info(
"Masking defects.")
1528 if self.config.numEdgeSuspect > 0:
1529 self.log.info(
"Masking edges as SUSPECT.")
1530 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1531 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1533 if self.config.doNanMasking:
1534 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1537 if self.config.doWidenSaturationTrails:
1538 self.log.info(
"Widening saturation trails.")
1539 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1541 if self.config.doCameraSpecificMasking:
1542 self.log.info(
"Masking regions for camera specific reasons.")
1543 self.masking.run(ccdExposure)
1545 if self.config.doBrighterFatter:
1555 interpExp = ccdExposure.clone()
1557 isrFunctions.interpolateFromMask(
1558 maskedImage=interpExp.getMaskedImage(),
1559 fwhm=self.config.fwhm,
1560 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1561 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1563 bfExp = interpExp.clone()
1565 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1566 type(bfKernel), type(bfGains))
1567 if self.config.doFluxConservingBrighterFatterCorrection:
1568 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1571 self.config.brighterFatterMaxIter,
1572 self.config.brighterFatterThreshold,
1573 self.config.brighterFatterApplyGain,
1577 bfResults = isrFunctions.brighterFatterCorrection(
1580 self.config.brighterFatterMaxIter,
1581 self.config.brighterFatterThreshold,
1582 self.config.brighterFatterApplyGain,
1585 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1586 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1589 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1591 image = ccdExposure.getMaskedImage().getImage()
1592 bfCorr = bfExp.getMaskedImage().getImage()
1593 bfCorr -= interpExp.getMaskedImage().getImage()
1602 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1603 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1606 if self.config.brighterFatterMaskGrowSize > 0:
1607 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1608 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1609 isrFunctions.growMasks(ccdExposure.getMask(),
1610 radius=self.config.brighterFatterMaskGrowSize,
1611 maskNameList=maskPlane,
1612 maskValue=maskPlane)
1614 self.
debugView(ccdExposure,
"doBrighterFatter")
1616 if self.config.doDark:
1617 self.log.info(
"Applying dark correction.")
1621 if self.config.doFringe
and not self.config.fringeAfterFlat:
1622 self.log.info(
"Applying fringe correction before flat.")
1623 self.fringe.run(ccdExposure, **fringes.getDict())
1626 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1627 self.log.info(
"Checking strayLight correction.")
1628 self.strayLight.run(ccdExposure, strayLightData)
1629 self.
debugView(ccdExposure,
"doStrayLight")
1631 if self.config.doFlat:
1632 self.log.info(
"Applying flat correction.")
1636 if self.config.doApplyGains:
1637 self.log.info(
"Applying gain correction instead of flat.")
1638 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1640 exposureMetadata[
"LSST ISR UNITS"] =
"electrons"
1642 if self.config.doFringe
and self.config.fringeAfterFlat:
1643 self.log.info(
"Applying fringe correction after flat.")
1644 self.fringe.run(ccdExposure, **fringes.getDict())
1646 if self.config.doVignette:
1647 if self.config.doMaskVignettePolygon:
1648 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1650 self.log.info(
"Constructing and attaching vignette polygon.")
1652 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1653 vignetteValue=self.config.vignetteValue, log=self.log)
1655 if self.config.doAttachTransmissionCurve:
1656 self.log.info(
"Adding transmission curves.")
1657 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1658 filterTransmission=filterTransmission,
1659 sensorTransmission=sensorTransmission,
1660 atmosphereTransmission=atmosphereTransmission)
1662 flattenedThumb =
None
1663 if self.config.qa.doThumbnailFlattened:
1664 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1666 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1667 self.log.info(
"Performing illumination correction.")
1668 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1669 illumMaskedImage, illumScale=self.config.illumScale,
1670 trimToFit=self.config.doTrimToMatchCalib)
1673 if self.config.doSaveInterpPixels:
1674 preInterpExp = ccdExposure.clone()
1689 if self.config.doSetBadRegions:
1690 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1691 if badPixelCount > 0:
1692 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1694 if self.config.doInterpolate:
1695 self.log.info(
"Interpolating masked pixels.")
1696 isrFunctions.interpolateFromMask(
1697 maskedImage=ccdExposure.getMaskedImage(),
1698 fwhm=self.config.fwhm,
1699 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1700 maskNameList=list(self.config.maskListToInterpolate)
1706 if self.config.doAmpOffset:
1707 self.log.info(
"Correcting amp offsets.")
1708 self.ampOffset.run(ccdExposure)
1710 if self.config.doMeasureBackground:
1711 self.log.info(
"Measuring background level.")
1714 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1716 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1718 afwMath.MEDIAN | afwMath.STDEVCLIP)
1719 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1720 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1721 qaStats.getValue(afwMath.STDEVCLIP)
1722 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1723 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1724 qaStats.getValue(afwMath.STDEVCLIP))
1727 if self.config.doStandardStatistics:
1728 metadata = ccdExposure.getMetadata()
1730 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1731 ampName = amp.getName()
1732 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1733 ampExposure.getMaskedImage(),
1734 [self.config.saturatedMaskName]
1736 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1737 ampExposure.getMaskedImage(),
1741 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1743 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1744 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1745 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1747 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1748 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1749 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1750 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1752 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1755 outputStatistics =
None
1756 if self.config.doCalculateStatistics:
1757 outputStatistics = self.isrStats.run(ccdExposure, overscanResults=overscans,
1758 bias=bias, dark=dark, flat=flat, ptc=ptc).results
1761 outputBin1Exposure =
None
1762 outputBin2Exposure =
None
1763 if self.config.doBinnedExposures:
1764 outputBin1Exposure, outputBin2Exposure = self.
makeBinnedImages(ccdExposure)
1766 self.
debugView(ccdExposure,
"postISRCCD")
1768 return pipeBase.Struct(
1769 exposure=ccdExposure,
1771 flattenedThumb=flattenedThumb,
1773 outputBin1Exposure=outputBin1Exposure,
1774 outputBin2Exposure=outputBin2Exposure,
1776 preInterpExposure=preInterpExp,
1777 outputExposure=ccdExposure,
1778 outputOssThumbnail=ossThumb,
1779 outputFlattenedThumbnail=flattenedThumb,
1780 outputStatistics=outputStatistics,