| from __future__ import annotations |
|
|
| from typing import Any, List, Tuple |
|
|
| import numpy as np |
| import plotly.graph_objects as go |
|
|
| MAX_RENDER_POINTS = 30000 |
|
|
| GT_COLORSCALE = [ |
| [0.0, "rgb(43, 111, 214)"], |
| [0.55, "rgb(91, 151, 236)"], |
| [1.0, "rgb(176, 210, 255)"], |
| ] |
| INF_COLORSCALE = [ |
| [0.0, "rgb(204, 61, 46)"], |
| [0.55, "rgb(244, 103, 82)"], |
| [1.0, "rgb(255, 182, 155)"], |
| ] |
|
|
| TWO_COLUMN_SLIDER_CSS = """ |
| .slider-two-column-row { |
| display: grid !important; |
| grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important; |
| gap: 16px !important; |
| align-items: start !important; |
| } |
| .slider-two-column-row > div { |
| min-width: 0 !important; |
| width: 100% !important; |
| } |
| @media (max-width: 900px) { |
| .slider-two-column-row { |
| grid-template-columns: 1fr !important; |
| } |
| } |
| """ |
|
|
|
|
| def prepare_points_for_plot(points: np.ndarray, max_points: int = MAX_RENDER_POINTS) -> np.ndarray: |
| if points.shape[0] <= max_points: |
| return points |
| indices = np.linspace(0, points.shape[0] - 1, max_points, dtype=np.int64) |
| return points[indices] |
|
|
|
|
| def add_point_cloud_trace( |
| fig: go.Figure, |
| points: np.ndarray, |
| name: str, |
| colorscale: List[List[Any]], |
| ) -> None: |
| if points.size == 0: |
| return |
|
|
| render_points = prepare_points_for_plot(points) |
| depth_color = render_points[:, 2] |
| fig.add_trace( |
| go.Scatter3d( |
| x=render_points[:, 0], |
| y=render_points[:, 1], |
| z=render_points[:, 2], |
| mode="markers", |
| marker=dict( |
| size=2.2, |
| color=depth_color, |
| colorscale=colorscale, |
| opacity=0.92, |
| line=dict(width=0), |
| ), |
| name=name, |
| hoverinfo="skip", |
| ) |
| ) |
|
|
|
|
| def make_point_cloud_figures( |
| points_gt: np.ndarray, |
| points_inferred: np.ndarray, |
| gt_name: str, |
| inf_name: str, |
| ) -> Tuple[go.Figure, go.Figure]: |
| fig_gt = go.Figure() |
| fig_inf = go.Figure() |
| add_point_cloud_trace(fig_gt, points_gt, gt_name, GT_COLORSCALE) |
| add_point_cloud_trace(fig_inf, points_inferred, inf_name, INF_COLORSCALE) |
|
|
| all_points = ( |
| np.vstack([points_gt, points_inferred]) |
| if points_inferred.size > 0 and points_gt.size > 0 |
| else (points_gt if points_gt.size > 0 else points_inferred) |
| ) |
| if all_points.size > 0: |
| min_b, max_b = all_points.min(axis=0), all_points.max(axis=0) |
| center = (max_b + min_b) / 2.0 |
| max_dim = (max_b - min_b).max() |
| view_dim = max(max_dim * 1.35, 0.24) |
| half_dim = view_dim / 2.0 |
| axis_style = dict(visible=False, showgrid=False, zeroline=False, showbackground=False) |
| scene_config = dict( |
| xaxis=dict(axis_style, range=[center[0] - half_dim, center[0] + half_dim]), |
| yaxis=dict(axis_style, range=[center[1] - half_dim, center[1] + half_dim]), |
| zaxis=dict(axis_style, range=[center[2] - half_dim, center[2] + half_dim]), |
| aspectmode="cube", |
| ) |
| else: |
| axis_style = dict(visible=False, showgrid=False, zeroline=False, showbackground=False) |
| scene_config = dict( |
| xaxis=dict(axis_style, range=[-0.2, 0.2]), |
| yaxis=dict(axis_style, range=[-0.2, 0.2]), |
| zaxis=dict(axis_style, range=[-0.2, 0.2]), |
| aspectmode="cube", |
| ) |
|
|
| common_layout = dict( |
| height=520, |
| margin=dict(l=0, r=0, b=0, t=0), |
| paper_bgcolor="white", |
| plot_bgcolor="white", |
| showlegend=False, |
| scene_camera=dict( |
| eye=dict(x=1.25, y=-1.75, z=0.9), |
| projection=dict(type="orthographic"), |
| ), |
| scene=scene_config, |
| ) |
| fig_gt.update_layout(**common_layout) |
| fig_inf.update_layout(**common_layout) |
| return fig_gt, fig_inf |
|
|