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 

31 

32import lsst.utils 

33import lsst.utils.tests 

34import lsst.geom 

35import lsst.afw.image as afwImage 

36from lsst.afw.image.utils import defineFilter 

37from lsst.afw.coord import Weather 

38import lsst.afw.geom as afwGeom 

39import lsst.afw.table as afwTable 

40import lsst.pex.exceptions as pexExcept 

41from lsst.afw.fits import readMetadata, FitsError 

42from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

43from lsst.log import Log 

44from testTableArchivesLib import DummyPsf 

45 

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

47 

48try: 

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

50except pexExcept.NotFoundError: 

51 dataDir = None 

52else: 

53 InputMaskedImageName = "871034p_1_MI.fits" 

54 InputMaskedImageNameSmall = "small_MI.fits" 

55 InputImageNameSmall = "small" 

56 OutputMaskedImageName = "871034p_1_MInew.fits" 

57 

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

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

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

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

62 

63 

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

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

66 """ 

67 A test case for the Exposure Class 

68 """ 

69 

70 def setUp(self): 

71 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

72 maskedImageMD = readMetadata(inFilePathSmall) 

73 

74 self.smallExposure = afwImage.ExposureF(inFilePathSmall) 

75 self.width = maskedImage.getWidth() 

76 self.height = maskedImage.getHeight() 

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

78 self.md = maskedImageMD 

79 self.psf = DummyPsf(2.0) 

80 self.detector = DetectorWrapper().detector 

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

82 

83 self.exposureBlank = afwImage.ExposureF() 

84 self.exposureMiOnly = afwImage.makeExposure(maskedImage) 

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

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

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

88 # test with ExtentI(100, 100) too 

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

90 

91 afwImage.Filter.reset() 

92 afwImage.FilterProperty.reset() 

93 

94 defineFilter("g", 470.0) 

95 

96 def tearDown(self): 

97 del self.smallExposure 

98 del self.wcs 

99 del self.psf 

100 del self.detector 

101 del self.extras 

102 

103 del self.exposureBlank 

104 del self.exposureMiOnly 

105 del self.exposureMiWcs 

106 del self.exposureCrWcs 

107 del self.exposureCrOnly 

108 

109 def testGetMaskedImage(self): 

110 """ 

111 Test to ensure a MaskedImage can be obtained from each 

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

113 therefore each of the Exposures should return a MaskedImage. 

114 

115 MaskedImage class should throw appropriate 

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

117 obtained. 

118 """ 

119 maskedImageBlank = self.exposureBlank.getMaskedImage() 

120 blankWidth = maskedImageBlank.getWidth() 

121 blankHeight = maskedImageBlank.getHeight() 

122 if blankWidth != blankHeight != 0: 

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

124 

125 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage() 

126 miOnlyWidth = maskedImageMiOnly.getWidth() 

127 miOnlyHeight = maskedImageMiOnly.getHeight() 

128 self.assertAlmostEqual(miOnlyWidth, self.width) 

129 self.assertAlmostEqual(miOnlyHeight, self.height) 

130 

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

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

133 # the WCS being copied/created. 

134 

135 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage() 

136 miWcsWidth = maskedImageMiWcs.getWidth() 

137 miWcsHeight = maskedImageMiWcs.getHeight() 

138 self.assertAlmostEqual(miWcsWidth, self.width) 

139 self.assertAlmostEqual(miWcsHeight, self.height) 

140 

141 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage() 

142 crWcsWidth = maskedImageCrWcs.getWidth() 

143 crWcsHeight = maskedImageCrWcs.getHeight() 

144 if crWcsWidth != crWcsHeight != 0: 

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

146 

147 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage() 

148 crOnlyWidth = maskedImageCrOnly.getWidth() 

149 crOnlyHeight = maskedImageCrOnly.getHeight() 

150 if crOnlyWidth != crOnlyHeight != 0: 

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

152 

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

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

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

156 

157 def testProperties(self): 

158 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, 

159 self.exposureMiOnly.getMaskedImage()) 

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

161 mi2.image.array[:] = 5.0 

162 mi2.variance.array[:] = 3.0 

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

164 self.exposureMiOnly.maskedImage = mi2 

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

166 self.assertImagesEqual(self.exposureMiOnly.image, 

167 self.exposureMiOnly.maskedImage.image) 

168 

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

