Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22""" 

23Test lsst.afw.image.Exposure 

24""" 

25 

26import os.path 

27import unittest 

28 

29import numpy as np 

30from numpy.testing import assert_allclose 

31import yaml 

32 

33import lsst.utils 

34import lsst.utils.tests 

35import lsst.geom 

36import lsst.afw.image as afwImage 

37from lsst.afw.image.utils import defineFilter 

38from lsst.afw.coord import Weather 

39import lsst.afw.geom as afwGeom 

40import lsst.afw.table as afwTable 

41import lsst.pex.exceptions as pexExcept 

42from lsst.afw.fits import readMetadata, FitsError 

43from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

44from lsst.log import Log 

45from testTableArchivesLib import DummyPsf 

46 

47Log.getLogger("afw.image.Mask").setLevel(Log.INFO) 

48 

49try: 

50 dataDir = os.path.join(lsst.utils.getPackageDir("afwdata"), "data") 

51except pexExcept.NotFoundError: 

52 dataDir = None 

53else: 

54 InputMaskedImageName = "871034p_1_MI.fits" 

55 InputMaskedImageNameSmall = "small_MI.fits" 

56 InputImageNameSmall = "small" 

57 OutputMaskedImageName = "871034p_1_MInew.fits" 

58 

59 currDir = os.path.abspath(os.path.dirname(__file__)) 

60 inFilePath = os.path.join(dataDir, InputMaskedImageName) 

61 inFilePathSmall = os.path.join(dataDir, InputMaskedImageNameSmall) 

62 inFilePathSmallImage = os.path.join(dataDir, InputImageNameSmall) 

63 

64 

65@unittest.skipIf(dataDir is None, "afwdata not setup") 

66class ExposureTestCase(lsst.utils.tests.TestCase): 

67 """ 

68 A test case for the Exposure Class 

69 """ 

70 

71 def setUp(self): 

72 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

73 maskedImageMD = readMetadata(inFilePathSmall) 

74 

75 self.smallExposure = afwImage.ExposureF(inFilePathSmall) 

76 self.width = maskedImage.getWidth() 

77 self.height = maskedImage.getHeight() 

78 self.wcs = afwGeom.makeSkyWcs(maskedImageMD, False) 

79 self.md = maskedImageMD 

80 self.psf = DummyPsf(2.0) 

81 self.detector = DetectorWrapper().detector 

82 self.extras = {"MISC": DummyPsf(3.5)} 

83 

84 self.exposureBlank = afwImage.ExposureF() 

85 self.exposureMiOnly = afwImage.makeExposure(maskedImage) 

86 self.exposureMiWcs = afwImage.makeExposure(maskedImage, self.wcs) 

87 # n.b. the (100, 100, ...) form 

88 self.exposureCrWcs = afwImage.ExposureF(100, 100, self.wcs) 

89 # test with ExtentI(100, 100) too 

90 self.exposureCrOnly = afwImage.ExposureF(lsst.geom.ExtentI(100, 100)) 

91 

92 afwImage.Filter.reset() 

93 afwImage.FilterProperty.reset() 

94 

95 defineFilter("g", 470.0) 

96 

97 def tearDown(self): 

98 del self.smallExposure 

99 del self.wcs 

100 del self.psf 

101 del self.detector 

102 del self.extras 

103 

104 del self.exposureBlank 

105 del self.exposureMiOnly 

106 del self.exposureMiWcs 

107 del self.exposureCrWcs 

108 del self.exposureCrOnly 

109 

110 def testGetMaskedImage(self): 

111 """ 

112 Test to ensure a MaskedImage can be obtained from each 

113 Exposure. An Exposure is required to have a MaskedImage, 

114 therefore each of the Exposures should return a MaskedImage. 

115 

116 MaskedImage class should throw appropriate 

117 lsst::pex::exceptions::NotFound if the MaskedImage can not be 

118 obtained. 

119 """ 

120 maskedImageBlank = self.exposureBlank.getMaskedImage() 

121 blankWidth = maskedImageBlank.getWidth() 

122 blankHeight = maskedImageBlank.getHeight() 

123 if blankWidth != blankHeight != 0: 

124 self.fail(f"{blankWidth} = {blankHeight} != 0") 

125 

126 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage() 

127 miOnlyWidth = maskedImageMiOnly.getWidth() 

128 miOnlyHeight = maskedImageMiOnly.getHeight() 

129 self.assertAlmostEqual(miOnlyWidth, self.width) 

130 self.assertAlmostEqual(miOnlyHeight, self.height) 

131 

132 # NOTE: Unittests for Exposures created from a MaskedImage and 

133 # a WCS object are incomplete. No way to test the validity of 

134 # the WCS being copied/created. 

135 

136 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage() 

137 miWcsWidth = maskedImageMiWcs.getWidth() 

138 miWcsHeight = maskedImageMiWcs.getHeight() 

139 self.assertAlmostEqual(miWcsWidth, self.width) 

140 self.assertAlmostEqual(miWcsHeight, self.height) 

141 

142 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage() 

143 crWcsWidth = maskedImageCrWcs.getWidth() 

144 crWcsHeight = maskedImageCrWcs.getHeight() 

145 if crWcsWidth != crWcsHeight != 0: 

146 self.fail(f"{crWcsWidth} != {crWcsHeight} != 0") 

147 

148 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage() 

149 crOnlyWidth = maskedImageCrOnly.getWidth() 

150 crOnlyHeight = maskedImageCrOnly.getHeight() 

151 if crOnlyWidth != crOnlyHeight != 0: 

152 self.fail(f"{crOnlyWidth} != {crOnlyHeight} != 0") 

153 

154 # Check Exposure.getWidth() returns the MaskedImage's width 

155 self.assertEqual(crOnlyWidth, self.exposureCrOnly.getWidth()) 

156 self.assertEqual(crOnlyHeight, self.exposureCrOnly.getHeight()) 

157 # check width/height properties 

158 self.assertEqual(crOnlyWidth, self.exposureCrOnly.width) 

