Coverage for tests/test_isrTask.py: 92%

406 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-25 22:35 +0000

1# 

2# LSST Data Management System 

3# Copyright 2008-2017 AURA/LSST. 

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 <https://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23import unittest 

24import unittest.mock 

25import numpy as np 

26 

27import lsst.afw.image as afwImage 

28import lsst.ip.isr.isrMock as isrMock 

29import lsst.utils.tests 

30from lsst.ip.isr.isrTask import (IsrTask, IsrTaskConfig) 

31from lsst.ip.isr.masking import DECamEdgeBleedMaskTask 

32from lsst.ip.isr.isrQa import IsrQaConfig 

33from lsst.pipe.base import Struct 

34from lsst.ip.isr import PhotonTransferCurveDataset 

35 

36 

37def countMaskedPixels(maskedImage, maskPlane): 

38 """Function to count the number of masked pixels of a given type. 

39 

40 Parameters 

41 ---------- 

42 maskedImage : `lsst.afw.image.MaskedImage` 

43 Image to measure the mask on. 

44 maskPlane : `str` 

45 Name of the mask plane to count 

46 

47 Returns 

48 ------- 

49 nMask : `int` 

50 Number of masked pixels. 

51 """ 

52 bitMask = maskedImage.getMask().getPlaneBitMask(maskPlane) 

53 isBit = maskedImage.getMask().getArray() & bitMask > 0 

54 numBit = np.sum(isBit) 

55 return numBit 

56 

57 

58def computeImageMedianAndStd(image): 

59 """Function to calculate median and std of image data. 

60 

61 Parameters 

62 ---------- 

63 image : `lsst.afw.image.Image` 

64 Image to measure statistics on. 

65 

66 Returns 

67 ------- 

68 median : `float` 

69 Image median. 

70 std : `float` 

71 Image stddev. 

72 """ 

73 median = np.nanmedian(image.getArray()) 

74 std = np.nanstd(image.getArray()) 

75 return (median, std) 

76 

77 

78class IsrTaskTestCases(lsst.utils.tests.TestCase): 

79 """Test IsrTask methods with trimmed raw data. 

80 """ 

81 def setUp(self): 

82 self.config = IsrTaskConfig() 

83 self.config.overscan.doParallelOverscan = True 

84 self.config.qa = IsrQaConfig() 

85 self.task = IsrTask(config=self.config) 

86 self.camera = isrMock.IsrMock().getCamera() 

87 

88 self.inputExp = isrMock.TrimmedRawMock().run() 

89 self.amp = self.inputExp.getDetector()[0] 

90 self.mi = self.inputExp.getMaskedImage() 

91 

92 def validateIsrData(self, results): 

93 """results should be a struct with components that are 

94 not None if included in the configuration file. 

95 """ 

96 self.assertIsInstance(results, Struct) 

97 if self.config.doBias is True: 

98 self.assertIsNotNone(results.bias) 

99 if self.config.doDark is True: 

100 self.assertIsNotNone(results.dark) 

101 if self.config.doFlat is True: 

102 self.assertIsNotNone(results.flat) 

103 if self.config.doFringe is True: 

104 self.assertIsNotNone(results.fringes) 

105 if self.config.doDefect is True: 

106 self.assertIsNotNone(results.defects) 

107 if self.config.doBrighterFatter is True: 

108 self.assertIsNotNone(results.bfKernel) 

109 if self.config.doAttachTransmissionCurve is True: 

110 self.assertIsNotNone(results.opticsTransmission) 

111 self.assertIsNotNone(results.filterTransmission) 

112 self.assertIsNotNone(results.sensorTransmission) 

113 self.assertIsNotNone(results.atmosphereTransmission) 

114 

115 def test_ensureExposure(self): 

116 """Test that an exposure has a usable instance class. 

117 """ 

118 self.assertIsInstance(self.task.ensureExposure(self.inputExp, self.camera, 0), 

119 afwImage.Exposure) 

120 

121 def test_convertItoF(self): 

122 """Test conversion from integer to floating point pixels. 

123 """ 

124 result = self.task.convertIntToFloat(self.inputExp) 

125 self.assertEqual(result.getImage().getArray().dtype, np.dtype("float32")) 

126 self.assertEqual(result, self.inputExp) 

127 

128 def test_updateVariance(self): 

129 """Expect The variance image should have a larger median value after 

130 this operation. 

131 """ 

132 ampName = self.amp.getName() 