170 image3.array[:] = 3.0 

171 self.exposureMiOnly.image = image3 

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

173 

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

175 mask3.array[:] = 0x2 

176 self.exposureMiOnly.mask = mask3 

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

178 

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

180 var3.array[:] = 2.0 

181 self.exposureMiOnly.variance = var3 

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

183 

184 def testGetWcs(self): 

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

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

187 """ 

188 # These exposures don't contain a WCS 

189 self.assertIsNone(self.exposureBlank.getWcs()) 

190 self.assertIsNone(self.exposureMiOnly.getWcs()) 

191 self.assertIsNone(self.exposureCrOnly.getWcs()) 

192 

193 # These exposures should contain a WCS 

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

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

196 

197 def testExposureInfoConstructor(self): 

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

199 exposureInfo = afwImage.ExposureInfo() 

200 exposureInfo.setWcs(self.wcs) 

201 exposureInfo.setDetector(self.detector) 

202 gFilter = afwImage.Filter("g") 

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

204 exposureInfo.setFilter(gFilter) 

205 exposureInfo.setFilterLabel(gFilterLabel) 

206 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

207 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

208 

209 self.assertTrue(exposure.hasWcs()) 

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

211 self.wcs.getPixelOrigin()) 

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

213 self.detector.getName()) 

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

215 self.detector.getSerial()) 

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

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

218 

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

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

221 self.wcs.getPixelOrigin()) 

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

223 self.detector.getName()) 

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

225 self.detector.getSerial()) 

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

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

228 

229 def testNullWcs(self): 

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

231 

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

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

234 """ 

235 maskedImage = self.exposureMiOnly.getMaskedImage() 

236 exposure = afwImage.ExposureF(maskedImage, None) 

237 self.assertFalse(exposure.hasWcs()) 

238 self.assertFalse(exposure.hasPsf()) 

239 

240 def testExposureInfoSetNone(self): 

241 exposureInfo = afwImage.ExposureInfo() 

242 exposureInfo.setDetector(None) 

243 exposureInfo.setValidPolygon(None) 

244 exposureInfo.setPsf(None) 

245 exposureInfo.setWcs(None) 

246 exposureInfo.setPhotoCalib(None) 

247 exposureInfo.setCoaddInputs(None) 

248 exposureInfo.setVisitInfo(None) 

249 exposureInfo.setApCorrMap(None) 

250 for key in self.extras: 

251 exposureInfo.setComponent(key, None) 

252 

253 def testSetExposureInfo(self): 

254 exposureInfo = afwImage.ExposureInfo() 

255 exposureInfo.setWcs(self.wcs) 

256 exposureInfo.setDetector(self.detector) 

257 gFilter = afwImage.Filter("g") 

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

259 exposureInfo.setFilter(gFilter) 

260 exposureInfo.setFilterLabel(gFilterLabel) 

261 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

262 exposure = afwImage.ExposureF(maskedImage) 

263 self.assertFalse(exposure.hasWcs()) 

264 

265 exposure.setInfo(exposureInfo) 

266 

267 self.assertTrue(exposure.hasWcs()) 

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

269 self.wcs.getPixelOrigin()) 

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

271 self.detector.getName()) 

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

273 self.detector.getSerial()) 

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

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

276 

277 def testDefaultFilter(self): 

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

279 """ 

280 exposureInfo = afwImage.ExposureInfo() 

281 noFilter = afwImage.Filter() 

282 exposureInfo.setFilter(noFilter) 

283 self.assertFalse(exposureInfo.hasFilterLabel()) 

284 self.assertIsNone(exposureInfo.getFilterLabel()) 

285 

286 def testVisitInfoFitsPersistence(self): 

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

288 exposureId = 5 

289 exposureTime = 12.3 

290 boresightRotAngle = 45.6 * lsst.geom.degrees 

291 weather = Weather(1.1, 2.2, 0.3) 

292 visitInfo = afwImage.VisitInfo( 

293 exposureId=exposureId, 

294 exposureTime=exposureTime, 

295 boresightRotAngle=boresightRotAngle, 

296 weather=weather, 

297 ) 

298 photoCalib = afwImage.PhotoCalib(3.4, 5.6) 

299 exposureInfo = afwImage.ExposureInfo() 

300 exposureInfo.setVisitInfo(visitInfo) 

301 exposureInfo.setPhotoCalib(photoCalib) 

302 exposureInfo.setDetector(self.detector) 

303 gFilter = afwImage.Filter("g") 

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

305 exposureInfo.setFilter(gFilter) 

306 exposureInfo.setFilterLabel(gFilterLabel) 

307 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

308 exposure = afwImage.ExposureF(maskedImage, exposureInfo) 

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

310 exposure.writeFits(tmpFile) 

311 rtExposure = afwImage.ExposureF(tmpFile) 

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

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

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

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

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

317 

318 def testSetMembers(self): 

319 """ 

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

