1036 inputs = butlerQC.get(inputRefs)
1039 inputs[
'detectorNum'] = inputRefs.ccdExposure.dataId[
'detector']
1040 except Exception
as e:
1041 raise ValueError(
"Failure to find valid detectorNum value for Dataset %s: %s." %
1044 detector = inputs[
'ccdExposure'].getDetector()
1047 additionalInputDates = {}
1049 if self.config.doCrosstalk
is True:
1052 if 'crosstalk' in inputs
and inputs[
'crosstalk']
is not None:
1053 if not isinstance(inputs[
'crosstalk'], CrosstalkCalib):
1054 inputs[
'crosstalk'] = CrosstalkCalib.fromTable(inputs[
'crosstalk'])
1056 coeffVector = (self.config.crosstalk.crosstalkValues
1057 if self.config.crosstalk.useConfigCoefficients
else None)
1058 crosstalkCalib =
CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1059 inputs[
'crosstalk'] = crosstalkCalib
1060 if inputs[
'crosstalk'].interChip
and len(inputs[
'crosstalk'].interChip) > 0:
1061 if 'crosstalkSources' not in inputs:
1062 self.log.warning(
"No crosstalkSources found for chip with interChip terms!")
1065 if 'linearizer' in inputs:
1066 if isinstance(inputs[
'linearizer'], dict):
1068 linearizer.fromYaml(inputs[
'linearizer'])
1069 self.log.warning(
"Dictionary linearizers will be deprecated in DM-28741.")
1070 elif isinstance(inputs[
'linearizer'], numpy.ndarray):
1074 self.log.warning(
"Bare lookup table linearizers will be deprecated in DM-28741.")
1076 linearizer = inputs[
'linearizer']
1077 self.log.info(
"Loading linearizer from the Butler.")
1078 linearizer.log = self.log
1079 inputs[
'linearizer'] = linearizer
1082 self.log.info(
"Constructing linearizer from cameraGeom information.")
1084 if self.config.doDefect
is True:
1085 if "defects" in inputs
and inputs[
'defects']
is not None:
1089 if not isinstance(inputs[
"defects"], Defects):
1090 inputs[
"defects"] = Defects.fromTable(inputs[
"defects"])
1094 brighterFatterSource =
None
1095 if self.config.doBrighterFatter:
1096 brighterFatterKernel = inputs.pop(
'newBFKernel',
None)
1097 if brighterFatterKernel
is None:
1101 brighterFatterKernel = inputs.get(
'bfKernel',
None)
1102 brighterFatterSource =
'bfKernel'
1103 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1105 if brighterFatterKernel
is None:
1107 raise RuntimeError(
"No brighter-fatter kernel was supplied.")
1108 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1115 brighterFatterSource =
'newBFKernel'
1116 additionalInputDates[brighterFatterSource] = self.
extractCalibDate(brighterFatterKernel)
1118 detName = detector.getName()
1119 level = brighterFatterKernel.level
1122 inputs[
'bfGains'] = brighterFatterKernel.gain
1123 if self.config.brighterFatterLevel ==
'DETECTOR':
1125 if level ==
'DETECTOR':
1126 if detName
in brighterFatterKernel.detKernels:
1127 kernel = brighterFatterKernel.detKernels[detName]
1129 raise RuntimeError(
"Failed to extract kernel from new-style BF kernel.")
1130 elif level ==
'AMP':
1131 self.log.warning(
"Making DETECTOR level kernel from AMP based brighter "
1133 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1134 kernel = brighterFatterKernel.detKernels[detName]
1136 raise RuntimeError(
"Could not identify brighter-fatter kernel!")
1140 inputs[
'bfKernel'] = numpy.transpose(kernel)
1141 elif self.config.brighterFatterLevel ==
'AMP':
1142 raise NotImplementedError(
"Per-amplifier brighter-fatter correction not implemented")
1144 if self.config.doFringe
is True and self.fringe.checkFilter(inputs[
'ccdExposure']):
1145 expId = inputs[
'ccdExposure'].info.id
1146 inputs[
'fringes'] = self.fringe.loadFringes(inputs[
'fringes'],
1148 assembler=self.assembleCcd
1149 if self.config.doAssembleIsrExposures
else None)
1151 inputs[
'fringes'] = pipeBase.Struct(fringes=
None)
1153 if self.config.doStrayLight
is True and self.strayLight.checkFilter(inputs[
'ccdExposure']):
1154 if 'strayLightData' not in inputs:
1155 inputs[
'strayLightData'] =
None
1157 if self.config.doHeaderProvenance:
1159 exposureMetadata = inputs[
'ccdExposure'].getMetadata()
1163 additionalInputs = []
1164 if self.config.doBrighterFatter:
1165 additionalInputs.append(brighterFatterSource)
1167 for inputName
in sorted(list(inputs.keys()) + additionalInputs):
1168 reference = getattr(inputRefs, inputName,
None)
1169 if reference
is not None and hasattr(reference,
"run"):
1170 runKey = f
"LSST CALIB RUN {inputName.upper()}"
1171 runValue = reference.run
1172 idKey = f
"LSST CALIB UUID {inputName.upper()}"
1173 idValue = str(reference.id)
1174 dateKey = f
"LSST CALIB DATE {inputName.upper()}"
1176 if inputName
in additionalInputDates:
1177 dateValue = additionalInputDates[inputName]
1181 exposureMetadata[runKey] = runValue
1182 exposureMetadata[idKey] = idValue
1183 exposureMetadata[dateKey] = dateValue
1185 outputs = self.
run(**inputs)
1186 butlerQC.put(outputs, outputRefs)
1189 def run(self, ccdExposure, *, camera=None, bias=None, linearizer=None,
1190 crosstalk=None, crosstalkSources=None,
1191 dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None,
1192 fringes=pipeBase.Struct(fringes=
None), opticsTransmission=
None, filterTransmission=
None,
1193 sensorTransmission=
None, atmosphereTransmission=
None,
1194 detectorNum=
None, strayLightData=
None, illumMaskedImage=
None,
1195 deferredChargeCalib=
None,
1197 """Perform instrument signature removal on an exposure.
1199 Steps included in the ISR processing, in order performed, are:
1201 - saturation and suspect pixel masking
1202 - overscan subtraction
1203 - CCD assembly of individual amplifiers
1205 - variance image construction
1206 - linearization of non-linear response
1208 - brighter-fatter correction
1211 - stray light subtraction
1213 - masking of known defects and camera specific features
1214 - vignette calculation
1215 - appending transmission curve and distortion model
1219 ccdExposure : `lsst.afw.image.Exposure`
1220 The raw exposure that is to be run through ISR. The
1221 exposure is modified by this method.
1222 camera : `lsst.afw.cameraGeom.Camera`, optional
1223 The camera geometry for this exposure. Required if
1224 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1225 ``flat`` does not have an associated detector.
1226 bias : `lsst.afw.image.Exposure`, optional
1227 Bias calibration frame.
1228 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1229 Functor for linearization.
1230 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1231 Calibration for crosstalk.
1232 crosstalkSources : `list`, optional
1233 List of possible crosstalk sources.
1234 dark : `lsst.afw.image.Exposure`, optional
1235 Dark calibration frame.
1236 flat : `lsst.afw.image.Exposure`, optional
1237 Flat calibration frame.
1238 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1239 Photon transfer curve dataset, with, e.g., gains
1241 bfKernel : `numpy.ndarray`, optional
1242 Brighter-fatter kernel.
1243 bfGains : `dict` of `float`, optional
1244 Gains used to override the detector's nominal gains for the
1245 brighter-fatter correction. A dict keyed by amplifier name for
1246 the detector in question.
1247 defects : `lsst.ip.isr.Defects`, optional
1249 fringes : `lsst.pipe.base.Struct`, optional
1250 Struct containing the fringe correction data, with
1254 fringe calibration frame (`lsst.afw.image.Exposure`)
1256 random seed derived from the ``ccdExposureId`` for random
1257 number generator (`numpy.uint32`)
1258 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1259 A ``TransmissionCurve`` that represents the throughput of the,
1260 optics, to be evaluated in focal-plane coordinates.
1261 filterTransmission : `lsst.afw.image.TransmissionCurve`
1262 A ``TransmissionCurve`` that represents the throughput of the
1263 filter itself, to be evaluated in focal-plane coordinates.
1264 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1265 A ``TransmissionCurve`` that represents the throughput of the
1266 sensor itself, to be evaluated in post-assembly trimmed detector
1268 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1269 A ``TransmissionCurve`` that represents the throughput of the
1270 atmosphere, assumed to be spatially constant.
1271 detectorNum : `int`, optional
1272 The integer number for the detector to process.
1273 strayLightData : `object`, optional
1274 Opaque object containing calibration information for stray-light
1275 correction. If `None`, no correction will be performed.
1276 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1277 Illumination correction image.
1281 result : `lsst.pipe.base.Struct`
1282 Result struct with component:
1285 The fully ISR corrected exposure.
1286 (`lsst.afw.image.Exposure`)
1288 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1290 Thumbnail image of the exposure after overscan subtraction.
1293 Thumbnail image of the exposure after flat-field correction.
1295 ``outputStatistics``
1296 Values of the additional statistics calculated.
1301 Raised if a configuration option is set to `True`, but the
1302 required calibration data has not been specified.
1306 The current processed exposure can be viewed by setting the
1307 appropriate `lsstDebug` entries in the ``debug.display``
1308 dictionary. The names of these entries correspond to some of
1309 the `IsrTaskConfig` Boolean options, with the value denoting the
1310 frame to use. The exposure is shown inside the matching
1311 option check and after the processing of that step has
1312 finished. The steps with debug points are:
1323 In addition, setting the ``postISRCCD`` entry displays the
1324 exposure after all ISR processing has finished.
1327 ccdExposure = self.
ensureExposure(ccdExposure, camera, detectorNum)
1332 ccd = ccdExposure.getDetector()
1333 filterLabel = ccdExposure.getFilter()
1334 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1337 assert not self.config.doAssembleCcd,
"You need a Detector to run assembleCcd."
1338 ccd = [
FakeAmp(ccdExposure, self.config)]
1341 if self.config.doBias
and bias
is None:
1342 raise RuntimeError(
"Must supply a bias exposure if config.doBias=True.")
1344 raise RuntimeError(
"Must supply a linearizer if config.doLinearize=True for this detector.")
1345 if self.config.doBrighterFatter
and bfKernel
is None:
1346 raise RuntimeError(
"Must supply a kernel if config.doBrighterFatter=True.")
1347 if self.config.doDark
and dark
is None:
1348 raise RuntimeError(
"Must supply a dark exposure if config.doDark=True.")
1349 if self.config.doFlat
and flat
is None:
1350 raise RuntimeError(
"Must supply a flat exposure if config.doFlat=True.")
1351 if self.config.doDefect
and defects
is None:
1352 raise RuntimeError(
"Must supply defects if config.doDefect=True.")
1353 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters
1354 and fringes.fringes
is None):
1359 raise RuntimeError(
"Must supply fringe exposure as a pipeBase.Struct.")
1360 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters
1361 and illumMaskedImage
is None):
1362 raise RuntimeError(
"Must supply an illumcor if config.doIlluminationCorrection=True.")
1363 if (self.config.doDeferredCharge
and deferredChargeCalib
is None):
1364 raise RuntimeError(
"Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1365 if (self.config.usePtcGains
and ptc
is None):
1366 raise RuntimeError(
"No ptcDataset provided to use PTC gains.")
1367 if (self.config.usePtcReadNoise
and ptc
is None):
1368 raise RuntimeError(
"No ptcDataset provided to use PTC read noise.")
1371 exposureMetadata = ccdExposure.getMetadata()
1372 if self.config.doBias:
1375 if self.config.doBrighterFatter:
1377 if self.config.doCrosstalk:
1379 if self.config.doDark:
1382 if self.config.doDefect:
1384 if self.config.doDeferredCharge:
1386 if self.config.doFlat:
1389 if (self.config.doFringe
and physicalFilter
in self.fringe.config.filters):
1391 if (self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters):
1395 if self.config.usePtcGains
or self.config.usePtcReadNoise:
1397 if self.config.doStrayLight:
1403 exposureMetadata[
"LSST ISR UNITS"] =
"adu"
1406 if self.config.doConvertIntToFloat:
1407 self.log.info(
"Converting exposure to floating point values.")
1410 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1411 self.log.info(
"Applying bias correction.")
1412 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1413 trimToFit=self.config.doTrimToMatchCalib)
1419 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1421 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1426 if ccdExposure.getBBox().contains(amp.getBBox()):
1431 if self.config.doOverscan
and not badAmp:
1434 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1435 if overscanResults
is not None and \
1436 self.config.qa
is not None and self.config.qa.saveStats
is True:
1437 if isinstance(overscanResults.overscanMean, float):
1439 mean = overscanResults.overscanMean
1440 median = overscanResults.overscanMedian
1441 sigma = overscanResults.overscanSigma
1442 residMean = overscanResults.residualMean
1443 residMedian = overscanResults.residualMedian
1444 residSigma = overscanResults.residualSigma
1448 mean = overscanResults.overscanMean[0]
1449 median = overscanResults.overscanMedian[0]
1450 sigma = overscanResults.overscanSigma[0]
1451 residMean = overscanResults.residualMean[0]
1452 residMedian = overscanResults.residualMedian[0]
1453 residSigma = overscanResults.residualSigma[0]
1455 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = median
1456 self.metadata[f
"FIT MEAN {amp.getName()}"] = mean
1457 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1458 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1459 amp.getName(), mean, sigma)
1461 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMedian
1462 self.metadata[f
"RESIDUAL MEAN {amp.getName()}"] = residMean
1463 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1464 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1465 amp.getName(), residMean, residSigma)
1467 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1470 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1471 overscanResults =
None
1473 overscans.append(overscanResults
if overscanResults
is not None else None)
1475 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1481 if self.config.doDeferredCharge:
1482 self.log.info(
"Applying deferred charge/CTI correction.")
1483 self.deferredChargeCorrection.run(
1485 deferredChargeCalib,
1488 self.
debugView(ccdExposure,
"doDeferredCharge")
1490 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1491 self.log.info(
"Applying crosstalk correction.")
1492 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1493 crosstalkSources=crosstalkSources, camera=camera)
1494 self.
debugView(ccdExposure,
"doCrosstalk")
1496 if self.config.doAssembleCcd:
1497 self.log.info(
"Assembling CCD from amplifiers.")
1498 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1500 if self.config.expectWcs
and not ccdExposure.getWcs():
1501 self.log.warning(
"No WCS found in input exposure.")
1502 self.
debugView(ccdExposure,
"doAssembleCcd")
1505 if self.config.qa.doThumbnailOss:
1506 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1508 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1509 self.log.info(
"Applying bias correction.")
1510 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1511 trimToFit=self.config.doTrimToMatchCalib)
1514 if self.config.doVariance:
1516 if ccdExposure.getBBox().contains(amp.getBBox()):
1517 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1518 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1521 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1523 afwMath.MEDIAN | afwMath.STDEVCLIP)
1524 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1525 qaStats.getValue(afwMath.MEDIAN)
1526 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1527 qaStats.getValue(afwMath.STDEVCLIP)
1528 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1529 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1530 qaStats.getValue(afwMath.STDEVCLIP))
1531 if self.config.maskNegativeVariance:
1535 self.log.info(
"Applying linearizer.")
1536 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1537 detector=ccd, log=self.log)
1539 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1540 self.log.info(
"Applying crosstalk correction.")
1541 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1542 crosstalkSources=crosstalkSources, isTrimmed=
True)
1543 self.
debugView(ccdExposure,
"doCrosstalk")
1548 if self.config.doDefect:
1549 self.log.info(
"Masking defects.")
1552 if self.config.numEdgeSuspect > 0:
1553 self.log.info(
"Masking edges as SUSPECT.")
1554 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1555 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1557 if self.config.doNanMasking:
1558 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1561 if self.config.doWidenSaturationTrails:
1562 self.log.info(
"Widening saturation trails.")
1563 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1565 if self.config.doCameraSpecificMasking:
1566 self.log.info(
"Masking regions for camera specific reasons.")
1567 self.masking.run(ccdExposure)
1569 if self.config.doBrighterFatter:
1579 interpExp = ccdExposure.clone()
1581 isrFunctions.interpolateFromMask(
1582 maskedImage=interpExp.getMaskedImage(),
1583 fwhm=self.config.fwhm,
1584 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1585 maskNameList=list(self.config.brighterFatterMaskListToInterpolate),
1586 useLegacyInterp=self.config.useLegacyInterp,
1588 bfExp = interpExp.clone()
1590 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1591 type(bfKernel), type(bfGains))
1592 if self.config.doFluxConservingBrighterFatterCorrection:
1593 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1596 self.config.brighterFatterMaxIter,
1597 self.config.brighterFatterThreshold,
1598 self.config.brighterFatterApplyGain,
1602 bfResults = isrFunctions.brighterFatterCorrection(
1605 self.config.brighterFatterMaxIter,
1606 self.config.brighterFatterThreshold,
1607 self.config.brighterFatterApplyGain,
1610 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1611 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1614 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1616 image = ccdExposure.getMaskedImage().getImage()
1617 bfCorr = bfExp.getMaskedImage().getImage()
1618 bfCorr -= interpExp.getMaskedImage().getImage()
1627 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1628 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1631 if self.config.brighterFatterMaskGrowSize > 0:
1632 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1633 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1634 isrFunctions.growMasks(ccdExposure.getMask(),
1635 radius=self.config.brighterFatterMaskGrowSize,
1636 maskNameList=maskPlane,
1637 maskValue=maskPlane)
1639 self.
debugView(ccdExposure,
"doBrighterFatter")
1641 if self.config.doDark:
1642 self.log.info(
"Applying dark correction.")
1646 if self.config.doFringe
and not self.config.fringeAfterFlat:
1647 self.log.info(
"Applying fringe correction before flat.")
1648 self.fringe.run(ccdExposure, **fringes.getDict())
1651 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1652 self.log.info(
"Checking strayLight correction.")
1653 self.strayLight.run(ccdExposure, strayLightData)
1654 self.
debugView(ccdExposure,
"doStrayLight")
1656 if self.config.doFlat:
1657 self.log.info(
"Applying flat correction.")
1661 if self.config.doApplyGains:
1662 self.log.info(
"Applying gain correction instead of flat.")
1663 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1665 exposureMetadata[
"LSST ISR UNITS"] =
"electron"
1667 if self.config.doFringe
and self.config.fringeAfterFlat:
1668 self.log.info(
"Applying fringe correction after flat.")
1669 self.fringe.run(ccdExposure, **fringes.getDict())
1671 if self.config.doVignette:
1672 if self.config.doMaskVignettePolygon:
1673 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1675 self.log.info(
"Constructing and attaching vignette polygon.")
1677 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1678 vignetteValue=self.config.vignetteValue, log=self.log)
1680 if self.config.doAttachTransmissionCurve:
1681 self.log.info(
"Adding transmission curves.")
1682 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1683 filterTransmission=filterTransmission,
1684 sensorTransmission=sensorTransmission,
1685 atmosphereTransmission=atmosphereTransmission)
1687 flattenedThumb =
None
1688 if self.config.qa.doThumbnailFlattened:
1689 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1691 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1692 self.log.info(
"Performing illumination correction.")
1693 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1694 illumMaskedImage, illumScale=self.config.illumScale,
1695 trimToFit=self.config.doTrimToMatchCalib)
1698 if self.config.doSaveInterpPixels:
1699 preInterpExp = ccdExposure.clone()
1714 if self.config.doSetBadRegions:
1715 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1716 if badPixelCount > 0:
1717 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1719 if self.config.doInterpolate:
1720 self.log.info(
"Interpolating masked pixels.")
1721 isrFunctions.interpolateFromMask(
1722 maskedImage=ccdExposure.getMaskedImage(),
1723 fwhm=self.config.fwhm,
1724 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1725 maskNameList=list(self.config.maskListToInterpolate),
1726 useLegacyInterp=self.config.useLegacyInterp,
1732 if self.config.doAmpOffset:
1733 if self.config.ampOffset.doApplyAmpOffset:
1734 self.log.info(
"Measuring and applying amp offset corrections.")
1736 self.log.info(
"Measuring amp offset corrections only, without applying them.")
1737 self.ampOffset.run(ccdExposure)
1739 if self.config.doMeasureBackground:
1740 self.log.info(
"Measuring background level.")
1743 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1745 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1747 afwMath.MEDIAN | afwMath.STDEVCLIP)
1748 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1749 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1750 qaStats.getValue(afwMath.STDEVCLIP)
1751 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1752 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1753 qaStats.getValue(afwMath.STDEVCLIP))
1756 if self.config.doStandardStatistics:
1757 metadata = ccdExposure.getMetadata()
1759 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1760 ampName = amp.getName()
1761 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1762 ampExposure.getMaskedImage(),
1763 [self.config.saturatedMaskName]
1765 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1766 ampExposure.getMaskedImage(),
1770 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1772 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1773 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1774 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1776 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1777 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1778 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1779 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1781 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1784 outputStatistics =
None
1785 if self.config.doCalculateStatistics:
1786 outputStatistics = self.isrStats.run(ccdExposure, overscanResults=overscans,
1787 bias=bias, dark=dark, flat=flat, ptc=ptc,
1788 defects=defects).results
1791 outputBin1Exposure =
None
1792 outputBin2Exposure =
None
1793 if self.config.doBinnedExposures:
1794 self.log.info(
"Creating binned exposures.")
1795 outputBin1Exposure = self.binning.run(
1797 binFactor=self.config.binFactor1,
1799 outputBin2Exposure = self.binning.run(
1801 binFactor=self.config.binFactor2,
1804 self.
debugView(ccdExposure,
"postISRCCD")
1806 return pipeBase.Struct(
1807 exposure=ccdExposure,
1809 flattenedThumb=flattenedThumb,
1811 outputBin1Exposure=outputBin1Exposure,
1812 outputBin2Exposure=outputBin2Exposure,
1814 preInterpExposure=preInterpExp,
1815 outputExposure=ccdExposure,
1816 outputOssThumbnail=ossThumb,
1817 outputFlattenedThumbnail=flattenedThumb,
1818 outputStatistics=outputStatistics,