159 self.assertEqual(crOnlyHeight, self.exposureCrOnly.height) 

160 

161 def testProperties(self): 

162 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, 

163 self.exposureMiOnly.getMaskedImage()) 

164 mi2 = afwImage.MaskedImageF(self.exposureMiOnly.getDimensions()) 

165 mi2.image.array[:] = 5.0 

166 mi2.variance.array[:] = 3.0 

167 mi2.mask.array[:] = 0x1 

168 self.exposureMiOnly.maskedImage = mi2 

169 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, mi2) 

170 self.assertImagesEqual(self.exposureMiOnly.image, 

171 self.exposureMiOnly.maskedImage.image) 

172 

173 image3 = afwImage.ImageF(self.exposureMiOnly.getDimensions()) 

174 image3.array[:] = 3.0 

175 self.exposureMiOnly.image = image3 

176 self.assertImagesEqual(self.exposureMiOnly.image, image3) 

177 

178 mask3 = afwImage.MaskX(self.exposureMiOnly.getDimensions()) 

179 mask3.array[:] = 0x2 

180 self.exposureMiOnly.mask = mask3 

181 self.assertMasksEqual(self.exposureMiOnly.mask, mask3) 

182 

183 var3 = afwImage.ImageF(self.exposureMiOnly.getDimensions()) 

184 var3.array[:] = 2.0 

185 self.exposureMiOnly.variance = var3 

186 self.assertImagesEqual(self.exposureMiOnly.variance, var3) 

187 

188 # Test the property getter for a null VisitInfo. 

189 self.assertIsNone(self.exposureMiOnly.visitInfo) 

190 

191 def testGetWcs(self): 

192 """Test that a WCS can be obtained from each Exposure created with 

193 a WCS, and that an Exposure lacking a WCS returns None. 

194 """ 

195 # These exposures don't contain a WCS 

196 self.assertIsNone(self.exposureBlank.getWcs()) 

197 self.assertIsNone(self.exposureMiOnly.getWcs()) 

198 self.assertIsNone(self.exposureCrOnly.getWcs()) 

199 

200 # These exposures should contain a WCS 

201 self.assertEqual(self.wcs, self.exposureMiWcs.getWcs()) 

202 self.assertEqual(self.wcs, self.exposureCrWcs.getWcs()) 

203 

204 def testExposureInfoConstructor(self): 

205 """Test the Exposure(maskedImage, exposureInfo) constructor""" 

206 exposureInfo = afwImage.ExposureInfo() 

207 exposureInfo.setWcs(self.wcs) 

208 exposureInfo.setDetector(self.detector) 

209 gFilter = afwImage.Filter("g") 

210 gFilterLabel = afwImage.FilterLabel(band="g") 

211 exposureInfo.setFilter(gFilter) 

212 exposureInfo.setFilterLabel(gFilterLabel) 

213 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

214 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

215 

216 self.assertTrue(exposure.hasWcs()) 

217 self.assertEqual(exposure.getWcs().getPixelOrigin(), 

218 self.wcs.getPixelOrigin()) 

219 self.assertEqual(exposure.getDetector().getName(), 

220 self.detector.getName()) 

221 self.assertEqual(exposure.getDetector().getSerial(), 

222 self.detector.getSerial()) 

223 self.assertEqual(exposure.getFilter(), gFilter) 

224 self.assertEqual(exposure.getFilterLabel(), gFilterLabel) 

225 

226 self.assertTrue(exposure.getInfo().hasWcs()) 

227 # check the ExposureInfo property 

228 self.assertTrue(exposure.info.hasWcs()) 

229 self.assertEqual(exposure.getInfo().getWcs().getPixelOrigin(), 

230 self.wcs.getPixelOrigin()) 

231 self.assertEqual(exposure.getInfo().getDetector().getName(), 

232 self.detector.getName()) 

233 self.assertEqual(exposure.getInfo().getDetector().getSerial(), 

234 self.detector.getSerial()) 

235 self.assertEqual(exposure.getInfo().getFilter(), gFilter) 

236 self.assertEqual(exposure.getInfo().getFilterLabel(), gFilterLabel) 

237 

238 def testNullWcs(self): 

239 """Test that an Exposure constructed with second argument None is usable 

240 

241 When the exposureInfo constructor was first added, trying to get a WCS 

242 or other info caused a segfault because the ExposureInfo did not exist. 

243 """ 

244 maskedImage = self.exposureMiOnly.getMaskedImage() 

245 exposure = afwImage.ExposureF(maskedImage, None) 

246 self.assertFalse(exposure.hasWcs()) 

247 self.assertFalse(exposure.hasPsf()) 

248 

249 def testExposureInfoSetNone(self): 

250 exposureInfo = afwImage.ExposureInfo() 

251 exposureInfo.setDetector(None) 

252 exposureInfo.setValidPolygon(None) 

253 exposureInfo.setPsf(None) 

254 exposureInfo.setWcs(None) 

255 exposureInfo.setPhotoCalib(None) 

256 exposureInfo.setCoaddInputs(None) 

257 exposureInfo.setVisitInfo(None) 

258 exposureInfo.setApCorrMap(None) 

259 for key in self.extras: 

260 exposureInfo.setComponent(key, None) 

261 

262 def testSetExposureInfo(self): 

263 exposureInfo = afwImage.ExposureInfo() 

264 exposureInfo.setWcs(self.wcs) 

265 exposureInfo.setDetector(self.detector) 

266 gFilter = afwImage.Filter("g") 

267 gFilterLabel = afwImage.FilterLabel(band="g") 

268 exposureInfo.setFilter(gFilter) 

269 exposureInfo.setFilterLabel(gFilterLabel) 

270 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

271 exposure = afwImage.ExposureF(maskedImage) 

272 self.assertFalse(exposure.hasWcs()) 

273 

274 exposure.setInfo(exposureInfo) 

275 

276 self.assertTrue(exposure.hasWcs()) 

