Coverage for tests/test_defects.py: 16%
126 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-01 03:20 -0700
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-01 03:20 -0700
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 os
23import unittest
25import lsst.geom
26import lsst.afw.image as afwImage
27import lsst.meas.algorithms as algorithms
28import lsst.utils.tests
29from lsst.daf.base import PropertyList
30from lsst.ip.isr import Defects
32try:
33 type(display)
34except NameError:
35 display = False
36else:
37 import lsst.afw.display as afwDisplay
38 afwDisplay.setDefaultMaskTransparency(75)
40# Determine if we have afwdata
41try:
42 afwdataDir = lsst.utils.getPackageDir('afwdata')
43except Exception:
44 afwdataDir = None
46TESTDIR = os.path.abspath(os.path.dirname(__file__))
49class DefectsTestCase(lsst.utils.tests.TestCase):
50 """Tests for collections of Defect."""
52 def assertMetadata(self, first, second):
53 """Compare the metadata associated with Defects"""
55 # Must strip out DATE metadata before comparison
56 meta1 = first.getMetadata()
57 meta2 = second.getMetadata()
58 for d in (meta1, meta2):
59 for k in ("DATE", "CALIB_CREATION_DATE", "CALIB_CREATION_TIME"):
60 if k in d:
61 del d[k]
63 self.assertEqual(meta1, meta2)
64 meta1["NEW"] = "additional header"
65 self.assertNotEqual(first.getMetadata(), second.getMetadata())
66 del meta1["NEW"]
68 def test_defects(self):
69 defects = Defects()
71 defects.append(algorithms.Defect(lsst.geom.Box2I(lsst.geom.Point2I(5, 6),
72 lsst.geom.Point2I(41, 50))))
74 defects.append(lsst.geom.Box2I(lsst.geom.Point2I(0, 0),
75 lsst.geom.Point2I(4, 5)))
76 defects.append(lsst.geom.Point2I(50, 50))
77 defects.append(afwImage.DefectBase(lsst.geom.Box2I(lsst.geom.Point2I(100, 200),
78 lsst.geom.Extent2I(5, 5))))
79 self.assertEqual(len(defects), 4)
81 for d in defects:
82 self.assertIsInstance(d, algorithms.Defect)
84 # Transposition
85 transposed = defects.transpose()
86 self.assertEqual(len(transposed), len(defects))
88 # Check that an individual defect is found properly transposed within
89 # the outputs.
90 found = False
91 for defect in transposed:
92 if defect.getBBox() == lsst.geom.Box2I(lsst.geom.Point2I(6, 5), lsst.geom.Extent2I(45, 37)):
93 found = True
94 break
95 self.assertTrue(found)
97 # Serialization round trip
98 meta = PropertyList()
99 meta["TESTHDR"] = "testing"
100 defects.setMetadata(meta)
102 table = defects.toFitsRegionTable()
104 defects2 = Defects.fromTable([table])
106 self.assertEqual(defects2, defects)
108 # via FITS
109 with lsst.utils.tests.getTempFilePath(".fits") as tmpFile:
110 defects.writeFits(tmpFile)
111 defects2 = Defects.readFits(tmpFile)
113 # Equality tests the bounding boxes so metadata is tested separately.
114 self.assertEqual(defects2, defects)
115 self.assertMetadata(defects2, defects)
117 # via text file
118 with lsst.utils.tests.getTempFilePath(".ecsv") as tmpFile:
119 defects.writeText(tmpFile)
120 defects2 = Defects.readText(tmpFile)
122 # Equality tests the bounding boxes so metadata is tested separately.
123 self.assertEqual(defects2, defects)
124 self.assertMetadata(defects2, defects)
126 # Check bad values
127 with self.assertRaises(ValueError):
128 defects.append(lsst.geom.Box2D(lsst.geom.Point2D(0., 0.),
129 lsst.geom.Point2D(3.1, 3.1)))
130 with self.assertRaises(ValueError):
131 defects.append("defect")
133 def testAstropyRegion(self):
134 """Read a FITS region file created by Astropy regions."""
135 # The file contains three regions:
136 #
137 # - Point2I(340, 344)
138 # - Point2I(340, 344)
139 # - Box2I(minimum=Point2I(5, -5), dimensions=Extent2I(10, 20))
140 #
141 # The two coincident points are combined on read, so we end up with two
142 # defects.
144 with self.assertLogs():
145 defects = Defects.readFits(os.path.join(TESTDIR, "data", "fits_region.fits"),
146 normalize_on_init=True)
148 self.assertEqual(len(defects), 2)
150 def testLsstTextfile(self):
151 """Read legacy LSST text file format"""
152 with lsst.utils.tests.getTempFilePath(".txt") as tmpFile:
153 with open(tmpFile, "w") as fh:
154 print("""# X0 Y0 width height
155 996 0 56 24
156 0 4156 2048 20
157 0 0 17 4176
158 1998 4035 50 141
159 1023 0 2 4176
160 2027 0 21 4176
161 0 4047 37 129
162# Some rows without fixed column widths
16314 20 2000 50
16410 10 10 10
165""", file=fh)
167 defects = Defects.readLsstDefectsFile(tmpFile, normalize_on_init=True)
169 # Although there are 9 defects listed above, we record 11 after
170 # normalization. This is due to non-optimal behaviour in
171 # Defects.fromMask; see DM-24781.
172 self.assertEqual(len(defects), 11)
174 def test_normalize_defects(self):
175 """A test for the lsst.meas.algorithms.Defect.normalize() method.
176 """
177 defects = Defects()
179 # First series of 1-pixel contiguous defects
180 for yPix in range(1, 6):
181 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(15, yPix),
182 dimensions=lsst.geom.Extent2I(1, 1)))
184 # Defects are normalized as they are added; check that the above have
185 # been merged into a single bounding box.
186 self.assertEqual(len(defects), 1)
188 # Second series of 1-pixel contiguous defects in bulk mode
189 with defects.bulk_update():
190 for yPix in range(11, 16):
191 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(20, yPix),
192 dimensions=lsst.geom.Extent2I(1, 1)))
193 # In bulk mode, defects are not normalized.
194 self.assertEqual(len(defects), 6)
196 # Normalization applied on exiting bulk mode.
197 self.assertEqual(len(defects), 2)
199 boxesMeasured = []
200 for defect in defects:
201 boxesMeasured.append(defect.getBBox())
203 # The normalizing function should have created the following two boxes
204 # out of the individual 1-pixel defects from above
205 expectedDefects = [lsst.geom.Box2I(corner=lsst.geom.Point2I(15, 1),
206 dimensions=lsst.geom.Extent2I(1, 5)),
207 lsst.geom.Box2I(corner=lsst.geom.Point2I(20, 11),
208 dimensions=lsst.geom.Extent2I(1, 5))]
210 self.assertEqual(len(expectedDefects), len(boxesMeasured))
211 for expDef, measDef in zip(expectedDefects, boxesMeasured):
212 self.assertEqual(expDef, measDef)
214 # Normalize two distinct sets of Defects and ensure they compare to the
215 # same thing.
216 defects = Defects()
217 # Set 1
218 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1), dimensions=lsst.geom.Extent2I(1, 1)))
219 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 2), dimensions=lsst.geom.Extent2I(1, 1)))
220 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 3), dimensions=lsst.geom.Extent2I(1, 1)))
221 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 4), dimensions=lsst.geom.Extent2I(1, 1)))
222 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 5), dimensions=lsst.geom.Extent2I(1, 1)))
223 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 6), dimensions=lsst.geom.Extent2I(1, 1)))
224 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 7), dimensions=lsst.geom.Extent2I(1, 1)))
225 defects.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 8), dimensions=lsst.geom.Extent2I(1, 1)))
227 # Set 2
228 defects2 = Defects()
229 defects2.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1), dimensions=lsst.geom.Extent2I(1, 5)))
230 defects2.append(lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 5), dimensions=lsst.geom.Extent2I(1, 4)))
232 self.assertEqual(defects, defects2)
234 boxesMeasured, boxesMeasured2 = [], []
235 for defect, defect2 in zip(defects, defects2):
236 boxesMeasured.append(defect.getBBox())
237 boxesMeasured2.append(defect2.getBBox())
239 expectedDefects = [lsst.geom.Box2I(corner=lsst.geom.Point2I(25, 1),
240 dimensions=lsst.geom.Extent2I(1, 8))]
242 self.assertEqual(len(expectedDefects), len(boxesMeasured))
243 for expDef, measDef in zip(expectedDefects, boxesMeasured):
244 self.assertEqual(expDef, measDef)
246 self.assertEqual(len(expectedDefects), len(boxesMeasured2))
247 for expDef, measDef in zip(expectedDefects, boxesMeasured2):
248 self.assertEqual(expDef, measDef)
251class TestMemory(lsst.utils.tests.MemoryTestCase):
252 pass
255def setup_module(module):
256 lsst.utils.tests.init()
259if __name__ == "__main__": 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true
260 lsst.utils.tests.init()
261 unittest.main()