321 """ 

322 exposure = afwImage.ExposureF() 

323 

324 maskedImage = afwImage.MaskedImageF(inFilePathSmall) 

325 exposure.setMaskedImage(maskedImage) 

326 exposure.setWcs(self.wcs) 

327 exposure.setDetector(self.detector) 

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

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

330 

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

332 self.detector.getName()) 

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

334 self.detector.getSerial()) 

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

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

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

338 

339 # The PhotoCalib tests are in test_photoCalib.py; 

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

341 self.assertIsNone(exposure.getPhotoCalib()) 

342 

343 photoCalib = afwImage.PhotoCalib(511.1, 44.4) 

344 exposure.setPhotoCalib(photoCalib) 

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

346 

347 # Psfs next 

348 self.assertFalse(exposure.hasPsf()) 

349 exposure.setPsf(self.psf) 

350 self.assertTrue(exposure.hasPsf()) 

351 

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

353 

354 # extras next 

355 info = exposure.getInfo() 

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

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

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

359 info.setComponent(key, value) 

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

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

362 info.removeComponent(key) 

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

364 

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

366 # that already has both 

367 self.exposureMiWcs.setMaskedImage(maskedImage) 

368 exposure.setWcs(self.wcs) 

369 

370 def testHasWcs(self): 

371 """ 

372 Test if an Exposure has a WCS or not. 

373 """ 

374 self.assertFalse(self.exposureBlank.hasWcs()) 

375 

376 self.assertFalse(self.exposureMiOnly.hasWcs()) 

377 self.assertTrue(self.exposureMiWcs.hasWcs()) 

378 self.assertTrue(self.exposureCrWcs.hasWcs()) 

379 self.assertFalse(self.exposureCrOnly.hasWcs()) 

380 

381 def testGetSubExposure(self): 

382 """ 

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

384 

385 The MaskedImage class should throw a 

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

387 subRegion is not fully contained within the original 

388 MaskedImage. 

389 

390 """ 

391 # 

392 # This subExposure is valid 

393 # 

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

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

396 subExposure = self.exposureCrWcs.Factory( 

397 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

398 

399 self.checkWcs(self.exposureCrWcs, subExposure) 

400 

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

402 # from the MaskedImage class and should trigger an exception 

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

404 

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

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

407 

408 def getSubRegion(): 

409 self.exposureCrWcs.Factory( 

410 self.exposureCrWcs, subRegion3, afwImage.LOCAL) 

411 

412 self.assertRaises(pexExcept.LengthError, getSubRegion) 

413 

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

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

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

417 

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

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

420 

421 def getSubRegion(): 

422 self.exposureCrWcs.Factory( 

423 self.exposureCrWcs, subRegion4, afwImage.LOCAL) 

424 

425 self.assertRaises(pexExcept.LengthError, getSubRegion) 

426 

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

428 # transformation 

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

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

431 subExposure = self.exposureCrWcs.Factory( 

432 self.exposureCrWcs, subBBox, afwImage.LOCAL) 

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

434 

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

436 

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

438 

439 def testReadWriteFits(self): 

440 """Test readFits and writeFits. 

