Coverage for tests / test_stamps.py: 23%
123 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-26 09:07 +0000
1# This file is part of meas_algorithms.
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/>.
22import unittest
23import numpy as np
24import tempfile
26from lsst.meas.algorithms import stamps
27from lsst.afw import image as afwImage
28from lsst.afw.geom.testUtils import TransformTestBaseClass
29from lsst.daf.base import PropertyList
30import lsst.geom as geom
31import lsst.afw.geom.transformFactory as tF
32import lsst.utils.tests
34_RNG = np.random.Generator(np.random.MT19937(5))
37def make_stamps(n_stamps=3, use_archive=False):
38 stampSize = 25
39 # create dummy stamp stamps
40 stampImages = [afwImage.MaskedImageF(stampSize, stampSize)
41 for _ in range(n_stamps)]
42 for stampIm in stampImages:
43 stampImArray = stampIm.image.array
44 stampImArray += _RNG.random((stampSize, stampSize))
45 stampMaskArray = stampIm.mask.array
46 stampMaskArray += 10
47 stampVarArray = stampIm.variance.array
48 stampVarArray += 1000.
49 ras = _RNG.random(n_stamps)*360.
50 decs = _RNG.random(n_stamps)*180 - 90
51 archive_elements = [tF.makeTransform(geom.AffineTransform(_RNG.random(2))) if use_archive else None
52 for _ in range(n_stamps)]
54 metadata = PropertyList()
55 metadata['RA_DEG'] = ras
56 metadata['DEC_DEG'] = decs
58 stamp_list = [stamps.Stamp(stamp_im=stampIm,
59 position=geom.SpherePoint(geom.Angle(ra, geom.degrees),
60 geom.Angle(dec, geom.degrees)),
61 archive_element=ae,
62 metadata=metadata)
63 for stampIm, ra, dec, ae in zip(stampImages, ras, decs, archive_elements)]
65 return stamps.Stamps(stamp_list, metadata=metadata, use_archive=True)
68class StampsBaseTestCase(lsst.utils.tests.TestCase):
69 """Test StampsBase.
70 """
71 def testReadFitsWithOptionsNotImplementedErrorRaised(self):
72 """
73 Test that subclasses have their own version
74 of this implemented or an error is raised.
75 """
76 class FakeStampsBase(stamps.StampsBase):
77 def __init__(self):
78 return
80 with self.assertRaises(NotImplementedError):
81 FakeStampsBase.readFitsWithOptions('noFile', {})
83 def testReadFitsWithOptionsMetadataError(self):
84 """Test that error is raised when STAMPCLS returns None
85 """
86 with tempfile.NamedTemporaryFile() as f:
87 ss = make_stamps()
88 emptyMetadata = PropertyList()
89 stamps.writeFits(
90 f.name, [ss[0]], emptyMetadata, None, True, True
91 )
92 with self.assertRaises(RuntimeError):
93 stamps.StampsBase.readFits(f.name)
95 def testReadFitsReturnsNewClass(self):
96 """Test that readFits will return subclass
97 """
98 class FakeStampsBase(stamps.StampsBase):
99 def __init__(self):
100 self._metadata = {}
101 return
103 @classmethod
104 def readFitsWithOptions(cls, filename, options):
105 return cls()
107 def _refresh_metadata(self):
108 self._metadata = {}
110 fakeStamps = FakeStampsBase.readFitsWithOptions('noFile', {})
111 self.assertEqual(type(fakeStamps), FakeStampsBase)
114class StampsTestCase(lsst.utils.tests.TestCase):
115 """Test Stamps.
116 """
117 def testAppend(self):
118 """Test ability to append to a Stamps object
119 """
120 ss = make_stamps()
121 s = ss[-1]
122 ss.append(s)
123 self.roundtrip(ss)
124 # check if appending something other than a Stamp raises
125 with self.assertRaises(ValueError):
126 ss.append('hello world')
128 def testExtend(self):
129 ss = make_stamps()
130 ss2 = make_stamps()
131 ss.extend([s for s in ss2])
132 # check if extending with something other than a Stamps
133 # object raises
134 with self.assertRaises(ValueError):
135 ss.extend(['hello', 'world'])
137 def testIO(self):
138 """Test the class' write and readFits methods.
139 """
140 self.roundtrip(make_stamps())
142 def testIOone(self):
143 """Test the class' write and readFits methods for the special case of
144 one stamp.
145 """
146 self.roundtrip(make_stamps(1))
148 def testIOsub(self):
149 """Test the class' write and readFits when passing on a bounding box.
150 """
151 bbox = geom.Box2I(geom.Point2I(3, 9), geom.Extent2I(11, 7))
152 ss = make_stamps()
153 with tempfile.NamedTemporaryFile() as f:
154 ss.writeFits(f.name)
155 options = {'bbox': bbox}
156 subStamps = stamps.Stamps.readFitsWithOptions(f.name, options)
157 for s1, s2 in zip(ss, subStamps):
158 self.assertEqual(bbox.getDimensions(), s2.stamp_im.getDimensions())
159 self.assertMaskedImagesAlmostEqual(s1.stamp_im[bbox], s2.stamp_im)
161 def testIOarchive(self):
162 """Test the class' write and readFits when Stamps contain Persistables.
163 """
164 self.roundtripWithArchive(make_stamps(use_archive=True))
166 def testMetadata(self):
167 """Test that metadata is correctly written and read.
168 """
169 stamps = make_stamps()
170 for stamp in stamps:
171 self.assertTrue(stamp.metadata is not None)
172 self.assertIn('RA_DEG', stamp.metadata)
173 self.assertIn('DEC_DEG', stamp.metadata)
175 def roundtrip(self, ss):
176 """Round trip a Stamps object to disk and check values
177 """
178 with tempfile.NamedTemporaryFile() as f:
179 ss.writeFits(f.name)
180 options = PropertyList()
181 ss2 = stamps.Stamps.readFitsWithOptions(f.name, options)
182 self.assertEqual(len(ss), len(ss2))
183 for s1, s2 in zip(ss, ss2):
184 self.assertMaskedImagesAlmostEqual(s1.stamp_im, s2.stamp_im)
185 self.assertAlmostEqual(s1.position.getRa().asDegrees(),
186 s2.position.getRa().asDegrees())
187 self.assertAlmostEqual(s1.position.getDec().asDegrees(),
188 s2.position.getDec().asDegrees())
190 for k, v in s1.metadata.items():
191 self.assertIn(k, s2.metadata)
192 self.assertAlmostEqual(v, s2.metadata[k])
194 def roundtripWithArchive(self, ss):
195 """Round trip a Stamps object, including Archive elements, and check values
196 """
197 transformTest = TransformTestBaseClass()
198 with tempfile.NamedTemporaryFile() as f:
199 ss.writeFits(f.name)
200 options = PropertyList()
201 ss2 = stamps.Stamps.readFitsWithOptions(f.name, options)
202 self.assertEqual(len(ss), len(ss2))
203 for s1, s2 in zip(ss, ss2):
204 self.assertMaskedImagesAlmostEqual(s1.stamp_im, s2.stamp_im)
205 self.assertAlmostEqual(s1.position.getRa().asDegrees(),
206 s2.position.getRa().asDegrees())
207 self.assertAlmostEqual(s1.position.getDec().asDegrees(),
208 s2.position.getDec().asDegrees())
209 transformTest.assertTransformsEqual(s1.archive_element, s2.archive_element)
212class MemoryTester(lsst.utils.tests.MemoryTestCase):
213 pass
216def setup_module(module):
217 lsst.utils.tests.init()
220if __name__ == "__main__": 220 ↛ 221line 220 didn't jump to line 221 because the condition on line 220 was never true
221 lsst.utils.tests.init()
222 unittest.main()