feat: add world origin triad and explicit render-space mapping to visualization
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user