277 self.assertEqual(exposure.getWcs().getPixelOrigin(), 

278 self.wcs.getPixelOrigin()) 

279 self.assertEqual(exposure.getDetector().getName(), 

280 self.detector.getName()) 

281 self.assertEqual(exposure.getDetector().getSerial(), 

282 self.detector.getSerial()) 

283 self.assertEqual(exposure.getFilter(), gFilter) 

284 self.assertEqual(exposure.getFilterLabel(), gFilterLabel) 

285 

286 # test properties 

287 self.assertEqual(exposure.detector.getName(), self.detector.getName()) 

288 self.assertEqual(exposure.filterLabel, gFilterLabel) 

289 self.assertEqual(exposure.wcs, self.wcs) 

290 

291 def testDefaultFilter(self): 

292 """Test that old convention of having a "default" filter replaced with `None`. 

293 """ 

294 exposureInfo = afwImage.ExposureInfo() 

295 noFilter = afwImage.Filter() 

296 exposureInfo.setFilter(noFilter) 

297 self.assertFalse(exposureInfo.hasFilterLabel()) 

298 self.assertIsNone(exposureInfo.getFilterLabel()) 

299 

300 def testVisitInfoFitsPersistence(self): 

301 """Test saving an exposure to FITS and reading it back in preserves (some) VisitInfo fields""" 

302 exposureId = 5 

303 exposureTime = 12.3 

304 boresightRotAngle = 45.6 * lsst.geom.degrees 

305 weather = Weather(1.1, 2.2, 0.3) 

306 visitInfo = afwImage.VisitInfo( 

307 exposureId=exposureId, 

308 exposureTime=exposureTime, 

309 boresightRotAngle=boresightRotAngle, 

310 weather=weather, 

311 ) 

312 photoCalib = afwImage.PhotoCalib(3.4, 5.6) 

313 exposureInfo = afwImage.ExposureInfo() 

314 exposureInfo.setVisitInfo(visitInfo) 

315 exposureInfo.setPhotoCalib(photoCalib) 

316 exposureInfo.setDetector(self.detector) 

317 gFilter = afwImage.Filter("g") 

318 gFilterLabel = afwImage.FilterLabel(band="g") 

319 exposureInfo.setFilter(gFilter) 

320 exposureInfo.setFilterLabel(gFilterLabel) 

321 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

322 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

323 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

324 exposure.writeFits(tmpFile) 

325 rtExposure = afwImage.ExposureF(tmpFile) 

326 rtVisitInfo = rtExposure.getInfo().getVisitInfo() 

327 self.assertEqual(rtVisitInfo.getWeather(), weather) 

328 self.assertEqual(rtExposure.getPhotoCalib(), photoCalib) 

329 self.assertEqual(rtExposure.getFilter(), gFilter) 

330 self.assertEqual(rtExposure.getFilterLabel(), gFilterLabel) 

331 

332 # Test property getters. 

333 self.assertEqual(rtExposure.photoCalib, photoCalib) 

334 self.assertEqual(rtExposure.filterLabel, gFilterLabel) 

335 # NOTE: we can't test visitInfo equality, because most fields are NaN. 

336 self.assertIsNotNone(rtExposure.visitInfo) 

337 

338 def testSetMembers(self): 

339 """ 

340 Test that the MaskedImage and the WCS of an Exposure can be set. 

341 """ 

342 exposure = afwImage.ExposureF() 

343 

344 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

345 exposure.setMaskedImage(maskedImage) 

346 exposure.setWcs(self.wcs) 

347 exposure.setDetector(self.detector) 

348 exposure.setFilter(afwImage.Filter("g")) 

349 exposure.setFilterLabel(afwImage.FilterLabel(band="g")) 

350 

351 self.assertEqual(exposure.getDetector().getName(), 

352 self.detector.getName()) 

353 self.assertEqual(exposure.getDetector().getSerial(), 

354 self.detector.getSerial()) 

355 self.assertEqual(exposure.getFilter().getName(), "g") 

356 self.assertEqual(exposure.getFilterLabel().bandLabel, "g") 

357 self.assertEqual(exposure.getWcs(), self.wcs) 

358 

359 # The PhotoCalib tests are in test_photoCalib.py; 

360 # here we just check that it's gettable and settable. 

361 self.assertIsNone(exposure.getPhotoCalib()) 

362 

363 photoCalib = afwImage.PhotoCalib(511.1, 44.4) 

364 exposure.setPhotoCalib(photoCalib) 

365 self.assertEqual(exposure.getPhotoCalib(), photoCalib) 

366 

367 # Psfs next 

368 self.assertFalse(exposure.hasPsf()) 

369 exposure.setPsf(self.psf) 

370 self.assertTrue(exposure.hasPsf()) 

371 

372 exposure.setPsf(DummyPsf(1.0)) # we can reset the Psf 

373 

374 # extras next 

375 info = exposure.getInfo() 

376 for key, value in self.extras.items(): 

377 self.assertFalse(info.hasComponent(key)) 

378 self.assertIsNone(info.getComponent(key)) 

379 info.setComponent(key, value) 

380 self.assertTrue(info.hasComponent(key)) 

381 self.assertEqual(info.getComponent(key), value) 

382 info.removeComponent(key) 

383 self.assertFalse(info.hasComponent(key)) 

384 

385 # Test that we can set the MaskedImage and WCS of an Exposure 

386 # that already has both 

387 self.exposureMiWcs.setMaskedImage(maskedImage) 

388 exposure.setWcs(self.wcs) 

389 

390 def testHasWcs(self): 

391 """ 

392 Test if an Exposure has a WCS or not. 

393 """ 

394 self.assertFalse(self.exposureBlank.hasWcs()) 

395 

396 self.assertFalse(self.exposureMiOnly.hasWcs()) 

397 self.assertTrue(self.exposureMiWcs.hasWcs()) 

398 self.assertTrue(self.exposureCrWcs.hasWcs()) 

399 self.assertFalse(self.exposureCrOnly.hasWcs()) 

