Coverage for tests / test_color_image.py: 27%

67 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-23 08:27 +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 RoundtripNdf, 

22 assert_images_equal, 

23 assert_projections_equal, 

24 make_random_projection, 

25) 

26 

27try: 

28 import h5py 

29 

30 from lsst.images.ndf import _hds 

31 

32 HAVE_H5PY = True 

33except ImportError: 

34 HAVE_H5PY = False 

35 

36 

37class ColorImageTestCase(unittest.TestCase): 

38 """Tests for ColorImage.""" 

39 

40 def setUp(self) -> None: 

41 self.maxDiff = None 

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

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

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

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

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

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

48 

49 def test_properties(self) -> None: 

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

51 setUp. 

52 """ 

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

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

55 assert_images_equal( 

56 self, 

57 self.color_image.red, 

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

59 expect_view="array", 

60 ) 

61 assert_images_equal( 

62 self, 

63 self.color_image.green, 

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

65 expect_view="array", 

66 ) 

67 assert_images_equal( 

68 self, 

69 self.color_image.blue, 

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

71 expect_view="array", 

72 ) 

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

74 

75 def test_constructor(self) -> None: 

76 """Test alternate constructor arguments.""" 

77 self.assert_color_images_equal( 

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

79 self.color_image, 

80 expect_view=True, 

81 ) 

82 self.assert_color_images_equal( 

83 ColorImage.from_channels( 

84 self.color_image.red, 

85 self.color_image.green, 

86 self.color_image.blue, 

87 projection=self.projection, 

88 ), 

89 self.color_image, 

90 expect_view=False, 

91 ) 

92 

93 def test_fits_roundtrip(self) -> None: 

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

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

96 pass 

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

98 

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

100 def test_ndf_roundtrip(self) -> None: 

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

102 with RoundtripNdf(self, self.color_image) as roundtrip: 

103 pass 

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

105 

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

107 def test_ndf_layout(self) -> None: 

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

109 with RoundtripNdf(self, self.color_image) as roundtrip: 

110 f = roundtrip.inspect() 

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

112 self.assertIn("LSST", f) 

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

114 self.assertNotIn("MORE", f) 

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

116 with self.subTest(channel=channel): 

117 self.assertIn(channel, f) 

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

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

120 np.testing.assert_array_equal( 

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

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

123 ) 

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

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

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

127 

128 def assert_color_images_equal( 

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

130 ) -> None: 

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

132 in setUp. 

133 """ 

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

135 if expect_view is not None: 

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

137 if not expect_view: 

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

139 

140 

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

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

143 if isinstance(val, bytes): 

144 return val.decode("ascii") 

145 return str(val) 

146 

147 

148if __name__ == "__main__": 

149 unittest.main()