mirror of
https://github.com/facebookresearch/pytorch3d.git
synced 2025-08-02 20:02:49 +08:00
Up vector for plotly
Summary: We previously did not send an `up` vector in to plotly when visualization_cameras is supplied. This meant the image would have the default orientation instead of the correct one. Now we use the library function `camera_to_eye_at_up` to calculate the plotly position, which includes the `up` vector. Reviewed By: nikhilaravi Differential Revision: D25981183 fbshipit-source-id: abec72b349f3a5519209e0e6c0518133c3750807
This commit is contained in:
parent
cf9bb7c48c
commit
8eba1684cb
@ -8,6 +8,7 @@ import plotly.graph_objects as go
|
|||||||
import torch
|
import torch
|
||||||
from plotly.subplots import make_subplots
|
from plotly.subplots import make_subplots
|
||||||
from pytorch3d.renderer import TexturesVertex
|
from pytorch3d.renderer import TexturesVertex
|
||||||
|
from pytorch3d.renderer.camera_utils import camera_to_eye_at_up
|
||||||
from pytorch3d.renderer.cameras import CamerasBase
|
from pytorch3d.renderer.cameras import CamerasBase
|
||||||
from pytorch3d.structures import Meshes, Pointclouds, join_meshes_as_scene
|
from pytorch3d.structures import Meshes, Pointclouds, join_meshes_as_scene
|
||||||
|
|
||||||
@ -224,23 +225,14 @@ def plot_scene(
|
|||||||
}
|
}
|
||||||
viewpoints_eye_at_up_world = None
|
viewpoints_eye_at_up_world = None
|
||||||
if viewpoint_cameras:
|
if viewpoint_cameras:
|
||||||
if len(viewpoint_cameras) == len(subplots) or len(viewpoint_cameras) == 1:
|
n_viewpoint_cameras = len(viewpoint_cameras)
|
||||||
|
if n_viewpoint_cameras == len(subplots) or n_viewpoint_cameras == 1:
|
||||||
# Calculate the vectors eye, at, up in world space
|
# Calculate the vectors eye, at, up in world space
|
||||||
# to initialize the position of the camera in
|
# to initialize the position of the camera in
|
||||||
# the plotly figure
|
# the plotly figure
|
||||||
# TODO(T77879494): correct the up vector calculation in the world space.
|
viewpoints_eye_at_up_world = camera_to_eye_at_up(
|
||||||
eye_at_up_view = (
|
viewpoint_cameras.get_world_to_view_transform().cpu()
|
||||||
torch.tensor([[0, 0, 0], [0, 0, 1], [0, 1, 0]])
|
|
||||||
.float()
|
|
||||||
.to(viewpoint_cameras.device)
|
|
||||||
)
|
)
|
||||||
viewpoints_eye_at_up_world = (
|
|
||||||
viewpoint_cameras.get_world_to_view_transform()
|
|
||||||
.inverse()
|
|
||||||
.transform_points(eye_at_up_view)
|
|
||||||
)
|
|
||||||
if len(viewpoints_eye_at_up_world.shape) < 3:
|
|
||||||
viewpoints_eye_at_up_world = viewpoints_eye_at_up_world.unsqueeze(0)
|
|
||||||
else:
|
else:
|
||||||
msg = "Invalid number {} of viewpoint cameras were provided. Either 1 \
|
msg = "Invalid number {} of viewpoint cameras were provided. Either 1 \
|
||||||
or {} cameras are required".format(
|
or {} cameras are required".format(
|
||||||
@ -290,29 +282,33 @@ def plot_scene(
|
|||||||
# update camera viewpoint if provided
|
# update camera viewpoint if provided
|
||||||
if viewpoints_eye_at_up_world is not None:
|
if viewpoints_eye_at_up_world is not None:
|
||||||
# Use camera params for batch index or the first camera if only one provided.
|
# Use camera params for batch index or the first camera if only one provided.
|
||||||
viewpoint_idx = min(len(viewpoints_eye_at_up_world) - 1, subplot_idx)
|
viewpoint_idx = min(n_viewpoint_cameras - 1, subplot_idx)
|
||||||
|
|
||||||
eye, at, _up = viewpoints_eye_at_up_world[viewpoint_idx]
|
|
||||||
|
|
||||||
|
eye, at, up = (i[viewpoint_idx] for i in viewpoints_eye_at_up_world)
|
||||||
eye_x, eye_y, eye_z = eye.tolist()
|
eye_x, eye_y, eye_z = eye.tolist()
|
||||||
|
|
||||||
at_x, at_y, at_z = at.tolist()
|
at_x, at_y, at_z = at.tolist()
|
||||||
|
up_x, up_y, up_z = up.tolist()
|
||||||
|
|
||||||
# scale camera eye to plotly [-1, 1] ranges
|
# scale camera eye to plotly [-1, 1] ranges
|
||||||
x_range = xaxis["range"]
|
x_range = xaxis["range"]
|
||||||
y_range = yaxis["range"]
|
y_range = yaxis["range"]
|
||||||
z_range = zaxis["range"]
|
z_range = zaxis["range"]
|
||||||
|
|
||||||
eye_x = _scale_camera_to_bounds(eye_x, x_range)
|
eye_x = _scale_camera_to_bounds(eye_x, x_range, True)
|
||||||
eye_y = _scale_camera_to_bounds(eye_y, y_range)
|
eye_y = _scale_camera_to_bounds(eye_y, y_range, True)
|
||||||
eye_z = _scale_camera_to_bounds(eye_z, z_range)
|
eye_z = _scale_camera_to_bounds(eye_z, z_range, True)
|
||||||
|
|
||||||
at_x = _scale_camera_to_bounds(at_x, x_range)
|
at_x = _scale_camera_to_bounds(at_x, x_range, True)
|
||||||
at_y = _scale_camera_to_bounds(at_y, y_range)
|
at_y = _scale_camera_to_bounds(at_y, y_range, True)
|
||||||
at_z = _scale_camera_to_bounds(at_z, z_range)
|
at_z = _scale_camera_to_bounds(at_z, z_range, True)
|
||||||
|
|
||||||
|
up_x = _scale_camera_to_bounds(up_x, x_range, False)
|
||||||
|
up_y = _scale_camera_to_bounds(up_y, y_range, False)
|
||||||
|
up_z = _scale_camera_to_bounds(up_z, z_range, False)
|
||||||
|
|
||||||
camera["eye"] = {"x": eye_x, "y": eye_y, "z": eye_z}
|
camera["eye"] = {"x": eye_x, "y": eye_y, "z": eye_z}
|
||||||
camera["center"] = {"x": at_x, "y": at_y, "z": at_z}
|
camera["center"] = {"x": at_x, "y": at_y, "z": at_z}
|
||||||
|
camera["up"] = {"x": up_x, "y": up_y, "z": up_z}
|
||||||
|
|
||||||
current_layout.update(
|
current_layout.update(
|
||||||
{
|
{
|
||||||
@ -751,7 +747,9 @@ def _update_axes_bounds(
|
|||||||
current_layout.update({"xaxis": xaxis, "yaxis": yaxis, "zaxis": zaxis})
|
current_layout.update({"xaxis": xaxis, "yaxis": yaxis, "zaxis": zaxis})
|
||||||
|
|
||||||
|
|
||||||
def _scale_camera_to_bounds(coordinate: float, axis_bounds: Tuple[float, float]):
|
def _scale_camera_to_bounds(
|
||||||
|
coordinate: float, axis_bounds: Tuple[float, float], is_position: bool
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
We set our plotly plot's axes' bounding box to [-1,1]x[-1,1]x[-1,1]. As such,
|
We set our plotly plot's axes' bounding box to [-1,1]x[-1,1]x[-1,1]. As such,
|
||||||
the plotly camera location has to be scaled accordingly to have its world coordinates
|
the plotly camera location has to be scaled accordingly to have its world coordinates
|
||||||
@ -762,7 +760,12 @@ def _scale_camera_to_bounds(coordinate: float, axis_bounds: Tuple[float, float])
|
|||||||
coordinate: the float value to be transformed
|
coordinate: the float value to be transformed
|
||||||
axis_bounds: the bounds of the plotly plot for the axis which
|
axis_bounds: the bounds of the plotly plot for the axis which
|
||||||
the coordinate argument refers to
|
the coordinate argument refers to
|
||||||
|
is_position: If true, the float value is the coordinate of a position, and so must
|
||||||
|
be moved in to [-1,1]. Otherwise it is a component of a direction, and so needs only
|
||||||
|
to be scaled.
|
||||||
"""
|
"""
|
||||||
scale = (axis_bounds[1] - axis_bounds[0]) / 2
|
scale = (axis_bounds[1] - axis_bounds[0]) / 2
|
||||||
|
if not is_position:
|
||||||
|
return coordinate / scale
|
||||||
offset = (axis_bounds[1] / scale) - 1
|
offset = (axis_bounds[1] / scale) - 1
|
||||||
return coordinate / scale - offset
|
return coordinate / scale - offset
|
||||||
|
Loading…
x
Reference in New Issue
Block a user