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"
1404 exposureMetadata[
"LSST ISR CROSSTALK APPLIED"] =
False
1405 exposureMetadata[
"LSST ISR LINEARIZER APPLIED"] =
False
1406 exposureMetadata[
"LSST ISR CTI APPLIED"] =
False
1407 exposureMetadata[
"LSST ISR BIAS APPLIED"] =
False
1408 exposureMetadata[
"LSST ISR DARK APPLIED"] =
False
1409 exposureMetadata[
"LSST ISR BF APPLIED"] =
False
1410 exposureMetadata[
"LSST ISR FLAT APPLIED"] =
False
1413 if self.config.doConvertIntToFloat:
1414 self.log.info(
"Converting exposure to floating point values.")
1417 if self.config.doBias
and self.config.doBiasBeforeOverscan:
1418 self.log.info(
"Applying bias correction.")
1419 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1420 trimToFit=self.config.doTrimToMatchCalib)
1422 ccdExposure.metadata[
"LSST ISR BIAS APPLIED"] =
True
1427 if self.config.doOverscan
and self.config.overscan.doParallelOverscan:
1429 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1434 if ccdExposure.getBBox().contains(amp.getBBox()):
1439 if self.config.doOverscan
and not badAmp:
1442 self.log.debug(
"Corrected overscan for amplifier %s.", amp.getName())
1443 if overscanResults
is not None and \
1444 self.config.qa
is not None and self.config.qa.saveStats
is True:
1445 if isinstance(overscanResults.overscanMean, float):
1447 mean = overscanResults.overscanMean
1448 median = overscanResults.overscanMedian
1449 sigma = overscanResults.overscanSigma
1450 residMean = overscanResults.residualMean
1451 residMedian = overscanResults.residualMedian
1452 residSigma = overscanResults.residualSigma
1456 mean = overscanResults.overscanMean[0]
1457 median = overscanResults.overscanMedian[0]
1458 sigma = overscanResults.overscanSigma[0]
1459 residMean = overscanResults.residualMean[0]
1460 residMedian = overscanResults.residualMedian[0]
1461 residSigma = overscanResults.residualSigma[0]
1463 self.metadata[f
"FIT MEDIAN {amp.getName()}"] = median
1464 self.metadata[f
"FIT MEAN {amp.getName()}"] = mean
1465 self.metadata[f
"FIT STDEV {amp.getName()}"] = sigma
1466 self.log.debug(
" Overscan stats for amplifer %s: %f +/- %f",
1467 amp.getName(), mean, sigma)
1469 self.metadata[f
"RESIDUAL MEDIAN {amp.getName()}"] = residMedian
1470 self.metadata[f
"RESIDUAL MEAN {amp.getName()}"] = residMean
1471 self.metadata[f
"RESIDUAL STDEV {amp.getName()}"] = residSigma
1472 self.log.debug(
" Overscan stats for amplifer %s after correction: %f +/- %f",
1473 amp.getName(), residMean, residSigma)
1475 ccdExposure.getMetadata().set(
'OVERSCAN',
"Overscan corrected")
1478 self.log.warning(
"Amplifier %s is bad.", amp.getName())
1479 overscanResults =
None
1481 overscans.append(overscanResults
if overscanResults
is not None else None)
1483 self.log.info(
"Skipped OSCAN for %s.", amp.getName())
1489 if self.config.doDeferredCharge:
1490 self.log.info(
"Applying deferred charge/CTI correction.")
1491 self.deferredChargeCorrection.run(
1493 deferredChargeCalib,
1496 self.
debugView(ccdExposure,
"doDeferredCharge")
1497 ccdExposure.metadata[
"LSST ISR CTI APPLIED"] =
True
1499 if self.config.doCrosstalk
and self.config.doCrosstalkBeforeAssemble:
1500 self.log.info(
"Applying crosstalk correction.")
1501 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1502 crosstalkSources=crosstalkSources, camera=camera)
1503 self.
debugView(ccdExposure,
"doCrosstalk")
1504 ccdExposure.metadata[
"LSST ISR CROSSTALK APPLIED"] =
True
1506 if self.config.doAssembleCcd:
1507 self.log.info(
"Assembling CCD from amplifiers.")
1508 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1510 if self.config.expectWcs
and not ccdExposure.getWcs():
1511 self.log.warning(
"No WCS found in input exposure.")
1512 self.
debugView(ccdExposure,
"doAssembleCcd")
1515 if self.config.qa.doThumbnailOss:
1516 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1518 if self.config.doBias
and not self.config.doBiasBeforeOverscan:
1519 self.log.info(
"Applying bias correction.")
1520 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1521 trimToFit=self.config.doTrimToMatchCalib)
1523 ccdExposure.metadata[
"LSST ISR BIAS APPLIED"] =
True
1525 if self.config.doVariance:
1527 if ccdExposure.getBBox().contains(amp.getBBox()):
1528 self.log.debug(
"Constructing variance map for amplifer %s.", amp.getName())
1529 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1532 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1534 afwMath.MEDIAN | afwMath.STDEVCLIP)
1535 self.metadata[f
"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1536 qaStats.getValue(afwMath.MEDIAN)
1537 self.metadata[f
"ISR VARIANCE {amp.getName()} STDEV"] = \
1538 qaStats.getValue(afwMath.STDEVCLIP)
1539 self.log.debug(
" Variance stats for amplifer %s: %f +/- %f.",
1540 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1541 qaStats.getValue(afwMath.STDEVCLIP))
1542 if self.config.maskNegativeVariance:
1546 self.log.info(
"Applying linearizer.")
1547 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1548 detector=ccd, log=self.log)
1549 ccdExposure.metadata[
"LSST ISR LINEARIZER APPLIED"] =
True
1551 if self.config.doCrosstalk
and not self.config.doCrosstalkBeforeAssemble:
1552 self.log.info(
"Applying crosstalk correction.")
1553 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1554 crosstalkSources=crosstalkSources)
1555 self.
debugView(ccdExposure,
"doCrosstalk")
1556 ccdExposure.metadata[
"LSST ISR CROSSTALK APPLIED"] =
True
1561 if self.config.doDefect:
1562 self.log.info(
"Masking defects.")
1565 if self.config.numEdgeSuspect > 0:
1566 self.log.info(
"Masking edges as SUSPECT.")
1567 self.
maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1568 maskPlane=
"SUSPECT", level=self.config.edgeMaskLevel)
1570 if self.config.doNanMasking:
1571 self.log.info(
"Masking non-finite (NAN, inf) value pixels.")
1574 if self.config.doWidenSaturationTrails:
1575 self.log.info(
"Widening saturation trails.")
1576 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1578 if self.config.doCameraSpecificMasking:
1579 self.log.info(
"Masking regions for camera specific reasons.")
1580 self.masking.run(ccdExposure)
1582 if self.config.doBrighterFatter:
1592 interpExp = ccdExposure.clone()
1594 isrFunctions.interpolateFromMask(
1595 maskedImage=interpExp.getMaskedImage(),
1596 fwhm=self.config.fwhm,
1597 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1598 maskNameList=list(self.config.brighterFatterMaskListToInterpolate),
1599 useLegacyInterp=self.config.useLegacyInterp,
1601 bfExp = interpExp.clone()
1603 self.log.info(
"Applying brighter-fatter correction using kernel type %s / gains %s.",
1604 type(bfKernel), type(bfGains))
1605 if self.config.doFluxConservingBrighterFatterCorrection:
1606 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1609 self.config.brighterFatterMaxIter,
1610 self.config.brighterFatterThreshold,
1611 self.config.brighterFatterApplyGain,
1615 bfResults = isrFunctions.brighterFatterCorrection(
1618 self.config.brighterFatterMaxIter,
1619 self.config.brighterFatterThreshold,
1620 self.config.brighterFatterApplyGain,
1623 bfCorrIters = bfResults[1]
1624 self.metadata[
"LSST ISR BF ITERS"] = bfCorrIters
1625 if bfCorrIters == self.config.brighterFatterMaxIter - 1:
1626 self.log.warning(
"Brighter-fatter correction did not converge, final difference %f.",
1629 self.log.info(
"Finished brighter-fatter correction in %d iterations.",
1631 image = ccdExposure.getMaskedImage().getImage()
1632 bfCorr = bfExp.getMaskedImage().getImage()
1633 bfCorr -= interpExp.getMaskedImage().getImage()
1642 self.log.info(
"Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1643 self.
maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1646 if self.config.brighterFatterMaskGrowSize > 0:
1647 self.log.info(
"Growing masks to account for brighter-fatter kernel convolution.")
1648 for maskPlane
in self.config.brighterFatterMaskListToInterpolate:
1649 isrFunctions.growMasks(ccdExposure.getMask(),
1650 radius=self.config.brighterFatterMaskGrowSize,
1651 maskNameList=maskPlane,
1652 maskValue=maskPlane)
1654 self.
debugView(ccdExposure,
"doBrighterFatter")
1655 ccdExposure.metadata[
"LSST ISR BF APPLIED"] =
True
1657 if self.config.doDark:
1658 self.log.info(
"Applying dark correction.")
1661 ccdExposure.metadata[
"LSST ISR DARK APPLIED"] =
True
1663 if self.config.doFringe
and not self.config.fringeAfterFlat:
1664 self.log.info(
"Applying fringe correction before flat.")
1665 self.fringe.run(ccdExposure, **fringes.getDict())
1668 if self.config.doStrayLight
and self.strayLight.check(ccdExposure):
1669 self.log.info(
"Checking strayLight correction.")
1670 self.strayLight.run(ccdExposure, strayLightData)
1671 self.
debugView(ccdExposure,
"doStrayLight")
1673 if self.config.doFlat:
1674 self.log.info(
"Applying flat correction.")
1677 ccdExposure.metadata[
"LSST ISR FLAT APPLIED"] =
True
1681 if self.config.doApplyGains:
1682 self.log.info(
"Applying gain correction instead of flat.")
1683 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1685 exposureMetadata[
"LSST ISR UNITS"] =
"electron"
1687 if self.config.doFringe
and self.config.fringeAfterFlat:
1688 self.log.info(
"Applying fringe correction after flat.")
1689 self.fringe.run(ccdExposure, **fringes.getDict())
1691 if self.config.doVignette:
1692 if self.config.doMaskVignettePolygon:
1693 self.log.info(
"Constructing, attaching, and masking vignette polygon.")
1695 self.log.info(
"Constructing and attaching vignette polygon.")
1697 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1698 vignetteValue=self.config.vignetteValue, log=self.log)
1700 if self.config.doAttachTransmissionCurve:
1701 self.log.info(
"Adding transmission curves.")
1702 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1703 filterTransmission=filterTransmission,
1704 sensorTransmission=sensorTransmission,
1705 atmosphereTransmission=atmosphereTransmission)
1707 flattenedThumb =
None
1708 if self.config.qa.doThumbnailFlattened:
1709 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1711 if self.config.doIlluminationCorrection
and physicalFilter
in self.config.illumFilters:
1712 self.log.info(
"Performing illumination correction.")
1713 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1714 illumMaskedImage, illumScale=self.config.illumScale,
1715 trimToFit=self.config.doTrimToMatchCalib)
1718 if self.config.doSaveInterpPixels:
1719 preInterpExp = ccdExposure.clone()
1734 if self.config.doSetBadRegions:
1735 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1736 if badPixelCount > 0:
1737 self.log.info(
"Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1739 if self.config.doInterpolate:
1740 self.log.info(
"Interpolating masked pixels.")
1741 isrFunctions.interpolateFromMask(
1742 maskedImage=ccdExposure.getMaskedImage(),
1743 fwhm=self.config.fwhm,
1744 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1745 maskNameList=list(self.config.maskListToInterpolate),
1746 useLegacyInterp=self.config.useLegacyInterp,
1752 if self.config.doAmpOffset:
1753 if self.config.ampOffset.doApplyAmpOffset:
1754 self.log.info(
"Measuring and applying amp offset corrections.")
1756 self.log.info(
"Measuring amp offset corrections only, without applying them.")
1757 self.ampOffset.run(ccdExposure)
1759 if self.config.doMeasureBackground:
1760 self.log.info(
"Measuring background level.")
1763 if self.config.qa
is not None and self.config.qa.saveStats
is True:
1765 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1767 afwMath.MEDIAN | afwMath.STDEVCLIP)
1768 self.metadata[f
"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1769 self.metadata[f
"ISR BACKGROUND {amp.getName()} STDEV"] = \
1770 qaStats.getValue(afwMath.STDEVCLIP)
1771 self.log.debug(
" Background stats for amplifer %s: %f +/- %f",
1772 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1773 qaStats.getValue(afwMath.STDEVCLIP))
1776 if self.config.doStandardStatistics:
1777 metadata = ccdExposure.getMetadata()
1779 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1780 ampName = amp.getName()
1781 metadata[f
"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1782 ampExposure.getMaskedImage(),
1783 [self.config.saturatedMaskName]
1785 metadata[f
"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1786 ampExposure.getMaskedImage(),
1790 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1792 metadata[f
"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1793 metadata[f
"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1794 metadata[f
"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1796 k1 = f
"LSST ISR FINAL MEDIAN {ampName}"
1797 k2 = f
"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1798 if self.config.doOverscan
and k1
in metadata
and k2
in metadata:
1799 metadata[f
"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1801 metadata[f
"LSST ISR LEVEL {ampName}"] = numpy.nan
1804 outputStatistics =
None
1805 if self.config.doCalculateStatistics:
1806 outputStatistics = self.isrStats.run(ccdExposure, serialOverscanResults=overscans,
1807 parallelOverscanResults=[
None for _
in overscans],
1808 bias=bias, dark=dark, flat=flat, ptc=ptc,
1810 doLegacyCtiStatistics=
True).results
1813 outputBin1Exposure =
None
1814 outputBin2Exposure =
None
1815 if self.config.doBinnedExposures:
1816 self.log.info(
"Creating binned exposures.")
1817 outputBin1Exposure = self.binning.run(
1819 binFactor=self.config.binFactor1,
1821 outputBin2Exposure = self.binning.run(
1823 binFactor=self.config.binFactor2,
1826 self.
debugView(ccdExposure,
"postISRCCD")
1828 return pipeBase.Struct(
1829 exposure=ccdExposure,
1831 flattenedThumb=flattenedThumb,
1833 outputBin1Exposure=outputBin1Exposure,
1834 outputBin2Exposure=outputBin2Exposure,
1836 preInterpExposure=preInterpExp,
1837 outputExposure=ccdExposure,
1838 outputOssThumbnail=ossThumb,
1839 outputFlattenedThumbnail=flattenedThumb,
1840 outputStatistics=outputStatistics,