Loading [MathJax]/extensions/tex2jax.js
LSST Applications g0fba68d861+05816baf74,g1ec0fe41b4+f536777771,g1fd858c14a+a9301854fb,g35bb328faa+fcb1d3bbc8,g4af146b050+a5c07d5b1d,g4d2262a081+6e5fcc2a4e,g53246c7159+fcb1d3bbc8,g56a49b3a55+9c12191793,g5a012ec0e7+3632fc3ff3,g60b5630c4e+ded28b650d,g67b6fd64d1+ed4b5058f4,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g8352419a5c+fcb1d3bbc8,g87b7deb4dc+7b42cf88bf,g8852436030+e5453db6e6,g89139ef638+ed4b5058f4,g8e3bb8577d+d38d73bdbd,g9125e01d80+fcb1d3bbc8,g94187f82dc+ded28b650d,g989de1cb63+ed4b5058f4,g9d31334357+ded28b650d,g9f33ca652e+50a8019d8c,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+d9fb1f8026,gb58c049af0+f03b321e39,gb665e3612d+2a0c9e9e84,gb89ab40317+ed4b5058f4,gcf25f946ba+e5453db6e6,gd6cbbdb0b4+bb83cc51f8,gdd1046aedd+ded28b650d,gde0f65d7ad+941d412827,ge278dab8ac+d65b3c2b70,ge410e46f29+ed4b5058f4,gf23fb2af72+b7cae620c0,gf5e32f922b+fcb1d3bbc8,gf67bdafdda+ed4b5058f4,w.2025.16
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
overscanAmpConfig.py
Go to the documentation of this file.
1import lsst.pex.config as pexConfig
2import hashlib
3
4from .overscan import SerialOverscanCorrectionTaskConfig, ParallelOverscanCorrectionTaskConfig
5
6
7__all__ = [
8 "OverscanAmpConfig",
9 "OverscanDetectorConfig",
10 "OverscanCameraConfig",
11]
12
13
14class OverscanAmpConfig(pexConfig.Config):
15 """Overscan configurations applicable to a single amplifier."""
16 doSerialOverscan = pexConfig.Field(
17 dtype=bool,
18 doc="Do serial overscan subtraction?",
19 default=True,
20 )
21 serialOverscanConfig = pexConfig.ConfigField(
22 dtype=SerialOverscanCorrectionTaskConfig,
23 doc="Serial overscan configuration.",
24 )
25 # TODO: Remove on DM-48394
26 doParallelOverscanCrosstalk = pexConfig.Field(
27 dtype=bool,
28 doc="Apply crosstalk correction in parallel overscan region?",
29 default=True,
30 deprecated="This field is no longer used, and will be removed after v29.",
31 )
32 doParallelOverscan = pexConfig.Field(
33 dtype=bool,
34 doc="Do parallel overscan subtraction?",
35 default=True,
36 )
37 parallelOverscanConfig = pexConfig.ConfigField(
38 dtype=ParallelOverscanCorrectionTaskConfig,
39 doc="Parallel overscan configuration.",
40 )
41 saturation = pexConfig.Field(
42 dtype=float,
43 doc="The saturation level to use to override any detector/calibration product value "
44 "(ignored if NaN). Units are ADU.",
45 default=float("NaN"),
46 )
47 suspectLevel = pexConfig.Field(
48 dtype=float,
49 doc="The ``suspect`` level to use to override any detector/calibration product value "
50 "(ignored if NaN). Units are ADU.",
51 default=float("NaN"),
52 )
53 gain = pexConfig.Field(
54 dtype=float,
55 doc="The gain to use to override any calibration product value (ignored if NaN). "
56 "Units are e-/ADU.",
57 default=float("NaN"),
58 )
59
60 def setDefaults(self):
61 super().setDefaults()
62
63 self.serialOverscanConfig.fitType = "MEDIAN_PER_ROW"
64 self.serialOverscanConfig.leadingToSkip = 3
65 self.serialOverscanConfig.trailingToSkip = 3
66 self.serialOverscanConfig.overscanIsInt = False
67 self.parallelOverscanConfig.fitType = "MEDIAN_PER_ROW"
68 self.parallelOverscanConfig.leadingToSkip = 3
69 self.parallelOverscanConfig.trailingToSkip = 3
70 self.parallelOverscanConfig.overscanIsInt = False
71 # We expect the parallel overscan to not deviate much
72 # after serial overscan subtraction and crosstalk correction.
73 self.parallelOverscanConfig.maxDeviation = 100.0
74
75 @property
76 def _stringForHash(self):
77 """Turn this config into a simple string for hashing.
78
79 Only essential data for tracking is returned.
80
81 Returns
82 -------
83 stringForHash : `str`
84 """
85 stringForHash = (f"doSerial={self.doSerialOverscan} "
86 f"serialFitType={self.serialOverscanConfig.fitType} "
87 f"doParallel={self.doParallelOverscan} "
88 f"parallelFitType={self.parallelOverscanConfig.fitType}")
89 return stringForHash
90
91
92class OverscanDetectorConfig(pexConfig.Config):
93 """Overscan configurations applicable to multiple amplifiers in
94 a single detector.
95 """
96 ampRules = pexConfig.ConfigDictField(
97 doc="Amplifier level rules for overscan, keyed by amp name.",
98 keytype=str,
99 itemtype=OverscanAmpConfig,
100 default={},
101 )
102 defaultAmpConfig = pexConfig.ConfigField(
103 dtype=OverscanAmpConfig,
104 doc="Default configuration for amplifiers.",
105 )
106 integerDitherMode = pexConfig.ChoiceField(
107 dtype=str,
108 doc="Dithering mode to cancel integerization of counts.",
109 default="SYMMETRIC",
110 allowed={
111 "POSITIVE": "Dithering is done with a uniform random in the range [0, 1).",
112 "NEGATIVE": "Dithering is done with a uniform random in the range [-1, 0).",
113 "SYMMETRIC": "Dithering is done with a uniform random in the range [-0.5, 0.5).",
114 "NONE": "No dithering is performed.",
115 },
116 )
117 itlDipMinHeight = pexConfig.Field(
118 dtype=int,
119 doc="Minimum height for a saturated footprint column to contribute to a dip.",
120 default=50,
121 )
122 itlDipMinWidth = pexConfig.Field(
123 dtype=int,
124 doc="Minimum number of columns in a saturated footprint with idlDipMinHeight "
125 "to contribute to a dip.",
126 default=15,
127 )
128 itlDipMaxWidth = pexConfig.Field(
129 dtype=int,
130 doc="Maximum number of columns to use for a dip mask.",
131 default=50,
132 )
133 itlDipWidthScale = pexConfig.Field(
134 dtype=float,
135 doc="Scaling factor to widen saturated core for dip masking.",
136 default=1.5,
137 )
138 itlDipBackgroundFraction = pexConfig.Field(
139 dtype=float,
140 doc="Fraction of background (scaled by width) that is in the center of the dip. "
141 "Only dips that are greater than itlDipMinSkyNoiseFraction will be masked. "
142 "If equal to 0.0, dip masking will be skipped.",
143 default=0.0,
144 )
145 itlDipMinBackgroundNoiseFraction = pexConfig.Field(
146 dtype=float,
147 doc="Only max model dip depth greater than this fraction of the approximate "
148 "background noise will be masked.",
149 default=0.5,
150 )
151 itlDipMaxColsPerImage = pexConfig.Field(
152 dtype=int,
153 doc="Maximum number of columns detected as ``dip`` columns before dip masking "
154 "is disabled on the image.",
155 default=500,
156 )
157
158 @property
160 """Check if any of the amp configs have doSerialOverscan.
161
162 Returns
163 -------
164 doAnySerialOverscan : `bool`
165 """
166 if self.defaultAmpConfig.doSerialOverscan:
167 return True
168
169 for _, ampRule in self.ampRules.items():
170 if ampRule.doSerialOverscan:
171 return True
172
173 return False
174
175 @property
177 """Check if any of the amp configs have doParallelOverscan.
178
179 Returns
180 -------
181 doAnyParallelOverscan : `bool`
182 """
183 if self.defaultAmpConfig.doParallelOverscan:
184 return True
185
186 for _, ampRule in self.ampRules.items():
187 if ampRule.doParallelOverscan:
188 return True
189
190 return False
191
192 def getOverscanAmpConfig(self, amplifier):
193 """Get the OverscanAmpConfig for a specific amplifier.
194
195 Parameters
196 ----------
197 amplifier : `lsst.afw.cameraGeom.Amplifier`
198
199 Returns
200 -------
201 overscanAmpConfig : `lsst.ip.isr.overscanAmpConfig.OverscanAmpConfig`
202 """
203 ampKey = amplifier.getName()
204
205 if ampKey in self.ampRules.keys():
206 overscanAmpConfig = self.ampRules[ampKey]
207 else:
208 overscanAmpConfig = self.defaultAmpConfig
209
210 return overscanAmpConfig
211
212 @property
213 def _stringForHash(self):
214 """Turn this config into a simple string for hashing.
215
216 Only the default and amps that are different than the
217 default are used in the string representation.
218
219 Returns
220 -------
221 stringForHash : `str`
222 """
223 defaultString = self.defaultAmpConfig._stringForHash
224
225 stringForHash = f"default: {defaultString}"
226 for ampName in self.ampRules:
227 ampString = self.ampRules[ampName]._stringForHash
228 if ampString != defaultString:
229 stringForHash += f" {ampName}: {ampString}"
230
231 return stringForHash
232
233 @property
234 def md5(self):
235 """Compute the MD5 hash of this config (detector + amps).
236
237 This can be used to ensure overscan configs are consistent.
238
239 Returns
240 -------
241 md5Hash : `str`
242 """
243 return hashlib.md5(self._stringForHash.encode("UTF-8")).hexdigest()
244
245
246class OverscanCameraConfig(pexConfig.Config):
247 """Overscan configurations applicable to multiple detectors in
248 a single camera.
249 """
250 detectorRules = pexConfig.ConfigDictField(
251 doc="Detector level rules for overscan",
252 keytype=str,
253 itemtype=OverscanDetectorConfig,
254 default={},
255 )
256 defaultDetectorConfig = pexConfig.ConfigField(
257 dtype=OverscanDetectorConfig,
258 doc="Default configuration for detectors.",
259 )
260 detectorRuleKeyType = pexConfig.ChoiceField(
261 doc="Detector rule key type.",
262 dtype=str,
263 default="NAME",
264 allowed={
265 "NAME": "DetectorRules has a key that is the detector name.",
266 "SERIAL": "DetectorRules has a key that is the detector serial number.",
267 "ID": "DetectorRules has a key that is the detector id number.",
268 },
269 )
270
271 @property
273 """Check if any of the detector/amp configs have doSerialOverscan.
274
275 Returns
276 -------
277 doAnySerialOverscan : `bool`
278 """
279 if self.defaultDetectorConfig.doAnySerialOverscan:
280 return True
281
282 for _, detectorRule in self.detectorRules.items():
283 if detectorRule.doAnySerialOverscan:
284 return True
285
286 return False
287
288 @property
290 """Check if any of the detector/amp configs have
291 doParallelOverscan.
292
293 Returns
294 -------
295 doAnyParallelOverscan : `bool`
296 """
297
298 if self.defaultDetectorConfig.doAnyParallelOverscan:
299 return True
300
301 for _, detectorRule in self.detectorRules.items():
302 if detectorRule.doAnyParallelOverscan:
303 return True
304
305 return False
306
307 def getOverscanDetectorConfig(self, detector):
308 """Get the OverscanDetectorConfig for a specific detector.
309
310 Parameters
311 ----------
312 detector : `lsst.afw.cameraGeom.Detector`
313
314 Returns
315 -------
316 overscanDetectorConfig : `OverscanDetectorConfig`
317 """
318 match self.detectorRuleKeyType:
319 case "NAME":
320 key = detector.getName()
321 case "SERIAL":
322 key = detector.getSerial()
323 case "ID":
324 key = str(detector.getId())
325
326 if key in self.detectorRules.keys():
327 overscanDetectorConfig = self.detectorRules[key]
328 else:
329 overscanDetectorConfig = self.defaultDetectorConfig
330
331 return overscanDetectorConfig