Add prior, use calibrations
							parent
							
								
									60ce938e31
								
							
						
					
					
						commit
						bbba497ca1
					
				|  | @ -71,7 +71,7 @@ def compute_ground_truth(method, poses, cal): | |||
|         E1 = EssentialMatrix.FromPose3(poses[0].between(poses[1])) | ||||
|         E2 = EssentialMatrix.FromPose3(poses[0].between(poses[2])) | ||||
| 
 | ||||
|         # Assert that E1.matrix and F1 are the same | ||||
|         # Assert that E1.matrix and F1 are the same, with known calibration | ||||
|         invK = np.linalg.inv(cal.K()) | ||||
|         G1 = invK.transpose() @ E1.matrix() @ invK | ||||
|         G2 = invK.transpose() @ E2.matrix() @ invK | ||||
|  | @ -94,6 +94,10 @@ def build_factor_graph(method, num_cameras, measurements): | |||
|         FactorClass = gtsam.TransferFactorFundamentalMatrix | ||||
|     elif method == "EssentialMatrix": | ||||
|         FactorClass = gtsam.EssentialTransferFactorCal3f | ||||
|         # add priors on all calibrations: | ||||
|         for i in range(num_cameras): | ||||
|             model = gtsam.noiseModel.Isotropic.Sigma(1, 10.0) | ||||
|             graph.addPriorCal3f(K(i), Cal3f(50.0, 50.0, 50.0), model) | ||||
|     else: | ||||
|         raise ValueError(f"Unknown method {method}") | ||||
| 
 | ||||
|  | @ -120,7 +124,7 @@ def build_factor_graph(method, num_cameras, measurements): | |||
|     return graph | ||||
| 
 | ||||
| 
 | ||||
| def get_initial_estimate(method, num_cameras, ground_truth, cal, std=1e-6): | ||||
| def get_initial_estimate(method, num_cameras, ground_truth, cal): | ||||
|     """get initial estimate for method""" | ||||
|     initialEstimate = Values() | ||||
|     total_dimension = 0 | ||||
|  | @ -130,22 +134,16 @@ def get_initial_estimate(method, num_cameras, ground_truth, cal, std=1e-6): | |||
|         for a in range(num_cameras): | ||||
|             b = (a + 1) % num_cameras  # Next camera | ||||
|             c = (a + 2) % num_cameras  # Camera after next | ||||
|             # Retract with delta drawn from zero-mean normal | ||||
|             F1_perturbed = F1.retract(np.random.normal(0, std, F1.dim())) | ||||
|             F2_perturbed = F2.retract(np.random.normal(0, 1e-4, F2.dim())) | ||||
|             initialEstimate.insert(EdgeKey(a, b).key(), F1_perturbed) | ||||
|             initialEstimate.insert(EdgeKey(a, c).key(), F2_perturbed) | ||||
|             initialEstimate.insert(EdgeKey(a, b).key(), F1) | ||||
|             initialEstimate.insert(EdgeKey(a, c).key(), F2) | ||||
|             total_dimension += F1.dim() + F2.dim() | ||||
|     elif method == "EssentialMatrix": | ||||
|         E1, E2 = ground_truth | ||||
|         for a in range(num_cameras): | ||||
|             b = (a + 1) % num_cameras  # Next camera | ||||
|             c = (a + 2) % num_cameras  # Camera after next | ||||
|             # Retract with delta drawn from zero-mean normal | ||||
|             E1_perturbed = E1.retract(np.random.normal(0, std, E1.dim())) | ||||
|             E2_perturbed = E2.retract(np.random.normal(0, 1e-4, E2.dim())) | ||||
|             initialEstimate.insert(EdgeKey(a, b).key(), E1_perturbed) | ||||
|             initialEstimate.insert(EdgeKey(a, c).key(), E2_perturbed) | ||||
|             initialEstimate.insert(EdgeKey(a, b).key(), E1) | ||||
|             initialEstimate.insert(EdgeKey(a, c).key(), E2) | ||||
|             total_dimension += E1.dim() + E2.dim() | ||||
|         # Insert initial calibrations | ||||
|         for i in range(num_cameras): | ||||
|  | @ -158,14 +156,17 @@ def get_initial_estimate(method, num_cameras, ground_truth, cal, std=1e-6): | |||
|     return initialEstimate | ||||
| 
 | ||||
