feat: add world origin triad and explicit render-space mapping to visualization

This commit is contained in:
2026-02-07 17:25:06 +00:00
parent 15989195f1
commit 57f0dffbc5
2 changed files with 63 additions and 4 deletions
+3 -2
View File
@@ -75,11 +75,12 @@ Use `--birdseye` for a top-down X-Z view (looking down Y axis).
uv run visualize_extrinsics.py -i output/extrinsics.json --birdseye --show
```
**Ground Plane Overlay:**
Render a semi-transparent X-Z ground plane to anchor camera poses.
**Ground Plane & Origin Overlay:**
Render a semi-transparent X-Z ground plane and/or a world origin triad.
- `--show-ground/--no-show-ground`: Toggle ground plane (default: show).
- `--ground-y FLOAT`: Set the Y height of the plane (default: 0.0).
- `--ground-size FLOAT`: Set the side length of the plane in meters (default: 8.0).
- `--show-origin-axes/--no-show-origin-axes`: Toggle world origin triad (X:red, Y:green, Z:blue) (default: show).
*Example: Ground plane at Y=-1.5 with 10m size*
```bash
+60 -2
View File
@@ -474,6 +474,11 @@ def run_diagnostics(poses: Dict[str, np.ndarray], convention: str):
default=8.0,
help="Size of the ground plane (side length in meters).",
)
@click.option(
"--show-origin-axes/--no-show-origin-axes",
default=True,
help="Show a world-origin axis triad (X:red, Y:green, Z:blue).",
)
def main(
input: str,
output: Optional[str],
@@ -491,6 +496,7 @@ def main(
show_ground: bool,
ground_y: float,
ground_size: float,
show_origin_axes: bool,
):
"""Visualize camera extrinsics from JSON using Plotly."""
try:
@@ -546,6 +552,52 @@ def main(
intrinsics=cam_intrinsics,
)
if show_origin_axes:
origin = np.zeros(3)
axis_len = scale
fig.add_trace(
go.Scatter3d(
x=[origin[0], origin[0] + axis_len],
y=[origin[1], origin[1]],
z=[origin[2], origin[2]],
mode="lines",
line=dict(color="red", width=4),
name="World X",
legendgroup="Origin",
showlegend=True,
hoverinfo="text",
text="World X",
)
)
fig.add_trace(
go.Scatter3d(
x=[origin[0], origin[0]],
y=[origin[1], origin[1] + axis_len],
z=[origin[2], origin[2]],
mode="lines",
line=dict(color="green", width=4),
name="World Y",
legendgroup="Origin",
showlegend=True,
hoverinfo="text",
text="World Y",
)
)
fig.add_trace(
go.Scatter3d(
x=[origin[0], origin[0]],
y=[origin[1], origin[1]],
z=[origin[2], origin[2] + axis_len],
mode="lines",
line=dict(color="blue", width=4),
name="World Z",
legendgroup="Origin",
showlegend=True,
hoverinfo="text",
text="World Z",
)
)
if show_ground:
half_size = ground_size / 2.0
x_grid = np.linspace(-half_size, half_size, 2)
@@ -583,10 +635,16 @@ def main(
eye=dict(x=0, y=2.5, z=0),
)
render_desc = (
"OpenCV: local +X,+Y,+Z (Y-down)"
if render_space == "opencv"
else "OpenGL: local +X,-Y,-Z (Y-up, Z-back) rel. to OpenCV"
)
fig.update_layout(
title=f"Camera Extrinsics ({pose_convention}, {render_space})",
title=f"Camera Extrinsics ({pose_convention})<br><sup>{render_desc}</sup>",
scene=scene_dict,
margin=dict(l=0, r=0, b=0, t=40),
margin=dict(l=0, r=0, b=0, t=60),
legend=dict(x=0, y=1),
)