Coverage for tests / test_findGlintTrails.py: 15%

107 statements  

« 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/>. 

21 

22import unittest 

23 

24import numpy as np 

25 

26from lsst.meas.algorithms import findGlintTrails 

27import lsst.geom 

28import lsst.meas.base.tests 

29import lsst.utils.tests 

30 

31# Set this and have display_ds9 setup to see the fit on the image. 

32display = False 

33if display: 33 ↛ 34line 33 didn't jump to line 34 because the condition on line 33 was never true

34 import lsst.afw.display 

35 display = lsst.afw.display.Display() 

36 

37 

38class TestFindGlintTrails(lsst.utils.tests.TestCase): 

39 """Generate two glint trails and test that they are both found, while 

40 outlier points are not included. 

41 """ 

42 

43 def _make_image_3_trails(self): 

44 """Make an image with 3 trails on it, one of them negative. 

45 """ 

46 rng = np.random.default_rng(10) 

47 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-5, -4), lsst.geom.Point2I(1005, 1084)) 

48 dataset = lsst.meas.base.tests.TestDataset(bbox) 

49 flux = 10000 

50 

51 x0 = 200 

52 y0 = 300 

53 scale = 50 

54 # one outlier source near the first glint trail. 

55 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(x0 - 31, y0 - 27)) 

56 # two glint trails 

57 for i in range(8): 

58 dataset.addSource(instFlux=flux, 

59 centroid=lsst.geom.Point2D(i*scale + x0 + rng.random(), 

60 i*scale + y0 + rng.random())) 

61 

62 # A negative trail (e.g. came from the template) that shouldn't be 

63 # included in any of the fits. 

64 x0 = 170 

65 y0 = 690 

66 step = 70 

67 for i in range(6): 

68 dataset.addSource(instFlux=-flux, 

69 centroid=lsst.geom.Point2D(i*step + x0 + rng.random(), 

70 -i*step + y0 + rng.random()), 

71 negative=True) 

72 

73 # a source that shouldn't appear in the found trails 

74 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(500, 200)) 

75 

76 # Perpendicular trail that has a point lying on the same line as the 

77 # first trail. 

78 x0 = 380 

79 y0 = 760 

80 step = 70 

81 for i in range(5): 

82 dataset.addSource(instFlux=flux, 

83 centroid=lsst.geom.Point2D(i*step + x0 + rng.random(), 

84 -i*step + y0 + rng.random())) 

85 

86 schema = dataset.makeMinimalSchema() 

87 schema.addField("ip_diffim_DipoleFit_classification", type="Flag") 

88 exposure, catalog = dataset.realize(10.0, schema=schema) 

89 # So that the catalog ids don't look like simple indices when debugging. 

90 catalog["id"] += 100 

91 return exposure, catalog 

92 

93 def test_simple(self): 

94 """Test the basic operation of the glint finder on an image with two 

95 glint trails. 

96 """ 

97 exposure, catalog = self._make_image_3_trails() 

98 

99 config = findGlintTrails.FindGlintTrailsTask.ConfigClass() 

100 # Limit the search radius so that we can test the line-extension 

101 # part of the fitter. 

102 config.radius = 300 

103 # Use a tighter threshold for this simulated data, which has better 

104 # controlled centroids than real data. 

105 config.threshold = 1.5 

106 task = findGlintTrails.FindGlintTrailsTask(config=config) 

107 result = task.run(catalog) 

108 

109 if display: 

110 display.frame = 1 

111 display.image(exposure, title="something") 

112 for i, trail in enumerate(result.trails): 

113 display.centroids(trail, size=5, ctype="cyan", symbol=i) 

114 

115 self.assertEqual(len(result.trails), 2) 

116 # Note that if you add sources to the simulated catalog, the ids in 

117 # the fitted trails may change. 

118 self.assertSetEqual(set(result.trails[0]["id"]), 

119 {102, 103, 104, 105, 106, 107, 108, 109, 119}) 

