Estimate SIM(3) transform: source -> target. Use SVD to solve for s, R, t such that target ≈ s * R @ source + t. Args: source_points: (M, 3) source points target_points: (M, 3) target points Returns: s: float, scale factor R: (3, 3) rotation matrix
(source_points, target_points)
| 7 | |
| 8 | |
| 9 | def estimate_sim3(source_points, target_points): |
| 10 | """Estimate SIM(3) transform: source -> target. |
| 11 | |
| 12 | Use SVD to solve for s, R, t such that target ≈ s * R @ source + t. |
| 13 | |
| 14 | Args: |
| 15 | source_points: (M, 3) source points |
| 16 | target_points: (M, 3) target points |
| 17 | |
| 18 | Returns: |
| 19 | s: float, scale factor |
| 20 | R: (3, 3) rotation matrix |
| 21 | t: (3,) translation vector |
| 22 | """ |
| 23 | mu_src = np.mean(source_points, axis=0) |
| 24 | mu_tgt = np.mean(target_points, axis=0) |
| 25 | |
| 26 | src_centered = source_points - mu_src |
| 27 | tgt_centered = target_points - mu_tgt |
| 28 | |
| 29 | scale_src = np.sqrt((src_centered ** 2).sum(axis=1).mean()) |
| 30 | scale_tgt = np.sqrt((tgt_centered ** 2).sum(axis=1).mean()) |
| 31 | s = scale_tgt / (scale_src + 1e-12) |
| 32 | |
| 33 | src_scaled = src_centered * s |
| 34 | |
| 35 | H = src_scaled.T @ tgt_centered |
| 36 | U, _, Vt = np.linalg.svd(H) |
| 37 | R = Vt.T @ U.T |
| 38 | if np.linalg.det(R) < 0: |
| 39 | Vt[2, :] *= -1 |
| 40 | R = Vt.T @ U.T |
| 41 | |
| 42 | t = mu_tgt - s * R @ mu_src |
| 43 | return s, R, t |
| 44 | |
| 45 | |
| 46 | def weighted_estimate_sim3(source_points, target_points, weights): |
no outgoing calls
no test coverage detected