Coverage for tests/test_exposure.py: 12%
680 statements
« prev ^ index » next coverage.py v6.4, created at 2022-06-02 03:42 -0700
« prev ^ index » next coverage.py v6.4, created at 2022-06-02 03:42 -0700
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.image.utils import defineFilter
39from lsst.afw.coord import Weather
40import lsst.afw.geom as afwGeom
41import lsst.afw.table as afwTable
42import lsst.pex.exceptions as pexExcept
43from lsst.afw.fits import readMetadata, FitsError
44from lsst.afw.cameraGeom.testUtils import DetectorWrapper
45from lsst.log import Log
46from testTableArchivesLib import DummyPsf
48Log.getLogger("lsst.afw.image.Mask").setLevel(Log.INFO)
50try:
51 dataDir = os.path.join(lsst.utils.getPackageDir("afwdata"), "data")
52except LookupError:
53 dataDir = None
54else:
55 InputMaskedImageName = "871034p_1_MI.fits"
56 InputMaskedImageNameSmall = "small_MI.fits"
57 InputImageNameSmall = "small"
58 OutputMaskedImageName = "871034p_1_MInew.fits"
60 currDir = os.path.abspath(os.path.dirname(__file__))
61 inFilePath = os.path.join(dataDir, InputMaskedImageName)
62 inFilePathSmall = os.path.join(dataDir, InputMaskedImageNameSmall)
63 inFilePathSmallImage = os.path.join(dataDir, InputImageNameSmall)
66@unittest.skipIf(dataDir is None, "afwdata not setup")
67class ExposureTestCase(lsst.utils.tests.TestCase):
68 """
69 A test case for the Exposure Class
70 """
72 def setUp(self):
73 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
74 maskedImageMD = readMetadata(inFilePathSmall)
76 self.smallExposure = afwImage.ExposureF(inFilePathSmall)
77 self.width = maskedImage.getWidth()
78 self.height = maskedImage.getHeight()
79 self.wcs = afwGeom.makeSkyWcs(maskedImageMD, False)
80 self.md = maskedImageMD
81 self.psf = DummyPsf(2.0)
82 self.detector = DetectorWrapper().detector
83 self.id = 42
84 self.extras = {"MISC": DummyPsf(3.5)}
86 self.exposureBlank = afwImage.ExposureF()
87 self.exposureMiOnly = afwImage.makeExposure(maskedImage)
88 self.exposureMiWcs = afwImage.makeExposure(maskedImage, self.wcs)
89 # n.b. the (100, 100, ...) form
90 self.exposureCrWcs = afwImage.ExposureF(100, 100, self.wcs)
91 # test with ExtentI(100, 100) too
92 self.exposureCrOnly = afwImage.ExposureF(lsst.geom.ExtentI(100, 100))
94 afwImage.Filter.reset()
95 afwImage.FilterProperty.reset()
97 defineFilter("g", 470.0)
99 def tearDown(self):
100 del self.smallExposure
101 del self.wcs
102 del self.psf
103 del self.detector
104 del self.extras
106 del self.exposureBlank
107 del self.exposureMiOnly
108 del self.exposureMiWcs
109 del self.exposureCrWcs
110 del self.exposureCrOnly
112 def testGetMaskedImage(self):
113 """
114 Test to ensure a MaskedImage can be obtained from each
115 Exposure. An Exposure is required to have a MaskedImage,
116 therefore each of the Exposures should return a MaskedImage.
118 MaskedImage class should throw appropriate
119 lsst::pex::exceptions::NotFound if the MaskedImage can not be
120 obtained.
121 """
122 maskedImageBlank = self.exposureBlank.getMaskedImage()
123 blankWidth = maskedImageBlank.getWidth()
124 blankHeight = maskedImageBlank.getHeight()
125 if blankWidth != blankHeight != 0:
126 self.fail(f"{blankWidth} = {blankHeight} != 0")
128 maskedImageMiOnly = self.exposureMiOnly.getMaskedImage()
129 miOnlyWidth = maskedImageMiOnly.getWidth()
130 miOnlyHeight = maskedImageMiOnly.getHeight()
131 self.assertAlmostEqual(miOnlyWidth, self.width)
132 self.assertAlmostEqual(miOnlyHeight, self.height)
134 # NOTE: Unittests for Exposures created from a MaskedImage and
135 # a WCS object are incomplete. No way to test the validity of
136 # the WCS being copied/created.
138 maskedImageMiWcs = self.exposureMiWcs.getMaskedImage()
139 miWcsWidth = maskedImageMiWcs.getWidth()
140 miWcsHeight = maskedImageMiWcs.getHeight()
141 self.assertAlmostEqual(miWcsWidth, self.width)
142 self.assertAlmostEqual(miWcsHeight, self.height)
144 maskedImageCrWcs = self.exposureCrWcs.getMaskedImage()
145 crWcsWidth = maskedImageCrWcs.getWidth()
146 crWcsHeight = maskedImageCrWcs.getHeight()
147 if crWcsWidth != crWcsHeight != 0:
148 self.fail(f"{crWcsWidth} != {crWcsHeight} != 0")
150 maskedImageCrOnly = self.exposureCrOnly.getMaskedImage()
151 crOnlyWidth = maskedImageCrOnly.getWidth()
152 crOnlyHeight = maskedImageCrOnly.getHeight()
153 if crOnlyWidth != crOnlyHeight != 0:
154 self.fail(f"{crOnlyWidth} != {crOnlyHeight} != 0")
156 # Check Exposure.getWidth() returns the MaskedImage's width
157 self.assertEqual(crOnlyWidth, self.exposureCrOnly.getWidth())
158 self.assertEqual(crOnlyHeight, self.exposureCrOnly.getHeight())
159 # check width/height properties
160 self.assertEqual(crOnlyWidth, self.exposureCrOnly.width)
161 self.assertEqual(crOnlyHeight, self.exposureCrOnly.height)
163 def testProperties(self):
164 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage,
165 self.exposureMiOnly.getMaskedImage())
166 mi2 = afwImage.MaskedImageF(self.exposureMiOnly.getDimensions())
167 mi2.image.array[:] = 5.0
168 mi2.variance.array[:] = 3.0
169 mi2.mask.array[:] = 0x1
170 self.exposureMiOnly.maskedImage = mi2
171 self.assertMaskedImagesEqual(self.exposureMiOnly.maskedImage, mi2)
172 self.assertImagesEqual(self.exposureMiOnly.image,
173 self.exposureMiOnly.maskedImage.image)
175 image3 = afwImage.ImageF(self.exposureMiOnly.getDimensions())
176 image3.array[:] = 3.0
177 self.exposureMiOnly.image = image3
178 self.assertImagesEqual(self.exposureMiOnly.image, image3)
180 mask3 = afwImage.MaskX(self.exposureMiOnly.getDimensions())
181 mask3.array[:] = 0x2
182 self.exposureMiOnly.mask = mask3
183 self.assertMasksEqual(self.exposureMiOnly.mask, mask3)
185 var3 = afwImage.ImageF(self.exposureMiOnly.getDimensions())
186 var3.array[:] = 2.0
187 self.exposureMiOnly.variance = var3
188 self.assertImagesEqual(self.exposureMiOnly.variance, var3)
190 # Test the property getter for a null VisitInfo.
191 self.assertIsNone(self.exposureMiOnly.visitInfo)
193 def testGetWcs(self):
194 """Test that a WCS can be obtained from each Exposure created with
195 a WCS, and that an Exposure lacking a WCS returns None.
196 """
197 # These exposures don't contain a WCS
198 self.assertIsNone(self.exposureBlank.getWcs())
199 self.assertIsNone(self.exposureMiOnly.getWcs())
200 self.assertIsNone(self.exposureCrOnly.getWcs())
202 # These exposures should contain a WCS
203 self.assertEqual(self.wcs, self.exposureMiWcs.getWcs())
204 self.assertEqual(self.wcs, self.exposureCrWcs.getWcs())
206 def testExposureInfoConstructor(self):
207 """Test the Exposure(maskedImage, exposureInfo) constructor"""
208 exposureInfo = afwImage.ExposureInfo()
209 exposureInfo.setWcs(self.wcs)
210 exposureInfo.setDetector(self.detector)
211 gFilter = afwImage.Filter("g")
212 gFilterLabel = afwImage.FilterLabel(band="g")
213 exposureInfo.setFilter(gFilter)
214 exposureInfo.setFilterLabel(gFilterLabel)
215 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
216 exposure = afwImage.ExposureF(maskedImage, exposureInfo)
218 self.assertTrue(exposure.hasWcs())
219 self.assertEqual(exposure.getWcs().getPixelOrigin(),
220 self.wcs.getPixelOrigin())
221 self.assertEqual(exposure.getDetector().getName(),
222 self.detector.getName())
223 self.assertEqual(exposure.getDetector().getSerial(),
224 self.detector.getSerial())
225 self.assertEqual(exposure.getFilter(), gFilter)
226 self.assertEqual(exposure.getFilterLabel(), gFilterLabel)
228 self.assertTrue(exposure.getInfo().hasWcs())
229 # check the ExposureInfo property
230 self.assertTrue(exposure.info.hasWcs())
231 self.assertEqual(exposure.getInfo().getWcs().getPixelOrigin(),
232 self.wcs.getPixelOrigin())
233 self.assertEqual(exposure.getInfo().getDetector().getName(),
234 self.detector.getName())
235 self.assertEqual(exposure.getInfo().getDetector().getSerial(),
236 self.detector.getSerial())
237 self.assertEqual(exposure.getInfo().getFilter(), gFilter)
238 self.assertEqual(exposure.getInfo().getFilterLabel(), gFilterLabel)
240 def testNullWcs(self):
241 """Test that an Exposure constructed with second argument None is usable
243 When the exposureInfo constructor was first added, trying to get a WCS
244 or other info caused a segfault because the ExposureInfo did not exist.
245 """
246 maskedImage = self.exposureMiOnly.getMaskedImage()
247 exposure = afwImage.ExposureF(maskedImage, None)
248 self.assertFalse(exposure.hasWcs())
249 self.assertFalse(exposure.hasPsf())
251 def testExposureInfoSetNone(self):
252 exposureInfo = afwImage.ExposureInfo()
253 exposureInfo.setDetector(None)
254 exposureInfo.setValidPolygon(None)
255 exposureInfo.setPsf(None)
256 exposureInfo.setWcs(None)
257 exposureInfo.setPhotoCalib(None)
258 exposureInfo.setCoaddInputs(None)
259 exposureInfo.setVisitInfo(None)
260 exposureInfo.setApCorrMap(None)
261 for key in self.extras:
262 exposureInfo.setComponent(key, None)
264 def testSetExposureInfo(self):
265 exposureInfo = afwImage.ExposureInfo()
266 exposureInfo.setWcs(self.wcs)
267 exposureInfo.setDetector(self.detector)
268 gFilter = afwImage.Filter("g")
269 gFilterLabel = afwImage.FilterLabel(band="g")
270 exposureInfo.setFilter(gFilter)
271 exposureInfo.setFilterLabel(gFilterLabel)
272 exposureInfo.setId(self.id)
273 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
274 exposure = afwImage.ExposureF(maskedImage)
275 self.assertFalse(exposure.hasWcs())
277 exposure.setInfo(exposureInfo)
279 self.assertTrue(exposure.hasWcs())
280 self.assertEqual(exposure.getWcs().getPixelOrigin(),
281 self.wcs.getPixelOrigin())
282 self.assertEqual(exposure.getDetector().getName(),
283 self.detector.getName())
284 self.assertEqual(exposure.getDetector().getSerial(),
285 self.detector.getSerial())
286 self.assertEqual(exposure.getFilter(), gFilter)
287 self.assertEqual(exposure.getFilterLabel(), gFilterLabel)
289 # test properties
290 self.assertEqual(exposure.detector.getName(), self.detector.getName())
291 self.assertEqual(exposure.filterLabel, gFilterLabel)
292 self.assertEqual(exposure.wcs, self.wcs)
294 def testDefaultFilter(self):
295 """Test that old convention of having a "default" filter replaced with `None`.
296 """
297 exposureInfo = afwImage.ExposureInfo()
298 noFilter = afwImage.Filter()
299 exposureInfo.setFilter(noFilter)
300 self.assertFalse(exposureInfo.hasFilterLabel())
301 self.assertIsNone(exposureInfo.getFilterLabel())
303 def testVisitInfoFitsPersistence(self):
304 """Test saving an exposure to FITS and reading it back in preserves (some) VisitInfo fields"""
305 exposureId = 5
306 exposureTime = 12.3
307 boresightRotAngle = 45.6 * lsst.geom.degrees
308 weather = Weather(1.1, 2.2, 0.3)
309 visitInfo = afwImage.VisitInfo(
310 exposureId=exposureId,
311 exposureTime=exposureTime,
312 boresightRotAngle=boresightRotAngle,
313 weather=weather,
314 )
315 photoCalib = afwImage.PhotoCalib(3.4, 5.6)
316 exposureInfo = afwImage.ExposureInfo()
317 exposureInfo.setVisitInfo(visitInfo)
318 exposureInfo.setPhotoCalib(photoCalib)
319 exposureInfo.setDetector(self.detector)
320 gFilter = afwImage.Filter("g")
321 gFilterLabel = afwImage.FilterLabel(band="g")
322 exposureInfo.setFilter(gFilter)
323 exposureInfo.setFilterLabel(gFilterLabel)
324 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
325 exposure = afwImage.ExposureF(maskedImage, exposureInfo)
326 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
327 exposure.writeFits(tmpFile)
328 rtExposure = afwImage.ExposureF(tmpFile)
329 rtVisitInfo = rtExposure.getInfo().getVisitInfo()
330 self.assertEqual(rtVisitInfo.getWeather(), weather)
331 self.assertEqual(rtExposure.getPhotoCalib(), photoCalib)
332 self.assertEqual(rtExposure.getFilter(), gFilter)
333 self.assertEqual(rtExposure.getFilterLabel(), gFilterLabel)
335 # Test property getters.
336 self.assertEqual(rtExposure.photoCalib, photoCalib)
337 self.assertEqual(rtExposure.filterLabel, gFilterLabel)
338 # NOTE: we can't test visitInfo equality, because most fields are NaN.
339 self.assertIsNotNone(rtExposure.visitInfo)
341 def testSetMembers(self):
342 """
343 Test that the MaskedImage and the WCS of an Exposure can be set.
344 """
345 exposure = afwImage.ExposureF()
347 maskedImage = afwImage.MaskedImageF(inFilePathSmall)
348 exposure.setMaskedImage(maskedImage)
349 exposure.setWcs(self.wcs)
350 exposure.setDetector(self.detector)
351 exposure.setFilter(afwImage.Filter("g"))
352 exposure.setFilterLabel(afwImage.FilterLabel(band="g"))
354 self.assertEqual(exposure.getDetector().getName(),
355 self.detector.getName())
356 self.assertEqual(exposure.getDetector().getSerial(),
357 self.detector.getSerial())
358 self.assertEqual(exposure.getFilter().getName(), "g")
359 self.assertEqual(exposure.getFilterLabel().bandLabel, "g")
360 self.assertEqual(exposure.getWcs(), self.wcs)
362 # The PhotoCalib tests are in test_photoCalib.py;
363 # here we just check that it's gettable and settable.
364 self.assertIsNone(exposure.getPhotoCalib())
366 photoCalib = afwImage.PhotoCalib(511.1, 44.4)
367 exposure.setPhotoCalib(photoCalib)
368 self.assertEqual(exposure.getPhotoCalib(), photoCalib)
370 # Psfs next
371 self.assertFalse(exposure.hasPsf())
372 exposure.setPsf(self.psf)
373 self.assertTrue(exposure.hasPsf())
375 exposure.setPsf(DummyPsf(1.0)) # we can reset the Psf
377 # extras next
378 info = exposure.getInfo()
379 for key, value in self.extras.items():
380 self.assertFalse(info.hasComponent(key))
381 self.assertIsNone(info.getComponent(key))
382 info.setComponent(key, value)
383 self.assertTrue(info.hasComponent(key))
384 self.assertEqual(info.getComponent(key), value)
385 info.removeComponent(key)
386 self.assertFalse(info.hasComponent(key))
388 # Test that we can set the MaskedImage and WCS of an Exposure
389 # that already has both
390 self.exposureMiWcs.setMaskedImage(maskedImage)
391 exposure.setWcs(self.wcs)
393 def testHasWcs(self):
394 """
395 Test if an Exposure has a WCS or not.
396 """
397 self.assertFalse(self.exposureBlank.hasWcs())
399 self.assertFalse(self.exposureMiOnly.hasWcs())
400 self.assertTrue(self.exposureMiWcs.hasWcs())
401 self.assertTrue(self.exposureCrWcs.hasWcs())
402 self.assertFalse(self.exposureCrOnly.hasWcs())
404 def testGetSubExposure(self):
405 """
406 Test that a subExposure of the original Exposure can be obtained.
408 The MaskedImage class should throw a
409 lsst::pex::exceptions::InvalidParameter if the requested
410 subRegion is not fully contained within the original
411 MaskedImage.
413 """
414 #
415 # This subExposure is valid
416 #
417 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50),
418 lsst.geom.Extent2I(10, 10))
419 subExposure = self.exposureCrWcs.Factory(
420 self.exposureCrWcs, subBBox, afwImage.LOCAL)
422 self.checkWcs(self.exposureCrWcs, subExposure)
424 # this subRegion is not valid and should trigger an exception
425 # from the MaskedImage class and should trigger an exception
426 # from the WCS class for the MaskedImage 871034p_1_MI.
428 subRegion3 = lsst.geom.Box2I(lsst.geom.Point2I(100, 100),
429 lsst.geom.Extent2I(10, 10))
431 def getSubRegion():
432 self.exposureCrWcs.Factory(
433 self.exposureCrWcs, subRegion3, afwImage.LOCAL)
435 self.assertRaises(pexExcept.LengthError, getSubRegion)
437 # this subRegion is not valid and should trigger an exception
438 # from the MaskedImage class only for the MaskedImage small_MI.
439 # small_MI (cols, rows) = (256, 256)
441 subRegion4 = lsst.geom.Box2I(lsst.geom.Point2I(250, 250),
442 lsst.geom.Extent2I(10, 10))
444 def getSubRegion():
445 self.exposureCrWcs.Factory(
446 self.exposureCrWcs, subRegion4, afwImage.LOCAL)
448 self.assertRaises(pexExcept.LengthError, getSubRegion)
450 # check the sub- and parent- exposures are using the same Wcs
451 # transformation
452 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(40, 50),
453 lsst.geom.Extent2I(10, 10))
454 subExposure = self.exposureCrWcs.Factory(
455 self.exposureCrWcs, subBBox, afwImage.LOCAL)
456 parentSkyPos = self.exposureCrWcs.getWcs().pixelToSky(0, 0)
458 subExpSkyPos = subExposure.getWcs().pixelToSky(0, 0)
460 self.assertSpherePointsAlmostEqual(parentSkyPos, subExpSkyPos, msg="Wcs in sub image has changed")
462 def testReadWriteFits(self):
463 """Test readFits and writeFits.
464 """
465 # This should pass without an exception
466 mainExposure = afwImage.ExposureF(inFilePathSmall)
467 mainExposure.info.setId(self.id)
468 mainExposure.setDetector(self.detector)
470 subBBox = lsst.geom.Box2I(lsst.geom.Point2I(10, 10),
471 lsst.geom.Extent2I(40, 50))
472 subExposure = mainExposure.Factory(
473 mainExposure, subBBox, afwImage.LOCAL)
474 self.checkWcs(mainExposure, subExposure)
475 det = subExposure.getDetector()
476 self.assertTrue(det)
478 subExposure = afwImage.ExposureF(
479 inFilePathSmall, subBBox, afwImage.LOCAL)
481 self.checkWcs(mainExposure, subExposure)
483 # This should throw an exception
484 def getExposure():
485 afwImage.ExposureF(inFilePathSmallImage)
487 self.assertRaises(FitsError, getExposure)
489 mainExposure.setPsf(self.psf)
491 # Make sure we can write without an exception
492 photoCalib = afwImage.PhotoCalib(1e-10, 1e-12)
493 mainExposure.setPhotoCalib(photoCalib)
495 mainInfo = mainExposure.getInfo()
496 for key, value in self.extras.items():
497 mainInfo.setComponent(key, value)
499 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
500 mainExposure.writeFits(tmpFile)
502 readExposure = type(mainExposure)(tmpFile)
504 #
505 # Check the round-tripping
506 #
507 self.assertEqual(mainExposure.getFilter().getName(),
508 readExposure.getFilter().getName())
509 self.assertIsNotNone(mainExposure.getFilterLabel())
510 self.assertEqual(mainExposure.getFilterLabel(),
511 readExposure.getFilterLabel())
513 self.assertEqual(photoCalib, readExposure.getPhotoCalib())
515 readInfo = readExposure.getInfo()
516 self.assertEqual(mainExposure.info.getId(), readInfo.id)
517 for key, value in self.extras.items():
518 self.assertEqual(value, readInfo.getComponent(key))
520 psf = readExposure.getPsf()
521 self.assertIsNotNone(psf)
522 self.assertEqual(psf, self.psf)
523 # check psf property getter
524 self.assertEqual(readExposure.psf, self.psf)
526 def checkWcs(self, parentExposure, subExposure):
527 """Compare WCS at corner points of a sub-exposure and its parent exposure
528 By using the function indexToPosition, we should be able to convert the indices
529 (of the four corners (of the sub-exposure)) to positions and use the wcs
530 to get the same sky coordinates for each.
531 """
532 subMI = subExposure.getMaskedImage()
533 subDim = subMI.getDimensions()
535 # Note: pixel positions must be computed relative to XY0 when working
536 # with WCS
537 mainWcs = parentExposure.getWcs()
538 subWcs = subExposure.getWcs()
540 for xSubInd in (0, subDim.getX()-1):
541 for ySubInd in (0, subDim.getY()-1):
542 self.assertSpherePointsAlmostEqual(
543 mainWcs.pixelToSky(
544 afwImage.indexToPosition(xSubInd),
545 afwImage.indexToPosition(ySubInd),
546 ),
547 subWcs.pixelToSky(
548 afwImage.indexToPosition(xSubInd),
549 afwImage.indexToPosition(ySubInd),
550 ))
552 def cmpExposure(self, e1, e2):
553 self.assertEqual(e1.getDetector().getName(),
554 e2.getDetector().getName())
555 self.assertEqual(e1.getDetector().getSerial(),
556 e2.getDetector().getSerial())
557 self.assertEqual(e1.getFilter().getName(), e2.getFilter().getName())
558 self.assertEqual(e1.getFilterLabel(), e2.getFilterLabel())
559 xy = lsst.geom.Point2D(0, 0)
560 self.assertEqual(e1.getWcs().pixelToSky(xy)[0],
561 e2.getWcs().pixelToSky(xy)[0])
562 self.assertEqual(e1.getPhotoCalib(), e2.getPhotoCalib())
563 # check PSF identity
564 if not e1.getPsf():
565 self.assertFalse(e2.getPsf())
566 else:
567 self.assertEqual(e1.getPsf(), e2.getPsf())
568 # Check extra components
569 i1 = e1.getInfo()
570 i2 = e2.getInfo()
571 for key in self.extras:
572 self.assertEqual(i1.hasComponent(key), i2.hasComponent(key))
573 if i1.hasComponent(key):
574 self.assertEqual(i1.getComponent(key), i2.getComponent(key))
576 def testCopyExposure(self):
577 """Copy an Exposure (maybe changing type)"""
579 exposureU = afwImage.ExposureU(inFilePathSmall, allowUnsafe=True)
580 exposureU.setWcs(self.wcs)
581 exposureU.setDetector(self.detector)
582 exposureU.setFilter(afwImage.Filter("g"))
583 exposureU.setFilterLabel(afwImage.FilterLabel(band="g"))
584 exposureU.setPsf(DummyPsf(4.0))
585 infoU = exposureU.getInfo()
586 for key, value in self.extras.items():
587 infoU.setComponent(key, value)
589 exposureF = exposureU.convertF()
590 self.cmpExposure(exposureF, exposureU)
592 nexp = exposureF.Factory(exposureF, False)
593 self.cmpExposure(exposureF, nexp)
595 # Ensure that the copy was deep.
596 # (actually this test is invalid since getDetector() returns a shared_ptr)
597 # cen0 = exposureU.getDetector().getCenterPixel()
598 # x0,y0 = cen0
599 # det = exposureF.getDetector()
600 # det.setCenterPixel(lsst.geom.Point2D(999.0, 437.8))
601 # self.assertEqual(exposureU.getDetector().getCenterPixel()[0], x0)
602 # self.assertEqual(exposureU.getDetector().getCenterPixel()[1], y0)
604 def testDeepCopyData(self):
605 """Make sure a deep copy of an Exposure has its own data (ticket #2625)
606 """
607 exp = afwImage.ExposureF(6, 7)
608 mi = exp.getMaskedImage()
609 mi.getImage().set(100)
610 mi.getMask().set(5)
611 mi.getVariance().set(200)
613 expCopy = exp.clone()
614 miCopy = expCopy.getMaskedImage()
615 miCopy.getImage().set(-50)
616 miCopy.getMask().set(2)
617 miCopy.getVariance().set(175)
619 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50)
620 self.assertTrue(np.all(miCopy.getMask().getArray() == 2))
621 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175)
623 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100)
624 self.assertTrue(np.all(mi.getMask().getArray() == 5))
625 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200)
627 def testDeepCopySubData(self):
628 """Make sure a deep copy of a subregion of an Exposure has its own data (ticket #2625)
629 """
630 exp = afwImage.ExposureF(6, 7)
631 mi = exp.getMaskedImage()
632 mi.getImage().set(100)
633 mi.getMask().set(5)
634 mi.getVariance().set(200)
636 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 4))
637 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True)
638 miCopy = expCopy.getMaskedImage()
639 miCopy.getImage().set(-50)
640 miCopy.getMask().set(2)
641 miCopy.getVariance().set(175)
643 self.assertFloatsAlmostEqual(miCopy.getImage().getArray(), -50)
644 self.assertTrue(np.all(miCopy.getMask().getArray() == 2))
645 self.assertFloatsAlmostEqual(miCopy.getVariance().getArray(), 175)
647 self.assertFloatsAlmostEqual(mi.getImage().getArray(), 100)
648 self.assertTrue(np.all(mi.getMask().getArray() == 5))
649 self.assertFloatsAlmostEqual(mi.getVariance().getArray(), 200)
651 def testDeepCopyMetadata(self):
652 """Make sure a deep copy of an Exposure has a deep copy of metadata (ticket #2568)
653 """
654 exp = afwImage.ExposureF(10, 10)
655 expMeta = exp.getMetadata()
656 expMeta.set("foo", 5)
657 expCopy = exp.clone()
658 expCopyMeta = expCopy.getMetadata()
659 expCopyMeta.set("foo", 6)
660 self.assertEqual(expCopyMeta.getScalar("foo"), 6)
661 # this will fail if the bug is present
662 self.assertEqual(expMeta.getScalar("foo"), 5)
664 def testDeepCopySubMetadata(self):
665 """Make sure a deep copy of a subregion of an Exposure has a deep copy of metadata (ticket #2568)
666 """
667 exp = afwImage.ExposureF(10, 10)
668 expMeta = exp.getMetadata()
669 expMeta.set("foo", 5)
670 bbox = lsst.geom.Box2I(lsst.geom.Point2I(1, 0), lsst.geom.Extent2I(5, 5))
671 expCopy = exp.Factory(exp, bbox, afwImage.PARENT, True)
672 expCopyMeta = expCopy.getMetadata()
673 expCopyMeta.set("foo", 6)
674 self.assertEqual(expCopyMeta.getScalar("foo"), 6)
675 # this will fail if the bug is present
676 self.assertEqual(expMeta.getScalar("foo"), 5)
678 def testMakeExposureLeaks(self):
679 """Test for memory leaks in makeExposure (the test is in lsst.utils.tests.MemoryTestCase)"""
680 afwImage.makeMaskedImage(afwImage.ImageU(lsst.geom.Extent2I(10, 20)))
681 afwImage.makeExposure(afwImage.makeMaskedImage(
682 afwImage.ImageU(lsst.geom.Extent2I(10, 20))))
684 def testImageSlices(self):
685 """Test image slicing, which generate sub-images using Box2I under the covers"""
686 exp = afwImage.ExposureF(10, 20)
687 mi = exp.getMaskedImage()
688 mi.image[9, 19] = 10
689 # N.b. Exposures don't support setting/getting the pixels so can't
690 # replicate e.g. Image's slice tests
691 sexp = exp[1:4, 6:10]
692 self.assertEqual(sexp.getDimensions(), lsst.geom.ExtentI(3, 4))
693 sexp = exp[:, -3:, afwImage.LOCAL]
694 self.assertEqual(sexp.getDimensions(),
695 lsst.geom.ExtentI(exp.getWidth(), 3))
696 self.assertEqual(sexp.maskedImage[-1, -1, afwImage.LOCAL],
697 exp.maskedImage[-1, -1, afwImage.LOCAL])
699 def testConversionToScalar(self):
700 """Test that even 1-pixel Exposures can't be converted to scalars"""
701 im = afwImage.ExposureF(10, 20)
703 # only single pixel images may be converted
704 self.assertRaises(TypeError, float, im)
705 # actually, can't convert (img, msk, var) to scalar
706 self.assertRaises(TypeError, float, im[0, 0])
708 def testReadMetadata(self):
709 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
710 self.exposureCrWcs.getMetadata().set("FRAZZLE", True)
711 # This will write the main metadata (inc. FRAZZLE) to the primary HDU, and the
712 # WCS to subsequent HDUs, along with INHERIT=T.
713 self.exposureCrWcs.writeFits(tmpFile)
714 # This should read the first non-empty HDU (i.e. it skips the primary), but
715 # goes back and reads it if it finds INHERIT=T. That should let us read
716 # frazzle and the Wcs from the PropertySet returned by
717 # testReadMetadata.
718 md = readMetadata(tmpFile)
719 wcs = afwGeom.makeSkyWcs(md, False)
720 self.assertPairsAlmostEqual(wcs.getPixelOrigin(), self.wcs.getPixelOrigin())
721 self.assertSpherePointsAlmostEqual(wcs.getSkyOrigin(), self.wcs.getSkyOrigin())
722 assert_allclose(wcs.getCdMatrix(), self.wcs.getCdMatrix(), atol=1e-10)
723 frazzle = md.getScalar("FRAZZLE")
724 self.assertTrue(frazzle)
726 def testArchiveKeys(self):
727 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
728 exposure1 = afwImage.ExposureF(100, 100, self.wcs)
729 exposure1.setPsf(self.psf)
730 exposure1.writeFits(tmpFile)
731 exposure2 = afwImage.ExposureF(tmpFile)
732 self.assertFalse(exposure2.getMetadata().exists("AR_ID"))
733 self.assertFalse(exposure2.getMetadata().exists("PSF_ID"))
734 self.assertFalse(exposure2.getMetadata().exists("WCS_ID"))
736 def testTicket2861(self):
737 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
738 exposure1 = afwImage.ExposureF(100, 100, self.wcs)
739 exposure1.setPsf(self.psf)
740 schema = afwTable.ExposureTable.makeMinimalSchema()
741 coaddInputs = afwImage.CoaddInputs(schema, schema)
742 exposure1.getInfo().setCoaddInputs(coaddInputs)
743 exposure2 = afwImage.ExposureF(exposure1, True)
744 self.assertIsNotNone(exposure2.getInfo().getCoaddInputs())
745 exposure2.writeFits(tmpFile)
746 exposure3 = afwImage.ExposureF(tmpFile)
747 self.assertIsNotNone(exposure3.getInfo().getCoaddInputs())
749 def testGetCutout(self):
750 wcs = self.smallExposure.getWcs()
752 dimensions = [lsst.geom.Extent2I(100, 50), lsst.geom.Extent2I(15, 15), lsst.geom.Extent2I(0, 10),
753 lsst.geom.Extent2I(25, 30), lsst.geom.Extent2I(15, -5),
754 2*self.smallExposure.getDimensions()]
755 locations = [("center", self._getExposureCenter(self.smallExposure)),
756 ("edge", wcs.pixelToSky(lsst.geom.Point2D(0, 0))),
757 ("rounding test", wcs.pixelToSky(lsst.geom.Point2D(0.2, 0.7))),
758 ("just inside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 + 1e-4, -0.5 + 1e-4))),
759 ("just outside", wcs.pixelToSky(lsst.geom.Point2D(-0.5 - 1e-4, -0.5 - 1e-4))),
760 ("outside", wcs.pixelToSky(lsst.geom.Point2D(-1000, -1000)))]
761 for cutoutSize in dimensions:
762 for label, cutoutCenter in locations:
763 msg = 'Cutout size = %s, location = %s' % (cutoutSize, label)
764 if "outside" not in label and all(cutoutSize.gt(0)):
765 cutout = self.smallExposure.getCutout(cutoutCenter, cutoutSize)
766 centerInPixels = wcs.skyToPixel(cutoutCenter)
767 precision = (1 + 1e-4)*np.sqrt(0.5)*wcs.getPixelScale(centerInPixels)
768 self._checkCutoutProperties(cutout, cutoutSize, cutoutCenter, precision, msg)
769 self._checkCutoutPixels(
770 cutout,
771 self._getValidCorners(self.smallExposure.getBBox(), cutout.getBBox()),
772 msg)
774 # Need a valid WCS
775 with self.assertRaises(pexExcept.LogicError, msg=msg):
776 self.exposureMiOnly.getCutout(cutoutCenter, cutoutSize)
777 else:
778 with self.assertRaises(pexExcept.InvalidParameterError, msg=msg):
779 self.smallExposure.getCutout(cutoutCenter, cutoutSize)
781 def testGetConvexPolygon(self):
782 """Test the convex polygon."""
783 # Check that we do not have a convex polygon for the plain exposure.
784 self.assertIsNone(self.exposureMiOnly.convex_polygon)
786 # Check that all the points in the padded bounding box are in the polygon
787 bbox = self.exposureMiWcs.getBBox()
788 # Grow by the default padding.
789 bbox.grow(10)
790 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX(), dtype=np.float64),
791 np.arange(bbox.getBeginY(), bbox.getEndY(), dtype=np.float64))
792 wcs = self.exposureMiWcs.wcs
793 ra, dec = wcs.pixelToSkyArray(x.ravel(),
794 y.ravel())
796 poly = self.exposureMiWcs.convex_polygon
797 contains = poly.contains(ra, dec)
798 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
800 # Check that points one pixel outside of the bounding box are not in the polygon
801 bbox.grow(1)
803 ra, dec = wcs.pixelToSkyArray(
804 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100),
805 np.full(100, bbox.getBeginY()))
806 contains = poly.contains(ra, dec)
807 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
809 ra, dec = wcs.pixelToSkyArray(
810 np.linspace(bbox.getBeginX(), bbox.getEndX(), 100),
811 np.full(100, bbox.getEndY()))
812 contains = poly.contains(ra, dec)
813 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
815 ra, dec = wcs.pixelToSkyArray(
816 np.full(100, bbox.getBeginX()),
817 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100))
818 contains = poly.contains(ra, dec)
819 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
821 ra, dec = wcs.pixelToSkyArray(
822 np.full(100, bbox.getEndX()),
823 np.linspace(bbox.getBeginY(), bbox.getEndY(), 100))
824 contains = poly.contains(ra, dec)
825 np.testing.assert_array_equal(contains, np.zeros(len(contains), dtype=bool))
827 def testContainsSkyCoords(self):
828 """Test the sky coord containment code."""
829 self.assertRaisesRegex(ValueError,
830 "Exposure does not have a valid WCS",
831 self.exposureMiOnly.containsSkyCoords,
832 0.0,
833 0.0)
835 # Check that all the points within the bounding box are contained
836 bbox = self.exposureMiWcs.getBBox()
837 x, y = np.meshgrid(np.arange(bbox.getBeginX() + 1, bbox.getEndX() - 1),
838 np.arange(bbox.getBeginY() + 1, bbox.getEndY() - 1))
839 wcs = self.exposureMiWcs.wcs
840 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64),
841 y.ravel().astype(np.float64))
843 contains = self.exposureMiWcs.containsSkyCoords(ra*units.radian,
844 dec*units.radian)
845 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
847 # Same test, everything in degrees.
848 ra, dec = wcs.pixelToSkyArray(x.ravel().astype(np.float64),
849 y.ravel().astype(np.float64),
850 degrees=True)
852 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree,
853 dec*units.degree)
854 np.testing.assert_array_equal(contains, np.ones(len(contains), dtype=bool))
856 # Prepend and append some positions out of the box.
857 ra = np.concatenate(([300.0], ra, [180.]))
858 dec = np.concatenate(([50.0], dec, [50.0]))
860 contains = self.exposureMiWcs.containsSkyCoords(ra*units.degree,
861 dec*units.degree)
862 compare = np.ones(len(contains), dtype=bool)
863 compare[0] = False
864 compare[-1] = False
865 np.testing.assert_array_equal(contains, compare)
867 def _checkCutoutProperties(self, cutout, size, center, precision, msg):
868 """Test whether a cutout has the desired size and position.
870 Parameters
871 ----------
872 cutout : `lsst.afw.image.Exposure`
873 The cutout to test.
874 size : `lsst.geom.Extent2I`
875 The expected dimensions of ``cutout``.
876 center : `lsst.geom.SpherePoint`
877 The expected center of ``cutout``.
878 precision : `lsst.geom.Angle`
879 The precision to which ``center`` must match.
880 msg : `str`
881 An error message suffix describing test parameters.
882 """
883 newCenter = self._getExposureCenter(cutout)
884 self.assertIsNotNone(cutout, msg=msg)
885 self.assertSpherePointsAlmostEqual(newCenter, center, maxSep=precision, msg=msg)
886 self.assertEqual(cutout.getWidth(), size[0], msg=msg)
887 self.assertEqual(cutout.getHeight(), size[1], msg=msg)
889 def _checkCutoutPixels(self, cutout, validCorners, msg):
890 """Test whether a cutout has valid/empty pixels where expected.
892 Parameters
893 ----------
894 cutout : `lsst.afw.image.Exposure`
895 The cutout to test.
896 validCorners : iterable of `lsst.geom.Point2I`
897 The corners of ``cutout`` that should be drawn from the original image.
898 msg : `str`
899 An error message suffix describing test parameters.
900 """
901 mask = cutout.getMaskedImage().getMask()
902 edgeMask = mask.getPlaneBitMask("NO_DATA")
904 for corner in cutout.getBBox().getCorners():
905 maskBitsSet = mask[corner] & edgeMask
906 if corner in validCorners:
907 self.assertEqual(maskBitsSet, 0, msg=msg)
908 else:
909 self.assertEqual(maskBitsSet, edgeMask, msg=msg)
911 def _getExposureCenter(self, exposure):
912 """Return the sky coordinates of an Exposure's center.
914 Parameters
915 ----------
916 exposure : `lsst.afw.image.Exposure`
917 The image whose center is desired.
919 Returns
920 -------
921 center : `lsst.geom.SpherePoint`
922 The position at the center of ``exposure``.
923 """
924 return exposure.getWcs().pixelToSky(lsst.geom.Box2D(exposure.getBBox()).getCenter())
926 def _getValidCorners(self, imageBox, cutoutBox):
927 """Return the corners of a cutout that are constrained by the original image.
929 Parameters
930 ----------
931 imageBox: `lsst.geom.Extent2I`
932 The bounding box of the original image.
933 cutoutBox : `lsst.geom.Box2I`
934 The bounding box of the cutout.
936 Returns
937 -------
938 corners : iterable of `lsst.geom.Point2I`
939 The corners that are drawn from the original image.
940 """
941 return [corner for corner in cutoutBox.getCorners() if corner in imageBox]
944class ExposureInfoTestCase(lsst.utils.tests.TestCase):
945 def setUp(self):
946 super().setUp()
948 afwImage.Filter.reset()
949 afwImage.FilterProperty.reset()
950 defineFilter("g", 470.0)
952 self.wcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(0.0, 0.0),
953 lsst.geom.SpherePoint(2.0, 34.0, lsst.geom.degrees),
954 np.identity(2),
955 )
956 self.photoCalib = afwImage.PhotoCalib(1.5)
957 self.psf = DummyPsf(2.0)
958 self.detector = DetectorWrapper().detector
959 self.summaryStats = afwImage.ExposureSummaryStats(ra=100.0)
960 self.polygon = afwGeom.Polygon(lsst.geom.Box2D(lsst.geom.Point2D(0.0, 0.0),
961 lsst.geom.Point2D(25.0, 20.0)))
962 self.coaddInputs = afwImage.CoaddInputs()
963 self.apCorrMap = afwImage.ApCorrMap()
964 self.transmissionCurve = afwImage.TransmissionCurve.makeIdentity()
966 self.exposureInfo = afwImage.ExposureInfo()
967 self.gFilterLabel = afwImage.FilterLabel(band="g")
968 self.exposureId = 42
970 def _checkAlias(self, exposureInfo, key, value, has, get):
971 self.assertFalse(has())
972 self.assertFalse(exposureInfo.hasComponent(key))
973 self.assertIsNone(get())
974 self.assertIsNone(exposureInfo.getComponent(key))
976 self.exposureInfo.setComponent(key, value)
977 self.assertTrue(has())
978 self.assertTrue(exposureInfo.hasComponent(key))
979 self.assertIsNotNone(get())
980 self.assertIsNotNone(exposureInfo.getComponent(key))
981 self.assertEqual(get(), value)
982 self.assertEqual(exposureInfo.getComponent(key), value)
984 self.exposureInfo.removeComponent(key)
985 self.assertFalse(has())
986 self.assertFalse(exposureInfo.hasComponent(key))
987 self.assertIsNone(get())
988 self.assertIsNone(exposureInfo.getComponent(key))
990 def testAliases(self):
991 cls = type(self.exposureInfo)
992 self._checkAlias(self.exposureInfo, cls.KEY_WCS, self.wcs,
993 self.exposureInfo.hasWcs, self.exposureInfo.getWcs)
994 self._checkAlias(self.exposureInfo, cls.KEY_PSF, self.psf,
995 self.exposureInfo.hasPsf, self.exposureInfo.getPsf)
996 self._checkAlias(self.exposureInfo, cls.KEY_PHOTO_CALIB, self.photoCalib,
997 self.exposureInfo.hasPhotoCalib, self.exposureInfo.getPhotoCalib)
998 self._checkAlias(self.exposureInfo, cls.KEY_DETECTOR, self.detector,
999 self.exposureInfo.hasDetector, self.exposureInfo.getDetector)
1000 self._checkAlias(self.exposureInfo, cls.KEY_VALID_POLYGON, self.polygon,
1001 self.exposureInfo.hasValidPolygon, self.exposureInfo.getValidPolygon)
1002 self._checkAlias(self.exposureInfo, cls.KEY_COADD_INPUTS, self.coaddInputs,
1003 self.exposureInfo.hasCoaddInputs, self.exposureInfo.getCoaddInputs)
1004 self._checkAlias(self.exposureInfo, cls.KEY_AP_CORR_MAP, self.apCorrMap,
1005 self.exposureInfo.hasApCorrMap, self.exposureInfo.getApCorrMap)
1006 self._checkAlias(self.exposureInfo, cls.KEY_TRANSMISSION_CURVE, self.transmissionCurve,
1007 self.exposureInfo.hasTransmissionCurve, self.exposureInfo.getTransmissionCurve)
1008 self._checkAlias(self.exposureInfo, cls.KEY_SUMMARY_STATS, self.summaryStats,
1009 self.exposureInfo.hasSummaryStats, self.exposureInfo.getSummaryStats)
1010 self._checkAlias(self.exposureInfo, cls.KEY_FILTER, self.gFilterLabel,
1011 self.exposureInfo.hasFilterLabel, self.exposureInfo.getFilterLabel)
1013 def testId(self):
1014 self.exposureInfo.setVisitInfo(afwImage.VisitInfo())
1016 self.assertFalse(self.exposureInfo.hasId())
1017 self.assertIsNone(self.exposureInfo.getId())
1018 with self.assertWarns(FutureWarning):
1019 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), 0)
1020 self.assertIsNone(self.exposureInfo.id)
1022 self.exposureInfo.setId(self.exposureId)
1023 self.assertTrue(self.exposureInfo.hasId())
1024 self.assertIsNotNone(self.exposureInfo.getId())
1025 self.assertIsNotNone(self.exposureInfo.id)
1026 self.assertEqual(self.exposureInfo.getId(), self.exposureId)
1027 with self.assertWarns(FutureWarning):
1028 self.assertEqual(self.exposureInfo.getVisitInfo().getExposureId(), self.exposureId)
1029 self.assertEqual(self.exposureInfo.id, self.exposureId)
1031 self.exposureInfo.id = 99899
1032 self.assertEqual(self.exposureInfo.getId(), 99899)
1034 self.exposureInfo.setVisitInfo(afwImage.VisitInfo(exposureId=12321))
1035 self.assertEqual(self.exposureInfo.id, 12321)
1037 self.exposureInfo.id = None
1038 self.assertFalse(self.exposureInfo.hasId())
1039 self.assertIsNone(self.exposureInfo.getId())
1040 self.assertIsNone(self.exposureInfo.id)
1042 def testCopy(self):
1043 # Test that ExposureInfos have independently settable state
1044 copy = afwImage.ExposureInfo(self.exposureInfo, True)
1045 self.assertEqual(self.exposureInfo.getWcs(), copy.getWcs())
1047 newWcs = afwGeom.makeSkyWcs(lsst.geom.Point2D(-23.0, 8.0),
1048 lsst.geom.SpherePoint(0.0, 0.0, lsst.geom.degrees),
1049 np.identity(2),
1050 )
1051 copy.setWcs(newWcs)
1052 self.assertEqual(copy.getWcs(), newWcs)
1053 self.assertNotEqual(self.exposureInfo.getWcs(), copy.getWcs())
1055 def testMissingProperties(self):
1056 # Test that invalid properties return None instead of raising
1057 exposureInfo = afwImage.ExposureInfo()
1059 self.assertIsNone(exposureInfo.id)
1062class ExposureNoAfwdataTestCase(lsst.utils.tests.TestCase):
1063 """Tests of Exposure that don't require afwdata.
1065 These tests use the trivial exposures written to ``afw/tests/data``.
1066 """
1067 def setUp(self):
1068 self.dataDir = os.path.join(os.path.split(__file__)[0], "data")
1070 # Check the values below against what was written by comparing with
1071 # the code in `afw/tests/data/makeTestExposure.py`
1072 nx = ny = 10
1073 image = afwImage.ImageF(np.arange(nx*ny, dtype='f').reshape(nx, ny))
1074 variance = afwImage.ImageF(np.ones((nx, ny), dtype='f'))
1075 mask = afwImage.MaskX(nx, ny)
1076 mask.array[5, 5] = 5
1077 self.maskedImage = afwImage.MaskedImageF(image, mask, variance)
1078 self.exposureId = 12345
1080 self.v0PhotoCalib = afwImage.makePhotoCalibFromCalibZeroPoint(1e6, 2e4)
1081 self.v1PhotoCalib = afwImage.PhotoCalib(1e6, 2e4)
1082 self.v1FilterLabel = afwImage.FilterLabel(physical="ha")
1083 self.v2FilterLabel = afwImage.FilterLabel(band="N656", physical="ha")
1085 def testReadUnversioned(self):
1086 """Test that we can read an unversioned (implicit verison 0) file.
1087 """
1088 filename = os.path.join(self.dataDir, "exposure-noversion.fits")
1089 exposure = afwImage.ExposureF.readFits(filename)
1091 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1093 self.assertEqual(exposure.info.id, self.exposureId)
1094 with self.assertWarns(FutureWarning):
1095 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1096 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib)
1097 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel)
1099 def testReadVersion0(self):
1100 """Test that we can read a version 0 file.
1101 This file should be identical to the unversioned one, except that it
1102 is marked as ExposureInfo version 0 in the header.
1103 """
1104 filename = os.path.join(self.dataDir, "exposure-version-0.fits")
1105 exposure = afwImage.ExposureF.readFits(filename)
1107 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1109 self.assertEqual(exposure.info.id, self.exposureId)
1110 with self.assertWarns(FutureWarning):
1111 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1112 self.assertEqual(exposure.getPhotoCalib(), self.v0PhotoCalib)
1113 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel)
1115 # Check that the metadata reader parses the file correctly
1116 reader = afwImage.ExposureFitsReader(filename)
1117 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v0PhotoCalib)
1118 self.assertEqual(reader.readPhotoCalib(), self.v0PhotoCalib)
1120 def testReadVersion1(self):
1121 """Test that we can read a version 1 file.
1122 Version 1 replaced Calib with PhotoCalib.
1123 """
1124 filename = os.path.join(self.dataDir, "exposure-version-1.fits")
1125 exposure = afwImage.ExposureF.readFits(filename)
1127 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1129 self.assertEqual(exposure.info.id, self.exposureId)
1130 with self.assertWarns(FutureWarning):
1131 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1132 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib)
1133 self.assertEqual(exposure.getFilterLabel(), self.v1FilterLabel)
1135 # Check that the metadata reader parses the file correctly
1136 reader = afwImage.ExposureFitsReader(filename)
1137 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib)
1138 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib)
1140 def testReadVersion2(self):
1141 """Test that we can read a version 2 file.
1142 Version 2 replaced Filter with FilterLabel.
1143 """
1144 filename = os.path.join(self.dataDir, "exposure-version-2.fits")
1145 exposure = afwImage.ExposureF.readFits(filename)
1147 self.assertMaskedImagesEqual(exposure.maskedImage, self.maskedImage)
1149 self.assertEqual(exposure.info.id, self.exposureId)
1150 with self.assertWarns(FutureWarning):
1151 self.assertEqual(exposure.info.getVisitInfo().getExposureId(), self.exposureId)
1152 self.assertEqual(exposure.getPhotoCalib(), self.v1PhotoCalib)
1153 self.assertEqual(exposure.getFilterLabel(), self.v2FilterLabel)
1155 # Check that the metadata reader parses the file correctly
1156 reader = afwImage.ExposureFitsReader(filename)
1157 self.assertEqual(reader.readExposureInfo().getPhotoCalib(), self.v1PhotoCalib)
1158 self.assertEqual(reader.readPhotoCalib(), self.v1PhotoCalib)
1160 def testExposureSummaryExtraComponents(self):
1161 """Test that we can read an exposure summary with extra components.
1162 """
1163 testDict = {'ra': 0.0,
1164 'decl': 0.0,
1165 'nonsense': 1.0}
1166 bytes = yaml.dump(testDict, encoding='utf-8')
1167 with self.assertWarns(FutureWarning):
1168 summaryStats = lsst.afw.image.ExposureSummaryStats._read(bytes)
1170 self.assertEqual(summaryStats.ra, testDict['ra'])
1171 self.assertEqual(summaryStats.decl, testDict['decl'])
1174class MemoryTester(lsst.utils.tests.MemoryTestCase):
1175 pass
1178def setup_module(module):
1179 lsst.utils.tests.init()
1182if __name__ == "__main__": 1182 ↛ 1183line 1182 didn't jump to line 1183, because the condition on line 1182 was never true
1183 lsst.utils.tests.init()
1184 unittest.main()