Coverage for tests/test_color_image.py: 26%
72 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 09:00 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-05-30 09:00 +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 RoundtripJson,
22 RoundtripNdf,
23 assert_images_equal,
24 assert_projections_equal,
25 make_random_projection,
26)
28try:
29 import h5py
31 from lsst.images.ndf import _hds
33 HAVE_H5PY = True
34except ImportError:
35 HAVE_H5PY = False
38class ColorImageTestCase(unittest.TestCase):
39 """Tests for ColorImage."""
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)
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)
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 )
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)
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)
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)
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")
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)
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)
159if __name__ == "__main__":
160 unittest.main()