441 """ 

442 # This should pass without an exception 

443 mainExposure = afwImage.ExposureF(inFilePathSmall) 

444 mainExposure.setDetector(self.detector) 

445 

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

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

448 subExposure = mainExposure.Factory( 

449 mainExposure, subBBox, afwImage.LOCAL) 

450 self.checkWcs(mainExposure, subExposure) 

451 det = subExposure.getDetector() 

452 self.assertTrue(det) 

453 

454 subExposure = afwImage.ExposureF( 

455 inFilePathSmall, subBBox, afwImage.LOCAL) 

456 

457 self.checkWcs(mainExposure, subExposure) 

458 

459 # This should throw an exception 

460 def getExposure(): 

461 afwImage.ExposureF(inFilePathSmallImage) 

462 

463 self.assertRaises(FitsError, getExposure) 

464 

465 mainExposure.setPsf(self.psf) 

466 

467 # Make sure we can write without an exception 

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

469 mainExposure.setPhotoCalib(photoCalib) 

470 

471 mainInfo = mainExposure.getInfo() 

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

473 mainInfo.setComponent(key, value) 

474 

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

476 mainExposure.writeFits(tmpFile) 

477 

478 readExposure = type(mainExposure)(tmpFile) 

479 

480 # 

481 # Check the round-tripping 

482 # 

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

484 readExposure.getFilter().getName()) 

485 self.assertIsNotNone(mainExposure.getFilterLabel()) 

486 self.assertEqual(mainExposure.getFilterLabel(), 

487 readExposure.getFilterLabel()) 

488 

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

490 

491 readInfo = readExposure.getInfo() 

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

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

494 

495 psf = readExposure.getPsf() 

496 self.assertIsNotNone(psf) 

497 self.assertEqual(psf, self.psf) 

498 

499 def checkWcs(self, parentExposure, subExposure): 

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

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

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

503 to get the same sky coordinates for each. 

504 """ 

505 subMI = subExposure.getMaskedImage() 

506 subDim = subMI.getDimensions() 

507 

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

509 # with WCS 

510 mainWcs = parentExposure.getWcs() 

511 subWcs = subExposure.getWcs() 

512 

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

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

515 self.assertSpherePointsAlmostEqual( 

516 mainWcs.pixelToSky( 

517 afwImage.indexToPosition(xSubInd), 

518 afwImage.indexToPosition(ySubInd), 

519 ), 

520 subWcs.pixelToSky( 

521 afwImage.indexToPosition(xSubInd), 

522 afwImage.indexToPosition(ySubInd), 

523 )) 

524 

525 def cmpExposure(self, e1, e2): 

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

527 e2.getDetector().getName()) 

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

529 e2.getDetector().getSerial()) 

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

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

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

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

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

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

536 # check PSF identity 

537 if not e1.getPsf(): 

538 self.assertFalse(e2.getPsf()) 

539 else: 

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

541 # Check extra components 

542 i1 = e1.getInfo() 

543 i2 = e2.getInfo() 

544 for key in self.extras: 

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

546 if i1.hasComponent(key): 

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

548 

549 def testCopyExposure(self): 

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

551 

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

553 exposureU.setWcs(self.wcs) 

554 exposureU.setDetector(self.detector) 

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

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

557 exposureU.setPsf(DummyPsf(4.0)) 

558 infoU = exposureU.getInfo() 

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

560 infoU.setComponent(key, value) 

561 

562 exposureF = exposureU.convertF() 

563 self.cmpExposure(exposureF, exposureU) 

564 

565 nexp = exposureF.Factory(exposureF, False) 

566 self.cmpExposure(exposureF, nexp) 

567 

568 # Ensure that the copy was deep. 

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

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

571 # x0,y0 = cen0 

572 # det = exposureF.getDetector() 

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

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

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

576 

577 def testDeepCopyData(self): 

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

579 """ 

580 exp = afwImage.ExposureF(6, 7) 

581 mi = exp.getMaskedImage() 

582 mi.getImage().set(100) 

583 mi.getMask().set(5) 

584 mi.getVariance().set(200) 

585 

586 expCopy = exp.clone() 

587 miCopy = expCopy.getMaskedImage() 

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

589 miCopy.getMask().set(2) 

590 miCopy.getVariance().set(175) 

591 

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

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

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

595 

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

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

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

599 

600 def testDeepCopySubData(self): 

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

602 """ 

603 exp = afwImage.ExposureF(6, 7) 

604 mi = exp.getMaskedImage() 

605 mi.getImage().set(100) 

606 mi.getMask().set(5) 

607 mi.getVariance().set(200) 

608 

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

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

611 miCopy = expCopy.getMaskedImage() 

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

613 miCopy.getMask().set(2) 

614 miCopy.getVariance().set(175) 

615 

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

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

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

619 

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

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

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

623 

624 def testDeepCopyMetadata(self): 

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