400 

401 def testGetSubExposure(self): 

402 """ 

403 Test that a subExposure of the original Exposure can be obtained. 

404 

405 The MaskedImage class should throw a 

406 lsst::pex::exceptions::InvalidParameter if the requested 

407 subRegion is not fully contained within the original 

408 MaskedImage. 

409 

410 """ 

411 # 

412 # This subExposure is valid 

413 # 

414 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50), 

415 lsst.geom.Extent2I(10, 10)) 

416 subExposure = self.exposureCrWcs.Factory( 

417 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

418 

419 self.checkWcs(self.exposureCrWcs, subExposure) 

420 

421 # this subRegion is not valid and should trigger an exception 

422 # from the MaskedImage class and should trigger an exception 

423 # from the WCS class for the MaskedImage 871034p_1_MI. 

424 

425 subRegion3 = lsst.geom.Box2I(lsst.geom.Point2I(100, 100), 

426 lsst.geom.Extent2I(10, 10)) 

427 

428 def getSubRegion(): 

429 self.exposureCrWcs.Factory( 

430 self.exposureCrWcs, subRegion3, afwImage.LOCAL) 

431 

432 self.assertRaises(pexExcept.LengthError, getSubRegion) 

433 

434 # this subRegion is not valid and should trigger an exception 

435 # from the MaskedImage class only for the MaskedImage small_MI. 

436 # small_MI (cols, rows) = (256, 256) 

437 

438 subRegion4 = lsst.geom.Box2I(lsst.geom.Point2I(250, 250), 

439 lsst.geom.Extent2I(10, 10)) 

440 

441 def getSubRegion(): 

442 self.exposureCrWcs.Factory( 

443 self.exposureCrWcs, subRegion4, afwImage.LOCAL) 

444 

445 self.assertRaises(pexExcept.LengthError, getSubRegion) 

446 

447 # check the sub- and parent- exposures are using the same Wcs 

448 # transformation 

449 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50), 

450 lsst.geom.Extent2I(10, 10)) 

451 subExposure = self.exposureCrWcs.Factory( 

452 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

453 parentSkyPos = self.exposureCrWcs.getWcs().pixelToSky(0, 0) 

454 

455 subExpSkyPos = subExposure.getWcs().pixelToSky(0, 0) 

456 

457 self.assertSpherePointsAlmostEqual(parentSkyPos, subExpSkyPos, msg="Wcs in sub image has changed") 

458 

459 def testReadWriteFits(self): 

460 """Test readFits and writeFits. 

461 """ 

462 # This should pass without an exception 

463 mainExposure = afwImage.ExposureF(inFilePathSmall) 

464 mainExposure.setDetector(self.detector) 

465 

466 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 10), 

467 lsst.geom.Extent2I(40, 50)) 

468 subExposure = mainExposure.Factory( 

469 mainExposure, subBBox, afwImage.LOCAL) 

470 self.checkWcs(mainExposure, subExposure) 

471 det = subExposure.getDetector() 

472 self.assertTrue(det) 

473 

474 subExposure = afwImage.ExposureF( 

475 inFilePathSmall, subBBox, afwImage.LOCAL) 

476 

477 self.checkWcs(mainExposure, subExposure) 

478 

479 # This should throw an exception 

480 def getExposure(): 

481 afwImage.ExposureF(inFilePathSmallImage) 

482 

483 self.assertRaises(FitsError, getExposure) 

484 

485 mainExposure.setPsf(self.psf) 

486 

487 # Make sure we can write without an exception 

488 photoCalib = afwImage.PhotoCalib(1e-10, 1e-12) 

489 mainExposure.setPhotoCalib(photoCalib) 

490 

491 mainInfo = mainExposure.getInfo() 

492 for key, value in self.extras.items(): 

493 mainInfo.setComponent(key, value) 

494 

495 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

496 mainExposure.writeFits(tmpFile) 

497 

498 readExposure = type(mainExposure)(tmpFile) 

499 

500 # 

501 # Check the round-tripping 

502 # 

503 self.assertEqual(mainExposure.getFilter().getName(), 

504 readExposure.getFilter().getName()) 

505 self.assertIsNotNone(mainExposure.getFilterLabel()) 

506 self.assertEqual(mainExposure.getFilterLabel(), 

507 readExposure.getFilterLabel()) 

508 

509 self.assertEqual(photoCalib, readExposure.getPhotoCalib()) 

510 

511 readInfo = readExposure.getInfo() 

512 for key, value in self.extras.items(): 

513 self.assertEqual(value, readInfo.getComponent(key)) 

514 

515 psf = readExposure.getPsf() 

516 self.assertIsNotNone(psf) 

517 self.assertEqual(psf, self.psf) 

518 # check psf property getter 

519 self.assertEqual(readExposure.psf, self.psf) 

520 

521 def checkWcs(self, parentExposure, subExposure): 

522 """Compare WCS at corner points of a sub-exposure and its parent exposure 

523 By using the function indexToPosition, we should be able to convert the indices 

524 (of the four corners (of the sub-exposure)) to positions and use the wcs 

525 to get the same sky coordinates for each. 

526 """ 

527 subMI = subExposure.getMaskedImage() 

528 subDim = subMI.getDimensions() 

529 

530 # Note: pixel positions must be computed relative to XY0 when working 

531 # with WCS 

532 mainWcs = parentExposure.getWcs() 

533 subWcs = subExposure.getWcs() 

534 

535 for xSubInd in (0, subDim.getX()-1): 

536 for ySubInd in (0, subDim.getY()-1): 

537 self.assertSpherePointsAlmostEqual( 

538 mainWcs.pixelToSky( 

539 afwImage.indexToPosition(xSubInd), 

540 afwImage.indexToPosition(ySubInd), 

541 ), 

542 subWcs.pixelToSky( 

543 afwImage.indexToPosition(xSubInd), 

544 afwImage.indexToPosition(ySubInd), 

545 )) 

546 

547 def cmpExposure(self, e1, e2): 

