24import lsst.pipe.base
as pipeBase
31 """Image statistics options.
33 doCtiStatistics = pexConfig.Field(
35 doc="Measure CTI statistics from image and overscans?",
38 stat = pexConfig.Field(
41 doc=
"Statistic name to use to measure regions.",
43 nSigmaClip = pexConfig.Field(
46 doc=
"Clipping threshold for background",
48 nIter = pexConfig.Field(
51 doc=
"Clipping iterations for background",
53 badMask = pexConfig.ListField(
55 default=[
"BAD",
"INTRP",
"SAT"],
56 doc=
"Mask planes to ignore when identifying source pixels."
61 """Task to measure arbitrary statistics on ISR processed exposures.
63 The goal is to wrap a number of optional measurements that are
64 useful
for calibration production
and detector stability.
66 ConfigClass = IsrStatisticsTaskConfig
67 _DefaultName = "isrStatistics"
69 def __init__(self, statControl=None, **kwargs):
71 self.
statControl = afwMath.StatisticsControl(self.config.nSigmaClip, self.config.nIter,
72 afwImage.Mask.getPlaneBitMask(self.config.badMask))
73 self.
statType = afwMath.stringToStatisticsProperty(self.config.stat)
75 def run(self, inputExp, ptc=None, overscanResults=None, **kwargs):
76 """Task to run arbitrary statistics.
78 The statistics should be measured by individual methods, and
79 add to the dictionary
in the
return struct.
84 The exposure to measure.
85 ptc : `lsst.ip.isr.PtcDataset`, optional
86 A PTC object containing gains to use.
87 overscanResults : `list` [`lsst.pipe.base.Struct`], optional
88 List of overscan results. Expected fields are:
91 Value
or fit subtracted
from the amplifier image data
94 Value
or fit subtracted
from the overscan image data
97 Image of the overscan region
with the overscan
99 quantity
is used to estimate the amplifier read noise
104 resultStruct : `lsst.pipe.base.Struct`
105 Contains the measured statistics
as a dict stored
in a
106 field named ``results``.
111 Raised
if the amplifier gains could
not be found.
114 detector = inputExp.getDetector()
117 elif detector
is not None:
118 gains = {amp.getName(): amp.getGain()
for amp
in detector.getAmplifiers()}
120 raise RuntimeError(
"No source of gains provided.")
121 if self.config.doCtiStatistics:
122 ctiResults = self.
measureCti(inputExp, overscanResults, gains)
124 return pipeBase.Struct(
125 results={
'CTI': ctiResults, },
129 """Task to measure CTI statistics.
135 overscans : `list` [`lsst.pipe.base.Struct`]
136 List of overscan results. Expected fields are:
139 Value or fit subtracted
from the amplifier image data
142 Value
or fit subtracted
from the overscan image data
145 Image of the overscan region
with the overscan
147 quantity
is used to estimate the amplifier read noise
149 gains : `dict` [`str` `float]
150 Dictionary of per-amplifier gains, indexed by amplifier name.
154 outputStats : `dict` [`str`, [`dict` [`str`,`float]]
155 Dictionary of measurements, keyed by amplifier name
and
160 detector = inputExp.getDetector()
161 image = inputExp.image
164 assert len(overscans) == len(detector.getAmplifiers())
166 for ampIter, amp
in enumerate(detector.getAmplifiers()):
168 assert ampIter == amp.getId()
171 gain = gains[amp.getName()]
172 readoutCorner = amp.getReadoutCorner()
174 dataRegion = image[amp.getBBox()]
175 ampStats[
'IMAGE_MEAN'] = afwMath.makeStatistics(dataRegion, self.
statType,
179 pixelA = afwMath.makeStatistics(dataRegion.array[:, 0],
182 pixelZ = afwMath.makeStatistics(dataRegion.array[:, -1],
188 if readoutCorner
in (ReadoutCorner.LR, ReadoutCorner.UR):
189 ampStats[
'FIRST_MEAN'] = pixelZ
190 ampStats[
'LAST_MEAN'] = pixelA
192 ampStats[
'FIRST_MEAN'] = pixelA
193 ampStats[
'LAST_MEAN'] = pixelZ
196 if overscans[ampIter]
is None:
199 self.log.warn(
"No overscan information available for ISR statistics for amp %s.",
201 nCols = amp.getSerialOverscanBBox().getWidth()
202 ampStats[
'OVERSCAN_COLUMNS'] = np.full((nCols, ), np.nan)
203 ampStats[
'OVERSCAN_VALUES'] = np.full((nCols, ), np.nan)
205 overscanImage = overscans[ampIter].overscanImage
208 for column
in range(0, overscanImage.getWidth()):
209 osMean = afwMath.makeStatistics(overscanImage.image.array[:, column],
211 columns.append(column)
212 values.append(gain * osMean)
216 if readoutCorner
in (ReadoutCorner.LR, ReadoutCorner.UR):
217 ampStats[
'OVERSCAN_COLUMNS'] = reversed(columns)
218 ampStats[
'OVERSCAN_VALUES'] = reversed(values)
220 ampStats[
'OVERSCAN_COLUMNS'] = columns
221 ampStats[
'OVERSCAN_VALUES'] = values
223 outputStats[amp.getName()] = ampStats
def run(self, inputExp, ptc=None, overscanResults=None, **kwargs)
def __init__(self, statControl=None, **kwargs)
def measureCti(self, inputExp, overscans, gains)