133 effectivePtc = PhotonTransferCurveDataset([ampName], "TEST_PTC", 1) 

134 effectivePtc.gain[ampName] = self.amp.getGain() 

135 effectivePtc.noise[ampName] = self.amp.getReadNoise() 

136 statBefore = computeImageMedianAndStd(self.inputExp.variance[self.amp.getBBox()]) 

137 # effectivePtc will have noise and gain. 

138 self.task.updateVariance(self.inputExp, self.amp, effectivePtc) 

139 statAfter = computeImageMedianAndStd(self.inputExp.variance[self.amp.getBBox()]) 

140 self.assertGreater(statAfter[0], statBefore[0]) 

141 self.assertFloatsAlmostEqual(statBefore[0], 0.0, atol=1e-2) 

142 self.assertFloatsAlmostEqual(statAfter[0], 8170.0195, atol=1e-2) 

143 

144 def test_defineEffectivePtc(self): 

145 ampName = self.amp.getName() 

146 inputPtc = PhotonTransferCurveDataset([ampName], "TEST_PTC", 1) 

147 inputPtc.gain[ampName] = 1.2 

148 inputPtc.noise[ampName] = 5 

149 

150 exposureMetadata = self.inputExp.getMetadata() 

151 detector = self.inputExp.getDetector() 

152 overscans = [self.task.overscanCorrection(self.inputExp, self.amp)] 

153 bfGains = {ampName: 1.5} 

154 

155 # Defaults 

156 # doEmpiricalReadNoise = False 

157 # usePtcReadNoise = False 

158 # usePtcGains = False 

159 # doRaiseOnCalibMismatch= False 

160 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains, 

161 overscans, 

162 exposureMetadata) 

163 self.assertEqual(effectivePtc.gain[ampName], self.amp.getGain()) 

164 self.assertEqual(effectivePtc.noise[ampName], self.amp.getReadNoise()) 

165 

166 # Use input PTC values 

167 self.config.usePtcGains = True 

168 self.config.usePtcReadNoise = True 

169 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains, 

170 overscans, 

171 exposureMetadata) 

172 self.assertEqual(effectivePtc.gain[ampName], inputPtc.gain[ampName]) 

173 self.assertEqual(effectivePtc.noise[ampName], inputPtc.noise[ampName]) 

174 

175 # use empirical readout noise from overscan and PTC gain 

176 self.config.usePtcGains = True 

177 self.config.doEmpiricalReadNoise = True 

178 effectivePtc = self.task.defineEffectivePtc(inputPtc, detector, bfGains, 

179 overscans, 

180 exposureMetadata) 

181 self.assertFloatsAlmostEqual( 

182 effectivePtc.noise[ampName], 

183 39.8794613621691*effectivePtc.gain[ampName], 

184 ) 

185 self.assertEqual(effectivePtc.gain[ampName], inputPtc.gain[ampName]) 

186 

187 # Raise if the PTC and BFK gains don't match 

188 results = None 

189 with self.assertRaises(RuntimeError): 

190 self.config.doRaiseOnCalibMismatch = True 

191 results = self.task.defineEffectivePtc(inputPtc, detector, bfGains, 

192 overscans, 

193 exposureMetadata) 

194 self.assertIsNone(results) 

195 

196 def test_darkCorrection(self): 

197 """Expect the median image value should decrease after this operation. 

198 """ 

199 darkIm = isrMock.DarkMock().run() 

200 

201 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

202 self.task.darkCorrection(self.inputExp, darkIm) 

203 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

204 self.assertLess(statAfter[0], statBefore[0]) 

205 self.assertFloatsAlmostEqual(statBefore[0], 8070.0195, atol=1e-2) 

206 self.assertFloatsAlmostEqual(statAfter[0], 8045.7773, atol=1e-2) 

207 

208 def test_darkCorrection_noVisitInfo(self): 

209 """Expect the median image value should decrease after this operation. 

210 """ 

211 darkIm = isrMock.DarkMock().run() 

212 darkIm.getInfo().setVisitInfo(None) 

213 

214 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

215 self.task.darkCorrection(self.inputExp, darkIm) 

216 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

217 self.assertLess(statAfter[0], statBefore[0]) 

218 self.assertFloatsAlmostEqual(statBefore[0], 8070.0195, atol=1e-2) 

219 self.assertFloatsAlmostEqual(statAfter[0], 8045.7773, atol=1e-2) 

220 

221 def test_flatCorrection(self): 

222 """Expect the image median should increase (divide by < 1). 

223 """ 