| 
 | ||||
| def optimize(graph, initialEstimate): | ||||
| def optimize(graph, initialEstimate, method): | ||||
|     """optimize the graph""" | ||||
|     params = LevenbergMarquardtParams() | ||||
|     params.setlambdaInitial(1000.0)  # Initialize lambda to a high value | ||||
|     params.setAbsoluteErrorTol(1.0) | ||||
|     params.setlambdaInitial(1e10)  # Initialize lambda to a high value | ||||
|     params.setlambdaUpperBound(1e10) | ||||
|     # params.setAbsoluteErrorTol(0.1) | ||||
|     params.setVerbosityLM("SUMMARY") | ||||
|     optimizer = LevenbergMarquardtOptimizer(graph, initialEstimate, params) | ||||
|     result = optimizer.optimize() | ||||
|     # if method == "EssentialMatrix": | ||||
|     #     result.print("Final results:\n", formatter) | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
|  | @ -195,9 +196,15 @@ def compute_distances(method, result, ground_truth, num_cameras, cal): | |||
|         elif method == "EssentialMatrix": | ||||
|             E_est_ab = result.atEssentialMatrix(key_ab) | ||||
|             E_est_ac = result.atEssentialMatrix(key_ac) | ||||
| 
 | ||||
|             # Retrieve correct calibrations from result: | ||||
|             cal_a = result.atCal3f(K(a)) | ||||
|             cal_b = result.atCal3f(K(b)) | ||||
|             cal_c = result.atCal3f(K(c)) | ||||
| 
 | ||||
|             # Convert estimated EssentialMatrices to FundamentalMatrices | ||||
|             F_est_ab = gtsam.FundamentalMatrix(cal.K(), E_est_ab, cal.K()) | ||||
|             F_est_ac = gtsam.FundamentalMatrix(cal.K(), E_est_ac, cal.K()) | ||||
|             F_est_ab = gtsam.FundamentalMatrix(cal_a.K(), E_est_ab, cal_b.K()) | ||||
|             F_est_ac = gtsam.FundamentalMatrix(cal_a.K(), E_est_ac, cal_c.K()) | ||||
| 
 | ||||
|         # Compute local coordinates (geodesic distance on the F-manifold) | ||||
|         dist_ab = np.linalg.norm(F1.localCoordinates(F_est_ab)) | ||||
|  | @ -240,7 +247,7 @@ def main(): | |||
|     parser.add_argument("--num_cameras", type=int, default=4, help="Number of cameras (default: 4)") | ||||
|     parser.add_argument("--nr_trials", type=int, default=5, help="Number of trials (default: 5)") | ||||
|     parser.add_argument("--seed", type=int, default=42, help="Random seed (default: 42)") | ||||
|     parser.add_argument("--noise_std", type=float, default=1.0, help="Standard deviation of noise (default: 1.0)") | ||||
|     parser.add_argument("--noise_std", type=float, default=0.5, help="Standard deviation of noise (default: 0.5)") | ||||
|     args = parser.parse_args() | ||||
| 
 | ||||
|     # Initialize the random number generator | ||||
|  | @ -273,17 +280,14 @@ def main(): | |||
|             graph = build_factor_graph(method, args.num_cameras, measurements) | ||||
| 
 | ||||
|             # Assert that the initial error is the same for both methods: | ||||
|             if trial == 0 and method == methods[0]: | ||||
|                 initial_error = graph.error(initial_estimate[method]) | ||||
|             if method == methods[0]: | ||||
|                 error0 = graph.error(initial_estimate[method]) | ||||
|             else: | ||||
|                 current_error = graph.error(initial_estimate[method]) | ||||
|                 if not np.allclose(initial_error, current_error): | ||||
|                     print(f"Initial error for {method} ({current_error}) does not match ") | ||||
|                     print(f"initial error for {methods[0]} ({initial_error})") | ||||
|                     exit(1) | ||||
|                 assert np.allclose(error0, current_error) | ||||
| 
 | ||||
|             # Optimize the graph | ||||
|             result = optimize(graph, initial_estimate[method]) | ||||
|             result = optimize(graph, initial_estimate[method], method) | ||||
| 
 | ||||
|             # Compute distances from ground truth | ||||
|             distances = compute_distances(method, result, ground_truth[method], args.num_cameras, cal) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue