| 57 | |
| 58 | |
| 59 | class VisualizerTrajectory(Visualizer[VisualizerTrajectoryCfg]): |
| 60 | def __init__(self, cfg: VisualizerTrajectoryCfg) -> None: |
| 61 | super().__init__(cfg) |
| 62 | self.ates = [] |
| 63 | |
| 64 | def visualize( |
| 65 | self, |
| 66 | batch: Batch, |
| 67 | flows: Flows, |
| 68 | tracks: list[Tracks] | None, |
| 69 | model_output: ModelOutput, |
| 70 | model: Model, |
| 71 | global_step: int, |
| 72 | ) -> dict[str, Float[Tensor, "3 _ _"] | Float[Tensor, ""]]: |
| 73 | # If there's no ground truth, do nothing. |
| 74 | if batch.extrinsics is None: |
| 75 | return {} |
| 76 | |
| 77 | # For now, only support batch size 1 for visualization. |
| 78 | b, _, _, _, _ = batch.videos.shape |
| 79 | assert b == 1 |
| 80 | |
| 81 | # Compute the ATE. |
| 82 | try: |
| 83 | ate, positions_gt, positions_hat = compute_ate( |
| 84 | batch.extrinsics[0, :, :3, 3], |
| 85 | model_output.extrinsics[0, :, :3, 3], |
| 86 | ) |
| 87 | except ValueError: |
| 88 | return {} |
| 89 | result = {"metrics/ate": ate} |
| 90 | |
| 91 | # Visualize the trajectory. |
| 92 | if self.cfg.generate_plot: |
| 93 | fg = generate_plot( |
| 94 | [positions_gt.cpu().numpy(), positions_hat.cpu().numpy()], |
| 95 | ["Ground-truth", "Predicted"], |
| 96 | ) |
| 97 | visualization = fig_to_image(fg) |
| 98 | plt.close(fg) |
| 99 | result["trajectory"] = add_border(visualization) |
| 100 | |
| 101 | if self.cfg.ate_save_path is not None: |
| 102 | # It's not ideal to write out a file during each optimization step, but this |
| 103 | # only needs to be run once to generate a plot in the paper, so it's fine. |
| 104 | self.ates.append(ate.item()) |
| 105 | self.cfg.ate_save_path.parent.mkdir(exist_ok=True, parents=True) |
| 106 | with self.cfg.ate_save_path.open("w") as f: |
| 107 | json.dump(self.ates, f) |
| 108 | |
| 109 | return result |
nothing calls this directly
no outgoing calls
no test coverage detected