548 self.assertEqual(e1.getDetector().getName(), 

549 e2.getDetector().getName()) 

550 self.assertEqual(e1.getDetector().getSerial(), 

551 e2.getDetector().getSerial()) 

552 self.assertEqual(e1.getFilter().getName(), e2.getFilter().getName()) 

553 self.assertEqual(e1.getFilterLabel(), e2.getFilterLabel()) 

554 xy = lsst.geom.Point2D(0, 0) 

555 self.assertEqual(e1.getWcs().pixelToSky(xy)[0], 

556 e2.getWcs().pixelToSky(xy)[0]) 

557 self.assertEqual(e1.getPhotoCalib(), e2.getPhotoCalib()) 

558 # check PSF identity 

559 if not e1.getPsf(): 

560 self.assertFalse(e2.getPsf()) 

561 else: 

562 self.assertEqual(e1.getPsf(), e2.getPsf()) 

563 # Check extra components 

564 i1 = e1.getInfo() 

565 i2 = e2.getInfo() 

566 for key in self.extras: 

567 self.assertEqual(i1.hasComponent(key), i2.hasComponent(key)) 

568 if i1.hasComponent(key): 

569 self.assertEqual(i1.getComponent(key), i2.getComponent(key)) 

570 

571 def testCopyExposure(self): 

572 """Copy an Exposure (maybe changing type)""" 

573 

574 exposureU = afwImage.ExposureU(inFilePathSmall, allowUnsafe=True) 

575 exposureU.setWcs(self.wcs) 

576 exposureU.setDetector(self.detector) 

577 exposureU.setFilter(afwImage.Filter("g")) 

578 exposureU.setFilterLabel(afwImage.FilterLabel(band="g")) 

579 exposureU.setPsf(DummyPsf(4.0)) 

580 infoU = exposureU.getInfo() 

581 for key, value in self.extras.items(): 

582 infoU.setComponent(key, value) 

583 

584 exposureF = exposureU.convertF() 

585 self.cmpExposure(exposureF, exposureU) 

586 

587 nexp = exposureF.Factory(exposureF, False) 

588 self.cmpExposure(exposureF, nexp) 

589 

590 # Ensure that the copy was deep. 

591 # (actually this test is invalid since getDetector() returns a shared_ptr) 

592 # cen0 = exposureU.getDetector().getCenterPixel() 

593 # x0,y0 = cen0 

594 # det = exposureF.getDetector() 

595 # det.setCenterPixel(lsst.geom.Point2D(999.0, 437.8)) 

596 # self.assertEqual(exposureU.getDetector().getCenterPixel()[0], x0) 

597 # self.assertEqual(exposureU.getDetector().getCenterPixel()[1], y0) 

598 

599 def testDeepCopyData(self): 

600 """Make sure a deep copy of an Exposure has its own data (ticket #2625) 

601 """ 

602 exp = afwImage.ExposureF(6, 7) 

603 mi = exp.getMaskedImage() 

604 mi.getImage().set(100) 

605 mi.getMask().set(5) 

606 mi.getVariance().set(200) 

607 

608 expCopy = exp.clone() 

609 miCopy = expCopy.getMaskedImage() 

610 miCopy.getImage().set(-50) 

611 miCopy.getMask().set(2) 

612 miCopy.getVariance().set(175) 

613 

614 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50) 

615 self.assertTrue(np.all(miCopy.getMask().getArray() == 2)) 

616 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175) 

617 

618 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100) 

619 self.assertTrue(np.all(mi.getMask().getArray() == 5)) 

620 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200) 

621 

622 def testDeepCopySubData(self): 

623 """Make sure a deep copy of a subregion of an Exposure has its own data (ticket #2625) 

624 """ 

625 exp = afwImage.ExposureF(6, 7) 

626 mi = exp.getMaskedImage() 

627 mi.getImage().set(100) 

628 mi.getMask().set(5) 

629 mi.getVariance().set(200) 

630 

631 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 4)) 

632 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True) 

633 miCopy = expCopy.getMaskedImage() 

634 miCopy.getImage().set(-50) 

635 miCopy.getMask().set(2) 

636 miCopy.getVariance().set(175) 

637 

638 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50) 

639 self.assertTrue(np.all(miCopy.getMask().getArray() == 2)) 

640 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175) 

641 

642 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100) 

643 self.assertTrue(np.all(mi.getMask().getArray() == 5)) 

644 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200) 

645 

646 def testDeepCopyMetadata(self): 

647 """Make sure a deep copy of an Exposure has a deep copy of metadata (ticket #2568) 

648 """ 

649 exp = afwImage.ExposureF(10, 10) 

650 expMeta = exp.getMetadata() 

651 expMeta.set("foo", 5) 

652 expCopy = exp.clone() 

653 expCopyMeta = expCopy.getMetadata() 

654 expCopyMeta.set("foo", 6) 

655 self.assertEqual(expCopyMeta.getScalar("foo"), 6) 

656 # this will fail if the bug is present 

657 self.assertEqual(expMeta.getScalar("foo"), 5) 

658 

659 def testDeepCopySubMetadata(self): 

660 """Make sure a deep copy of a subregion of an Exposure has a deep copy of metadata (ticket #2568) 

661 """ 

662 exp = afwImage.ExposureF(10, 10) 

663 expMeta = exp.getMetadata() 

664 expMeta.set("foo", 5) 

665 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 5)) 

666 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True) 

667 expCopyMeta = expCopy.getMetadata() 

668 expCopyMeta.set("foo", 6) 

669 self.assertEqual(expCopyMeta.getScalar("foo"), 6) 

670 # this will fail if the bug is present 

671 self.assertEqual(expMeta.getScalar("foo"), 5) 

672 

673 def testMakeExposureLeaks(self): 

674 """Test for memory leaks in makeExposure (the test is in lsst.utils.tests.MemoryTestCase)""" 

675 afwImage.makeMaskedImage(afwImage.ImageU(lsst.geom.Extent2I(10, 20))) 

