Coverage for tests/test_exposure.py: 11%
652 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 02:30 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 02:30 -0800
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/>.
22"""
23Test lsst.afw.image.Exposure
24"""
26import os.path
27import unittest
29import numpy as np
30from numpy.testing import assert_allclose
31import yaml
32import astropy.units as units
34import lsst.utils
35import lsst.utils.tests
36import lsst.geom
37import lsst.afw.image as afwImage
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
47Log.getLogger("lsst.afw.image.Mask").setLevel(Log.INFO)
49try:
50 dataDir = os.path.join(lsst.utils.getPackageDir("afwdata"), "data")
51except LookupError:
52 dataDir = None
53else:
54 InputMaskedImageName = "871034p_1_MI.fits"
55 InputMaskedImageNameSmall = "small_MI.fits"
56 InputImageNameSmall = "small"
57 OutputMaskedImageName = "871034p_1_MInew.fits"
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)
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 """
71 def setUp(self):
72 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
73 maskedImageMD = readMetadata(inFilePathSmall)
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.id = 42
83 self.extras = {"MISC": DummyPsf(3.5)}
85 self.exposureBlank = afwImage.ExposureF()
86 self.exposureMiOnly = afwImage.makeExposure(maskedImage)
87 self.exposureMiWcs = afwImage.makeExposure(maskedImage, self.wcs)
88 # n.b. the (100, 100, ...) form
89 self.exposureCrWcs = afwImage.ExposureF(100, 100, self.wcs)
90 # test with ExtentI(100, 100) too
91 self.exposureCrOnly = afwImage.ExposureF(lsst.geom.ExtentI(100, 100))
93 def tearDown(self):
94 del self.smallExposure
95 del self.wcs
96 del self.psf
97 del self.detector
98 del self.extras
100 del self.exposureBlank
101 del self.exposureMiOnly
102 del self.exposureMiWcs
103 del self.exposureCrWcs
104 del self.exposureCrOnly
106 def testGetMaskedImage(self):
107 """
108 Test to ensure a MaskedImage can be obtained from each
109 Exposure. An Exposure is required to have a MaskedImage,
110 therefore each of the Exposures should return a MaskedImage.
112 MaskedImage class should throw appropriate
113 lsst::pex::exceptions::NotFound if the MaskedImage can not be
114 obtained.
115 """
116 maskedImageBlank = self.exposureBlank.getMaskedImage()
117 blankWidth = maskedImageBlank.getWidth()
118 blankHeight = maskedImageBlank.getHeight()
119 if blankWidth != blankHeight != 0:
120 self.fail(f"{blankWidth} = {blankHeight} != 0")
122 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage()
123 miOnlyWidth = maskedImageMiOnly.getWidth()
124 miOnlyHeight = maskedImageMiOnly.getHeight()
125 self.assertAlmostEqual(miOnlyWidth, self.width)
126 self.assertAlmostEqual(miOnlyHeight, self.height)
128 # NOTE: Unittests for Exposures created from a MaskedImage and
129 # a WCS object are incomplete. No way to test the validity of
130 # the WCS being copied/created.
132 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage()
133 miWcsWidth = maskedImageMiWcs.getWidth()
134 miWcsHeight = maskedImageMiWcs.getHeight()
135 self.assertAlmostEqual(miWcsWidth, self.width)
136 self.assertAlmostEqual(miWcsHeight, self.height)
138 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage()
139 crWcsWidth = maskedImageCrWcs.getWidth()
140 crWcsHeight = maskedImageCrWcs.getHeight()
141 if crWcsWidth != crWcsHeight != 0:
142 self.fail(f"{crWcsWidth} != {crWcsHeight} != 0")
144 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage()
145 crOnlyWidth = maskedImageCrOnly.getWidth()
146 crOnlyHeight = maskedImageCrOnly.getHeight()
147 if crOnlyWidth != crOnlyHeight != 0:
148 self.fail(f"{crOnlyWidth} != {crOnlyHeight} != 0")
150 # Check Exposure.getWidth() returns the MaskedImage's width
151 self.assertEqual(crOnlyWidth, self.exposureCrOnly.getWidth())
152 self.assertEqual(crOnlyHeight, self.exposureCrOnly.getHeight())
153 # check width/height properties
154 self.assertEqual(crOnlyWidth, self.exposureCrOnly.width)
155 self.assertEqual(crOnlyHeight, self.exposureCrOnly.height)
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)
169 image3 = afwImage.ImageF(self.exposureMiOnly.getDimensions())
170 image3.array[:] = 3.0
171 self.exposureMiOnly.image = image3
172 self.assertImagesEqual(self.exposureMiOnly.image, image3)
174 mask3 = afwImage.MaskX(self.exposureMiOnly.getDimensions())
175 mask3.array[:] = 0x2
176 self.exposureMiOnly.mask = mask3
177 self.assertMasksEqual(self.exposureMiOnly.mask, mask3)
179 var3 = afwImage.ImageF(self.exposureMiOnly.getDimensions())
180 var3.array[:] = 2.0
181 self.exposureMiOnly.variance = var3
182 self.assertImagesEqual(self.exposureMiOnly.variance, var3)
184 # Test the property getter for a null VisitInfo.
185 self.assertIsNone(self.exposureMiOnly.visitInfo)
187 def testGetWcs(self):
188 """Test that a WCS can be obtained from each Exposure created with
189 a WCS, and that an Exposure lacking a WCS returns None.
190 """
191 # These exposures don't contain a WCS
192 self.assertIsNone(self.exposureBlank.getWcs())
193 self.assertIsNone(self.exposureMiOnly.getWcs())
194 self.assertIsNone(self.exposureCrOnly.getWcs())
196 # These exposures should contain a WCS
197 self.assertEqual(self.wcs, self.exposureMiWcs.getWcs())
198 self.assertEqual(self.wcs, self.exposureCrWcs.getWcs())
200 def testExposureInfoConstructor(self):
201 """Test the Exposure(maskedImage, exposureInfo) constructor"""
202 exposureInfo = afwImage.ExposureInfo()
203 exposureInfo.setWcs(self.wcs)
204 exposureInfo.setDetector(self.detector)
205 gFilterLabel = afwImage.FilterLabel(band="g")
206 exposureInfo.setFilter(gFilterLabel)
207 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
208 exposure = afwImage.ExposureF(maskedImage, exposureInfo)
210 self.assertTrue(exposure.hasWcs())
211 self.assertEqual(exposure.getWcs().getPixelOrigin(),
212 self.wcs.getPixelOrigin())
213 self.assertEqual(exposure.getDetector().getName(),
214 self.detector.getName())
215 self.assertEqual(exposure.getDetector().getSerial(),
216 self.detector.getSerial())
217 self.assertEqual(exposure.getFilter(), gFilterLabel)
219 self.assertTrue(exposure.getInfo().hasWcs())
220 # check the ExposureInfo property
221 self.assertTrue(exposure.info.hasWcs())
222 self.assertEqual(exposure.getInfo().getWcs().getPixelOrigin(),
223 self.wcs.getPixelOrigin())
224 self.assertEqual(exposure.getInfo().getDetector().getName(),
225 self.detector.getName())
226 self.assertEqual(exposure.getInfo().getDetector().getSerial(),
227 self.detector.getSerial())
228 self.assertEqual(exposure.getInfo().getFilter(), gFilterLabel)
230 def testNullWcs(self):
231 """Test that an Exposure constructed with second argument None is usable
233 When the exposureInfo constructor was first added, trying to get a WCS
234 or other info caused a segfault because the ExposureInfo did not exist.
235 """
236 maskedImage = self.exposureMiOnly.getMaskedImage()
237 exposure = afwImage.ExposureF(maskedImage, None)
238 self.assertFalse(exposure.hasWcs())
239 self.assertFalse(exposure.hasPsf())
241 def testExposureInfoSetNone(self):
242 exposureInfo = afwImage.ExposureInfo()
243 exposureInfo.setDetector(None)
244 exposureInfo.setValidPolygon(None)
245 exposureInfo.setPsf(None)
246 exposureInfo.setWcs(None)
247 exposureInfo.setPhotoCalib(None)
248 exposureInfo.setCoaddInputs(None)
249 exposureInfo.setVisitInfo(None)
250 exposureInfo.setApCorrMap(None)
251 for key in self.extras:
252 exposureInfo.setComponent(key, None)
254 def testSetExposureInfo(self):
255 exposureInfo = afwImage.ExposureInfo()
256 exposureInfo.setWcs(self.wcs)
257 exposureInfo.setDetector(self.detector)
258 gFilterLabel = afwImage.FilterLabel(band="g")
259 exposureInfo.setFilter(gFilterLabel)
260 exposureInfo.setId(self.id)
261 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
262 exposure = afwImage.ExposureF(maskedImage)
263 self.assertFalse(exposure.hasWcs())
265 exposure.setInfo(exposureInfo)
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(), gFilterLabel)
276 # test properties
277 self.assertEqual(exposure.detector.getName(), self.detector.getName())
278 self.assertEqual(exposure.filter, gFilterLabel)
279 self.assertEqual(exposure.wcs, self.wcs)
281 def testVisitInfoFitsPersistence(self):
282 """Test saving an exposure to FITS and reading it back in preserves (some) VisitInfo fields"""
283 exposureId = 5
284 exposureTime = 12.3
285 boresightRotAngle = 45.6 * lsst.geom.degrees
286 weather = Weather(1.1, 2.2, 0.3)
287 visitInfo = afwImage.VisitInfo(
288 exposureId=exposureId,
289 exposureTime=exposureTime,
290 boresightRotAngle=boresightRotAngle,
291 weather=weather,
292 )
293 photoCalib = afwImage.PhotoCalib(3.4, 5.6)
294 exposureInfo = afwImage.ExposureInfo()
295 exposureInfo.setVisitInfo(visitInfo)
296 exposureInfo.setPhotoCalib(photoCalib)
297 exposureInfo.setDetector(self.detector)
298 gFilterLabel = afwImage.FilterLabel(band="g")
299 exposureInfo.setFilter(gFilterLabel)
300 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
301 exposure = afwImage.ExposureF(maskedImage, exposureInfo)
302 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
303 exposure.writeFits(tmpFile)
304 rtExposure = afwImage.ExposureF(tmpFile)
305 rtVisitInfo = rtExposure.getInfo().getVisitInfo()
306 self.assertEqual(rtVisitInfo.getWeather(), weather)
307 self.assertEqual(rtExposure.getPhotoCalib(), photoCalib)
308 self.assertEqual(rtExposure.getFilter(), gFilterLabel)
310 # Test property getters.
311 self.assertEqual(rtExposure.photoCalib, photoCalib)
312 self.assertEqual(rtExposure.filter, gFilterLabel)
313 # NOTE: we can't test visitInfo equality, because most fields are NaN.
314 self.assertIsNotNone(rtExposure.visitInfo)
316 def testSetMembers(self):
317 """
318 Test that the MaskedImage and the WCS of an Exposure can be set.
319 """
320 exposure = afwImage.ExposureF()
322 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
323 exposure.setMaskedImage(maskedImage)
324 exposure.setWcs(self.wcs)
325 exposure.setDetector(self.detector)
326 exposure.setFilter(afwImage.FilterLabel(band="g"))
328 self.assertEqual(exposure.getDetector().getName(),
329 self.detector.getName())
330 self.assertEqual(exposure.getDetector().getSerial(),
331 self.detector.getSerial())
332 self.assertEqual(exposure.getFilter().bandLabel, "g")
333 self.assertEqual(exposure.getWcs(), self.wcs)
335 # The PhotoCalib tests are in test_photoCalib.py;
336 # here we just check that it's gettable and settable.
337 self.assertIsNone(exposure.getPhotoCalib())
339 photoCalib = afwImage.PhotoCalib(511.1, 44.4)
340 exposure.setPhotoCalib(photoCalib)
341 self.assertEqual(exposure.getPhotoCalib(), photoCalib)
343 # Psfs next
344 self.assertFalse(exposure.hasPsf())
345 exposure.setPsf(self.psf)
346 self.assertTrue(exposure.hasPsf())
348 exposure.setPsf(DummyPsf(1.0)) # we can reset the Psf
350 # extras next
351 info = exposure.getInfo()
352 for key, value in self.extras.items():
353 self.assertFalse(info.hasComponent(key))
354 self.assertIsNone(info.getComponent(key))
355 info.setComponent(key, value)
356 self.assertTrue(info.hasComponent(key))
357 self.assertEqual(info.getComponent(key), value)
358 info.removeComponent(key)
359 self.assertFalse(info.hasComponent(key))
361 # Test that we can set the MaskedImage and WCS of an Exposure
362 # that already has both
363 self.exposureMiWcs.setMaskedImage(maskedImage)
364 exposure.setWcs(self.wcs)
366 def testHasWcs(self):
367 """
368 Test if an Exposure has a WCS or not.
369 """
370 self.assertFalse(self.exposureBlank.hasWcs())
372 self.assertFalse(self.exposureMiOnly.hasWcs())
373 self.assertTrue(self.exposureMiWcs.hasWcs())
374 self.assertTrue(self.exposureCrWcs.hasWcs())
375 self.assertFalse(self.exposureCrOnly.hasWcs())
377 def testGetSubExposure(self):
378 """
379 Test that a subExposure of the original Exposure can be obtained.
381 The MaskedImage class should throw a
382 lsst::pex::exceptions::InvalidParameter if the requested
383 subRegion is not fully contained within the original
384 MaskedImage.
386 """
387 #
388 # This subExposure is valid
389 #
390 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50),
391 lsst.geom.Extent2I(10, 10))
392 subExposure = self.exposureCrWcs.Factory(
393 self.exposureCrWcs, subBBox, afwImage.LOCAL)
395 self.checkWcs(self.exposureCrWcs, subExposure)
397 # this subRegion is not valid and should trigger an exception
398 # from the MaskedImage class and should trigger an exception
399 # from the WCS class for the MaskedImage 871034p_1_MI.
401 subRegion3 = lsst.geom.Box2I(lsst.geom.Point2I(100, 100),
402 lsst.geom.Extent2I(10, 10))
404 def getSubRegion():
405 self.exposureCrWcs.Factory(
406 self.exposureCrWcs, subRegion3, afwImage.LOCAL)
408 self.assertRaises(pexExcept.LengthError, getSubRegion)
410 # this subRegion is not valid and should trigger an exception
411 # from the MaskedImage class only for the MaskedImage small_MI.
412 # small_MI (cols, rows) = (256, 256)
414 subRegion4 = lsst.geom.Box2I(lsst.geom.Point2I(250, 250),
415 lsst.geom.Extent2I(10, 10))
417 def getSubRegion():
418 self.exposureCrWcs.Factory(
419 self.exposureCrWcs, subRegion4, afwImage.LOCAL)
421 self.assertRaises(pexExcept.LengthError, getSubRegion)
423 # check the sub- and parent- exposures are using the same Wcs
424 # transformation
425 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50),
426 lsst.geom.Extent2I(10, 10))
427 subExposure = self.exposureCrWcs.Factory(
428 self.exposureCrWcs, subBBox, afwImage.LOCAL)
429 parentSkyPos = self.exposureCrWcs.getWcs().pixelToSky(0, 0)
431 subExpSkyPos = subExposure.getWcs().pixelToSky(0, 0)
433 self.assertSpherePointsAlmostEqual(parentSkyPos, subExpSkyPos, msg="Wcs in sub image has changed")
435 def testReadWriteFits(self):
436 """Test readFits and writeFits.
437 """
438 # This should pass without an exception
439 mainExposure = afwImage.ExposureF(inFilePathSmall)
440 mainExposure.info.setId(self.id)
441 mainExposure.setDetector(self.detector)
443 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 10),
444 lsst.geom.Extent2I(40, 50))
445 subExposure = mainExposure.Factory(
446 mainExposure, subBBox, afwImage.LOCAL)
447 self.checkWcs(mainExposure, subExposure)
448 det = subExposure.getDetector()
449 self.assertTrue(det)
451 subExposure = afwImage.ExposureF(
452 inFilePathSmall, subBBox, afwImage.LOCAL)
454 self.checkWcs(mainExposure, subExposure)
456 # This should throw an exception
457 def getExposure():
458 afwImage.ExposureF(inFilePathSmallImage)
460 self.assertRaises(FitsError, getExposure)
462 mainExposure.setPsf(self.psf)
464 # Make sure we can write without an exception
465 photoCalib = afwImage.PhotoCalib(1e-10, 1e-12)
466 mainExposure.setPhotoCalib(photoCalib)
468 mainInfo = mainExposure.getInfo()
469 for key, value in self.extras.items():
470 mainInfo.setComponent(key, value)
472 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
473 mainExposure.writeFits(tmpFile)
475 readExposure = type(mainExposure)(tmpFile)
477 #
478 # Check the round-tripping
479 #
480 self.assertIsNotNone(mainExposure.getFilter())
481 self.assertEqual(mainExposure.getFilter(),
482 readExposure.getFilter())
484 self.assertEqual(photoCalib, readExposure.getPhotoCalib())
486 readInfo = readExposure.getInfo()
487 self.assertEqual(mainExposure.info.getId(), readInfo.id)
488 for key, value in self.extras.items():
489 self.assertEqual(value, readInfo.getComponent(key))
491 psf = readExposure.getPsf()
492 self.assertIsNotNone(psf)
493 self.assertEqual(psf, self.psf)
494 # check psf property getter
495 self.assertEqual(readExposure.psf, self.psf)
497 def checkWcs(self, parentExposure, subExposure):
498 """Compare WCS at corner points of a sub-exposure and its parent exposure
499 By using the function indexToPosition, we should be able to convert the indices
500 (of the four corners (of the sub-exposure)) to positions and use the wcs
501 to get the same sky coordinates for each.
502 """
503 subMI = subExposure.getMaskedImage()
504 subDim = subMI.getDimensions()
506 # Note: pixel positions must be computed relative to XY0 when working
507 # with WCS
508 mainWcs = parentExposure.getWcs()
509 subWcs = subExposure.getWcs()
511 for xSubInd in (0, subDim.getX()-1):
512 for ySubInd in (0, subDim.getY()-1):
513 self.assertSpherePointsAlmostEqual(
514 mainWcs.pixelToSky(
515 afwImage.indexToPosition(xSubInd),
516 afwImage.indexToPosition(ySubInd),
517 ),
518 subWcs.pixelToSky(
519 afwImage.indexToPosition(xSubInd),
520 afwImage.indexToPosition(ySubInd),
521 ))
523 def cmpExposure(self, e1, e2):
524 self.assertEqual(e1.getDetector().getName(),
525 e2.getDetector().getName())
526 self.assertEqual(e1.getDetector().getSerial(),
527 e2.getDetector().getSerial())
528 self.assertEqual(e1.getFilter(), e2.getFilter())
529 xy = lsst.geom.Point2D(0, 0)
530 self.assertEqual(e1.getWcs().pixelToSky(xy)[0],
531 e2.getWcs().pixelToSky(xy)[0])
532 self.assertEqual(e1.getPhotoCalib(), e2.getPhotoCalib())
533 # check PSF identity
534 if not e1.getPsf():
535 self.assertFalse(e2.getPsf())
536 else:
537 self.assertEqual(e1.getPsf(), e2.getPsf())
538 # Check extra components
539 i1 = e1.getInfo()
540 i2 = e2.getInfo()
541 for key in self.extras:
542 self.assertEqual(i1.hasComponent(key), i2.hasComponent(key))
543 if i1.hasComponent(key):
544 self.assertEqual(i1.getComponent(key), i2.getComponent(key))
546 def testCopyExposure(self):
547 """Copy an Exposure (maybe changing type)"""
549 exposureU = afwImage.ExposureU(inFilePathSmall, allowUnsafe=True)
550 exposureU.setWcs(self.wcs)
551 exposureU.setDetector(self.detector)
552 exposureU.setFilter(afwImage.FilterLabel(band="g"))
553 exposureU.setPsf(DummyPsf(4.0))
554 infoU = exposureU.getInfo()
555 for key, value in self.extras.items():
556 infoU.setComponent(key, value)
558 exposureF = exposureU.convertF()
559 self.cmpExposure(exposureF, exposureU)
561 nexp = exposureF.Factory(exposureF, False)
562 self.cmpExposure(exposureF, nexp)
564 # Ensure that the copy was deep.
565 # (actually this test is invalid since getDetector() returns a shared_ptr)
566 # cen0 = exposureU.getDetector().getCenterPixel()
567 # x0,y0 = cen0
568 # det = exposureF.getDetector()
569 # det.setCenterPixel(lsst.geom.Point2D(999.0, 437.8))
570 # self.assertEqual(exposureU.getDetector().getCenterPixel()[0], x0)
571 # self.assertEqual(exposureU.getDetector().getCenterPixel()[1], y0)
573 def testDeepCopyData(self):
574 """Make sure a deep copy of an Exposure has its own data (ticket #2625)
575 """
576 exp = afwImage.ExposureF(6, 7)
577 mi = exp.getMaskedImage()
578 mi.getImage().set(100)
579 mi.getMask().set(5)
580 mi.getVariance().set(200)
582 expCopy = exp.clone()
583 miCopy = expCopy.getMaskedImage()
584 miCopy.getImage().set(-50)
585 miCopy.getMask().set(2)
586 miCopy.getVariance().set(175)
588 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50)
589 self.assertTrue(np.all(miCopy.getMask().getArray() == 2))
590 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175)
592 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100)
593 self.assertTrue(np.all(mi.getMask().getArray() == 5))
594 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200)
596 def testDeepCopySubData(self):
597 """Make sure a deep copy of a subregion of an Exposure has its own data (ticket #2625)
598 """
599 exp = afwImage.ExposureF(6, 7)
600 mi = exp.getMaskedImage()
601 mi.getImage().set(100)
602 mi.getMask().set(5)
603 mi.getVariance().set(200)
605 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 4))
606 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True)
607 miCopy = expCopy.getMaskedImage()
608 miCopy.getImage().set(-50)
609 miCopy.getMask().set(2)
610 miCopy.getVariance().set(175)
612 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50)
613 self.assertTrue(np.all(miCopy.getMask().getArray() == 2))
614 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175)
616 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100)
617 self.assertTrue(np.all(mi.getMask().getArray() == 5))
618 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200)
620 def testDeepCopyMetadata(self):
621 """Make sure a deep copy of an Exposure has a deep copy of metadata (ticket #2568)
622 """
623 exp = afwImage.ExposureF(10, 10)
624 expMeta = exp.getMetadata()
625 expMeta.set("foo", 5)
626 expCopy = exp.clone()
627 expCopyMeta = expCopy.getMetadata()
628 expCopyMeta.set("foo", 6)
629 self.assertEqual(expCopyMeta.getScalar("foo"), 6)
630 # this will fail if the bug is present
631 self.assertEqual(expMeta.getScalar("foo"), 5)
633 def testDeepCopySubMetadata(self):
634 """Make sure a deep copy of a subregion of an Exposure has a deep copy of metadata (ticket #2568)
635 """
636 exp = afwImage.ExposureF(10, 10)
637 expMeta = exp.getMetadata()
638 expMeta.set("foo", 5)
639 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 5))
640 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True)
641 expCopyMeta = expCopy.getMetadata()
642 expCopyMeta.set("foo", 6)
643 self.assertEqual(expCopyMeta.getScalar("foo"), 6)
644 # this will fail if the bug is present
645 self.assertEqual(expMeta.getScalar("foo"), 5)
647 def testMakeExposureLeaks(self):
648 """Test for memory leaks in makeExposure (the test is in lsst.utils.tests.MemoryTestCase)"""
649 afwImage.makeMaskedImage(afwImage.ImageU(lsst.geom.Extent2I(10, 20)))
650 afwImage.makeExposure(afwImage.makeMaskedImage(
651 afwImage.ImageU(lsst.geom.Extent2I(10, 20))))
653 def testImageSlices(self):
654 """Test image slicing, which generate sub-images using Box2I under the covers"""
655 exp = afwImage.ExposureF(10, 20)
656 mi = exp.getMaskedImage()
657 mi.image[9, 19] = 10
658 # N.b. Exposures don't support setting/getting the pixels so can't
659 # replicate e.g. Image's slice tests
660 sexp = exp[1:4, 6:10]
661 self.assertEqual(sexp.getDimensions(), lsst.geom.ExtentI(3, 4))
662 sexp = exp[:, -3:, afwImage.LOCAL]
663 self.assertEqual(sexp.getDimensions(),
664 lsst.geom.ExtentI(exp.getWidth(), 3))
665 self.assertEqual(sexp.maskedImage[-1, -1, afwImage.LOCAL],
666 exp.maskedImage[-1, -1, afwImage.LOCAL])
668 def testConversionToScalar(self):
669 """Test that even 1-pixel Exposures can't be converted to scalars"""
670 im = afwImage.ExposureF(10, 20)
672 # only single pixel images may be converted
673 self.assertRaises(TypeError, float, im)
674 # actually, can't convert (img, msk, var) to scalar
675 self.assertRaises(TypeError, float, im[0, 0])
677 def testReadMetadata(self):
678 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
679 self.exposureCrWcs.getMetadata().set("FRAZZLE", True)
680 # This will write the main metadata (inc. FRAZZLE) to the primary HDU, and the
681 # WCS to subsequent HDUs, along with INHERIT=T.
682 self.exposureCrWcs.writeFits(tmpFile)
683 # This should read the first non-empty HDU (i.e. it skips the primary), but
684 # goes back and reads it if it finds INHERIT=T. That should let us read
685 # frazzle and the Wcs from the PropertySet returned by
686 # testReadMetadata.
687 md = readMetadata(tmpFile)
688 wcs = afwGeom.makeSkyWcs(md, False)
689 self.assertPairsAlmostEqual(wcs.getPixelOrigin(), self.wcs.getPixelOrigin())
690 self.assertSpherePointsAlmostEqual(wcs.getSkyOrigin(), self.wcs.getSkyOrigin())
691 assert_allclose(wcs.getCdMatrix(), self.wcs.getCdMatrix(), atol=1e-10)
692 frazzle = md.getScalar("FRAZZLE")
693 self.assertTrue(frazzle)
695 def testArchiveKeys(self):
696 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
697 exposure1 = afwImage.ExposureF(100, 100, self.wcs)
698 exposure1.setPsf(self.psf)
699 exposure1.writeFits(tmpFile)
700 exposure2 = afwImage.ExposureF(tmpFile)
701 self.assertFalse(exposure2.getMetadata().exists("AR_ID"))
702 self.assertFalse(exposure2.getMetadata().exists("PSF_ID"))
703 self.assertFalse(exposure2.getMetadata().exists("WCS_ID"))
705 def testTicket2861(self):
706 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
707 exposure1 = afwImage.ExposureF(100, 100, self.wcs)
708 exposure1.setPsf(self.psf)
709 schema = afwTable.ExposureTable.makeMinimalSchema()
710 coaddInputs = afwImage.CoaddInputs(schema, schema)
711 exposure1.getInfo().setCoaddInputs(coaddInputs)
712 exposure2 = afwImage.ExposureF(exposure1, True)
713 self.assertIsNotNone(exposure2.getInfo().getCoaddInputs())
714 exposure2.writeFits(tmpFile)
715 exposure3 = afwImage.ExposureF(tmpFile)
716 self.assertIsNotNone(exposure3.getInfo().getCoaddInputs())
718 def testGetCutout(self):
719 wcs = self.smallExposure.getWcs()
721 dimensions = [lsst.geom.Extent2I(100, 50), lsst.geom.Extent2I(15, 15), lsst.geom.Extent2I(0, 10),
722 lsst.geom.Extent2I(25, 30), lsst.geom.Extent2I(15, -5),
723 2*self.smallExposure.getDimensions()]
724 locations = [("center", self._getExposureCenter(self.smallExposure)),
725 ("edge", wcs.pixelToSky(lsst.geom.Point2D(0, 0))),
726 ("rounding test", wcs.pixelToSky(lsst.geom.Point2D(0.2, 0.7))),
727 ("just inside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 + 1e-4, -0.5 + 1e-4))),
728 ("just outside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 - 1e-4, -0.5 - 1e-4))),
729 ("outside", wcs.pixelToSky(lsst.geom.Point2D(-1000, -1000)))]
730 for cutoutSize in dimensions:
731 for label, cutoutCenter in locations:
732 msg = 'Cutout size = %s, location = %s' % (cutoutSize, label)
733 if "outside" not in label and all(cutoutSize.gt(0)):
734 cutout = self.smallExposure.getCutout(cutoutCenter, cutoutSize)
735 centerInPixels = wcs.skyToPixel(cutoutCenter)
736 precision = (1 + 1e-4)*np.sqrt(0.5)*wcs.getPixelScale(centerInPixels)
737 self._checkCutoutProperties(cutout, cutoutSize, cutoutCenter, precision, msg)
738 self._checkCutoutPixels(
739 cutout,
740 self._getValidCorners(self.smallExposure.getBBox(), cutout.getBBox()),
741 msg)
743 # Need a valid WCS
744 with self.assertRaises(pexExcept.LogicError, msg=msg):
745 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize)
746 else:
747 with self.assertRaises(pexExcept.InvalidParameterError, msg=msg):
748 self.smallExposure.getCutout(cutoutCenter, cutoutSize)
750 def testGetConvexPolygon(self):
751 """Test the convex polygon."""
752 # Check that we do not have a convex polygon for the plain exposure.
753 self.assertIsNone(self.exposureMiOnly.convex_polygon)
755 # Check that all the points in the padded bounding box are in the polygon
756 bbox = self.exposureMiWcs.getBBox()
757 # Grow by the default padding.
758 bbox.grow(10)
759 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX(), dtype=np.float64),
760 np.arange(bbox.getBeginY(), bbox.getEndY(), dtype=np.float64))
761 wcs = self.exposureMiWcs.wcs
762 ra, dec = wcs.pixelToSkyArray(x.ravel(),
763 y.ravel())
765 poly = self.exposureMiWcs.convex_polygon
766 contains = poly.contains(ra, dec)
767 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
769 # Check that points one pixel outside of the bounding box are not in the polygon
770 bbox.grow(1)
772 ra, dec = wcs.pixelToSkyArray(
773 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100),
774 np.full(100, bbox.getBeginY()))
775 contains = poly.contains(ra, dec)
776 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
778 ra, dec = wcs.pixelToSkyArray(
779 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100),
780 np.full(100, bbox.getEndY()))
781 contains = poly.contains(ra, dec)
782 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
784 ra, dec = wcs.pixelToSkyArray(
785 np.full(100, bbox.getBeginX()),
786 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100))
787 contains = poly.contains(ra, dec)
788 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
790 ra, dec = wcs.pixelToSkyArray(
791 np.full(100, bbox.getEndX()),
792 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100))
793 contains = poly.contains(ra, dec)
794 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
796 def testContainsSkyCoords(self):
797 """Test the sky coord containment code."""
798 self.assertRaisesRegex(ValueError,
799 "Exposure does not have a valid WCS",
800 self.exposureMiOnly.containsSkyCoords,
801 0.0,
802 0.0)
804 # Check that all the points within the bounding box are contained
805 bbox = self.exposureMiWcs.getBBox()
806 x, y = np.meshgrid(np.arange(bbox.getBeginX() + 1, bbox.getEndX() - 1),
807 np.arange(bbox.getBeginY() + 1, bbox.getEndY() - 1))
808 wcs = self.exposureMiWcs.wcs
809 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64),
810 y.ravel().astype(np.float64))
812 contains = self.exposureMiWcs.containsSkyCoords(ra*units.radian,
813 dec*units.radian)
814 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
816 # Same test, everything in degrees.
817 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64),
818 y.ravel().astype(np.float64),
819 degrees=True)
821 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree,
822 dec*units.degree)
823 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
825 # Prepend and append some positions out of the box.
826 ra = np.concatenate(([300.0], ra, [180.]))
827 dec = np.concatenate(([50.0], dec, [50.0]))
829 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree,
830 dec*units.degree)
831 compare = np.ones(len(contains), dtype=bool)
832 compare[0] = False
833 compare[-1] = False
834 np.testing.assert_array_equal(contains, compare)
836 def _checkCutoutProperties(self, cutout, size, center, precision, msg):
837 """Test whether a cutout has the desired size and position.
839 Parameters
840 ----------
841 cutout : `lsst.afw.image.Exposure`
842 The cutout to test.
843 size : `lsst.geom.Extent2I`
844 The expected dimensions of ``cutout``.
845 center : `lsst.geom.SpherePoint`
846 The expected center of ``cutout``.
847 precision : `lsst.geom.Angle`
848 The precision to which ``center`` must match.
849 msg : `str`
850 An error message suffix describing test parameters.
851 """
852 newCenter = self._getExposureCenter(cutout)
853 self.assertIsNotNone(cutout, msg=msg)
854 self.assertSpherePointsAlmostEqual(newCenter, center, maxSep=precision, msg=msg)
855 self.assertEqual(cutout.getWidth(), size[0], msg=msg)
856 self.assertEqual(cutout.getHeight(), size[1], msg=msg)
858 def _checkCutoutPixels(self, cutout, validCorners, msg):
859 """Test whether a cutout has valid/empty pixels where expected.
861 Parameters
862 ----------
863 cutout : `lsst.afw.image.Exposure`
864 The cutout to test.
865 validCorners : iterable of `lsst.geom.Point2I`
866 The corners of ``cutout`` that should be drawn from the original image.
867 msg : `str`
868 An error message suffix describing test parameters.
869 """
870 mask = cutout.getMaskedImage().getMask()
871 edgeMask = mask.getPlaneBitMask("NO_DATA")
873 for corner in cutout.getBBox().getCorners():
874 maskBitsSet = mask[corner] & edgeMask
875 if corner in validCorners:
876 self.assertEqual(maskBitsSet, 0, msg=msg)
877 else:
878 self.assertEqual(maskBitsSet, edgeMask, msg=msg)
880 def _getExposureCenter(self, exposure):
881 """Return the sky coordinates of an Exposure's center.
883 Parameters
884 ----------
885 exposure : `lsst.afw.image.Exposure`
886 The image whose center is desired.
888 Returns
889 -------
890 center : `lsst.geom.SpherePoint`
891 The position at the center of ``exposure``.
892 """
893 return exposure.getWcs().pixelToSky(lsst.geom.Box2D(exposure.getBBox()).getCenter())
895 def _getValidCorners(self, imageBox, cutoutBox):
896 """Return the corners of a cutout that are constrained by the original image.
898 Parameters
899 ----------
900 imageBox: `lsst.geom.Extent2I`
901 The bounding box of the original image.
902 cutoutBox : `lsst.geom.Box2I`
903 The bounding box of the cutout.
905 Returns
906 -------
907 corners : iterable of `lsst.geom.Point2I`
908 The corners that are drawn from the original image.
909 """
910 return [corner for corner in cutoutBox.getCorners() if corner in imageBox]
913class ExposureInfoTestCase(lsst.utils.tests.TestCase):
914 def setUp(self):
915 super().setUp()
917 self.wcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(0.0, 0.0),
918 lsst.geom.SpherePoint(2.0, 34.0, lsst.geom.degrees),
919 np.identity(2),
920 )
921 self.photoCalib = afwImage.PhotoCalib(1.5)
922 self.psf = DummyPsf(2.0)
923 self.detector = DetectorWrapper().detector
924 self.summaryStats = afwImage.ExposureSummaryStats(ra=100.0)
925 self.polygon = afwGeom.Polygon(lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0),
926 lsst.geom.Point2D(25.0, 20.0)))
927 self.coaddInputs = afwImage.CoaddInputs()
928 self.apCorrMap = afwImage.ApCorrMap()
929 self.transmissionCurve = afwImage.TransmissionCurve.makeIdentity()
931 self.exposureInfo = afwImage.ExposureInfo()
932 self.gFilterLabel = afwImage.FilterLabel(band="g")
933 self.exposureId = 42
935 def _checkAlias(self, exposureInfo, key, value, has, get):
936 self.assertFalse(has())
937 self.assertFalse(exposureInfo.hasComponent(key))
938 self.assertIsNone(get())
939 self.assertIsNone(exposureInfo.getComponent(key))
941 self.exposureInfo.setComponent(key, value)
942 self.assertTrue(has())
943 self.assertTrue(exposureInfo.hasComponent(key))
944 self.assertIsNotNone(get())
945 self.assertIsNotNone(exposureInfo.getComponent(key))
946 self.assertEqual(get(), value)
947 self.assertEqual(exposureInfo.getComponent(key), value)
949 self.exposureInfo.removeComponent(key)
950 self.assertFalse(has())
951 self.assertFalse(exposureInfo.hasComponent(key))
952 self.assertIsNone(get())
953 self.assertIsNone(exposureInfo.getComponent(key))
955 def testAliases(self):
956 cls = type(self.exposureInfo)
957 self._checkAlias(self.exposureInfo, cls.KEY_WCS, self.wcs,
958 self.exposureInfo.hasWcs, self.exposureInfo.getWcs)
959 self._checkAlias(self.exposureInfo, cls.KEY_PSF, self.psf,
960 self.exposureInfo.hasPsf, self.exposureInfo.getPsf)
961 self._checkAlias(self.exposureInfo, cls.KEY_PHOTO_CALIB, self.photoCalib,
962 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib)
963 self._checkAlias(self.exposureInfo, cls.KEY_DETECTOR, self.detector,
964 self.exposureInfo.hasDetector, self.exposureInfo.getDetector)
965 self._checkAlias(self.exposureInfo, cls.KEY_VALID_POLYGON, self.polygon,
966 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon)
967 self._checkAlias(self.exposureInfo, cls.KEY_COADD_INPUTS, self.coaddInputs,
968 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs)
969 self._checkAlias(self.exposureInfo, cls.KEY_AP_CORR_MAP, self.apCorrMap,
970 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap)
971 self._checkAlias(self.exposureInfo, cls.KEY_TRANSMISSION_CURVE, self.transmissionCurve,
972 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve)
973 self._checkAlias(self.exposureInfo, cls.KEY_SUMMARY_STATS, self.summaryStats,
974 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats)
975 self._checkAlias(self.exposureInfo, cls.KEY_FILTER, self.gFilterLabel,
976 self.exposureInfo.hasFilter, self.exposureInfo.getFilter)
978 def testId(self):
979 self.exposureInfo.setVisitInfo(afwImage.VisitInfo())
981 self.assertFalse(self.exposureInfo.hasId())
982 self.assertIsNone(self.exposureInfo.getId())
983 with self.assertWarns(FutureWarning):
984 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), 0)
985 self.assertIsNone(self.exposureInfo.id)
987 self.exposureInfo.setId(self.exposureId)
988 self.assertTrue(self.exposureInfo.hasId())
989 self.assertIsNotNone(self.exposureInfo.getId())
990 self.assertIsNotNone(self.exposureInfo.id)
991 self.assertEqual(self.exposureInfo.getId(), self.exposureId)
992 with self.assertWarns(FutureWarning):
993 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), self.exposureId)
994 self.assertEqual(self.exposureInfo.id, self.exposureId)
996 self.exposureInfo.id = 99899
997 self.assertEqual(self.exposureInfo.getId(), 99899)
999 self.exposureInfo.setVisitInfo(afwImage.VisitInfo(exposureId=12321))
1000 self.assertEqual(self.exposureInfo.id, 12321)
1002 self.exposureInfo.id = None
1003 self.assertFalse(self.exposureInfo.hasId())
1004 self.assertIsNone(self.exposureInfo.getId())
1005 self.assertIsNone(self.exposureInfo.id)
1007 def testCopy(self):
1008 # Test that ExposureInfos have independently settable state
1009 copy = afwImage.ExposureInfo(self.exposureInfo, True)
1010 self.assertEqual(self.exposureInfo.getWcs(), copy.getWcs())
1012 newWcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(-23.0, 8.0),
1013 lsst.geom.SpherePoint(0.0, 0.0, lsst.geom.degrees),
1014 np.identity(2),
1015 )
1016 copy.setWcs(newWcs)
1017 self.assertEqual(copy.getWcs(), newWcs)
1018 self.assertNotEqual(self.exposureInfo.getWcs(), copy.getWcs())
1020 def testMissingProperties(self):
1021 # Test that invalid properties return None instead of raising
1022 exposureInfo = afwImage.ExposureInfo()
1024 self.assertIsNone(exposureInfo.id)
1027class ExposureNoAfwdataTestCase(lsst.utils.tests.TestCase):
1028 """Tests of Exposure that don't require afwdata.
1030 These tests use the trivial exposures written to ``afw/tests/data``.
1031 """
1032 def setUp(self):
1033 self.dataDir = os.path.join(os.path.split(__file__)[0], "data")
1035 # Check the values below against what was written by comparing with
1036 # the code in `afw/tests/data/makeTestExposure.py`
1037 nx = ny = 10
1038 image = afwImage.ImageF(np.arange(nx*ny, dtype='f').reshape(nx, ny))
1039 variance = afwImage.ImageF(np.ones((nx, ny), dtype='f'))
1040 mask = afwImage.MaskX(nx, ny)
1041 mask.array[5, 5] = 5
1042 self.maskedImage = afwImage.MaskedImageF(image, mask, variance)
1043 self.exposureId = 12345
1045 self.v0PhotoCalib = afwImage.makePhotoCalibFromCalibZeroPoint(1e6, 2e4)
1046 self.v1PhotoCalib = afwImage.PhotoCalib(1e6, 2e4)
1047 self.v1FilterLabel = afwImage.FilterLabel(physical="ha")
1048 self.v2FilterLabel = afwImage.FilterLabel(band="N656", physical="ha")
1050 def testReadUnversioned(self):
1051 """Test that we can read an unversioned (implicit verison 0) file.
1052 """
1053 filename = os.path.join(self.dataDir, "exposure-noversion.fits")
1054 exposure = afwImage.ExposureF.readFits(filename)
1056 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1058 self.assertEqual(exposure.info.id, self.exposureId)
1059 with self.assertWarns(FutureWarning):
1060 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1061 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib)
1062 self.assertEqual(exposure.getFilter(), self.v1FilterLabel)
1064 def testReadVersion0(self):
1065 """Test that we can read a version 0 file.
1066 This file should be identical to the unversioned one, except that it
1067 is marked as ExposureInfo version 0 in the header.
1068 """
1069 filename = os.path.join(self.dataDir, "exposure-version-0.fits")
1070 exposure = afwImage.ExposureF.readFits(filename)
1072 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1074 self.assertEqual(exposure.info.id, self.exposureId)
1075 with self.assertWarns(FutureWarning):
1076 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1077 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib)
1078 self.assertEqual(exposure.getFilter(), self.v1FilterLabel)
1080 # Check that the metadata reader parses the file correctly
1081 reader = afwImage.ExposureFitsReader(filename)
1082 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v0PhotoCalib)
1083 self.assertEqual(reader.readPhotoCalib(), self.v0PhotoCalib)
1085 def testReadVersion1(self):
1086 """Test that we can read a version 1 file.
1087 Version 1 replaced Calib with PhotoCalib.
1088 """
1089 filename = os.path.join(self.dataDir, "exposure-version-1.fits")
1090 exposure = afwImage.ExposureF.readFits(filename)
1092 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1094 self.assertEqual(exposure.info.id, self.exposureId)
1095 with self.assertWarns(FutureWarning):
1096 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1097 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib)
1098 self.assertEqual(exposure.getFilter(), self.v1FilterLabel)
1100 # Check that the metadata reader parses the file correctly
1101 reader = afwImage.ExposureFitsReader(filename)
1102 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib)
1103 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib)
1105 def testReadVersion2(self):
1106 """Test that we can read a version 2 file.
1107 Version 2 replaced Filter with FilterLabel.
1108 """
1109 filename = os.path.join(self.dataDir, "exposure-version-2.fits")
1110 exposure = afwImage.ExposureF.readFits(filename)
1112 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1114 self.assertEqual(exposure.info.id, self.exposureId)
1115 with self.assertWarns(FutureWarning):
1116 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1117 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib)
1118 self.assertEqual(exposure.getFilter(), self.v2FilterLabel)
1120 # Check that the metadata reader parses the file correctly
1121 reader = afwImage.ExposureFitsReader(filename)
1122 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib)
1123 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib)
1125 def testExposureSummaryExtraComponents(self):
1126 """Test that we can read an exposure summary with extra components.
1127 """
1128 testDict = {'ra': 0.0,
1129 'decl': 0.0,
1130 'nonsense': 1.0}
1131 bytes = yaml.dump(testDict, encoding='utf-8')
1132 with self.assertWarns(FutureWarning):
1133 summaryStats = lsst.afw.image.ExposureSummaryStats._read(bytes)
1135 self.assertEqual(summaryStats.ra, testDict['ra'])
1136 self.assertEqual(summaryStats.decl, testDict['decl'])
1139class MemoryTester(lsst.utils.tests.MemoryTestCase):
1140 pass
1143def setup_module(module):
1144 lsst.utils.tests.init()
1147if __name__ == "__main__": 1147 ↛ 1148line 1147 didn't jump to line 1148, because the condition on line 1147 was never true
1148 lsst.utils.tests.init()
1149 unittest.main()