Coverage for tests/test_pessimisticPatternMatcherB3D.py: 18%

Shortcuts on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

126 statements  

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23from copy import copy 

24import unittest 

25 

26import numpy as np 

27 

28from lsst.meas.astrom.pessimistic_pattern_matcher_b_3D \ 

29 import PessimisticPatternMatcherB 

30from lsst.log import Log 

31 

32__deg_to_rad__ = np.pi/180 

33 

34 

35class TestPessimisticPatternMatcherB(unittest.TestCase): 

36 

37 """Unittest suite for the Pessimistic Pattern Matcher B. 

38 """ 

39 

40 def setUp(self): 

41 np.random.seed(12345) 

42 

43 n_points = 1000 

44 # reference_obj_array is a number array representing 

45 # 3D points randomly draw on a 1 sq deg patch. 

46 self.reference_obj_array = np.empty((n_points, 4)) 

47 cos_theta_array = np.random.uniform( 

48 np.cos(np.pi/2 + 0.5*__deg_to_rad__), 

49 np.cos(np.pi/2 - 0.5*__deg_to_rad__), size=n_points) 

50 sin_theta_array = np.sqrt(1 - cos_theta_array**2) 

51 phi_array = np.random.uniform(-0.5, 0.5, size=n_points)*__deg_to_rad__ 

52 self.reference_obj_array[:, 0] = sin_theta_array*np.cos(phi_array) 

53 self.reference_obj_array[:, 1] = sin_theta_array*np.sin(phi_array) 

54 self.reference_obj_array[:, 2] = cos_theta_array 

55 self.reference_obj_array[:, 3] = ( 

56 np.random.power(1.2, size=n_points)*4 + 20) 

57 

58 # Our initial source catalog is a straight copy of the reference 

59 # array at first. In some of the tests we will add rotations and 

60 # shifts to the data in order to test the input and outputs of our 

61 # matcher. 

62 self.source_obj_array = copy(self.reference_obj_array) 

63 self.log = Log() 

64 

65 def testConstructPattern(self): 

66 """ Test that a specified pattern can be found in the reference 

67 data and that the explicit ids match. 

68 """ 

69 self.pyPPMb = PessimisticPatternMatcherB( 

70 reference_array=self.reference_obj_array[:, :3], 

71 log=self.log) 

72 

73 pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( 

74 self.source_obj_array[:6, :3], 6, np.cos(np.radians(60. / 3600.)), 

75 np.cos(np.radians(1.0)) ** 2, np.radians(5./3600.)) 

76 pattern_list = pattern_struct.ref_candidates 

77 self.assertGreater(len(pattern_list), 0) 

78 self.assertEqual(pattern_list[0], 0) 

79 self.assertEqual(pattern_list[1], 1) 

80 self.assertEqual(pattern_list[2], 2) 

81 self.assertEqual(pattern_list[3], 3) 

82 self.assertEqual(pattern_list[4], 4) 

83 self.assertEqual(pattern_list[5], 5) 

84 

85 pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( 

86 self.source_obj_array[:9, :3], 6, np.cos(np.radians(60. / 3600.)), 

87 np.cos(np.radians(1.0)) ** 2, np.radians(5./3600.)) 

88 pattern_list = pattern_struct.ref_candidates 

89 self.assertGreater(len(pattern_list), 0) 

90 self.assertEqual(pattern_list[0], 0) 

91 self.assertEqual(pattern_list[1], 1) 

92 self.assertEqual(pattern_list[2], 2) 

93 self.assertEqual(pattern_list[3], 3) 

94 self.assertEqual(pattern_list[4], 4) 

95 self.assertEqual(pattern_list[5], 5) 

96 

97 pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( 

98 self.source_obj_array[[2, 4, 8, 16, 32, 64], :3], 6, 

99 np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0)) ** 2, 

100 np.radians(5./3600.)) 

101 pattern_list = pattern_struct.ref_candidates 

102 self.assertEqual(pattern_list[0], 2) 

103 self.assertEqual(pattern_list[1], 4) 

104 self.assertEqual(pattern_list[2], 8) 

105 self.assertEqual(pattern_list[3], 16) 

106 self.assertEqual(pattern_list[4], 32) 

107 self.assertEqual(pattern_list[5], 64) 

108 

109 def testMatchPerfect(self): 

110 """ Input objects that have no shift or rotation to the matcher 

111 and test that we return a match. 

112 """ 

113 self.pyPPMb = PessimisticPatternMatcherB( 

114 reference_array=self.reference_obj_array[:, :3], 

115 log=self.log) 

116 

