Coverage for tests/test_trailedEdgeSources.py: 18%
193 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-11 11:26 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-11 11:26 +0000
1#
2# This file is part of meas_extensions_trailedSources.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (http://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
24import numpy as np
25import unittest
26import lsst.utils.tests
27import lsst.meas.extensions.trailedSources
28from lsst.meas.base.tests import AlgorithmTestCase
29from lsst.utils.tests import classParameters
30from lsst.geom import Point2I, Point2D, Box2I, Extent2I
31from unittest.mock import patch
33# Trailed-source length, angle, and centroid coordinates.
34trail_lengths = np.array([5, 5, 10, 4])
35trail_angles = np.array([100, 0, 5, 4])
36trail_x_coords = np.array([100, 20, -20, 90])
37trail_y_coords = np.array([100, 20, -30, 100])
40class TrailedEdgeSource:
41 """Holds a set of true trail parameters.
42 """
44 def __init__(self, instFlux, length, angle, xc, yc):
45 self.instFlux = instFlux
46 self.length = length
47 self.angle = angle
48 self.center = Point2D(xc, yc)
49 self.x0 = xc - length / 2 * np.cos(angle)
50 self.y0 = yc - length / 2 * np.sin(angle)
51 self.x1 = xc + length / 2 * np.cos(angle)
52 self.y1 = yc + length / 2 * np.sin(angle)
55class TrailedTaskSetup:
57 def makeTrailedSourceMeasurementTask(self, plugin=None, dependencies=(),
58 config=None, schema=None,
59 algMetadata=None):
60 """Set up a measurement task for a trailed source plugin.
61 """
62 config = self.makeSingleFrameMeasurementConfig(plugin=plugin,
63 dependencies=dependencies)
65 # Make sure the shape slot is base_SdssShape
66 config.slots.shape = "base_SdssShape"
67 return self.makeSingleFrameMeasurementTask(plugin=plugin,
68 dependencies=dependencies,
69 config=config,
70 schema=schema,
71 algMetadata=algMetadata)
74# "Extend" meas.base.tests.TestDataset
75class TrailedTestDataset(lsst.meas.base.tests.TestDataset):
76 """A dataset for testing trailed source measurements.
77 Given a `TrailedSource`, construct a record of the true values and an
78 Exposure.
79 """
81 def __init__(self, bbox, threshold=10.0, exposure=None, **kwds):
83 super().__init__(bbox, threshold, exposure, **kwds)
85 def addTrailedSource(self, trail, edge=True):
86 """Add a trailed source to the simulation.
88 Re-implemented version of
89 `lsst.meas.base.tests.TestDataset.addSource`. Numerically integrates a
90 Gaussian PSF over a line to obtain an image of a trailed source and
91 adds edge flags to the image.
92 """
93 record = self.catalog.addNew()
94 record.set(self.keys["centroid"], trail.center)
95 rng = np.random.default_rng(32)
96 covariance = rng.normal(0, 0.1, 4).reshape(2, 2)
97 covariance[0, 1] = covariance[1, 0]
98 record.set(self.keys["centroid_sigma"], covariance.astype(np.float32))
99 record.set(self.keys["shape"], self.psfShape)
100 record.set(self.keys["isStar"], False)
102 # Sum the psf at each
103 numIter = int(2 * trail.length)
104 xp = np.linspace(trail.x0, trail.x1, num=numIter)
105 yp = np.linspace(trail.y0, trail.y1, num=numIter)
106 for (x, y) in zip(xp, yp):
107 pt = Point2D(x, y)
108 im = self.drawGaussian(self.exposure.getBBox(), trail.instFlux,
109 lsst.afw.geom.Ellipse(self.psfShape, pt))
110 self.exposure.getMaskedImage().getImage().getArray()[:, :] += im.getArray()
112 planes = self.exposure.mask.getMaskPlaneDict()
113 dim = self.exposure.getBBox().getDimensions()
115 # Add edge flags to the first and last 20 columns and rows.
116 if edge:
117 for y in range(20):
118 self.exposure.mask.setMaskPlaneValues(planes['EDGE'], 0, dim[0] - 1, y)
119 self.exposure.mask.setMaskPlaneValues(planes['EDGE'], 0, dim[0] - 1, y + dim[1] - 20)
121 for y in range(dim[1]):
122 self.exposure.mask.setMaskPlaneValues(planes['EDGE'], 0, 20, y)
123 self.exposure.mask.setMaskPlaneValues(planes['EDGE'], dim[0] - 20, dim[0] - 1, y)
125 totFlux = self.exposure.image.array.sum()
126 self.exposure.image.array /= totFlux
127 self.exposure.image.array *= trail.instFlux
129 record.set(self.keys["instFlux"], trail.instFlux)
130 self._installFootprint(record, self.exposure.getImage())
132 return record, self.exposure.getImage()
135# Following from test_trailedSources
136@classParameters(length=trail_lengths, theta=trail_angles, xc=trail_x_coords, yc=trail_y_coords)
137class TrailedEdgeSourcesTestCase(AlgorithmTestCase, lsst.utils.tests.TestCase):
138 """ Test if ext_trailedSources_Naive_flag_edge is set correctly.
140 Given a `TrailedSource`, test if the edge flag is set correctly in the
141 source catalog after the
142 `lsst.meas.extensions.trailedSources.Naive.Plugin.makeTrailedSourceMeasurementTask`
143 has been run on the source catalog.
144 """
146 def setUp(self):
147 self.center = Point2D(50.1, 49.8)
148 self.bbox = Box2I(lsst.geom.Point2I(-20, -30), Extent2I(140, 160))
149 self.dataset = TrailedTestDataset(self.bbox)
151 # Trail which extends into edge pixels
152 self.trail = TrailedEdgeSource(100000.0, self.length, self.theta, self.xc, self.yc)
153 self.dataset.addTrailedSource(self.trail)
155 def tearDown(self):
156 del self.center
157 del self.bbox
158 del self.trail
159 del self.dataset
161 def testEdgeFlag(self):
162 """Test if edge flags are correctly set in NaivePlugin.py
164 Given a `TrailedTestDataset`, run the NaivePlugin measurement and
165 check that the trailed sources have the edge flag set. [100,100] does
166 not contain any edge pixels and should not have a flag set, [20,20]
167 crosses into the edge region on only one side and should have the edge
168 flag set, and [-20,-30] extends off the chip and should have the edge
169 flag set.
170 """
171 # Set up and run Naive measurement.
172 task = TrailedTaskSetup.makeTrailedSourceMeasurementTask(self,
173 plugin="ext_trailedSources_Naive",
174 dependencies=("base_SdssCentroid",
175 "base_SdssShape")
176 )
177 exposure, catalog = self.dataset.realize(5.0, task.schema, randomSeed=0)
178 task.run(catalog, exposure)
179 record = catalog[0]
181 # Check that x0, y0 or x1, y1 is flagged as an edge pixel
182 x1 = int(record['ext_trailedSources_Naive_x1'])
183 y1 = int(record['ext_trailedSources_Naive_y1'])
184 x0 = int(record['ext_trailedSources_Naive_x0'])
185 y0 = int(record['ext_trailedSources_Naive_y0'])
187 # Test Case with no edge pixels
188 if record['truth_x'] == 100:
189 # These are used to ensure the mask pixels the trailed sources are
190 # compared with have the correct flags set
191 begin_edge_pixel_set = (exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask(
192 'EDGE') != 0)
193 end_edge_pixel_set = (exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask(
194 'EDGE') != 0)
196 self.assertFalse(begin_edge_pixel_set)
197 self.assertTrue(end_edge_pixel_set)
199 # Make sure measurement edge flag is set, but Naive_flag is not.
200 # A failed trailed source measurement with the edge flag
201 # set means the edge flag was set despite the measurement
202 # failing.
203 self.assertTrue(record.get("ext_trailedSources_Naive_flag_edge"))
204 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
205 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
206 self.assertFalse(record.get("ext_trailedSources_Naive_flag_nan"))
208 x1 = int(record['ext_trailedSources_Naive_x1'])
209 y1 = int(record['ext_trailedSources_Naive_y1'])
211 self.assertFalse(exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
212 self.assertTrue(exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
214 # Test case with one end of trail containing edge pixels
215 elif record['truth_x'] == 20:
216 begin_edge_pixel_set = (exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask(
217 'EDGE') != 0)
218 end_edge_pixel_set = (exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask(
219 'EDGE') != 0)
221 self.assertFalse(begin_edge_pixel_set)
222 self.assertFalse(end_edge_pixel_set)
224 # Make sure measurement Naive_flag_edge and Naive_flag not set
225 self.assertFalse(record.get("ext_trailedSources_Naive_flag_edge"))
226 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
227 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
228 self.assertFalse(record.get("ext_trailedSources_Naive_flag_nan"))
230 x1 = int(record['ext_trailedSources_Naive_x1'])
231 y1 = int(record['ext_trailedSources_Naive_y1'])
233 self.assertFalse(exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
234 self.assertFalse(exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
236 # Test case trail fully contained
237 elif record["truth_x"] == 90:
238 begin_edge_pixel_set = (exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask(
239 'EDGE') != 0)
240 end_edge_pixel_set = (exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask(
241 'EDGE') != 0)
243 self.assertFalse(begin_edge_pixel_set)
244 self.assertFalse(end_edge_pixel_set)
246 # Make sure measurement Naive_flag_edge and Naive_flag not set
247 self.assertFalse(record.get("ext_trailedSources_Naive_flag_edge"))
248 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
249 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
250 self.assertFalse(record.get("ext_trailedSources_Naive_flag_nan"))
252 x1 = int(record['ext_trailedSources_Naive_x1'])
253 y1 = int(record['ext_trailedSources_Naive_y1'])
255 self.assertFalse(exposure.mask[Point2I(x0, y0)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
256 self.assertFalse(exposure.mask[Point2I(x1, y1)] & exposure.mask.getPlaneBitMask('EDGE') != 0)
258 # Test case with trailed source extending off chip.
259 else:
260 self.assertEquals(record['truth_x'], -20)
261 self.assertTrue(record.get("ext_trailedSources_Naive_flag_edge"))
262 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
263 self.assertTrue(record.get("ext_trailedSources_Naive_flag_off_image"))
264 self.assertFalse(record.get("ext_trailedSources_Naive_flag_nan"))
266 def testNanFlag(self):
267 """Test if nan flags are correctly set in NaivePlugin.py
269 Given a `TrailedTestDataset`, run the NaivePlugin measurement which
270 has trailed sources where one of the end point values results in a
271 nan.
272 """
273 # Set up and run Naive measurement.
274 task = TrailedTaskSetup.makeTrailedSourceMeasurementTask(self,
275 plugin="ext_trailedSources_Naive",
276 dependencies=("base_SdssCentroid",
277 "base_SdssShape")
278 )
280 exposure, catalog = self.dataset.realize(5.0, task.schema, randomSeed=0)
282 original_check_trail_function = task.plugins['ext_trailedSources_Naive'].check_trail
283 # Used to simulate a trailed source where one of the coordinates is a
284 # nan.
286 def check_trail_mock(*args, **kwargs):
287 measRecord = args[0]
288 exposure = args[1]
289 x0 = args[2]
290 y0 = args[3]
291 x1 = args[4]
292 y1 = np.nan # overriding to test NAN flagging
293 length = args[6]
294 measRecord['ext_trailedSources_Naive_y1'] = np.nan
295 return original_check_trail_function(measRecord, exposure, x0, y0, x1, y1, length)
297 # This patcher mocks check_trail so that one of the trailed sources
298 # includes it is checking contains a nan at one of its endpoints.
299 patcher = patch(
300 'lsst.meas.extensions.trailedSources.NaivePlugin.SingleFrameNaiveTrailPlugin.check_trail',
301 side_effect=check_trail_mock)
302 patcher.start()
303 task.run(catalog, exposure)
304 record = catalog[0]
306 # Test Case with no edge pixels, but one is set to nan.
307 if record['truth_x'] == 100:
308 self.assertFalse(record.get("ext_trailedSources_Naive_flag_edge"))
309 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
310 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
311 self.assertTrue(record.get("ext_trailedSources_Naive_flag_nan"))
313 # Test case with one end of trail containing edge pixels, but nan is
314 # set so edge does not end up set.
315 elif record['truth_x'] == 20:
317 self.assertFalse(record.get("ext_trailedSources_Naive_flag_edge"))
318 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
319 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
320 self.assertTrue(record.get("ext_trailedSources_Naive_flag_nan"))
322 # Test case trail fully contained, but contains one nan. Only nan flag
323 # is set.
324 elif record["truth_x"] == 90:
326 self.assertFalse(record.get("ext_trailedSources_Naive_flag_edge"))
327 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
328 self.assertFalse(record.get("ext_trailedSources_Naive_flag_off_image"))
329 self.assertTrue(record.get("ext_trailedSources_Naive_flag_nan"))
331 # Test case with trailed source extending off chip. One coordinate
332 # is off image the other is nan, so edge, off_image, and nan should
333 # be set.
334 else:
335 self.assertEquals(record['truth_x'], -20)
336 self.assertTrue(record.get("ext_trailedSources_Naive_flag_edge"))
337 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
338 self.assertTrue(record.get("ext_trailedSources_Naive_flag_off_image"))
339 self.assertTrue(record.get("ext_trailedSources_Naive_flag_nan"))
341 patcher.stop()
344@classParameters(length=[10], theta=[5], xc=[-20], yc=[-30])
345class TrailedEdgeSourcesOffImageTest(AlgorithmTestCase, lsst.utils.tests.TestCase):
346 """ Test if ext_trailedSources_Naive_flag_edge is set correctly.
348 Given a `TrailedSource`, test if the edge flag is set correctly in the
349 source catalog after the
350 'lsst.meas.extensions.trailedSources.Naive.Plugin.makeTrailedSourceMeasurementTask'
351 has been run on the source catalog.
352 """
354 def setUp(self):
355 self.center = Point2D(50.1, 49.8)
356 self.bbox = Box2I(lsst.geom.Point2I(-20, -30), Extent2I(140, 160))
357 self.dataset = TrailedTestDataset(self.bbox)
359 # Trail which extends into edge pixels
360 self.trail = TrailedEdgeSource(100000.0, self.length, self.theta,
361 self.xc, self.yc)
362 self.dataset.addTrailedSource(self.trail, edge=False)
364 def tearDown(self):
365 del self.center
366 del self.bbox
367 del self.trail
368 del self.dataset
370 def testOffImageEdgeFlag(self):
371 """Test if edge flags are correctly set in NaivePlugin.py when source
372 extends off the the image.
374 Given a `TrailedTestDataset`, run the NaivePlugin measurement and
375 check that the edge flag set when a source extends off the chip.
376 Edge pixels are not set in this test.
377 """
378 # Set up and run Naive measurement.
379 task = TrailedTaskSetup.makeTrailedSourceMeasurementTask(self,
380 plugin="ext_trailedSources_Naive",
381 dependencies=("base_SdssCentroid",
382 "base_SdssShape")
383 )
384 exposure, catalog = self.dataset.realize(5.0, task.schema, randomSeed=0)
385 task.run(catalog, exposure)
386 record = catalog[0]
388 self.assertTrue(record.get("ext_trailedSources_Naive_flag_edge"))
389 self.assertFalse(record.get("ext_trailedSources_Naive_flag"))
392class TestMemory(lsst.utils.tests.MemoryTestCase):
393 pass
396def setup_module(module):
397 lsst.utils.tests.init()
400if __name__ == "__main__": 400 ↛ 401line 400 didn't jump to line 401, because the condition on line 400 was never true
401 lsst.utils.tests.init()
402 unittest.main()