224 flatIm = isrMock.FlatMock().run() 

225 

226 statBefore = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

227 self.task.flatCorrection(self.inputExp, flatIm) 

228 statAfter = computeImageMedianAndStd(self.inputExp.image[self.amp.getBBox()]) 

229 self.assertGreater(statAfter[1], statBefore[1]) 

230 self.assertFloatsAlmostEqual(statAfter[1], 147407.02, atol=1e-2) 

231 self.assertFloatsAlmostEqual(statBefore[1], 147.55304, atol=1e-2) 

232 

233 def test_saturationDetection(self): 

234 """Expect the saturation level detection/masking to scale with 

235 threshold. 

236 """ 

237 ampB = self.amp.rebuild() 

238 ampB.setSaturation(9000.0) 

239 self.task.saturationDetection(self.inputExp, ampB.finish()) 

240 countBefore = countMaskedPixels(self.mi, "SAT") 

241 

242 ampB.setSaturation(8250.0) 

243 self.task.saturationDetection(self.inputExp, ampB.finish()) 

244 countAfter = countMaskedPixels(self.mi, "SAT") 

245 

246 self.assertLessEqual(countBefore, countAfter) 

247 self.assertEqual(countBefore, 43) 

248 self.assertEqual(countAfter, 136) 

249 

250 def test_measureBackground(self): 

251 """Expect the background measurement runs successfully and to save 

252 metadata values. 

253 """ 

254 self.config.qa.flatness.meshX = 20 

255 self.config.qa.flatness.meshY = 20 

256 self.task.measureBackground(self.inputExp, self.config.qa) 

257 self.assertIsNotNone(self.inputExp.getMetadata().getScalar('SKYLEVEL')) 

258 

259 def test_flatContext(self): 

260 """Expect the flat context manager runs successfully (applying both 

261 flat and dark within the context), and results in the same 

262 image data after completion. 

263 """ 

264 darkExp = isrMock.DarkMock().run() 

265 flatExp = isrMock.FlatMock().run() 

266 

267 mi = self.inputExp.getMaskedImage().clone() 

268 with self.task.flatContext(self.inputExp, flatExp, darkExp): 

269 contextStat = computeImageMedianAndStd(self.inputExp.getMaskedImage().getImage()) 

270 self.assertFloatsAlmostEqual(contextStat[0], 37165.594, atol=1e-2) 

271 

272 self.assertMaskedImagesAlmostEqual(mi, self.inputExp.getMaskedImage()) 

273 

274 

275class IsrTaskUnTrimmedTestCases(lsst.utils.tests.TestCase): 

276 """Test IsrTask methods using untrimmed raw data. 

277 """ 

278 def setUp(self): 

279 self.config = IsrTaskConfig() 

280 self.config.overscan.doParallelOverscan = True 

281 self.config.qa = IsrQaConfig() 

282 self.task = IsrTask(config=self.config) 

283 

284 self.mockConfig = isrMock.IsrMockConfig() 

285 self.mockConfig.isTrimmed = False 

286 self.doGenerateImage = True 

287 self.dataContainer = isrMock.MockDataContainer(config=self.mockConfig) 

288 self.camera = isrMock.IsrMock(config=self.mockConfig).getCamera() 

289 

290 self.inputExp = isrMock.RawMock(config=self.mockConfig).run() 

291 self.amp = self.inputExp.getDetector()[0] 

292 self.mi = self.inputExp.getMaskedImage() 

293 

294 def batchSetConfiguration(self, value): 

295 """Set the configuration state to a consistent value. 

296 

297 Disable options we do not need as well. 

298 

299 Parameters 

300 ---------- 

301 value : `bool` 

302 Value to switch common ISR configuration options to. 

303 """ 

304 self.config.qa.flatness.meshX = 20 

305 self.config.qa.flatness.meshY = 20 

306 self.config.doWrite = False 

307 self.config.doLinearize = False 

308 self.config.doCrosstalk = False 

309 

310 self.config.doConvertIntToFloat = value 

311 self.config.doSaturation = value 

312 self.config.doSuspect = value 

313 self.config.doSetBadRegions = value 

314 self.config.doOverscan = value 

315 self.config.doBias = value 

316 self.config.doVariance = value 

317 self.config.doWidenSaturationTrails = value 

318 self.config.doBrighterFatter = value 

319 self.config.doDefect = value 

320 self.config.doSaturationInterpolation = value 

321 self.config.doDark = value 

322 self.config.doStrayLight = value 

