1021 inputs = butlerQC.get(inputRefs)
1024 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
1025 except Exception
as e:
1026 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
1029 detector = inputs[
'ccdExposure'].getDetector()
1032 additionalInputDates = {}
1034 if self.config.doCrosstalk
is True:
1037 if 'crosstalk' in inputs
and inputs[
'crosstalk']
is not None:
1038 if not isinstance(inputs[
'crosstalk'], CrosstalkCalib):
1039 inputs[
'crosstalk'] = CrosstalkCalib.fromTable(inputs[
'crosstalk'])
1041 coeffVector = (self.config.crosstalk.crosstalkValues
1042 if self.config.crosstalk.useConfigCoefficients
else None)
1043 crosstalkCalib =
CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1044 inputs[
'crosstalk'] = crosstalkCalib
1045 if inputs[
'crosstalk'].interChip
and len(inputs[
'crosstalk'].interChip) > 0:
1046 if 'crosstalkSources' not in inputs:
1047 self.log.warning(
"No crosstalkSources found for chip with interChip terms!")
1050 if 'linearizer' in inputs:
1051 if isinstance(inputs[
'linearizer'], dict):
1053 linearizer.fromYaml(inputs[
'linearizer'])
1054 self.log.warning(
"Dictionary linearizers will be deprecated in DM-28741.")
1055 elif isinstance(inputs[
'linearizer'], numpy.ndarray):
1059 self.log.warning(
"Bare lookup table linearizers will be deprecated in DM-28741.")
1061 linearizer = inputs[
'linearizer']
1062 linearizer.log = self.log
1063 inputs[
'linearizer'] = linearizer
1066 self.log.warning(
"Constructing linearizer from cameraGeom information.")
1068 if self.config.doDefect
is True:
1069 if "defects" in inputs
and inputs[
'defects']
is not None:
1073 if not isinstance(inputs[
"defects"], Defects):
1074 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1078 brighterFatterSource =
None
1079 if self.config.doBrighterFatter:
1080 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1081 if brighterFatterKernel
is None:
1085 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1086 brighterFatterSource =
'bfKernel'
1087 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1089 if brighterFatterKernel
is None:
1091 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1092 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1099 brighterFatterSource =
'newBFKernel'
1100 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1102 detName = detector.getName()
1103 level = brighterFatterKernel.level
1106 inputs[
'bfGains'] = brighterFatterKernel.gain
1107 if self.config.brighterFatterLevel ==
'DETECTOR':
1109 if level ==
'DETECTOR':
1110 if detName
in brighterFatterKernel.detKernels:
1111 kernel = brighterFatterKernel.detKernels[detName]
1113 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1114 elif level ==
'AMP':
1115 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1117 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1118 kernel = brighterFatterKernel.detKernels[detName]
1120 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1124 inputs[
'bfKernel'] = numpy.transpose(kernel)
1125 elif self.config.brighterFatterLevel ==
'AMP':
1126 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1128 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1129 expId = inputs[
'ccdExposure'].info.id
1130 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1132 assembler=self.assembleCcd
1133 if self.config.doAssembleIsrExposures
else None)
1135 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1137 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1138 if 'strayLightData' not in inputs:
1139 inputs[
'strayLightData'] =
None
1141 if self.config.doHeaderProvenance:
1143 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1147 additionalInputs = []
1148 if self.config.doBrighterFatter:
1149 additionalInputs.append(brighterFatterSource)
1151 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1152 reference = getattr(inputRefs, inputName,
None)
1153 if reference
is not None and hasattr(reference,
"run"):
1154 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1155 runValue = reference.run
1156 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1157 idValue = str(reference.id)
1158 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1160 if inputName
in additionalInputDates:
1161 dateValue = additionalInputDates[inputName]
1165 exposureMetadata[runKey] = runValue
1166 exposureMetadata[idKey] = idValue
1167 exposureMetadata[dateKey] = dateValue
1169 outputs = self.
run(**inputs)
1170 butlerQC.put(outputs, outputRefs)
1173 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1174 crosstalk=None, crosstalkSources=None,
1175 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1176 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1177 sensorTransmission=
None, atmosphereTransmission=
None,
1178 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1179 deferredChargeCalib=
None,
1181 """Perform instrument signature removal on an exposure.
1183 Steps included in the ISR processing, in order performed, are:
1185 - saturation and suspect pixel masking
1186 - overscan subtraction
1187 - CCD assembly of individual amplifiers
1189 - variance image construction
1190 - linearization of non-linear response
1192 - brighter-fatter correction
1195 - stray light subtraction
1197 - masking of known defects and camera specific features
1198 - vignette calculation
1199 - appending transmission curve and distortion model
1203 ccdExposure : `lsst.afw.image.Exposure`
1204 The raw exposure that is to be run through ISR. The
1205 exposure is modified by this method.
1206 camera : `lsst.afw.cameraGeom.Camera`, optional
1207 The camera geometry for this exposure. Required if
1208 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1209 ``flat`` does not have an associated detector.
1210 bias : `lsst.afw.image.Exposure`, optional
1211 Bias calibration frame.
1212 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1213 Functor for linearization.
1214 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1215 Calibration for crosstalk.
1216 crosstalkSources : `list`, optional
1217 List of possible crosstalk sources.
1218 dark : `lsst.afw.image.Exposure`, optional
1219 Dark calibration frame.
1220 flat : `lsst.afw.image.Exposure`, optional
1221 Flat calibration frame.
1222 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1223 Photon transfer curve dataset, with, e.g., gains
1225 bfKernel : `numpy.ndarray`, optional
1226 Brighter-fatter kernel.
1227 bfGains : `dict` of `float`, optional
1228 Gains used to override the detector's nominal gains for the
1229 brighter-fatter correction. A dict keyed by amplifier name for
1230 the detector in question.
1231 defects : `lsst.ip.isr.Defects`, optional
1233 fringes : `lsst.pipe.base.Struct`, optional
1234 Struct containing the fringe correction data, with
1238 fringe calibration frame (`lsst.afw.image.Exposure`)
1240 random seed derived from the ``ccdExposureId`` for random
1241 number generator (`numpy.uint32`)
1242 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1243 A ``TransmissionCurve`` that represents the throughput of the,
1244 optics, to be evaluated in focal-plane coordinates.
1245 filterTransmission : `lsst.afw.image.TransmissionCurve`
1246 A ``TransmissionCurve`` that represents the throughput of the
1247 filter itself, to be evaluated in focal-plane coordinates.
1248 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1249 A ``TransmissionCurve`` that represents the throughput of the
1250 sensor itself, to be evaluated in post-assembly trimmed detector
1252 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1253 A ``TransmissionCurve`` that represents the throughput of the
1254 atmosphere, assumed to be spatially constant.
1255 detectorNum : `int`, optional
1256 The integer number for the detector to process.
1257 strayLightData : `object`, optional
1258 Opaque object containing calibration information for stray-light
1259 correction. If `None`, no correction will be performed.
1260 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1261 Illumination correction image.
1265 result : `lsst.pipe.base.Struct`
1266 Result struct with component:
1269 The fully ISR corrected exposure.
1270 (`lsst.afw.image.Exposure`)
1272 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1274 Thumbnail image of the exposure after overscan subtraction.
1277 Thumbnail image of the exposure after flat-field correction.
1279 ``outputStatistics``
1280 Values of the additional statistics calculated.
1285 Raised if a configuration option is set to `True`, but the
1286 required calibration data has not been specified.
1290 The current processed exposure can be viewed by setting the
1291 appropriate `lsstDebug` entries in the ``debug.display``
1292 dictionary. The names of these entries correspond to some of
1293 the `IsrTaskConfig` Boolean options, with the value denoting the
1294 frame to use. The exposure is shown inside the matching
1295 option check and after the processing of that step has
1296 finished. The steps with debug points are:
1307 In addition, setting the ``postISRCCD`` entry displays the
1308 exposure after all ISR processing has finished.
1311 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1316 ccd = ccdExposure.getDetector()
1317 filterLabel = ccdExposure.getFilter()
1318 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1321 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1322 ccd = [
FakeAmp(ccdExposure, self.config)]
1325 if self.config.doBias
and bias
is None:
1326 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1328 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1329 if self.config.doBrighterFatter
and bfKernel
is None:
1330 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1331 if self.config.doDark
and dark
is None:
1332 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1333 if self.config.doFlat
and flat
is None:
1334 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1335 if self.config.doDefect
and defects
is None:
1336 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1337 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1338 and fringes.fringes
is None):
1343 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1344 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1345 and illumMaskedImage
is None):
1346 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1347 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1348 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1349 if (self.config.usePtcGains
and ptc
is None):
1350 raise RuntimeError(
"No ptcDataset provided to use PTC gains.")
1351 if (self.config.usePtcReadNoise
and ptc
is None):
1352 raise RuntimeError(
"No ptcDataset provided to use PTC read noise.")
1355 exposureMetadata = ccdExposure.getMetadata()
1356 if self.config.doBias:
1358 if self.config.doBrighterFatter:
1360 if self.config.doCrosstalk:
1362 if self.config.doDark:
1364 if self.config.doDefect:
1366 if self.config.doDeferredCharge:
1368 if self.config.doFlat:
1370 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1372 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1376 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1378 if self.config.doStrayLight:
1384 exposureMetadata[
"LSST ISR UNITS"] =
"ADU"
1387 if self.config.doConvertIntToFloat:
1388 self.log.info(
"Converting exposure to floating point values.")
1391 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1392 self.log.info(
"Applying bias correction.")
1393 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1394 trimToFit=self.config.doTrimToMatchCalib)
1400 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1402 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1407 if ccdExposure.getBBox().contains(amp.getBBox()):
1412 if self.config.doOverscan
and not badAmp:
1415 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1416 if overscanResults
is not None and \
1417 self.config.qa
is not None and self.config.qa.saveStats
is True:
1418 if isinstance(overscanResults.overscanMean, float):
1420 mean = overscanResults.overscanMean
1421 sigma = overscanResults.overscanSigma
1422 residMean = overscanResults.residualMean
1423 residSigma = overscanResults.residualSigma
1427 mean = overscanResults.overscanMean[0]
1428 sigma = overscanResults.overscanSigma[0]
1429 residMean = overscanResults.residualMean[0]
1430 residSigma = overscanResults.residualSigma[0]
1432 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = mean
1433 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1434 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1435 amp.getName(), mean, sigma)
1437 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1438 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1439 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1440 amp.getName(), residMean, residSigma)
1442 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1445 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1446 overscanResults =
None
1448 overscans.append(overscanResults
if overscanResults
is not None else None)
1450 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1456 if self.config.doDeferredCharge:
1457 self.log.info(
"Applying deferred charge/CTI correction.")
1458 self.deferredChargeCorrection.
run(ccdExposure, deferredChargeCalib)
1459 self.
debugView(ccdExposure,
"doDeferredCharge")
1461 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1462 self.log.info(
"Applying crosstalk correction.")
1463 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1464 crosstalkSources=crosstalkSources, camera=camera)
1465 self.
debugView(ccdExposure,
"doCrosstalk")
1467 if self.config.doAssembleCcd:
1468 self.log.info(
"Assembling CCD from amplifiers.")
1469 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1471 if self.config.expectWcs
and not ccdExposure.getWcs():
1472 self.log.warning(
"No WCS found in input exposure.")
1473 self.
debugView(ccdExposure,
"doAssembleCcd")
1476 if self.config.qa.doThumbnailOss:
1477 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1479 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1480 self.log.info(
"Applying bias correction.")
1481 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1482 trimToFit=self.config.doTrimToMatchCalib)
1485 if self.config.doVariance:
1486 for amp, overscanResults
in zip(ccd, overscans):
1487 if ccdExposure.getBBox().contains(amp.getBBox()):
1488 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1489 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1492 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1493 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1494 afwMath.MEDIAN | afwMath.STDEVCLIP)
1495 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1496 qaStats.getValue(afwMath.MEDIAN)
1497 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1498 qaStats.getValue(afwMath.STDEVCLIP)
1499 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1500 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1501 qaStats.getValue(afwMath.STDEVCLIP))
1502 if self.config.maskNegativeVariance:
1506 self.log.info(
"Applying linearizer.")
1507 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1508 detector=ccd, log=self.log)
1510 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1511 self.log.info(
"Applying crosstalk correction.")
1512 self.crosstalk.
run(ccdExposure, crosstalk=crosstalk,
1513 crosstalkSources=crosstalkSources, isTrimmed=
True)
1514 self.
debugView(ccdExposure,
"doCrosstalk")
1519 if self.config.doDefect:
1520 self.log.info(
"Masking defects.")
1523 if self.config.numEdgeSuspect > 0:
1524 self.log.info(
"Masking edges as SUSPECT.")
1525 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1526 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1528 if self.config.doNanMasking:
1529 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1532 if self.config.doWidenSaturationTrails:
1533 self.log.info(
"Widening saturation trails.")
1534 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1536 if self.config.doCameraSpecificMasking:
1537 self.log.info(
"Masking regions for camera specific reasons.")
1538 self.masking.
run(ccdExposure)
1540 if self.config.doBrighterFatter:
1550 interpExp = ccdExposure.clone()
1552 isrFunctions.interpolateFromMask(
1553 maskedImage=interpExp.getMaskedImage(),
1554 fwhm=self.config.fwhm,
1555 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1556 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1558 bfExp = interpExp.clone()
1560 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1561 type(bfKernel), type(bfGains))
1562 if self.config.doFluxConservingBrighterFatterCorrection:
1563 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1566 self.config.brighterFatterMaxIter,
1567 self.config.brighterFatterThreshold,
1568 self.config.brighterFatterApplyGain,
1572 bfResults = isrFunctions.brighterFatterCorrection(
1575 self.config.brighterFatterMaxIter,
1576 self.config.brighterFatterThreshold,
1577 self.config.brighterFatterApplyGain,
1580 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1581 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1584 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1586 image = ccdExposure.getMaskedImage().getImage()
1587 bfCorr = bfExp.getMaskedImage().getImage()
1588 bfCorr -= interpExp.getMaskedImage().getImage()
1597 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1598 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1601 if self.config.brighterFatterMaskGrowSize > 0:
1602 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1603 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1604 isrFunctions.growMasks(ccdExposure.getMask(),
1605 radius=self.config.brighterFatterMaskGrowSize,
1606 maskNameList=maskPlane,
1607 maskValue=maskPlane)
1609 self.
debugView(ccdExposure,
"doBrighterFatter")
1611 if self.config.doDark:
1612 self.log.info(
"Applying dark correction.")
1616 if self.config.doFringe
and not self.config.fringeAfterFlat:
1617 self.log.info(
"Applying fringe correction before flat.")
1618 self.fringe.
run(ccdExposure, **fringes.getDict())
1621 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1622 self.log.info(
"Checking strayLight correction.")
1623 self.strayLight.
run(ccdExposure, strayLightData)
1624 self.
debugView(ccdExposure,
"doStrayLight")
1626 if self.config.doFlat:
1627 self.log.info(
"Applying flat correction.")
1631 if self.config.doApplyGains:
1632 self.log.info(
"Applying gain correction instead of flat.")
1633 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1635 exposureMetadata[
"LSST ISR UNITS"] =
"electrons"
1637 if self.config.doFringe
and self.config.fringeAfterFlat:
1638 self.log.info(
"Applying fringe correction after flat.")
1639 self.fringe.
run(ccdExposure, **fringes.getDict())
1641 if self.config.doVignette:
1642 if self.config.doMaskVignettePolygon:
1643 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1645 self.log.info(
"Constructing and attaching vignette polygon.")
1647 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1648 vignetteValue=self.config.vignetteValue, log=self.log)
1650 if self.config.doAttachTransmissionCurve:
1651 self.log.info(
"Adding transmission curves.")
1652 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1653 filterTransmission=filterTransmission,
1654 sensorTransmission=sensorTransmission,
1655 atmosphereTransmission=atmosphereTransmission)
1657 flattenedThumb =
None
1658 if self.config.qa.doThumbnailFlattened:
1659 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1661 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1662 self.log.info(
"Performing illumination correction.")
1663 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1664 illumMaskedImage, illumScale=self.config.illumScale,
1665 trimToFit=self.config.doTrimToMatchCalib)
1668 if self.config.doSaveInterpPixels:
1669 preInterpExp = ccdExposure.clone()
1684 if self.config.doSetBadRegions:
1685 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1686 if badPixelCount > 0:
1687 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1689 if self.config.doInterpolate:
1690 self.log.info(
"Interpolating masked pixels.")
1691 isrFunctions.interpolateFromMask(
1692 maskedImage=ccdExposure.getMaskedImage(),
1693 fwhm=self.config.fwhm,
1694 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1695 maskNameList=list(self.config.maskListToInterpolate)
1701 if self.config.doAmpOffset:
1702 self.log.info(
"Correcting amp offsets.")
1703 self.ampOffset.
run(ccdExposure)
1705 if self.config.doMeasureBackground:
1706 self.log.info(
"Measuring background level.")
1709 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1711 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1712 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1713 afwMath.MEDIAN | afwMath.STDEVCLIP)
1714 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1715 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1716 qaStats.getValue(afwMath.STDEVCLIP)
1717 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1718 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1719 qaStats.getValue(afwMath.STDEVCLIP))
1722 if self.config.doStandardStatistics:
1723 metadata = ccdExposure.getMetadata()
1725 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1726 ampName = amp.getName()
1727 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1728 ampExposure.getMaskedImage(),
1729 [self.config.saturatedMaskName]
1731 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1732 ampExposure.getMaskedImage(),
1735 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1736 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1738 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1739 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1740 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1742 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1743 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1744 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1745 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1747 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1750 outputStatistics =
None
1751 if self.config.doCalculateStatistics:
1752 outputStatistics = self.isrStats.
run(ccdExposure, overscanResults=overscans,
1756 outputBin1Exposure =
None
1757 outputBin2Exposure =
None
1758 if self.config.doBinnedExposures:
1759 outputBin1Exposure, outputBin2Exposure = self.
makeBinnedImages(ccdExposure)
1761 self.
debugView(ccdExposure,
"postISRCCD")
1763 return pipeBase.Struct(
1764 exposure=ccdExposure,
1766 flattenedThumb=flattenedThumb,
1768 outputBin1Exposure=outputBin1Exposure,
1769 outputBin2Exposure=outputBin2Exposure,
1771 preInterpExposure=preInterpExp,
1772 outputExposure=ccdExposure,
1773 outputOssThumbnail=ossThumb,
1774 outputFlattenedThumbnail=flattenedThumb,
1775 outputStatistics=outputStatistics,