626 """ 

627 exp = afwImage.ExposureF(10, 10) 

628 expMeta = exp.getMetadata() 

629 expMeta.set("foo", 5) 

630 expCopy = exp.clone() 

631 expCopyMeta = expCopy.getMetadata() 

632 expCopyMeta.set("foo", 6) 

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

634 # this will fail if the bug is present 

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

636 

637 def testDeepCopySubMetadata(self): 

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

639 """ 

640 exp = afwImage.ExposureF(10, 10) 

641 expMeta = exp.getMetadata() 

642 expMeta.set("foo", 5) 

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

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

645 expCopyMeta = expCopy.getMetadata() 

646 expCopyMeta.set("foo", 6) 

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

648 # this will fail if the bug is present 

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

650 

651 def testMakeExposureLeaks(self): 

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

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

654 afwImage.makeExposure(afwImage.makeMaskedImage( 

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

656 

657 def testImageSlices(self): 

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

659 exp = afwImage.ExposureF(10, 20) 

660 mi = exp.getMaskedImage() 

661 mi.image[9, 19] = 10 

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

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

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

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

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

667 self.assertEqual(sexp.getDimensions(), 

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

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

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

671 

672 def testConversionToScalar(self): 

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

674 im = afwImage.ExposureF(10, 20) 

675 

676 # only single pixel images may be converted 

677 self.assertRaises(TypeError, float, im) 

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

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

680 

681 def testReadMetadata(self): 

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

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

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

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

686 self.exposureCrWcs.writeFits(tmpFile) 

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

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

689 # frazzle and the Wcs from the PropertySet returned by 

690 # testReadMetadata. 

691 md = readMetadata(tmpFile) 

692 wcs = afwGeom.makeSkyWcs(md, False) 

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

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

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

696 frazzle = md.getScalar("FRAZZLE") 

697 self.assertTrue(frazzle) 

698 

699 def testArchiveKeys(self): 

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

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

702 exposure1.setPsf(self.psf) 

703 exposure1.writeFits(tmpFile) 

704 exposure2 = afwImage.ExposureF(tmpFile) 

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

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

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

708 

709 def testTicket2861(self): 

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

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

712 exposure1.setPsf(self.psf) 

713 schema = afwTable.ExposureTable.makeMinimalSchema() 

714 coaddInputs = afwImage.CoaddInputs(schema, schema) 

715 exposure1.getInfo().setCoaddInputs(coaddInputs) 

716 exposure2 = afwImage.ExposureF(exposure1, True) 

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

718 exposure2.writeFits(tmpFile) 

719 exposure3 = afwImage.ExposureF(tmpFile) 

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

721 

722 def testGetCutout(self): 

723 wcs = self.smallExposure.getWcs() 

724 

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

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

727 2*self.smallExposure.getDimensions()] 

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

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

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

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

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

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

734 for cutoutSize in dimensions: 

735 for label, cutoutCenter in locations: 

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

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

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

739 centerInPixels = wcs.skyToPixel(cutoutCenter) 

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

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

742 self._checkCutoutPixels( 

743 cutout, 

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

745 msg) 

746 

747 # Need a valid WCS 

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

749 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize) 

750 else: 

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

752 self.smallExposure.getCutout(cutoutCenter, cutoutSize) 

753 

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

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

756 

757 Parameters 

758 ---------- 

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

760 The cutout to test. 

761 size : `lsst.geom.Extent2I` 

762 The expected dimensions of ``cutout``. 

763 center : `lsst.geom.SpherePoint` 

764 The expected center of ``cutout``. 

765 precision : `lsst.geom.Angle` 

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

767 msg : `str` 

768 An error message suffix describing test parameters. 

769 """ 

770 newCenter = self._getExposureCenter(cutout) 

771 self.assertIsNotNone(cutout, msg=msg) 

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

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

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

775 

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

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

778 

779 Parameters 

780 ---------- 

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

782 The cutout to test. 

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

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

785 msg : `str` 

786 An error message suffix describing test parameters. 

787 """ 

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

789 edgeMask = mask.getPlaneBitMask("NO_DATA") 

790 

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

792 maskBitsSet = mask[corner] & edgeMask 

793 if corner in validCorners: 

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

795 else: 

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

797 

798 def _getExposureCenter(self, exposure): 

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

800 

801 Parameters 

802 ---------- 

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

804 The image whose center is desired. 

805 

806 Returns 

807 ------- 

808 center : `lsst.geom.SpherePoint` 

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

810 """ 

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

812 

813 def _getValidCorners(self, imageBox, cutoutBox): 

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

815 

816 Parameters 

817 ---------- 

818 imageBox: `lsst.geom.Extent2I` 

819 The bounding box of the original image. 

820 cutoutBox : `lsst.geom.Box2I` 

821 The bounding box of the cutout. 

822 

823 Returns 

824 ------- 

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

826 The corners that are drawn from the original image. 

827 """ 

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

829 

830 

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

832 def setUp(self): 

833 super().setUp() 

834 

835 afwImage.Filter.reset() 

836 afwImage.FilterProperty.reset() 

837 defineFilter("g", 470.0) 

838 

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

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

841 np.identity(2), 

842 ) 