676 afwImage.makeExposure(afwImage.makeMaskedImage( 

677 afwImage.ImageU(lsst.geom.Extent2I(10, 20)))) 

678 

679 def testImageSlices(self): 

680 """Test image slicing, which generate sub-images using Box2I under the covers""" 

681 exp = afwImage.ExposureF(10, 20) 

682 mi = exp.getMaskedImage() 

683 mi.image[9, 19] = 10 

684 # N.b. Exposures don't support setting/getting the pixels so can't 

685 # replicate e.g. Image's slice tests 

686 sexp = exp[1:4, 6:10] 

687 self.assertEqual(sexp.getDimensions(), lsst.geom.ExtentI(3, 4)) 

688 sexp = exp[:, -3:, afwImage.LOCAL] 

689 self.assertEqual(sexp.getDimensions(), 

690 lsst.geom.ExtentI(exp.getWidth(), 3)) 

691 self.assertEqual(sexp.maskedImage[-1, -1, afwImage.LOCAL], 

692 exp.maskedImage[-1, -1, afwImage.LOCAL]) 

693 

694 def testConversionToScalar(self): 

695 """Test that even 1-pixel Exposures can't be converted to scalars""" 

696 im = afwImage.ExposureF(10, 20) 

697 

698 # only single pixel images may be converted 

699 self.assertRaises(TypeError, float, im) 

700 # actually, can't convert (img, msk, var) to scalar 

701 self.assertRaises(TypeError, float, im[0, 0]) 

702 

703 def testReadMetadata(self): 

704 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

705 self.exposureCrWcs.getMetadata().set("FRAZZLE", True) 

706 # This will write the main metadata (inc. FRAZZLE) to the primary HDU, and the 

707 # WCS to subsequent HDUs, along with INHERIT=T. 

708 self.exposureCrWcs.writeFits(tmpFile) 

709 # This should read the first non-empty HDU (i.e. it skips the primary), but 

710 # goes back and reads it if it finds INHERIT=T. That should let us read 

711 # frazzle and the Wcs from the PropertySet returned by 

712 # testReadMetadata. 

713 md = readMetadata(tmpFile) 

714 wcs = afwGeom.makeSkyWcs(md, False) 

715 self.assertPairsAlmostEqual(wcs.getPixelOrigin(), self.wcs.getPixelOrigin()) 

716 self.assertSpherePointsAlmostEqual(wcs.getSkyOrigin(), self.wcs.getSkyOrigin()) 

717 assert_allclose(wcs.getCdMatrix(), self.wcs.getCdMatrix(), atol=1e-10) 

718 frazzle = md.getScalar("FRAZZLE") 

719 self.assertTrue(frazzle) 

720 

721 def testArchiveKeys(self): 

722 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

723 exposure1 = afwImage.ExposureF(100, 100, self.wcs) 

724 exposure1.setPsf(self.psf) 

725 exposure1.writeFits(tmpFile) 

726 exposure2 = afwImage.ExposureF(tmpFile) 

727 self.assertFalse(exposure2.getMetadata().exists("AR_ID")) 

728 self.assertFalse(exposure2.getMetadata().exists("PSF_ID")) 

729 self.assertFalse(exposure2.getMetadata().exists("WCS_ID")) 

730 

731 def testTicket2861(self): 

732 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile: 

733 exposure1 = afwImage.ExposureF(100, 100, self.wcs) 

734 exposure1.setPsf(self.psf) 

735 schema = afwTable.ExposureTable.makeMinimalSchema() 

736 coaddInputs = afwImage.CoaddInputs(schema, schema) 

737 exposure1.getInfo().setCoaddInputs(coaddInputs) 

738 exposure2 = afwImage.ExposureF(exposure1, True) 

739 self.assertIsNotNone(exposure2.getInfo().getCoaddInputs()) 

740 exposure2.writeFits(tmpFile) 

741 exposure3 = afwImage.ExposureF(tmpFile) 

742 self.assertIsNotNone(exposure3.getInfo().getCoaddInputs()) 

743 

744 def testGetCutout(self): 

745 wcs = self.smallExposure.getWcs() 

746 

747 dimensions = [lsst.geom.Extent2I(100, 50), lsst.geom.Extent2I(15, 15), lsst.geom.Extent2I(0, 10), 

748 lsst.geom.Extent2I(25, 30), lsst.geom.Extent2I(15, -5), 

749 2*self.smallExposure.getDimensions()] 

