Coverage for tests/test_imageIo2.py: 29%

Shortcuts 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

76 statements  

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23import unittest 

24 

25import numpy as np 

26import astropy.io.fits 

27import lsst.utils.tests 

28from lsst.geom import Box2I, Point2I 

29from lsst.afw.fits import ImageCompressionOptions, ImageScalingOptions, ImageWriteOptions 

30import lsst.afw.image as afwImage 

31 

32 

33class ImageIoTestCase(lsst.utils.tests.TestCase): 

34 """A test case for Image FITS I/O""" 

35 

36 def assertImagesEqual(self, image, original): 

37 self.assertEqual(image.getBBox(), original.getBBox()) 

38 super().assertImagesEqual(image, original) 

39 

40 def setUp(self): 

41 # ImageL (uint64) is not supported by FITS standard, needs special tests 

42 self.IntegerImages = (afwImage.ImageU, afwImage.ImageI) 

43 self.FloatImages = (afwImage.ImageF, afwImage.ImageD) 

44 self.bbox = Box2I(minimum=Point2I(3, 4), maximum=Point2I(9, 7)) 

45 

46 def doRoundTrip(self, image, compression=None, scaling=None): 

47 if compression is None: 

48 compression = dict(algorithm=ImageCompressionOptions.NONE) 

49 if scaling is None: 

50 scaling = dict(algorithm=ImageScalingOptions.NONE, bitpix=0) 

51 options = ImageWriteOptions(compression=ImageCompressionOptions(**compression), 

52 scaling=ImageScalingOptions(**scaling)) 

53 isCompressed = (compression.get("algorithm", ImageCompressionOptions.NONE) 

54 != ImageCompressionOptions.NONE) 

55 with lsst.utils.tests.getTempFilePath(f"_{type(image).__name__}.fits") as filename: 

56 image.writeFits(filename, options=options) 

57 readImage = type(image)(filename) 

58 with astropy.io.fits.open(filename) as hduList: 

59 hdu = hduList[1 if isCompressed else 0] 

60 if hdu.data.dtype.byteorder != '=': 

61 hdu.data = hdu.data.byteswap().newbyteorder() 

62 return readImage, hdu 

63 

64 def runRoundTripTest(self, cls, compression=None, scaling=None, addNaN=False, checkAstropy=True, rtol=0): 

65 original = cls(self.bbox) 

66 original.array[:, :] = np.random.randint(size=original.array.shape, low=1, high=255, dtype=np.uint8) 

67 if addNaN: 

68 original[5, 6] = np.nan 

69 

70 readImage, hdu = self.doRoundTrip(original, compression=compression, scaling=scaling) 

71 self.assertImagesAlmostEqual(original, readImage, rtol=rtol, atol=0) 

72 

73 # Integer LSST images never have missing pixels; FITS floating-point images always use NaN 

74 self.assertNotIn("BLANK", hdu.header.keys()) 

75 

76 if checkAstropy: 

77 # Compare to what astropy reads, to more-or-less check that we're not abusing FITS 

78 hduImage = cls(hdu.data, deep=False, xy0=self.bbox.getMin()) 

79 self.assertImagesAlmostEqual(original, hduImage, rtol=rtol, atol=0) 

80 

81 def testIntegerUncompression(self): 

82 """Test round-tripping integer images with no compression or scaling. 

83 """ 

84 for cls in self.IntegerImages: 

85 with self.subTest(cls=cls.__name__): 

86 self.runRoundTripTest(cls) 

87 

88 def testIntegerCompression(self): 

89 """Test round-tripping integer images with compression (and no scaling). 

90 """ 

91 for cls in self.IntegerImages: 

92 with self.subTest(cls=cls.__name__): 

93 self.runRoundTripTest(cls, compression=dict(algorithm=ImageCompressionOptions.RICE)) 

94 

95 def testUInt64(self): 

96 """Test round-tripping uint64 images (ImageL). 

97 

98 uint64 is supported by a CFITSIO convention, not the FITS standard, 

99 so we can't read them correctly with astropy or compression. 

100 """ 

101 self.runRoundTripTest(afwImage.ImageL, checkAstropy=False) 

102 

103 def testFloatUncompressed(self): 

104 """Test round-tripping floating-point images with no compression.""" 

105 for cls in self.FloatImages: 

106 with self.subTest(cls=cls.__name__): 

107 self.runRoundTripTest(cls, addNaN=True) 

108 

109 def testFloatCompressedLossless(self): 

110 """Test round-tripping floating-point images with lossless compression.""" 

111 for cls in self.FloatImages: 

112 with self.subTest(cls=cls.__name__): 

113 self.runRoundTripTest( 

114 cls, 

115 compression=dict(algorithm=ImageCompressionOptions.GZIP, quantizeLevel=0), 

116 addNaN=True 

117 ) 

118 

119 @unittest.skip("Fix deferred to DM-15644") 

120 def testFloatCompressedRange(self): 

121 """Test round-tripping floating-point images with lossy compression 

122 and RANGE scaling.""" 

123 for cls in self.FloatImages: 

124 with self.subTest(cls=cls.__name__): 

125 self.runRoundTripTest( 

126 cls, 

127 compression=dict(algorithm=ImageCompressionOptions.GZIP, quantizeLevel=1), 

128 scaling=dict(algorithm=ImageScalingOptions.RANGE, bitpix=32, fuzz=False), 

129 addNaN=True, 

130 checkAstropy=True 

131 ) 

132 

133 @unittest.skip("Fix deferred to DM-15644") 

134 def testFloatCompressedManual(self): 

135 """Test round-tripping floating-point images with lossy compression 

136 and MANUAL scaling.""" 

137 for cls in self.FloatImages: 

138 with self.subTest(cls=cls.__name__): 

139 self.runRoundTripTest( 

140 cls, 

141 compression=dict(algorithm=ImageCompressionOptions.GZIP, quantizeLevel=1), 

142 scaling=dict(algorithm=ImageScalingOptions.MANUAL, bitpix=32, fuzz=False, 

143 bzero=3, bscale=2), 

144 addNaN=True, 

145 checkAstropy=True 

146 ) 

147 

148 

149class TestMemory(lsst.utils.tests.MemoryTestCase): 

150 pass 

151 

152 

153def setup_module(module): 

154 lsst.utils.tests.init() 

155 

156 

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

158 lsst.utils.tests.init() 

159 unittest.main()