Coverage for tests/test_overscanCorrection.py: 7%
322 statements
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-13 02:35 -0700
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-13 02:35 -0700
1#
2# LSST Data Management System
3# Copyright 2008, 2009, 2010 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
23import unittest
24import numpy as np
26import lsst.utils.tests
27import lsst.geom
28import lsst.afw.image as afwImage
29import lsst.afw.cameraGeom as cameraGeom
30import lsst.ip.isr as ipIsr
31import lsst.pipe.base as pipeBase
34def computeImageMedianAndStd(image):
35 """Function to calculate median and std of image data.
37 Parameters
38 ----------
39 image : `lsst.afw.image.Image`
40 Image to measure statistics on.
42 Returns
43 -------
44 median : `float`
45 Image median.
46 std : `float`
47 Image stddev.
48 """
49 median = np.nanmedian(image.getArray())
50 std = np.nanstd(image.getArray())
52 return (median, std)
55class IsrTestCases(lsst.utils.tests.TestCase):
57 def updateConfigFromKwargs(self, config, **kwargs):
58 """Common config from keywords.
59 """
60 fitType = kwargs.get('fitType', None)
61 if fitType:
62 config.overscan.fitType = fitType
64 order = kwargs.get('order', None)
65 if order:
66 config.overscan.order = order
68 def makeExposure(self, addRamp=False, isTransposed=False):
69 # Define the camera geometry we'll use.
70 cameraBuilder = cameraGeom.Camera.Builder("Fake Camera")
71 detectorBuilder = cameraBuilder.add("Fake amp", 0)
73 ampBuilder = cameraGeom.Amplifier.Builder()
75 dataBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
76 lsst.geom.Extent2I(10, 10))
78 if isTransposed is True:
79 fullBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
80 lsst.geom.Point2I(12, 12))
81 serialOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 10),
82 lsst.geom.Point2I(9, 12))
83 parallelOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 0),
84 lsst.geom.Point2I(12, 9))
85 else:
86 fullBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
87 lsst.geom.Point2I(12, 12))
88 serialOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 0),
89 lsst.geom.Point2I(12, 9))
90 parallelOverscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 10),
91 lsst.geom.Point2I(9, 12))
93 ampBuilder.setRawBBox(fullBBox)
94 ampBuilder.setRawSerialOverscanBBox(serialOverscanBBox)
95 ampBuilder.setRawParallelOverscanBBox(parallelOverscanBBox)
96 ampBuilder.setRawDataBBox(dataBBox)
98 detectorBuilder.append(ampBuilder)
99 camera = cameraBuilder.finish()
100 detector = camera[0]
102 # Define image data.
103 maskedImage = afwImage.MaskedImageF(fullBBox)
104 maskedImage.set(2, 0x0, 1)
106 dataImage = afwImage.MaskedImageF(maskedImage, dataBBox)
107 dataImage.set(10, 0x0, 1)
109 if addRamp:
110 for column in range(dataBBox.getWidth()):
111 maskedImage.image.array[:, column] += column
113 exposure = afwImage.ExposureF(maskedImage, None)
114 exposure.setDetector(detector)
115 return exposure
117 def checkOverscanCorrectionY(self, **kwargs):
118 exposure = self.makeExposure(isTransposed=True)
119 detector = exposure.getDetector()
121 # These subimages are needed below.
122 overscan = exposure[detector.getAmplifiers()[0].getRawSerialOverscanBBox()]
123 maskedImage = exposure[detector.getAmplifiers()[0].getRawBBox()]
125 config = ipIsr.IsrTask.ConfigClass()
126 self.updateConfigFromKwargs(config, **kwargs)
128 if kwargs['fitType'] == "MEDIAN_PER_ROW":
129 # Add a bad point to test outlier rejection.
130 overscan.getImage().getArray()[0, 0] = 12345
132 # Shrink the sigma clipping limit to handle the fact that the
133 # bad point is not be rejected at higher thresholds (2/0.74).
134 config.overscan.numSigmaClip = 2.7
136 isrTask = ipIsr.IsrTask(config=config)
137 isrTask.overscan.run(exposure, detector.getAmplifiers()[0], isTransposed=True)
139 height = maskedImage.getHeight()
140 width = maskedImage.getWidth()
141 for j in range(height):
142 for i in range(width):
143 if j == 10 and i == 0 and kwargs['fitType'] == "MEDIAN_PER_ROW":
144 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 12343)
145 elif j >= 10 and i < 10:
146 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0)
147 elif i < 10:
148 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 8)
150 def checkOverscanCorrectionX(self, **kwargs):
151 exposure = self.makeExposure(isTransposed=False)
152 detector = exposure.getDetector()
154 # These subimages are needed below.
155 maskedImage = exposure[detector.getAmplifiers()[0].getRawBBox()]
157 config = ipIsr.IsrTask.ConfigClass()
158 self.updateConfigFromKwargs(config, **kwargs)
160 isrTask = ipIsr.IsrTask(config=config)
161 isrTask.overscan.run(exposure, detector.getAmplifiers()[0], isTransposed=False)
163 height = maskedImage.getHeight()
164 width = maskedImage.getWidth()
165 for j in range(height):
166 for i in range(width):
167 if i >= 10 and j < 10:
168 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0)
169 elif j < 10:
170 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 8)
172 def checkOverscanCorrectionSineWave(self, **kwargs):
173 """vertical sine wave along long direction"""
174 # Define the camera geometry we'll use.
175 cameraBuilder = cameraGeom.Camera.Builder("Fake Camera")
176 detectorBuilder = cameraBuilder.add("Fake amp", 0)
178 ampBuilder = cameraGeom.Amplifier.Builder()
180 dataBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
181 lsst.geom.Extent2I(70, 500))
183 fullBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
184 lsst.geom.Extent2I(100, 500))
186 overscanBBox = lsst.geom.Box2I(lsst.geom.Point2I(70, 0),
187 lsst.geom.Extent2I(30, 500))
189 ampBuilder.setRawBBox(fullBBox)
190 ampBuilder.setRawSerialOverscanBBox(overscanBBox)
191 ampBuilder.setRawDataBBox(dataBBox)
193 detectorBuilder.append(ampBuilder)
194 camera = cameraBuilder.finish()
195 detector = camera[0]
197 # Define image data.
198 maskedImage = afwImage.MaskedImageF(fullBBox)
199 maskedImage.set(50, 0x0, 1)
201 overscan = afwImage.MaskedImageF(maskedImage, overscanBBox)
202 overscan.set(0, 0x0, 1)
204 exposure = afwImage.ExposureF(maskedImage, None)
205 exposure.setDetector(detector)
207 # vertical sine wave along long direction
208 x = np.linspace(0, 2*3.14159, 500)
209 a, w = 15, 5*3.14159
210 sineWave = 20 + a*np.sin(w*x)
211 sineWave = sineWave.astype(int)
213 fullImage = np.repeat(sineWave, 100).reshape((500, 100))
214 maskedImage.image.array += fullImage
216 config = ipIsr.IsrTask.ConfigClass()
217 self.updateConfigFromKwargs(config, **kwargs)
219 isrTask = ipIsr.IsrTask(config=config)
220 isrTask.overscan.run(exposure, detector.getAmplifiers()[0])
222 height = maskedImage.getHeight()
223 width = maskedImage.getWidth()
225 for j in range(height):
226 for i in range(width):
227 if i >= 70:
228 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.0)
229 else:
230 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 50.0)
232 def test_MedianPerRowOverscanCorrection(self):
233 self.checkOverscanCorrectionY(fitType="MEDIAN_PER_ROW")
234 self.checkOverscanCorrectionX(fitType="MEDIAN_PER_ROW")
235 self.checkOverscanCorrectionSineWave(fitType="MEDIAN_PER_ROW")
237 def test_MedianOverscanCorrection(self):
238 self.checkOverscanCorrectionY(fitType="MEDIAN")
239 self.checkOverscanCorrectionX(fitType="MEDIAN")
241 def checkPolyOverscanCorrectionX(self, **kwargs):
242 exposure = self.makeExposure(isTransposed=False)
243 detector = exposure.getDetector()
245 # These subimages are needed below.
246 overscan = exposure[detector.getAmplifiers()[0].getRawSerialOverscanBBox()]
247 maskedImage = exposure[detector.getAmplifiers()[0].getRawBBox()]
249 bbox = detector.getAmplifiers()[0].getRawSerialOverscanBBox()
250 overscan.getMaskedImage().set(2, 0x0, 1)
251 for i in range(bbox.getDimensions()[1]):
252 for j, off in enumerate([-0.5, 0.0, 0.5]):
253 overscan.image[j, i, afwImage.LOCAL] = 2+i+off
255 config = ipIsr.IsrTask.ConfigClass()
256 self.updateConfigFromKwargs(config, **kwargs)
258 isrTask = ipIsr.IsrTask(config=config)
259 isrTask.overscan.run(exposure, detector.getAmplifiers()[0], isTransposed=False)
261 height = maskedImage.getHeight()
262 width = maskedImage.getWidth()
263 for j in range(height):
264 for i in range(width):
265 if j < 10:
266 if i == 10:
267 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], -0.5)
268 elif i == 11:
269 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0)
270 elif i == 12:
271 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.5)
272 else:
273 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 10 - 2 - j)
275 def checkPolyOverscanCorrectionY(self, **kwargs):
276 exposure = self.makeExposure(isTransposed=True)
277 detector = exposure.getDetector()
279 # These subimages are needed below.
280 overscan = exposure[detector.getAmplifiers()[0].getRawSerialOverscanBBox()]
281 maskedImage = exposure[detector.getAmplifiers()[0].getRawBBox()]
283 bbox = detector.getAmplifiers()[0].getRawSerialOverscanBBox()
284 overscan.getMaskedImage().set(2, 0x0, 1)
285 for i in range(bbox.getDimensions()[0]):
286 for j, off in enumerate([-0.5, 0.0, 0.5]):
287 overscan.image[i, j, afwImage.LOCAL] = 2+i+off
288 # maskedImage.getMaskedImage().set(10, 0x0, 1)
290 config = ipIsr.IsrTask.ConfigClass()
291 self.updateConfigFromKwargs(config, **kwargs)
293 isrTask = ipIsr.IsrTask(config=config)
294 isrTask.overscan.run(exposure, detector.getAmplifiers()[0], isTransposed=True)
296 height = maskedImage.getHeight()
297 width = maskedImage.getWidth()
298 for j in range(height):
299 for i in range(width):
300 if i < 10:
301 if j == 10:
302 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], -0.5)
303 elif j == 11:
304 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0)
305 elif j == 12:
306 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.5)
307 else:
308 self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 10 - 2 - i)
310 def test_PolyOverscanCorrection(self):
311 for fitType in ("POLY", "CHEB", "LEG"):
312 self.checkPolyOverscanCorrectionX(fitType=fitType, order=5)
313 self.checkPolyOverscanCorrectionY(fitType=fitType, order=5)
315 def test_SplineOverscanCorrection(self):
316 for fitType in ("NATURAL_SPLINE", "CUBIC_SPLINE", "AKIMA_SPLINE"):
317 self.checkPolyOverscanCorrectionX(fitType=fitType, order=5)
318 self.checkPolyOverscanCorrectionY(fitType=fitType, order=5)
320 def test_overscanCorrection(self):
321 """Expect that this should reduce the image variance with a full fit.
322 The default fitType of MEDIAN will reduce the median value.
324 This needs to operate on a RawMock() to have overscan data to use.
326 The output types may be different when fitType != MEDIAN.
327 """
328 exposure = self.makeExposure(isTransposed=False)
329 detector = exposure.getDetector()
330 amp = detector.getAmplifiers()[0]
332 statBefore = computeImageMedianAndStd(exposure.image[amp.getRawDataBBox()])
334 config = ipIsr.IsrTask.ConfigClass()
335 isrTask = ipIsr.IsrTask(config=config)
336 oscanResults = isrTask.overscan.run(exposure, amp)
338 self.assertIsInstance(oscanResults, pipeBase.Struct)
339 self.assertIsInstance(oscanResults.imageFit, float)
340 self.assertIsInstance(oscanResults.overscanFit, float)
341 self.assertIsInstance(oscanResults.overscanImage, afwImage.ExposureF)
343 statAfter = computeImageMedianAndStd(exposure.image[amp.getRawDataBBox()])
344 self.assertLess(statAfter[0], statBefore[0])
346 def test_parallelOverscanCorrection(self):
347 """Expect that this should reduce the image variance with a full fit.
348 The default fitType of MEDIAN will reduce the median value.
350 This needs to operate on a RawMock() to have overscan data to use.
352 This test checks that the outputs match, and that the serial
353 overscan is the trivial value (2.0), and that the parallel
354 overscan is the median of the ramp inserted (4.5)
355 """
356 exposure = self.makeExposure(addRamp=True, isTransposed=False)
357 detector = exposure.getDetector()
358 amp = detector.getAmplifiers()[0]
360 statBefore = computeImageMedianAndStd(exposure.image[amp.getRawDataBBox()])
362 for fitType in ('MEDIAN', 'MEDIAN_PER_ROW'):
363 # This tests these two types to cover scalar and vector
364 # calculations.
365 exposureCopy = exposure.clone()
366 config = ipIsr.IsrTask.ConfigClass()
367 config.overscan.doParallelOverscan = True
368 config.overscan.fitType = fitType
369 isrTask = ipIsr.IsrTask(config=config)
371 oscanResults = isrTask.overscan.run(exposureCopy, amp)
373 self.assertIsInstance(oscanResults, pipeBase.Struct)
374 if fitType == 'MEDIAN':
375 self.assertIsInstance(oscanResults.imageFit, float)
376 self.assertIsInstance(oscanResults.overscanFit, float)
377 else:
378 self.assertIsInstance(oscanResults.imageFit, np.ndarray)
379 self.assertIsInstance(oscanResults.overscanFit, np.ndarray)
380 self.assertIsInstance(oscanResults.overscanImage, afwImage.ExposureF)
382 statAfter = computeImageMedianAndStd(exposureCopy.image[amp.getRawDataBBox()])
383 self.assertLess(statAfter[0], statBefore[0])
385 # Test the output value for the serial and parallel overscans
386 self.assertAlmostEqual(oscanResults.overscanMean[0], 2.0, delta=0.001)
387 self.assertAlmostEqual(oscanResults.overscanMean[1], 4.5, delta=0.001)
388 if fitType != 'MEDIAN':
389 # The ramp that has been inserted should be fully
390 # removed by the overscan fit, removing all of the
391 # signal. This isn't true of the constant fit, so do
392 # not test that here.
393 self.assertLess(statAfter[1], statBefore[1])
394 self.assertAlmostEqual(statAfter[1], 0.0, delta=0.001)
396 def test_bleedParallelOverscanCorrection(self):
397 """Expect that this should reduce the image variance with a full fit.
398 The default fitType of MEDIAN will reduce the median value.
400 This needs to operate on a RawMock() to have overscan data to use.
402 This test adds a large artificial bleed to the overscan region,
403 which should be masked and patched with the median of the
404 other pixels.
405 """
406 exposure = self.makeExposure(addRamp=True, isTransposed=False)
407 detector = exposure.getDetector()
408 amp = detector.getAmplifiers()[0]
410 maskedImage = exposure.getMaskedImage()
411 overscanBleedBox = lsst.geom.Box2I(lsst.geom.Point2I(4, 10),
412 lsst.geom.Extent2I(2, 3))
413 overscanBleed = afwImage.MaskedImageF(maskedImage, overscanBleedBox)
414 overscanBleed.set(110000, 0x0, 1)
416 statBefore = computeImageMedianAndStd(exposure.image[amp.getRawDataBBox()])
418 for fitType in ('MEDIAN', 'MEDIAN_PER_ROW', 'POLY'):
419 # We only test these three types as this should cover the
420 # scalar calculations, the generic vector calculations,
421 # and the specific C++ MEDIAN_PER_ROW case.
422 exposureCopy = exposure.clone()
423 config = ipIsr.IsrTask.ConfigClass()
424 config.overscan.doParallelOverscan = True
425 config.overscan.parallelOverscanMaskGrowSize = 1
426 config.overscan.fitType = fitType
427 isrTask = ipIsr.IsrTask(config=config)
429 # This next line is usually run as part of IsrTask:
430 isrTask.overscan.maskParallelOverscan(exposureCopy, detector)
431 oscanResults = isrTask.overscan.run(exposureCopy, amp)
433 self.assertIsInstance(oscanResults, pipeBase.Struct)
434 if fitType == 'MEDIAN':
435 self.assertIsInstance(oscanResults.imageFit, float)
436 self.assertIsInstance(oscanResults.overscanFit, float)
437 else:
438 self.assertIsInstance(oscanResults.imageFit, np.ndarray)
439 self.assertIsInstance(oscanResults.overscanFit, np.ndarray)
440 self.assertIsInstance(oscanResults.overscanImage, afwImage.ExposureF)
442 statAfter = computeImageMedianAndStd(exposureCopy.image[amp.getRawDataBBox()])
443 self.assertLess(statAfter[0], statBefore[0])
445 # Test the output value for the serial and parallel
446 # overscans.
447 self.assertAlmostEqual(oscanResults.overscanMean[0], 2.0, delta=0.001)
448 self.assertAlmostEqual(oscanResults.overscanMean[1], 4.5, delta=0.001)
449 self.assertAlmostEqual(oscanResults.residualMean[1], 0.0, delta=0.001)
450 if fitType != 'MEDIAN':
451 # Check the bleed isn't oversubtracted. This is the
452 # average of the two mid-bleed pixels as the patching
453 # uses the median correction value there, and there is
454 # still a residual ramp in this region. The large
455 # delta allows the POLY fit to pass, which has sub-ADU
456 # differences.
457 self.assertAlmostEqual(exposureCopy.image.array[5][0],
458 0.5 * (exposureCopy.image.array[5][4]
459 + exposureCopy.image.array[5][5]), delta=0.3)
460 # These fits should also reduce the image stdev, as
461 # they are modeling the ramp.
462 self.assertLess(statAfter[1], statBefore[1])
464 def test_bleedParallelOverscanCorrectionFailure(self):
465 """Expect that this should reduce the image variance with a full fit.
466 The default fitType of MEDIAN will reduce the median value.
468 This needs to operate on a RawMock() to have overscan data to use.
470 This adds a large artificial bleed to the overscan region,
471 which should be masked and patched with the median of the
472 other pixels.
473 """
474 exposure = self.makeExposure(addRamp=True, isTransposed=False)
475 detector = exposure.getDetector()
476 amp = detector.getAmplifiers()[0]
478 maskedImage = exposure.getMaskedImage()
479 overscanBleedBox = lsst.geom.Box2I(lsst.geom.Point2I(4, 10),
480 lsst.geom.Extent2I(2, 3))
481 overscanBleed = afwImage.MaskedImageF(maskedImage, overscanBleedBox)
482 overscanBleed.set(10000, 0x0, 1) # This level is below the mask threshold.
484 statBefore = computeImageMedianAndStd(exposure.image[amp.getRawDataBBox()])
486 for fitType in ('MEDIAN', 'MEDIAN_PER_ROW'):
487 # We only test these three types as this should cover the
488 # scalar calculations, the generic vector calculations,
489 # and the specific C++ MEDIAN_PER_ROW case.
490 exposureCopy = exposure.clone()
491 config = ipIsr.IsrTask.ConfigClass()
492 config.overscan.doParallelOverscan = True
493 config.overscan.parallelOverscanMaskGrowSize = 1
494 # Ensure we don't mask anything
495 config.overscan.maxDeviation = 100000
496 config.overscan.fitType = fitType
497 isrTask = ipIsr.IsrTask(config=config)
499 # isrTask.overscan.maskParallelOverscan(exposureCopy, detector)
500 oscanResults = isrTask.overscan.run(exposureCopy, amp)
502 self.assertIsInstance(oscanResults, pipeBase.Struct)
503 if fitType == 'MEDIAN':
504 self.assertIsInstance(oscanResults.imageFit, float)
505 self.assertIsInstance(oscanResults.overscanFit, float)
506 else:
507 self.assertIsInstance(oscanResults.imageFit, np.ndarray)
508 self.assertIsInstance(oscanResults.overscanFit, np.ndarray)
509 self.assertIsInstance(oscanResults.overscanImage, afwImage.ExposureF)
511 statAfter = computeImageMedianAndStd(exposureCopy.image[amp.getRawDataBBox()])
512 self.assertLess(statAfter[0], statBefore[0])
514 # Test the output value for the serial and parallel
515 # overscans.
516 self.assertAlmostEqual(oscanResults.overscanMean[0], 2.0, delta=0.001)
517 # These are the wrong values:
518 if fitType == 'MEDIAN':
519 # Check that the constant case is now biased, at 6.5
520 # instead of 4.5:
521 self.assertAlmostEqual(oscanResults.overscanMean[1], 6.5, delta=0.001)
522 else:
523 # This is not correcting the bleed, so it will be printed
524 # onto the image, making the stdev after correction worse
525 # than before.
526 self.assertGreater(statAfter[1], statBefore[1])
528 # Check that the median overscan value matches the
529 # constant fit:
530 self.assertAlmostEqual(oscanResults.overscanMedian[1], 6.5, delta=0.001)
531 # Check that the mean isn't what we found before, and
532 # is larger:
533 self.assertNotEqual(oscanResults.overscanMean[1], 4.5)
534 self.assertGreater(oscanResults.overscanMean[1], 4.5)
535 self.assertGreater(exposureCopy.image.array[5][0],
536 0.5 * (exposureCopy.image.array[5][4]
537 + exposureCopy.image.array[5][5]))
539 def test_overscanCorrection_isNotInt(self):
540 """Expect smaller median/smaller std after.
541 Expect exception if overscan fit type isn't known.
542 """
543 exposure = self.makeExposure(isTransposed=False)
544 detector = exposure.getDetector()
545 amp = detector.getAmplifiers()[0]
547 for fitType in ('MEAN', 'MEDIAN', 'MEDIAN_PER_ROW', 'MEANCLIP', 'POLY', 'CHEB',
548 'NATURAL_SPLINE', 'CUBIC_SPLINE'):
549 if fitType in ('NATURAL_SPLINE', 'CUBIC_SPLINE'):
550 order = 3
551 else:
552 order = 1
553 config = ipIsr.IsrTask.ConfigClass()
554 config.overscan.order = order
555 config.overscan.fitType = fitType
556 isrTask = ipIsr.IsrTask(config=config)
558 response = isrTask.overscan.run(exposure, amp)
560 self.assertIsInstance(response, pipeBase.Struct,
561 msg=f"overscanCorrection overscanIsNotInt Bad response: {fitType}")
562 self.assertIsNotNone(response.imageFit,
563 msg=f"overscanCorrection overscanIsNotInt Bad imageFit: {fitType}")
564 self.assertIsNotNone(response.overscanFit,
565 msg=f"overscanCorrection overscanIsNotInt Bad overscanFit: {fitType}")
566 self.assertIsInstance(response.overscanImage, afwImage.ExposureF,
567 msg=f"overscanCorrection overscanIsNotInt Bad overscanImage: {fitType}")
570class MemoryTester(lsst.utils.tests.MemoryTestCase):
571 pass
574def setup_module(module):
575 lsst.utils.tests.init()
578if __name__ == "__main__": 578 ↛ 579line 578 didn't jump to line 579, because the condition on line 578 was never true
579 lsst.utils.tests.init()
580 unittest.main()