120 self.assertSetEqual(set(result.trails[1]["id"]), 

121 {117, 118, 119, 120, 121}) 

122 self.assertSetEqual(result.trailed_ids, {102, 103, 104, 105, 106, 107, 108, 

123 109, 117, 118, 119, 120, 121}) 

124 # Expected lengths from the step size of the simulated trails. 

125 self.assertFloatsAlmostEqual(result.parameters[0].length, 

126 np.sqrt((7*50)**2 + (7*50)**2), 

127 rtol=1e-3) 

128 self.assertFloatsAlmostEqual(result.parameters[1].length, 

129 np.sqrt((4*70)**2 + (4*70)**2), 

130 rtol=1e-3) 

131 self.assertFloatsAlmostEqual(np.degrees(result.parameters[0].angle), 45.0, rtol=2e-3) 

132 self.assertFloatsAlmostEqual(np.degrees(result.parameters[1].angle), -45.0, rtol=2e-3) 

133 

134 def test_empty_image(self): 

135 """Test that no trails are found on an empty image.""" 

136 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-5, -4), lsst.geom.Point2I(1005, 1084)) 

137 dataset = lsst.meas.base.tests.TestDataset(bbox) 

138 schema = dataset.makeMinimalSchema() 

139 schema.addField("ip_diffim_DipoleFit_classification", type="Flag") 

140 exposure, catalog = dataset.realize(10.0, schema=schema) 

141 

142 task = findGlintTrails.FindGlintTrailsTask() 

143 result = task.run(catalog) 

144 

145 self.assertEqual(len(result.trails), 0) 

146 self.assertEqual(result.trailed_ids, set()) 

147 

148 def test_many_points_no_trail(self): 

149 """Test that no trails are found on an image with many points, but no 

150 sets of three that could lie on the same line. 

151 """ 

152 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-5, -4), lsst.geom.Point2I(1005, 1084)) 

153 dataset = lsst.meas.base.tests.TestDataset(bbox) 

154 flux = 10000 

155 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(500, 500)) 

156 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(480, 610)) 

157 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(520, 680)) 

158 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(570, 500)) 

159 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(640, 630)) 

160 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(600, 700)) 

161 schema = dataset.makeMinimalSchema() 

162 schema.addField("ip_diffim_DipoleFit_classification", type="Flag") 

163 exposure, catalog = dataset.realize(10.0, schema=schema) 

164 

165 task = findGlintTrails.FindGlintTrailsTask() 

166 result = task.run(catalog) 

167 

168 self.assertEqual(len(result.trails), 0) 

169 self.assertEqual(result.trailed_ids, set()) 

170 

171 def test_rejected_trail_min_points(self): 

172 """Test that no trails are found on an image with an initially 

173 reasonable trail that is rejected during fitting for having too many 

174 outliers. 

175 """ 

176 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-5, -4), lsst.geom.Point2I(1005, 1084)) 

177 dataset = lsst.meas.base.tests.TestDataset(bbox) 

178 flux = 10000 

179 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(100, 100)) 

180 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(210, 200)) 

181 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(300, 310)) 

182 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(400, 400)) 

183 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(500, 500)) 

184 dataset.addSource(instFlux=flux, centroid=lsst.geom.Point2D(600, 600)) 

185 schema = dataset.makeMinimalSchema() 

186 schema.addField("ip_diffim_DipoleFit_classification", type="Flag") 

187 exposure, catalog = dataset.realize(10.0, schema=schema) 

188 

189 task = findGlintTrails.FindGlintTrailsTask() 

190 result = task.run(catalog) 

191 

192 self.assertEqual(len(result.trails), 0) 

193 self.assertEqual(result.trailed_ids, set()) 

194 

195 

196def setup_module(module): 

197 lsst.utils.tests.init() 

198 

199 

200if __name__ == "__main__": 200 ↛ 201line 200 didn't jump to line 201 because the condition on line 200 was never true

201 lsst.utils.tests.init() 

202 unittest.main()