750 locations = [("center", self._getExposureCenter(self.smallExposure)), 

751 ("edge", wcs.pixelToSky(lsst.geom.Point2D(0, 0))), 

752 ("rounding test", wcs.pixelToSky(lsst.geom.Point2D(0.2, 0.7))), 

753 ("just inside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 + 1e-4, -0.5 + 1e-4))), 

754 ("just outside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 - 1e-4, -0.5 - 1e-4))), 

755 ("outside", wcs.pixelToSky(lsst.geom.Point2D(-1000, -1000)))] 

756 for cutoutSize in dimensions: 

757 for label, cutoutCenter in locations: 

758 msg = 'Cutout size = %s, location = %s' % (cutoutSize, label) 

759 if "outside" not in label and all(cutoutSize.gt(0)): 

760 cutout = self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

761 centerInPixels = wcs.skyToPixel(cutoutCenter) 

762 precision = (1 + 1e-4)*np.sqrt(0.5)*wcs.getPixelScale(centerInPixels) 

763 self._checkCutoutProperties(cutout, cutoutSize, cutoutCenter, precision, msg) 

764 self._checkCutoutPixels( 

765 cutout, 

766 self._getValidCorners(self.smallExposure.getBBox(), cutout.getBBox()), 

767 msg) 

768 

769 # Need a valid WCS 

770 with self.assertRaises(pexExcept.LogicError, msg=msg): 

771 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize) 

772 else: 

773 with self.assertRaises(pexExcept.InvalidParameterError, msg=msg): 

774 self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

775 

776 def _checkCutoutProperties(self, cutout, size, center, precision, msg): 

777 """Test whether a cutout has the desired size and position. 

778 

779 Parameters 

780 ---------- 

781 cutout : `lsst.afw.image.Exposure` 

782 The cutout to test. 

783 size : `lsst.geom.Extent2I` 

784 The expected dimensions of ``cutout``. 

785 center : `lsst.geom.SpherePoint` 

786 The expected center of ``cutout``. 

787 precision : `lsst.geom.Angle` 

788 The precision to which ``center`` must match. 

789 msg : `str` 

790 An error message suffix describing test parameters. 

791 """ 

792 newCenter = self._getExposureCenter(cutout) 

793 self.assertIsNotNone(cutout, msg=msg) 

794 self.assertSpherePointsAlmostEqual(newCenter, center, maxSep=precision, msg=msg) 

795 self.assertEqual(cutout.getWidth(), size[0], msg=msg) 

796 self.assertEqual(cutout.getHeight(), size[1], msg=msg) 

797 

798 def _checkCutoutPixels(self, cutout, validCorners, msg): 

799 """Test whether a cutout has valid/empty pixels where expected. 

800 

801 Parameters 

802 ---------- 

803 cutout : `lsst.afw.image.Exposure` 

804 The cutout to test. 

805 validCorners : iterable of `lsst.geom.Point2I` 

806 The corners of ``cutout`` that should be drawn from the original image. 

807 msg : `str` 

808 An error message suffix describing test parameters. 

809 """ 

810 mask = cutout.getMaskedImage().getMask() 

811 edgeMask = mask.getPlaneBitMask("NO_DATA") 

812 

813 for corner in cutout.getBBox().getCorners(): 

814 maskBitsSet = mask[corner] & edgeMask 

815 if corner in validCorners: 

816 self.assertEqual(maskBitsSet, 0, msg=msg) 

817 else: 

818 self.assertEqual(maskBitsSet, edgeMask, msg=msg) 

819 

820 def _getExposureCenter(self, exposure): 

821 """Return the sky coordinates of an Exposure's center. 

822 

823 Parameters 

824 ---------- 

825 exposure : `lsst.afw.image.Exposure` 

826 The image whose center is desired. 

827 

828 Returns 

829 ------- 

830 center : `lsst.geom.SpherePoint` 

831 The position at the center of ``exposure``. 

832 """ 

833 return exposure.getWcs().pixelToSky(lsst.geom.Box2D(exposure.getBBox()).getCenter()) 

834 

835 def _getValidCorners(self, imageBox, cutoutBox): 

836 """Return the corners of a cutout that are constrained by the original image. 

837 

838 Parameters 

839 ---------- 

840 imageBox: `lsst.geom.Extent2I` 

841 The bounding box of the original image. 

842 cutoutBox : `lsst.geom.Box2I` 

843 The bounding box of the cutout. 

844 

845 Returns 

846 ------- 

847 corners : iterable of `lsst.geom.Point2I` 

848 The corners that are drawn from the original image. 

849 """ 

850 return [corner for corner in cutoutBox.getCorners() if corner in imageBox] 

851 

852 

853class ExposureInfoTestCase(lsst.utils.tests.TestCase): 

854 def setUp(self): 

855 super().setUp() 

856 

857 afwImage.Filter.reset() 

858 afwImage.FilterProperty.reset() 

859 defineFilter("g", 470.0) 

860 

861 self.wcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(0.0, 0.0), 

862 lsst.geom.SpherePoint(2.0, 34.0, lsst.geom.degrees), 

863 np.identity(2), 

864 ) 

865 self.photoCalib = afwImage.PhotoCalib(1.5) 

866 self.psf = DummyPsf(2.0) 

867 self.detector = DetectorWrapper().detector 

868 self.summaryStats = afwImage.ExposureSummaryStats(ra=100.0) 

869 self.polygon = afwGeom.Polygon(lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0), 

870 lsst.geom.Point2D(25.0, 20.0))) 

871 self.coaddInputs = afwImage.CoaddInputs() 

872 self.apCorrMap = afwImage.ApCorrMap() 

873 self.transmissionCurve = afwImage.TransmissionCurve.makeIdentity() 

874 

875 self.exposureInfo = afwImage.ExposureInfo() 

876 gFilter = afwImage.Filter("g") 

877 gFilterLabel = afwImage.FilterLabel(band="g") 

878 self.exposureInfo.setFilter(gFilter) 

879 self.exposureInfo.setFilterLabel(gFilterLabel) 

880 

881 def _checkAlias(self, exposureInfo, key, value, has, get): 

882 self.assertFalse(has()) 

883 self.assertFalse(exposureInfo.hasComponent(key)) 

884 self.assertIsNone(get()) 

885 self.assertIsNone(exposureInfo.getComponent(key)) 

886 

887 self.exposureInfo.setComponent(key, value) 

888 self.assertTrue(has()) 

889 self.assertTrue(exposureInfo.hasComponent(key)) 

890 self.assertIsNotNone(get()) 

891 self.assertIsNotNone(exposureInfo.getComponent(key)) 

892 self.assertEqual(get(), value) 

893 self.assertEqual(exposureInfo.getComponent(key), value) 

894 

895 self.exposureInfo.removeComponent(key) 

896 self.assertFalse(has()) 

897 self.assertFalse(exposureInfo.hasComponent(key)) 

898 self.assertIsNone(get()) 

899 self.assertIsNone(exposureInfo.getComponent(key)) 

900 

901 def testAliases(self): 

902 cls = type(self.exposureInfo) 

903 self._checkAlias(self.exposureInfo, cls.KEY_WCS, self.wcs, 

904 self.exposureInfo.hasWcs, self.exposureInfo.getWcs) 

905 self._checkAlias(self.exposureInfo, cls.KEY_PSF, self.psf, 

906 self.exposureInfo.hasPsf, self.exposureInfo.getPsf) 