323 self.config.doFlat = value 

324 self.config.doFringe = value 

325 self.config.doMeasureBackground = value 

326 self.config.doVignette = value 

327 self.config.doAttachTransmissionCurve = value 

328 self.config.doUseOpticsTransmission = value 

329 self.config.doUseFilterTransmission = value 

330 self.config.doUseSensorTransmission = value 

331 self.config.doUseAtmosphereTransmission = value 

332 self.config.qa.saveStats = value 

333 self.config.qa.doThumbnailOss = value 

334 self.config.qa.doThumbnailFlattened = value 

335 

336 self.config.doApplyGains = not value 

337 self.config.doCameraSpecificMasking = value 

338 

339 def validateIsrResults(self): 

340 """results should be a struct with components that are 

341 not None if included in the configuration file. 

342 

343 Returns 

344 ------- 

345 results : `pipeBase.Struct` 

346 Results struct generated from the current ISR configuration. 

347 """ 

348 self.task = IsrTask(config=self.config) 

349 results = self.task.run(self.inputExp, 

350 camera=self.camera, 

351 bias=self.dataContainer.get("bias"), 

352 dark=self.dataContainer.get("dark"), 

353 flat=self.dataContainer.get("flat"), 

354 bfKernel=self.dataContainer.get("bfKernel"), 

355 defects=self.dataContainer.get("defects"), 

356 fringes=Struct(fringes=self.dataContainer.get("fringe"), seed=1234), 

357 opticsTransmission=self.dataContainer.get("transmission_"), 

358 filterTransmission=self.dataContainer.get("transmission_"), 

359 sensorTransmission=self.dataContainer.get("transmission_"), 

360 atmosphereTransmission=self.dataContainer.get("transmission_") 

361 ) 

362 

363 self.assertIsInstance(results, Struct) 

364 self.assertIsInstance(results.exposure, afwImage.Exposure) 

365 return results 

366 

367 def test_run_allTrue(self): 

368 """Expect successful run with expected outputs when all non-exclusive 

369 configuration options are on. 

370 

371 Output results should be tested more precisely by the 

372 individual function tests. 

373 

374 """ 

375 self.batchSetConfiguration(True) 

376 self.validateIsrResults() 

377 

378 def test_run_allFalse(self): 

379 """Expect successful run with expected outputs when all non-exclusive 

380 configuration options are off. 

381 

382 Output results should be tested more precisely by the 

383 individual function tests. 

384 

385 """ 

386 self.batchSetConfiguration(False) 

387 self.validateIsrResults() 

388 

389 def test_failCases(self): 

390 """Expect failure with crosstalk enabled. 

391 

392 Output results should be tested more precisely by the 

393 individual function tests. 

394 """ 

395 self.batchSetConfiguration(True) 

396 

397 # This breaks it 

398 self.config.doCrosstalk = True 

399 

400 with self.assertRaises(RuntimeError): 

401 self.validateIsrResults() 

402 

403 def test_maskingCase_negativeVariance(self): 

404 """Test masking cases of configuration parameters. 

405 """ 

406 self.batchSetConfiguration(True) 

407 self.config.overscan.doParallelOverscan = False 

408 self.config.overscan.fitType = "POLY" 

409 self.config.overscan.order = 1 

410 

411 self.config.doSaturation = False 

412 self.config.doWidenSaturationTrails = False 

413 self.config.doSaturationInterpolation = False 

414 self.config.doSuspect = False 

415 self.config.doSetBadRegions = False 

416 self.config.doDefect = False 

417 self.config.doBrighterFatter = False 

418 

419 self.config.maskNegativeVariance = True 

420 self.config.doInterpolate = False 

421 

422 results = self.validateIsrResults() 

423 

424 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

425 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0) 

426 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

427 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 40800) 

428 

429 def test_maskingCase_noMasking(self): 

430 """Test masking cases of configuration parameters. 

431 """ 

432 self.batchSetConfiguration(True) 

433 self.config.overscan.fitType = "POLY" 

434 self.config.overscan.order = 1 

435 

436 self.config.doSaturation = False 

437 self.config.doWidenSaturationTrails = False 

438 self.config.doSaturationInterpolation = False 

439 self.config.doSuspect = False 

440 self.config.doSetBadRegions = False 

441 self.config.doDefect = False 

442 self.config.doBrighterFatter = False 

443 

444 self.config.maskNegativeVariance = False 

445 self.config.doInterpolate = False 

446 

447 results = self.validateIsrResults() 