117 match_struct = self.pyPPMb.match( 

118 source_array=self.source_obj_array, n_check=9, n_match=6, 

119 n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, 

120 max_dist=5., min_matches=30, pattern_skip_array=None) 

121 self.assertEqual(len(match_struct.match_ids), 

122 len(self.reference_obj_array)) 

123 self.assertTrue( 

124 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

125 

126 def testOptimisticMatch(self): 

127 """ Test the optimistic mode of the pattern matcher. That is 

128 the algorithm with the early exit strategy as described in 

129 Tabur 2007. 

130 """ 

131 self.pyPPMb = PessimisticPatternMatcherB( 

132 reference_array=self.reference_obj_array[:, :3], 

133 log=self.log) 

134 

135 match_struct = self.pyPPMb.match( 

136 source_array=self.source_obj_array, n_check=9, n_match=6, 

137 n_agree=1, max_n_patterns=100, max_shift=60., max_rotation=6.0, 

138 max_dist=5., min_matches=30, pattern_skip_array=None) 

139 self.assertEqual(len(match_struct.match_ids), 

140 len(self.reference_obj_array)) 

141 self.assertTrue( 

142 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

143 

144 def testMatchSkip(self): 

145 """ Test the ability to skip specified patterns in the matching 

146 process. 

147 """ 

148 self.pyPPMb = PessimisticPatternMatcherB( 

149 reference_array=self.reference_obj_array[:, :3], 

150 log=self.log) 

151 

152 match_struct = self.pyPPMb.match( 

153 source_array=self.source_obj_array, n_check=9, n_match=6, 

154 n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, 

155 max_dist=5., min_matches=30, pattern_skip_array=np.array([0])) 

156 self.assertEqual(len(match_struct.match_ids), 

157 len(self.reference_obj_array)) 

158 self.assertTrue( 

159 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

160 

161 def testMatchMoreSources(self): 

162 """ Test the case where we have more sources than references 

163 but no rotation or shift. 

164 """ 

165 self.pyPPMb = PessimisticPatternMatcherB( 

166 reference_array=self.reference_obj_array[:500, :3], 

167 log=self.log) 

168 

169 match_struct = self.pyPPMb.match( 

170 source_array=self.source_obj_array, n_check=9, n_match=6, 

171 n_agree=2, max_n_patterns=100, max_shift=60.0, max_rotation=5.0, 

172 max_dist=5., min_matches=30, pattern_skip_array=None) 

173 self.assertEqual(len(match_struct.match_ids), 

174 len(self.reference_obj_array[:500])) 

175 self.assertTrue( 

176 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

177 

178 def testMatchMoreReferences(self): 

179 """ Test the case where we have more references than sources 

180 but no rotation or shift. 

181 """ 

182 self.pyPPMb = PessimisticPatternMatcherB( 

183 reference_array=self.reference_obj_array[:, :3], 

184 log=self.log) 

185 

186 match_struct = self.pyPPMb.match( 

187 source_array=self.source_obj_array[:500], n_check=9, n_match=6, 

188 n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=1.0, 

189 max_dist=5., min_matches=30, pattern_skip_array=None) 

190 self.assertEqual(len(match_struct.match_ids), 

191 len(self.reference_obj_array[:500])) 

192 self.assertTrue( 

193 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

194 

195 def testShift(self): 

196 """ Test the matcher when a shift is applied to the data. 

197 

198 We say shift here as while we are rotating the unit-sphere in 3D, on 

199 our 'focal plane' this will appear as a shift. 

200 """ 

201 self.pyPPMb = PessimisticPatternMatcherB( 

202 reference_array=self.reference_obj_array[:, :3], 

203 log=self.log) 

204 theta = np.radians(45.0 / 3600.) 

205 cos_theta = np.cos(theta) 

206 sin_theta = np.sin(theta) 

207 theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( 

208 np.array([0, 0, 1]), cos_theta, sin_theta) 

209 

210 self.source_obj_array[:, :3] = np.dot( 

211 theta_rotation, 

212 self.source_obj_array[:, :3].transpose()).transpose() 

213 

214 match_struct = self.pyPPMb.match( 

215 source_array=self.source_obj_array, n_check=9, n_match=6, 

216 n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, 

217 max_dist=5., min_matches=30, pattern_skip_array=None) 

218 

219 self.assertEqual(len(match_struct.match_ids), 

220 len(self.reference_obj_array)) 

221 self.assertTrue( 

222 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

223 

224 def testRotation(self): 

225 """ Test the matcher for when a roation is applied to the data. 

226 """ 

227 self.pyPPMb = PessimisticPatternMatcherB( 

228 reference_array=self.reference_obj_array[:, :3], 

229 log=self.log) 

230 phi = 2.5*__deg_to_rad__ 

231 cos_phi = np.cos(phi) 

232 sin_phi = np.sin(phi) 

233 phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( 

234 np.array([1, 0, 0]), cos_phi, sin_phi) 

235 

236 self.source_obj_array[:, :3] = np.dot( 

237 phi_rotation, self.source_obj_array[:, :3].transpose()).transpose() 

238 

239 match_struct = self.pyPPMb.match( 

240 source_array=self.source_obj_array, n_check=9, n_match=6, 

241 n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, 

242 max_dist=5., min_matches=30, pattern_skip_array=None) 

243 

244 self.assertEqual(len(match_struct.match_ids), 

245 len(self.reference_obj_array)) 

246 self.assertTrue( 

247 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

248 

249 def testShiftRotation(self): 

250 """ Test both a shift and rotation being applied to the data. 

251 """ 

252 self.pyPPMb = PessimisticPatternMatcherB( 

253 reference_array=self.reference_obj_array[:, :3], 

254 log=self.log) 

255 theta = np.radians(45.0 / 3600.) 

256 cos_theta = np.cos(theta) 

257 sin_theta = np.sin(theta) 

258 theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( 

259 np.array([0, 0, 1]), cos_theta, sin_theta) 

260 

261 phi = 2.5 * __deg_to_rad__ 

262 cos_phi = np.cos(phi) 

263 sin_phi = np.sin(phi) 

264 phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( 

265 np.array([1, 0, 0]), cos_phi, sin_phi) 

266 

267 shift_rot_matrix = np.dot(theta_rotation, phi_rotation) 

268 

269 self.source_obj_array[:, :3] = np.dot( 

270 shift_rot_matrix, 

271 self.source_obj_array[:, :3].transpose()).transpose() 

272 

273 match_struct = self.pyPPMb.match( 

274 source_array=self.source_obj_array, n_check=9, n_match=6, 

275 n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, 

276 max_dist=5., min_matches=30, pattern_skip_array=None) 

277 

278 self.assertEqual(len(match_struct.match_ids), 

279 len(self.reference_obj_array)) 

280 self.assertTrue( 

281 np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__)) 

282 

283 def testLinearDistortion(self): 

284 """ Create a simple linear distortion and test that the correct 

285 references are still matched. 

286 """ 

287 

288 self.pyPPMb = PessimisticPatternMatcherB( 

289 reference_array=self.reference_obj_array[:, :3], 

290 log=self.log) 

291 

292 max_z = np.cos(np.pi/2 + 0.5 * __deg_to_rad__) 

293 min_z = np.cos(np.pi/2 - 0.5 * __deg_to_rad__) 

294 # The max shift in position we add to the position will be 25 

295 # arcseconds. 

296 max_distort = 25.0 / 3600. * __deg_to_rad__ 

297 self.source_obj_array[:, 2] = ( 

298 self.source_obj_array[:, 2] 

299 - max_distort * (self.source_obj_array[:, 2] - min_z) 

300 / (max_z - min_z)) 

301 # Renomalize the 3 vectors to be unit length. 

302 distorted_dists = np.sqrt(self.source_obj_array[:, 0] ** 2 

303 + self.source_obj_array[:, 1] ** 2 

304 + self.source_obj_array[:, 2] ** 2) 

305 self.source_obj_array[:, 0] /= distorted_dists 

306 self.source_obj_array[:, 1] /= distorted_dists 

307 self.source_obj_array[:, 2] /= distorted_dists 

308 

309 match_struct = self.pyPPMb.match( 

310 source_array=self.source_obj_array, n_check=9, n_match=6, 

311 n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, 

312 max_dist=5., min_matches=30, pattern_skip_array=None) 

313 

314 self.assertEqual(len(match_struct.match_ids), 

315 len(self.reference_obj_array)) 

316 self.assertTrue( 

317 np.all(match_struct.distances_rad < 10 / 3600.0 * __deg_to_rad__)) 

318 

319 def testNoReferenceSources(self): 

320 """Check that we get a helpful error when no reference objects are 

321 supplied. 

322 """ 

323 with self.assertRaisesRegex(ValueError, "No reference objects supplied"): 

324 PessimisticPatternMatcherB(np.ndarray((0, 3)), self.log) 

325 

326 

327if __name__ == '__main__': 327 ↛ 328line 327 didn't jump to line 328, because the condition on line 327 was never true

328 unittest.main()