CoolFace
Apppublic

PCGao/MatchAnything

sourceHugging Faceapache-2.0updated 5mo agoView on Hugging Face
0likes
viz_3d.py204 linesDownload Raw Back to utils
1"""23D visualization based on plotly.3Works for a small number of points and cameras, might be slow otherwise.4 51) Initialize a figure with `init_figure`62) Add 3D points, camera frustums, or both as a pycolmap.Reconstruction7 8Written by Paul-Edouard Sarlin and Philipp Lindenberger.9"""10 11from typing import Optional12 13import numpy as np14import plotly.graph_objects as go15import pycolmap16 17 18def to_homogeneous(points):19    pad = np.ones((points.shape[:-1] + (1,)), dtype=points.dtype)20    return np.concatenate([points, pad], axis=-1)21 22 23def init_figure(height: int = 800) -> go.Figure:24    """Initialize a 3D figure."""25    fig = go.Figure()26    axes = dict(27        visible=False,28        showbackground=False,29        showgrid=False,30        showline=False,31        showticklabels=True,32        autorange=True,33    )34    fig.update_layout(35        template="plotly_dark",36        height=height,37        scene_camera=dict(38            eye=dict(x=0.0, y=-0.1, z=-2),39            up=dict(x=0, y=-1.0, z=0),40            projection=dict(type="orthographic"),41        ),42        scene=dict(43            xaxis=axes,44            yaxis=axes,45            zaxis=axes,46            aspectmode="data",47            dragmode="orbit",48        ),49        margin=dict(l=0, r=0, b=0, t=0, pad=0),50        legend=dict(orientation="h", yanchor="top", y=0.99, xanchor="left", x=0.1),51    )52    return fig53 54 55def plot_points(56    fig: go.Figure,57    pts: np.ndarray,58    color: str = "rgba(255, 0, 0, 1)",59    ps: int = 2,60    colorscale: Optional[str] = None,61    name: Optional[str] = None,62):63    """Plot a set of 3D points."""64    x, y, z = pts.T65    tr = go.Scatter3d(66        x=x,67        y=y,68        z=z,69        mode="markers",70        name=name,71        legendgroup=name,72        marker=dict(size=ps, color=color, line_width=0.0, colorscale=colorscale),73    )74    fig.add_trace(tr)75 76 77def plot_camera(78    fig: go.Figure,79    R: np.ndarray,80    t: np.ndarray,81    K: np.ndarray,82    color: str = "rgb(0, 0, 255)",83    name: Optional[str] = None,84    legendgroup: Optional[str] = None,85    fill: bool = False,86    size: float = 1.0,87    text: Optional[str] = None,88):89    """Plot a camera frustum from pose and intrinsic matrix."""90    W, H = K[0, 2] * 2, K[1, 2] * 291    corners = np.array([[0, 0], [W, 0], [W, H], [0, H], [0, 0]])92    if size is not None:93        image_extent = max(size * W / 1024.0, size * H / 1024.0)94        world_extent = max(W, H) / (K[0, 0] + K[1, 1]) / 0.595        scale = 0.5 * image_extent / world_extent96    else:97        scale = 1.098    corners = to_homogeneous(corners) @ np.linalg.inv(K).T99    corners = (corners / 2 * scale) @ R.T + t100    legendgroup = legendgroup if legendgroup is not None else name101 102    x, y, z = np.concatenate(([t], corners)).T103    i = [0, 0, 0, 0]104    j = [1, 2, 3, 4]105    k = [2, 3, 4, 1]106 107    if fill:108        pyramid = go.Mesh3d(109            x=x,110            y=y,111            z=z,112            color=color,113            i=i,114            j=j,115            k=k,116            legendgroup=legendgroup,117            name=name,118            showlegend=False,119            hovertemplate=text.replace("\n", "<br>"),120        )121        fig.add_trace(pyramid)122 123    triangles = np.vstack((i, j, k)).T124    vertices = np.concatenate(([t], corners))125    tri_points = np.array([vertices[i] for i in triangles.reshape(-1)])126    x, y, z = tri_points.T127 128    pyramid = go.Scatter3d(129        x=x,130        y=y,131        z=z,132        mode="lines",133        legendgroup=legendgroup,134        name=name,135        line=dict(color=color, width=1),136        showlegend=False,137        hovertemplate=text.replace("\n", "<br>"),138    )139    fig.add_trace(pyramid)140 141 142def plot_camera_colmap(143    fig: go.Figure,144    image: pycolmap.Image,145    camera: pycolmap.Camera,146    name: Optional[str] = None,147    **kwargs,148):149    """Plot a camera frustum from PyCOLMAP objects"""150    world_t_camera = image.cam_from_world.inverse()151    plot_camera(152        fig,153        world_t_camera.rotation.matrix(),154        world_t_camera.translation,155        camera.calibration_matrix(),156        name=name or str(image.image_id),157        text=str(image),158        **kwargs,159    )160 161 162def plot_cameras(fig: go.Figure, reconstruction: pycolmap.Reconstruction, **kwargs):163    """Plot a camera as a cone with camera frustum."""164    for image_id, image in reconstruction.images.items():165        plot_camera_colmap(166            fig, image, reconstruction.cameras[image.camera_id], **kwargs167        )168 169 170def plot_reconstruction(171    fig: go.Figure,172    rec: pycolmap.Reconstruction,173    max_reproj_error: float = 6.0,174    color: str = "rgb(0, 0, 255)",175    name: Optional[str] = None,176    min_track_length: int = 2,177    points: bool = True,178    cameras: bool = True,179    points_rgb: bool = True,180    cs: float = 1.0,181):182    # Filter outliers183    bbs = rec.compute_bounding_box(0.001, 0.999)184    # Filter points, use original reproj error here185    p3Ds = [186        p3D187        for _, p3D in rec.points3D.items()188        if (189            (p3D.xyz >= bbs[0]).all()190            and (p3D.xyz <= bbs[1]).all()191            and p3D.error <= max_reproj_error192            and p3D.track.length() >= min_track_length193        )194    ]195    xyzs = [p3D.xyz for p3D in p3Ds]196    if points_rgb:197        pcolor = [p3D.color for p3D in p3Ds]198    else:199        pcolor = color200    if points:201        plot_points(fig, np.array(xyzs), color=pcolor, ps=1, name=name)202    if cameras:203        plot_cameras(fig, rec, color=color, legendgroup=name, size=cs)204