448 

449 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

450 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0) 

451 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

452 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0) 

453 

454 def test_maskingCase_satMasking(self): 

455 """Test masking cases of configuration parameters. 

456 """ 

457 self.batchSetConfiguration(True) 

458 self.config.overscan.fitType = "POLY" 

459 self.config.overscan.order = 1 

460 

461 self.config.saturation = 20000.0 

462 self.config.doSaturation = True 

463 self.config.doWidenSaturationTrails = True 

464 

465 self.config.doSaturationInterpolation = False 

466 self.config.doSuspect = False 

467 self.config.doSetBadRegions = False 

468 self.config.doDefect = False 

469 self.config.doBrighterFatter = False 

470 

471 self.config.maskNegativeVariance = False # These are mock images. 

472 

473 results = self.validateIsrResults() 

474 

475 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

476 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0) 

477 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

478 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0) 

479 

480 def test_maskingCase_decamEdgeBleedRetarget(self): 

481 """Test that the DECam edge bleed masking subtask can be retargeted 

482 into the camera-specific masking slot and its config reaches the 

483 function. 

484 """ 

485 self.batchSetConfiguration(True) 

486 self.config.overscan.fitType = "POLY" 

487 self.config.overscan.order = 1 

488 self.config.doSaturationInterpolation = False 

489 self.config.doBrighterFatter = False 

490 self.config.maskNegativeVariance = False # These are mock images. 

491 

492 self.config.doCameraSpecificMasking = True 

493 self.config.masking.retarget(DECamEdgeBleedMaskTask) 

494 self.config.masking.satMinArea = 11 

495 self.config.masking.satMaxArea = 22 

496 self.config.masking.approachRows = 33 

497 self.config.masking.nSigma = 4.4 

498 self.config.masking.nRowsCheck = 55 

499 self.config.masking.minLowPixelsPerRow = 66 

500 self.config.masking.minLowPixelsExtent = 7 

501 self.config.masking.marginFraction = 0.88 

502 self.config.masking.saturatedMaskName = "SUSPECT" 

503 

504 with unittest.mock.patch("lsst.ip.isr.isrFunctions.maskDECamEdgeBleed") as mocked: 

505 self.validateIsrResults() 

506 

507 mocked.assert_called_once() 

508 kwargs = mocked.call_args.kwargs 

509 self.assertEqual(kwargs["satMinArea"], 11) 

510 self.assertEqual(kwargs["satMaxArea"], 22) 

511 self.assertEqual(kwargs["approachRows"], 33) 

512 self.assertEqual(kwargs["nSigma"], 4.4) 

513 self.assertEqual(kwargs["nRowsCheck"], 55) 

514 self.assertEqual(kwargs["minLowPixelsPerRow"], 66) 

515 self.assertEqual(kwargs["minLowPixelsExtent"], 7) 

516 self.assertEqual(kwargs["marginFraction"], 0.88) 

517 self.assertEqual(kwargs["saturatedMaskName"], "SUSPECT") 

518 

519 def test_maskingCase_satMaskingAndInterp(self): 

520 """Test masking cases of configuration parameters. 

521 """ 

522 self.batchSetConfiguration(True) 

523 self.config.overscan.fitType = "POLY" 

524 self.config.overscan.order = 1 

525 

526 self.config.saturation = 20000.0 

527 self.config.doSaturation = True 

528 self.config.doWidenSaturationTrails = True 

529 self.config.doSaturationInterpolation = True 

530 

531 self.config.doSuspect = False 

532 self.config.doSetBadRegions = False 

533 self.config.doDefect = False 

534 self.config.doBrighterFatter = False 

535 

536 self.config.maskNegativeVariance = False # These are mock images. 

537 

538 results = self.validateIsrResults() 

539 

540 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

541 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0) 

542 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

543 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0) 

544 

545 def test_maskingCase_throughEdge(self): 

546 """Test masking cases of configuration parameters. 

547 """ 

548 self.batchSetConfiguration(True) 

549 self.config.overscan.fitType = "POLY" 

550 self.config.overscan.order = 1 

551 

552 self.config.saturation = 20000.0 

553 self.config.doSaturation = True 

554 self.config.doWidenSaturationTrails = True 

555 self.config.doSaturationInterpolation = True 

556 self.config.numEdgeSuspect = 5 

557 self.config.doSuspect = True 

558 

559 self.config.doSetBadRegions = False 

560 self.config.doDefect = False 

561 self.config.doBrighterFatter = False 