907 self._checkAlias(self.exposureInfo, cls.KEY_PHOTO_CALIB, self.photoCalib, 

908 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib) 

909 self._checkAlias(self.exposureInfo, cls.KEY_DETECTOR, self.detector, 

910 self.exposureInfo.hasDetector, self.exposureInfo.getDetector) 

911 self._checkAlias(self.exposureInfo, cls.KEY_VALID_POLYGON, self.polygon, 

912 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon) 

913 self._checkAlias(self.exposureInfo, cls.KEY_COADD_INPUTS, self.coaddInputs, 

914 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs) 

915 self._checkAlias(self.exposureInfo, cls.KEY_AP_CORR_MAP, self.apCorrMap, 

916 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap) 

917 self._checkAlias(self.exposureInfo, cls.KEY_TRANSMISSION_CURVE, self.transmissionCurve, 

918 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve) 

919 self._checkAlias(self.exposureInfo, cls.KEY_SUMMARY_STATS, self.summaryStats, 

920 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats) 

921 

922 def testCopy(self): 

923 # Test that ExposureInfos have independently settable state 

924 copy = afwImage.ExposureInfo(self.exposureInfo, True) 

925 self.assertEqual(self.exposureInfo.getWcs(), copy.getWcs()) 

926 

927 newWcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(-23.0, 8.0), 

928 lsst.geom.SpherePoint(0.0, 0.0, lsst.geom.degrees), 

929 np.identity(2), 

930 ) 

931 copy.setWcs(newWcs) 

932 self.assertEqual(copy.getWcs(), newWcs) 

933 self.assertNotEqual(self.exposureInfo.getWcs(), copy.getWcs()) 

934 

935 

936class ExposureNoAfwdataTestCase(lsst.utils.tests.TestCase): 

937 """Tests of Exposure that don't require afwdata. 

938 

939 These tests use the trivial exposures written to ``afw/tests/data``. 

940 """ 

941 def setUp(self): 

942 self.dataDir = os.path.join(os.path.split(__file__)[0], "data") 

943 

944 # Check the values below against what was written by comparing with 

945 # the code in `afw/tests/data/makeTestExposure.py` 

946 nx = ny = 10 

947 image = afwImage.ImageF(np.arange(nx*ny, dtype='f').reshape(nx, ny)) 

948 variance = afwImage.ImageF(np.ones((nx, ny), dtype='f')) 

949 mask = afwImage.MaskX(nx, ny) 

950 mask.array[5, 5] = 5 

951 self.maskedImage = afwImage.MaskedImageF(image, mask, variance) 

952 

953 self.v0PhotoCalib = afwImage.makePhotoCalibFromCalibZeroPoint(1e6, 2e4) 

954 self.v1PhotoCalib = afwImage.PhotoCalib(1e6, 2e4) 

955 self.v1FilterLabel = afwImage.FilterLabel(physical="ha") 

956 self.v2FilterLabel = afwImage.FilterLabel(band="N656", physical="ha") 

957 

958 def testReadUnversioned(self): 

959 """Test that we can read an unversioned (implicit verison 0) file. 

960 """ 

961 filename = os.path.join(self.dataDir, "exposure-noversion.fits") 

962 exposure = afwImage.ExposureF.readFits(filename) 

963 

964 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

965 

966 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib) 

967 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

968 

969 def testReadVersion0(self): 

970 """Test that we can read a version 0 file. 

971 This file should be identical to the unversioned one, except that it 

972 is marked as ExposureInfo version 0 in the header. 

973 """ 

974 filename = os.path.join(self.dataDir, "exposure-version-0.fits") 

975 exposure = afwImage.ExposureF.readFits(filename) 

976 

977 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

978 

979 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib) 

980 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

981 

982 # Check that the metadata reader parses the file correctly 

983 reader = afwImage.ExposureFitsReader(filename) 

984 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v0PhotoCalib) 

985 self.assertEqual(reader.readPhotoCalib(), self.v0PhotoCalib) 

986 

987 def testReadVersion1(self): 

988 """Test that we can read a version 1 file. 

989 Version 1 replaced Calib with PhotoCalib. 

990 """ 

991 filename = os.path.join(self.dataDir, "exposure-version-1.fits") 

992 exposure = afwImage.ExposureF.readFits(filename) 

993 

994 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

995 

996 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib) 

997 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel) 

998 

999 # Check that the metadata reader parses the file correctly 

1000 reader = afwImage.ExposureFitsReader(filename) 

1001 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib) 

1002 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib) 

1003 

1004 def testReadVersion2(self): 

1005 """Test that we can read a version 2 file. 

1006 Version 2 replaced Filter with FilterLabel. 

1007 """ 

1008 filename = os.path.join(self.dataDir, "exposure-version-2.fits") 

1009 exposure = afwImage.ExposureF.readFits(filename) 

1010 

1011 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage) 

1012 

1013 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib) 

1014 self.assertEqual(exposure.getFilterLabel(), self.v2FilterLabel) 

1015 

1016 # Check that the metadata reader parses the file correctly 

1017 reader = afwImage.ExposureFitsReader(filename) 

1018 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib) 

1019 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib) 

1020 

1021 def testExposureSummaryExtraComponents(self): 

1022 """Test that we can read an exposure summary with extra components. 

1023 """ 

1024 testDict = {'ra': 0.0, 

1025 'decl': 0.0, 

1026 'nonsense': 1.0} 

1027 bytes = yaml.dump(testDict, encoding='utf-8') 

1028 with self.assertWarns(FutureWarning): 

1029 summaryStats = lsst.afw.image.ExposureSummaryStats._read(bytes) 

1030 

1031 self.assertEqual(summaryStats.ra, testDict['ra']) 

1032 self.assertEqual(summaryStats.decl, testDict['decl']) 

1033 

1034 

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

1036 pass 

1037 

1038 

1039def setup_module(module): 

1040 lsst.utils.tests.init() 

1041 

1042 

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

1044 lsst.utils.tests.init() 

1045 unittest.main()