Coverage for tests/test_isrTask.py: 92%

410 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-17 09:14 +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.approachRows = 33 

496 self.config.masking.nSigma = 4.4 

497 self.config.masking.nRowsCheck = 55 

498 self.config.masking.nRowsCheckShort = 3 

499 self.config.masking.minUsablePixelsPerRow = 66 

500 self.config.masking.minLowFraction = 0.77 

501 self.config.masking.minLowFractionExtent = 0.07 

502 self.config.masking.minBlockedRows = 9 

503 self.config.masking.marginFraction = 0.88 

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

505 

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

507 self.validateIsrResults() 

508 

509 mocked.assert_called_once() 

510 kwargs = mocked.call_args.kwargs 

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

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

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

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

515 self.assertEqual(kwargs["nRowsCheckShort"], 3) 

516 self.assertEqual(kwargs["minUsablePixelsPerRow"], 66) 

517 self.assertEqual(kwargs["minLowFraction"], 0.77) 

518 self.assertEqual(kwargs["minLowFractionExtent"], 0.07) 

519 self.assertEqual(kwargs["minBlockedRows"], 9) 

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

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

522 

523 def test_maskingCase_satMaskingAndInterp(self): 

524 """Test masking cases of configuration parameters. 

525 """ 

526 self.batchSetConfiguration(True) 

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

528 self.config.overscan.order = 1 

529 

530 self.config.saturation = 20000.0 

531 self.config.doSaturation = True 

532 self.config.doWidenSaturationTrails = True 

533 self.config.doSaturationInterpolation = True 

534 

535 self.config.doSuspect = False 

536 self.config.doSetBadRegions = False 

537 self.config.doDefect = False 

538 self.config.doBrighterFatter = False 

539 

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

541 

542 results = self.validateIsrResults() 

543 

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

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

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

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

548 

549 def test_maskingCase_throughEdge(self): 

550 """Test masking cases of configuration parameters. 

551 """ 

552 self.batchSetConfiguration(True) 

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

554 self.config.overscan.order = 1 

555 

556 self.config.saturation = 20000.0 

557 self.config.doSaturation = True 

558 self.config.doWidenSaturationTrails = True 

559 self.config.doSaturationInterpolation = True 

560 self.config.numEdgeSuspect = 5 

561 self.config.doSuspect = True 

562 

563 self.config.doSetBadRegions = False 

564 self.config.doDefect = False 

565 self.config.doBrighterFatter = False 

566 

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

568 

569 results = self.validateIsrResults() 

570 

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

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

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

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

575 

576 def test_maskingCase_throughDefects(self): 

577 """Test masking cases of configuration parameters. 

578 """ 

579 self.batchSetConfiguration(True) 

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

581 self.config.overscan.order = 1 

582 

583 self.config.saturation = 20000.0 

584 self.config.doSaturation = True 

585 self.config.doWidenSaturationTrails = True 

586 self.config.doSaturationInterpolation = True 

587 self.config.numEdgeSuspect = 5 

588 self.config.doSuspect = True 

589 self.config.doDefect = True 

590 

591 self.config.doSetBadRegions = False 

592 self.config.doBrighterFatter = False 

593 

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

595 

596 results = self.validateIsrResults() 

597 

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

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

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

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

602 

603 def test_maskingCase_throughDefectsAmpEdges(self): 

604 """Test masking cases of configuration parameters. 

605 """ 

606 self.batchSetConfiguration(True) 

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

608 self.config.overscan.order = 1 

609 

610 self.config.saturation = 20000.0 

611 self.config.doSaturation = True 

612 self.config.doWidenSaturationTrails = True 

613 self.config.doSaturationInterpolation = True 

614 self.config.numEdgeSuspect = 5 

615 self.config.doSuspect = True 

616 self.config.doDefect = True 

617 self.config.edgeMaskLevel = 'AMP' 

618 

619 self.config.doSetBadRegions = False 

620 self.config.doBrighterFatter = False 

621 

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

623 

624 results = self.validateIsrResults() 

625 

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

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

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

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

630 

631 def test_maskingCase_throughBad(self): 

632 """Test masking cases of configuration parameters. 

633 """ 

634 self.batchSetConfiguration(True) 

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

636 self.config.overscan.order = 1 

637 

638 self.config.saturation = 20000.0 

639 self.config.doSaturation = True 

640 self.config.doWidenSaturationTrails = True 

641 self.config.doSaturationInterpolation = True 

642 

643 self.config.doSuspect = True 

644 self.config.doDefect = True 

645 self.config.doSetBadRegions = True 

646 self.config.doBrighterFatter = False 

647 

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

649 

650 results = self.validateIsrResults() 

651 

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

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

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

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

656 

657 def test_binnedExposures(self): 

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

659 self.batchSetConfiguration(True) 

660 self.config.doBinnedExposures = True 

661 self.config.binFactor1 = 8 

662 self.config.binFactor2 = 64 

663 

664 results = self.validateIsrResults() 

665 

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

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

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

669 

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

671 # binned images have the correct offset. 

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

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

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

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

676 

677 

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

679 pass 

680 

681 

682def setup_module(module): 

683 lsst.utils.tests.init() 

684 

685 

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

687 lsst.utils.tests.init() 

688 unittest.main(failfast=True)