562 

563 self.config.maskNegativeVariance = False # These are mock images. 

564 

565 results = self.validateIsrResults() 

566 

567 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

568 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 0) 

569 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

570 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 0) 

571 

572 def test_maskingCase_throughDefects(self): 

573 """Test masking cases of configuration parameters. 

574 """ 

575 self.batchSetConfiguration(True) 

576 self.config.overscan.fitType = "POLY" 

577 self.config.overscan.order = 1 

578 

579 self.config.saturation = 20000.0 

580 self.config.doSaturation = True 

581 self.config.doWidenSaturationTrails = True 

582 self.config.doSaturationInterpolation = True 

583 self.config.numEdgeSuspect = 5 

584 self.config.doSuspect = True 

585 self.config.doDefect = True 

586 

587 self.config.doSetBadRegions = False 

588 self.config.doBrighterFatter = False 

589 

590 self.config.maskNegativeVariance = False # These are mock images. 

591 

592 results = self.validateIsrResults() 

593 

594 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

595 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000) 

596 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 3940) 

597 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000) 

598 

599 def test_maskingCase_throughDefectsAmpEdges(self): 

600 """Test masking cases of configuration parameters. 

601 """ 

602 self.batchSetConfiguration(True) 

603 self.config.overscan.fitType = "POLY" 

604 self.config.overscan.order = 1 

605 

606 self.config.saturation = 20000.0 

607 self.config.doSaturation = True 

608 self.config.doWidenSaturationTrails = True 

609 self.config.doSaturationInterpolation = True 

610 self.config.numEdgeSuspect = 5 

611 self.config.doSuspect = True 

612 self.config.doDefect = True 

613 self.config.edgeMaskLevel = 'AMP' 

614 

615 self.config.doSetBadRegions = False 

616 self.config.doBrighterFatter = False 

617 

618 self.config.maskNegativeVariance = False # These are mock images. 

619 

620 results = self.validateIsrResults() 

621 

622 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

623 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000) 

624 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 11280) 

625 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000) 

626 

627 def test_maskingCase_throughBad(self): 

628 """Test masking cases of configuration parameters. 

629 """ 

630 self.batchSetConfiguration(True) 

631 self.config.overscan.fitType = "POLY" 

632 self.config.overscan.order = 1 

633 

634 self.config.saturation = 20000.0 

635 self.config.doSaturation = True 

636 self.config.doWidenSaturationTrails = True 

637 self.config.doSaturationInterpolation = True 

638 

639 self.config.doSuspect = True 

640 self.config.doDefect = True 

641 self.config.doSetBadRegions = True 

642 self.config.doBrighterFatter = False 

643 

644 self.config.maskNegativeVariance = False # These are mock images. 

645 

646 results = self.validateIsrResults() 

647 

648 self.assertEqual(countMaskedPixels(results.exposure, "SAT"), 0) 

649 self.assertEqual(countMaskedPixels(results.exposure, "INTRP"), 2000) 

650 self.assertEqual(countMaskedPixels(results.exposure, "SUSPECT"), 0) 

651 self.assertEqual(countMaskedPixels(results.exposure, "BAD"), 2000) 

652 

653 def test_binnedExposures(self): 

654 """Ensure that binned exposures have correct sizes.""" 

655 self.batchSetConfiguration(True) 

656 self.config.doBinnedExposures = True 

657 self.config.binFactor1 = 8 

658 self.config.binFactor2 = 64 

659 

660 results = self.validateIsrResults() 

661 

662 original = results.exposure.image.array.shape 

663 bin1 = results.outputBin1Exposure.image.array.shape 

664 bin2 = results.outputBin2Exposure.image.array.shape 

665 

666 # Binning truncates, so check that the original and obtained 

667 # binned images have the correct offset. 

668 self.assertEqual(original[0] - bin1[0] * 8, 4) 

669 self.assertEqual(original[1] - bin1[1] * 8, 0) 

670 self.assertEqual(original[0] - bin2[0] * 64, 12) 

671 self.assertEqual(original[1] - bin2[1] * 64, 8) 

672 

673 

674class MemoryTester(lsst.utils.tests.MemoryTestCase): 

675 pass 

676 

677 

678def setup_module(module): 

679 lsst.utils.tests.init() 

680 

681 

682if __name__ == "__main__": 682 ↛ 683line 682 didn't jump to line 683 because the condition on line 682 was never true

683 lsst.utils.tests.init() 

684 unittest.main(failfast=True)