from pathlib import Path import matplotlib.pyplot as plt import numpy as np from evo.core import sync from evo.core.trajectory import PoseTrajectory3D from evo.tools import plot from plyfile import PlyData, PlyElement def plot_trajectory(pred_traj, gt_traj=None, title="", filename="", align=True, correct_scale=True): assert isinstance(pred_traj, PoseTrajectory3D) if gt_traj is not None: assert isinstance(gt_traj, PoseTrajectory3D) gt_traj, pred_traj = sync.associate_trajectories(gt_traj, pred_traj) if align: pred_traj.align(gt_traj, correct_scale=correct_scale) plot_collection = plot.PlotCollection("PlotCol") fig = plt.figure(figsize=(8, 8)) plot_mode = plot.PlotMode.xz # ideal for planar movement ax = plot.prepare_axis(fig, plot_mode) ax.set_title(title) if gt_traj is not None: plot.traj(ax, plot_mode, gt_traj, '--', 'gray', "Ground Truth") plot.traj(ax, plot_mode, pred_traj, '-', 'blue', "Predicted") plot_collection.add_figure("traj (error)", fig) plot_collection.export(filename, confirm_overwrite=False) plt.close(fig=fig) print(f"Saved {filename}") def save_output_for_COLMAP(name: str, traj: PoseTrajectory3D, points: np.ndarray, colors: np.ndarray, fx, fy, cx, cy, H=480, W=640): """ Saves the sparse point cloud and camera poses such that it can be opened in COLMAP """ colmap_dir = Path(name) colmap_dir.mkdir(exist_ok=True) scale = 10 # for visualization # images images = "" traj = PoseTrajectory3D(poses_se3=list(map(np.linalg.inv, traj.poses_se3)), timestamps=traj.timestamps) for idx, (x,y,z), (qw, qx, qy, qz) in zip(range(1,traj.num_poses+1), traj.positions_xyz*scale, traj.orientations_quat_wxyz): images += f"{idx} {qw} {qx} {qy} {qz} {x} {y} {z} 1\n\n" (colmap_dir / "images.txt").write_text(images) # points points3D = "" colors_uint = (colors * 255).astype(np.uint8).tolist() for i, (p,c) in enumerate(zip((points*scale).tolist(), colors_uint), start=1): points3D += f"{i} " + ' '.join(map(str, p + c)) + " 0.0 0 0 0 0 0 0\n" (colmap_dir / "points3D.txt").write_text(points3D) # camera (colmap_dir / "cameras.txt").write_text(f"1 PINHOLE {W} {H} {fx} {fy} {cx} {cy}") print(f"Saved COLMAP-compatible reconstruction in {colmap_dir.resolve()}") def save_ply(name: str, points: np.ndarray, colors: np.ndarray): points_ply = np.array([(x,y,z,r,g,b) for (x,y,z),(r,g,b) in zip(points, colors)], dtype=[('x', '