Coverage for tests/test_color_image.py: 26%

72 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-03 08:10 +0000

1# This file is part of lsst-images. 

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# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12from __future__ import annotations 

13 

14import unittest 

15 

16import numpy as np 

17 

18from lsst.images import Box, ColorImage, Image, TractFrame 

19from lsst.images.tests import ( 

20 RoundtripFits, 

21 RoundtripJson, 

22 RoundtripNdf, 

23 assert_images_equal, 

24 assert_projections_equal, 

25 make_random_projection, 

26) 

27 

28try: 

29 import h5py 

30 

31 from lsst.images.ndf import _hds 

32 

33 HAVE_H5PY = True 

34except ImportError: 

35 HAVE_H5PY = False 

36 

37 

38class ColorImageTestCase(unittest.TestCase): 

39 """Tests for ColorImage.""" 

40 

41 def setUp(self) -> None: 

42 self.maxDiff = None 

43 self.rng = np.random.default_rng(500) 

44 self.pixel_frame = TractFrame(skymap="test_skymap", tract=33, bbox=Box.factory[:50, :64]) 

45 self.bbox = Box.factory[20:25, 40:48] 

46 self.projection = make_random_projection(self.rng, self.pixel_frame, self.pixel_frame.bbox) 

47 self.array = self.rng.integers(low=0, high=255, size=self.bbox.shape + (3,), dtype=np.uint8) 

48 self.color_image = ColorImage(self.array, bbox=self.bbox, projection=self.projection) 

49 

50 def test_properties(self) -> None: 

51 """Test the properties of the nominal ColorImage constructed in 

52 setUp. 

53 """ 

54 self.assertEqual(self.color_image.bbox, self.bbox) 

55 self.assertTrue(np.may_share_memory(self.color_image.array, self.array)) 

56 assert_images_equal( 

57 self, 

58 self.color_image.red, 

59 Image(self.array[:, :, 0], bbox=self.bbox, projection=self.projection), 

60 expect_view="array", 

61 ) 

62 assert_images_equal( 

63 self, 

64 self.color_image.green, 

65 Image(self.array[:, :, 1], bbox=self.bbox, projection=self.projection), 

66 expect_view="array", 

67 ) 

68 assert_images_equal( 

69 self, 

70 self.color_image.blue, 

71 Image(self.array[:, :, 2], bbox=self.bbox, projection=self.projection), 

72 expect_view="array", 

73 ) 

74 assert_projections_equal(self, self.color_image.projection, self.projection, expect_identity=True) 

75 

76 def test_constructor(self) -> None: 

77 """Test alternate constructor arguments.""" 

78 self.assert_color_images_equal( 

79 ColorImage(self.array, start=self.bbox.start, projection=self.projection), 

80 self.color_image, 

81 expect_view=True, 

82 ) 

83 self.assert_color_images_equal( 

84 ColorImage.from_channels( 

85 self.color_image.red, 

86 self.color_image.green, 

87 self.color_image.blue, 

88 projection=self.projection, 

89 ), 

90 self.color_image, 

91 expect_view=False, 

92 ) 

93 

94 def test_fits_roundtrip(self) -> None: 

95 """Test round-tripping through FITS, via the butler if available.""" 

96 with RoundtripFits(self, self.color_image, "ColorImage") as roundtrip: 

97 pass 

98 self.assert_color_images_equal(roundtrip.result, self.color_image, expect_view=False) 

99 

100 @unittest.skipUnless(HAVE_H5PY, "h5py is not installed") 

101 def test_ndf_roundtrip(self) -> None: 

102 """Test round-tripping through NDF.""" 

103 with RoundtripNdf(self, self.color_image, "ColorImage") as roundtrip: 

104 pass 

105 self.assert_color_images_equal(roundtrip.result, self.color_image, expect_view=False) 

106 

107 def test_fits_json_consistency(self) -> None: 

108 """FITS and JSON backends produce equal ColorImages on round-trip.""" 

109 with ( 

110 RoundtripFits(self, self.color_image) as fits_rt, 

111 RoundtripJson(self, self.color_image) as json_rt, 

112 ): 

113 self.assert_color_images_equal(fits_rt.result, self.color_image, expect_view=False) 

114 self.assert_color_images_equal(json_rt.result, self.color_image, expect_view=False) 

115 self.assert_color_images_equal(fits_rt.result, json_rt.result, expect_view=False) 

116 

117 @unittest.skipUnless(HAVE_H5PY, "h5py is not installed") 

118 def test_ndf_layout(self) -> None: 

119 """ColorImage writes a top-level container with RGB child NDFs.""" 

120 with RoundtripNdf(self, self.color_image, "ColorImage") as roundtrip: 

121 f = roundtrip.inspect() 

122 self.assertEqual(_cls(f["/"]), "EXT") 

123 self.assertIn("LSST", f) 

124 self.assertIn("JSON", f["/LSST"]) 

125 self.assertNotIn("MORE", f) 

126 for channel, index in (("RED", 0), ("GREEN", 1), ("BLUE", 2)): 

127 with self.subTest(channel=channel): 

128 self.assertIn(channel, f) 

129 self.assertEqual(_cls(f[channel]), "NDF") 

130 self.assertEqual(_cls(f[f"{channel}/DATA_ARRAY"]), "ARRAY") 

131 np.testing.assert_array_equal( 

132 f[f"{channel}/DATA_ARRAY/DATA"][()], 

133 self.array[:, :, index], 

134 ) 

135 self.assertEqual(list(f[f"{channel}/DATA_ARRAY/ORIGIN"][()]), [40, 20]) 

136 self.assertIn("WCS", f[channel]) 

137 self.assertEqual(_cls(f[f"{channel}/WCS"]), "WCS") 

138 

139 def assert_color_images_equal( 

140 self, a: ColorImage, b: ColorImage, expect_view: bool | None = None 

141 ) -> None: 

142 """Check that the given ColorImage matches the nominal one constructed 

143 in setUp. 

144 """ 

145 assert_projections_equal(self, a.projection, b.projection) 

146 if expect_view is not None: 

147 self.assertEqual(np.may_share_memory(a.array, b.array), expect_view) 

148 if not expect_view: 

149 np.testing.assert_array_equal(a.array, b.array) 

150 

151 

152def _cls(node: h5py.Group) -> str: 

153 val = node.attrs.get(_hds.ATTR_CLASS) 

154 if isinstance(val, bytes): 

155 return val.decode("ascii") 

156 return str(val) 

157 

158 

159if __name__ == "__main__": 

160 unittest.main()