Coverage for tests / test_bbox.py: 13%
145 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:40 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 08:40 +0000
1# This file is part of scarlet_lite.
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# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22from copy import deepcopy
24import numpy as np
25from lsst.scarlet.lite import Box
26from utils import ScarletTestCase
29class TestBox(ScarletTestCase):
30 def check_bbox(self, bbox: Box, shape: tuple[int, ...], origin: tuple[int, ...]):
31 """Check the attributes and properties of a Box
33 Parameters
34 ----------
35 bbox:
36 The box to test.
37 shape:
38 The shape of the Box.
39 origin:
40 The origin of the Box.
41 """
42 self.assertTupleEqual(bbox.shape, shape)
43 self.assertTupleEqual(bbox.origin, origin)
44 self.assertEqual(bbox.ndim, len(shape))
45 self.assertTupleEqual(bbox.start, origin)
47 def test_constructors(self):
48 shape = (2, 3, 4)
49 origin = (0, 0, 0)
50 bbox = Box(shape)
51 self.check_bbox(bbox, shape=shape, origin=origin)
53 shape = (2, 4)
54 origin = (5, 6)
55 bbox = Box(shape, origin)
56 self.check_bbox(bbox, shape=shape, origin=origin)
58 bbox = Box.from_bounds((5, 7), (6, 10))
59 self.check_bbox(bbox, shape, origin)
61 def test_from_data(self):
62 x = np.arange(25).reshape(5, 5)
63 x[0] = 0
64 x[:, -2:] = 0
65 bbox = Box.from_data(x)
66 self.assertBoxEqual(bbox, Box((4, 3), origin=(1, 0)))
68 x += 10
69 bbox = Box.from_data(x)
70 self.assertBoxEqual(bbox, Box((5, 5), origin=(0, 0)))
72 bbox = Box.from_data(x, threshold=10)
73 self.assertBoxEqual(bbox, Box((4, 3), origin=(1, 0)))
75 bbox = Box.from_data(x, threshold=100)
76 self.assertBoxEqual(bbox, Box((0, 0), origin=(0, 0)))
78 def test_contains(self):
79 bbox = Box((6, 4, 3), origin=(0, 1, 0))
80 p = (2, 2, 2)
81 self.assertTrue(bbox.contains(p))
83 p = (3, 0, 3)
84 self.assertFalse(bbox.contains(p))
86 p = (7, 3, 3)
87 self.assertFalse(bbox.contains(p))
89 p = (3, 3, -1)
90 self.assertFalse(bbox.contains(p))
92 with self.assertRaises(ValueError):
93 bbox.contains((1, 2))
95 def test_properties(self):
96 shape = (10, 3, 8)
97 origin = (2, 7, 5)
98 bbox = Box(shape, origin)
99 self.assertTupleEqual(bbox.stop, (12, 10, 13))
100 self.assertTupleEqual(bbox.center, (7, 8.5, 9))
101 self.assertTupleEqual(bbox.bounds, ((2, 12), (7, 10), (5, 13)))
102 self.assertTupleEqual(bbox.shape, shape)
103 self.assertTupleEqual(bbox.origin, origin)
104 self.assertEqual(len(bbox.slices), 3)
105 self.assertEqual(bbox.slices[0], slice(2, 12))
106 self.assertEqual(bbox.slices[1], slice(7, 10))
107 self.assertEqual(bbox.slices[2], slice(5, 13))
108 self.assertEqual(hash(bbox), hash((shape, origin)))
110 def test_simple_methods(self):
111 shape = (2, 4, 8, 16)
112 origin = (9, 5, 3, 9)
113 bbox = Box(shape, origin)
114 self.check_bbox(bbox, shape, origin)
116 # Grow the box
117 grown = bbox.grow(3)
118 self.check_bbox(grown, (8, 10, 14, 22), (6, 2, 0, 6))
120 # Shift the box
121 shifted = bbox.shifted_by((0, 5, 2, 10))
122 self.check_bbox(shifted, shape, (9, 10, 5, 19))
124 def test_union(self):
125 bbox1 = Box((3, 4), (20, 34))
126 bbox2 = Box((10, 15), (1, 2))
127 bbox3 = Box((20, 30, 40), (10, 20, 30))
129 result = bbox1 | bbox2
130 truth = Box((22, 36), (1, 2))
131 self.assertBoxEqual(result, truth)
133 with self.assertRaises(ValueError):
134 bbox1 | bbox3
136 def test_intersection(self):
137 bbox1 = Box((3, 4), (20, 34))
138 bbox2 = Box((20, 30), (10, 20))
139 bbox3 = Box((20, 30, 40), (10, 20, 30))
141 result = bbox1 & bbox2
142 truth = Box((3, 4), (20, 34))
143 self.assertBoxEqual(result, truth)
145 with self.assertRaises(ValueError):
146 bbox1 & bbox3
148 def test_intersections(self):
149 bbox1 = Box((3, 4), (20, 34))
150 bbox2 = Box((10, 15), (1, 2))
151 bbox3 = Box((20, 30), (10, 20))
153 # Test intersection test
154 self.assertFalse(bbox1.intersects(bbox2))
155 self.assertTrue(bbox1.intersects(bbox3))
156 self.assertFalse(bbox2.intersects(bbox1))
157 self.assertFalse(bbox2.intersects(bbox3))
158 self.assertTrue(bbox3.intersects(bbox1))
160 # Test overlapping slices
161 slices = bbox1.overlapped_slices(bbox2)
162 self.assertTupleEqual(slices, ((slice(0, 0), slice(0, 0)), (slice(0, 0), slice(0, 0))))
163 slices = bbox1.overlapped_slices(bbox3)
164 self.assertTupleEqual(slices, ((slice(0, 3), slice(0, 4)), (slice(10, 13), (slice(14, 18)))))
166 def test_offset(self):
167 shape = (2, 5, 7)
168 origin = (82, 34, 15)
169 bbox = Box(shape, origin)
170 bbox = bbox + 1
171 self.assertBoxEqual(bbox, Box(shape, (83, 35, 16)))
173 def test_arithmetic(self):
174 shape = (2, 5, 7)
175 origin = (82, 34, 15)
176 bbox = Box(shape, origin)
178 # Check addition
179 shifted = bbox + (2, 4, 6)
180 self.check_bbox(bbox, shape, origin)
181 self.check_bbox(shifted, shape, (84, 38, 21))
183 # Check subtraction
184 shifted = bbox - (2, 4, 6)
185 self.check_bbox(bbox, shape, origin)
186 self.check_bbox(shifted, shape, (80, 30, 9))
188 # Check "matrix multiplication"
189 prebox = Box((2, 5), (3, 4))
190 new_box = prebox @ bbox
191 self.check_bbox(new_box, (2, 5, 2, 5, 7), (3, 4, 82, 34, 15))
193 # Check equality
194 bbox1 = Box((1, 2, 3), (2, 4, 6))
195 bbox2 = Box((1, 2, 3), (2, 4, 6))
196 bbox3 = Box((1, 2), (5, 6))
198 self.assertBoxEqual(bbox1, bbox2)
199 with self.assertRaises(AssertionError):
200 self.assertBoxEqual(bbox2, bbox3)
202 # Check a copy
203 bbox2 = bbox.copy()
204 self.assertBoxEqual(bbox, bbox2)
206 self.assertFalse(bbox1 == shape)
207 self.assertNotEqual(bbox1, bbox2)
208 self.assertEqual(bbox1, bbox1)
210 def test_slicing(self):
211 bbox = Box((1, 2, 3, 4), (2, 4, 6, 8))
212 # Check integer index
213 self.assertBoxEqual(bbox[2], Box((3,), (6,)))
214 # check slice index
215 self.assertBoxEqual(bbox[:3], Box((1, 2, 3), (2, 4, 6)))
216 # check tuple index
217 self.assertBoxEqual(bbox[(3, 1)], Box((4, 2), (8, 4)))
219 def test_shallow_copy(self):
220 bbox = Box((3, 4, 5), (10, 20, 30))
221 bbox_copy = bbox.copy()
222 self.assertBoxEqual(bbox, bbox_copy)
223 self.assertIsNot(bbox, bbox_copy)
225 def test_deepcopy(self):
226 bbox = Box((3, 4, 5), (10, 20, 30))
227 bbox_deepcopy = deepcopy(bbox)
228 self.assertBoxEqual(bbox, bbox_deepcopy)
229 self.assertIsNot(bbox, bbox_deepcopy)