Coverage for tests / test_color_image.py: 27%
67 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-20 08:26 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-20 08:26 +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.
12from __future__ import annotations
14import unittest
16import numpy as np
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)
27try:
28 import h5py
30 from lsst.images.ndf import _hds
32 HAVE_H5PY = True
33except ImportError:
34 HAVE_H5PY = False
37class ColorImageTestCase(unittest.TestCase):
38 """Tests for ColorImage."""
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)
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)
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 )
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)
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)
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")
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)
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)
148if __name__ == "__main__":
149 unittest.main()