843 self.photoCalib = afwImage.PhotoCalib(1.5) 

844 self.psf = DummyPsf(2.0) 

845 self.detector = DetectorWrapper().detector 

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

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

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

849 self.coaddInputs = afwImage.CoaddInputs() 

850 self.apCorrMap = afwImage.ApCorrMap() 

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

852 

853 self.exposureInfo = afwImage.ExposureInfo() 

854 gFilter = afwImage.Filter("g") 

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

856 self.exposureInfo.setFilter(gFilter) 

857 self.exposureInfo.setFilterLabel(gFilterLabel) 

858 

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

860 self.assertFalse(has()) 

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

862 self.assertIsNone(get()) 

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

864 

865 self.exposureInfo.setComponent(key, value) 

866 self.assertTrue(has()) 

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

868 self.assertIsNotNone(get()) 

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

870 self.assertEqual(get(), value) 

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

872 

873 self.exposureInfo.removeComponent(key) 

874 self.assertFalse(has()) 

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

876 self.assertIsNone(get()) 

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

878 

879 def testAliases(self): 

880 cls = type(self.exposureInfo) 

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

882 self.exposureInfo.hasWcs, self.exposureInfo.getWcs) 

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

884 self.exposureInfo.hasPsf, self.exposureInfo.getPsf) 

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

886 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib) 

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

888 self.exposureInfo.hasDetector, self.exposureInfo.getDetector) 

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

890 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon) 

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

892 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs) 

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

894 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap) 

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

896 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve) 

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

898 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats) 

899 

900 def testCopy(self): 

901 # Test that ExposureInfos have independently settable state 

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

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

904 

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

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

907 np.identity(2), 

908 ) 

909 copy.setWcs(newWcs) 

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

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

912 

913 

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

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

916 

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

918 """ 

919 def setUp(self): 

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

921 

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

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

924 nx = ny = 10 

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

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

927 mask = afwImage.MaskX(nx, ny) 

928 mask.array[5, 5] = 5 

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

930 

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

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

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

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

935 

936 def testReadUnversioned(self): 

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

938 """ 

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

940 exposure = afwImage.ExposureF.readFits(filename) 

941 

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

943 

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

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

946 

947 def testReadVersion0(self): 

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

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

950 is marked as ExposureInfo version 0 in the header. 

951 """ 

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

953 exposure = afwImage.ExposureF.readFits(filename) 

954 

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

956 

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

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

959 

960 # Check that the metadata reader parses the file correctly 

961 reader = afwImage.ExposureFitsReader(filename) 

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

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

964 

965 def testReadVersion1(self): 

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

967 Version 1 replaced Calib with PhotoCalib. 

968 """ 

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

970 exposure = afwImage.ExposureF.readFits(filename) 

971 

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

973 

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

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

976 

977 # Check that the metadata reader parses the file correctly 

978 reader = afwImage.ExposureFitsReader(filename) 

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

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

981 

982 def testReadVersion2(self): 

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

984 Version 2 replaced Filter with FilterLabel. 

985 """ 

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

987 exposure = afwImage.ExposureF.readFits(filename) 

988 

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

990 

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

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

993 

994 # Check that the metadata reader parses the file correctly 

995 reader = afwImage.ExposureFitsReader(filename) 

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

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

998 

999 

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

1001 pass 

1002 

1003 

1004def setup_module(module): 

1005 lsst.utils.tests.init() 

1006 

1007 

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

1009 lsst.utils.tests.init() 

1010 unittest.main()