mirror of
https://github.com/saymrwulf/pytorch.git
synced 2026-05-14 20:57:59 +00:00
Support 3D mesh/point cloud (#20413)
Summary: I started adding support for the new **[mesh/point cloud](https://github.com/tensorflow/graphics/blob/master/tensorflow_graphics/g3doc/tensorboard.md)** data type introduced to TensorBoard recently. I created the functions to add the data, created the appropriate summaries. This new data type however requires a **Merged** summary containing the data for the vertices, colors and faces. I got stuck at this stage. Maybe someone can help. lanpa? I converted the example code by Google to PyTorch: ```python import numpy as np import trimesh import torch from torch.utils.tensorboard import SummaryWriter sample_mesh = 'https://storage.googleapis.com/tensorflow-graphics/tensorboard/test_data/ShortDance07_a175_00001.ply' log_dir = 'runs/torch' batch_size = 1 # Camera and scene configuration. config_dict = { 'camera': {'cls': 'PerspectiveCamera', 'fov': 75}, 'lights': [ { 'cls': 'AmbientLight', 'color': '#ffffff', 'intensity': 0.75, }, { 'cls': 'DirectionalLight', 'color': '#ffffff', 'intensity': 0.75, 'position': [0, -1, 2], }], 'material': { 'cls': 'MeshStandardMaterial', 'roughness': 1, 'metalness': 0 } } # Read all sample PLY files. mesh = trimesh.load_remote(sample_mesh) vertices = np.array(mesh.vertices) # Currently only supports RGB colors. colors = np.array(mesh.visual.vertex_colors[:, :3]) faces = np.array(mesh.faces) # Add batch dimension, so our data will be of shape BxNxC. vertices = np.expand_dims(vertices, 0) colors = np.expand_dims(colors, 0) faces = np.expand_dims(faces, 0) # Create data placeholders of the same shape as data itself. vertices_tensor = torch.as_tensor(vertices) faces_tensor = torch.as_tensor(faces) colors_tensor = torch.as_tensor(colors) writer = SummaryWriter(log_dir) writer.add_mesh('mesh_color_tensor', vertices=vertices_tensor, faces=faces_tensor, colors=colors_tensor, config_dict=config_dict) writer.close() ``` I tried adding only the vertex summary, hence the others are supposed to be optional. I got the following error from TensorBoard and it also didn't display the points: ``` Traceback (most recent call last): File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/werkzeug/serving.py", line 302, in run_wsgi execute(self.server.app) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/werkzeug/serving.py", line 290, in execute application_iter = app(environ, start_response) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/tensorboard/backend/application.py", line 309, in __call__ return self.data_applications[clean_path](environ, start_response) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/werkzeug/wrappers/base_request.py", line 235, in application resp = f(*args[:-2] + (request,)) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/tensorboard/plugins/mesh/mesh_plugin.py", line 252, in _serve_mesh_metadata tensor_events = self._collect_tensor_events(request) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/tensorboard/plugins/mesh/mesh_plugin.py", line 188, in _collect_tensor_events tensors = self._multiplexer.Tensors(run, instance_tag) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/tensorboard/backend/event_processing/plugin_event_multiplexer.py", line 400, in Tensors return accumulator.Tensors(tag) File "/home/dawars/workspace/pytorch/venv/lib/python3.6/site-packages/tensorboard/backend/event_processing/plugin_event_accumulator.py", line 437, in Tensors return self.tensors_by_tag[tag].Items(_TENSOR_RESERVOIR_KEY) KeyError: 'mesh_color_tensor_COLOR' ``` Pull Request resolved: https://github.com/pytorch/pytorch/pull/20413 Differential Revision: D15500737 Pulled By: orionr fbshipit-source-id: 426e8b966037d08c065bce5198fd485fd80a2b67
This commit is contained in:
parent
6063ffd055
commit
b5a5e296aa
4 changed files with 152 additions and 7 deletions
|
|
@ -45,12 +45,12 @@ if [[ "$BUILD_ENVIRONMENT" != *ppc64le* ]]; then
|
|||
# TODO: move this to Docker
|
||||
PYTHON_VERSION=$(python -c 'import platform; print(platform.python_version())'|cut -c1)
|
||||
echo $PYTHON_VERSION
|
||||
if [[ $PYTHON_VERSION == "2" ]]; then
|
||||
pip install -q https://s3.amazonaws.com/ossci-linux/wheels/tensorboard-1.14.0a0-py2-none-any.whl --user
|
||||
else
|
||||
pip install -q https://s3.amazonaws.com/ossci-linux/wheels/tensorboard-1.14.0a0-py3-none-any.whl --user
|
||||
fi
|
||||
|
||||
# if [[ $PYTHON_VERSION == "2" ]]; then
|
||||
# pip install -q https://s3.amazonaws.com/ossci-linux/wheels/tensorboard-1.14.0a0-py2-none-any.whl --user
|
||||
# else
|
||||
# pip install -q https://s3.amazonaws.com/ossci-linux/wheels/tensorboard-1.14.0a0-py3-none-any.whl --user
|
||||
# fi
|
||||
pip install -q tb-nightly --user
|
||||
# mypy will fail to install on Python <3.4. In that case,
|
||||
# we just won't run these tests.
|
||||
pip install mypy --user || true
|
||||
|
|
|
|||
|
|
@ -97,3 +97,4 @@ Expected result:
|
|||
.. automethod:: add_embedding
|
||||
.. automethod:: add_pr_curve
|
||||
.. automethod:: add_custom_scalars
|
||||
.. automethod:: add_mesh
|
||||
|
|
|
|||
|
|
@ -459,3 +459,94 @@ def compute_curve(labels, predictions, num_thresholds=None, weights=None):
|
|||
precision = tp / np.maximum(_MINIMUM_COUNT, tp + fp)
|
||||
recall = tp / np.maximum(_MINIMUM_COUNT, tp + fn)
|
||||
return np.stack((tp, fp, tn, fn, precision, recall))
|
||||
|
||||
|
||||
def _get_tensor_summary(name, display_name, description, tensor, content_type, json_config):
|
||||
"""Creates a tensor summary with summary metadata.
|
||||
|
||||
Args:
|
||||
name: Uniquely identifiable name of the summary op. Could be replaced by
|
||||
combination of name and type to make it unique even outside of this
|
||||
summary.
|
||||
display_name: Will be used as the display name in TensorBoard.
|
||||
Defaults to `name`.
|
||||
description: A longform readable description of the summary data. Markdown
|
||||
is supported.
|
||||
tensor: Tensor to display in summary.
|
||||
content_type: Type of content inside the Tensor.
|
||||
json_config: A string, JSON-serialized dictionary of ThreeJS classes
|
||||
configuration.
|
||||
|
||||
Returns:
|
||||
Tensor summary with metadata.
|
||||
"""
|
||||
import torch
|
||||
from tensorboard.plugins.mesh import metadata
|
||||
|
||||
tensor = torch.as_tensor(tensor)
|
||||
|
||||
tensor_metadata = metadata.create_summary_metadata(
|
||||
name,
|
||||
display_name,
|
||||
content_type,
|
||||
tensor.shape,
|
||||
description,
|
||||
json_config=json_config)
|
||||
|
||||
tensor = TensorProto(dtype='DT_FLOAT',
|
||||
float_val=tensor.reshape(-1).tolist(),
|
||||
tensor_shape=TensorShapeProto(dim=[
|
||||
TensorShapeProto.Dim(size=tensor.shape[0]),
|
||||
TensorShapeProto.Dim(size=tensor.shape[1]),
|
||||
TensorShapeProto.Dim(size=tensor.shape[2]),
|
||||
]))
|
||||
|
||||
tensor_summary = Summary.Value(
|
||||
tag=metadata.get_instance_name(name, content_type),
|
||||
tensor=tensor,
|
||||
metadata=tensor_metadata,
|
||||
)
|
||||
|
||||
return tensor_summary
|
||||
|
||||
|
||||
# https://github.com/tensorflow/tensorboard/blob/master/tensorboard/plugins/mesh/summary.py
|
||||
def mesh(tag, vertices, colors, faces, config_dict, display_name=None, description=None):
|
||||
"""Outputs a merged `Summary` protocol buffer with a mesh/point cloud.
|
||||
|
||||
Args:
|
||||
tag: A name for this summary operation.
|
||||
vertices: Tensor of shape `[dim_1, ..., dim_n, 3]` representing the 3D
|
||||
coordinates of vertices.
|
||||
faces: Tensor of shape `[dim_1, ..., dim_n, 3]` containing indices of
|
||||
vertices within each triangle.
|
||||
colors: Tensor of shape `[dim_1, ..., dim_n, 3]` containing colors for each
|
||||
vertex.
|
||||
display_name: If set, will be used as the display name in TensorBoard.
|
||||
Defaults to `name`.
|
||||
description: A longform readable description of the summary data. Markdown
|
||||
is supported.
|
||||
config_dict: Dictionary with ThreeJS classes names and configuration.
|
||||
|
||||
Returns:
|
||||
Merged summary for mesh/point cloud representation.
|
||||
"""
|
||||
from tensorboard.plugins.mesh.plugin_data_pb2 import MeshPluginData
|
||||
from tensorboard.plugins.mesh.summary import _get_json_config
|
||||
|
||||
json_config = _get_json_config(config_dict)
|
||||
|
||||
summaries = []
|
||||
tensors = [
|
||||
(vertices, MeshPluginData.VERTEX),
|
||||
(faces, MeshPluginData.FACE),
|
||||
(colors, MeshPluginData.COLOR)
|
||||
]
|
||||
|
||||
for tensor, content_type in tensors:
|
||||
if tensor is None:
|
||||
continue
|
||||
summaries.append(
|
||||
_get_tensor_summary(tag, display_name, description, tensor, content_type, json_config))
|
||||
|
||||
return Summary(value=summaries)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from ._pytorch_graph import graph
|
|||
from ._utils import figure_to_image
|
||||
from .summary import (
|
||||
scalar, histogram, histogram_raw, image, audio, text,
|
||||
pr_curve, pr_curve_raw, video, custom_scalars, image_boxes
|
||||
pr_curve, pr_curve_raw, video, custom_scalars, image_boxes, mesh
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -872,6 +872,59 @@ class SummaryWriter(object):
|
|||
"""
|
||||
self._get_file_writer().add_summary(custom_scalars(layout))
|
||||
|
||||
def add_mesh(self, tag, vertices, colors=None, faces=None, config_dict=None, global_step=None, walltime=None):
|
||||
"""Add meshes or 3D point clouds to TensorBoard. The visualization is based on Three.js,
|
||||
so it allows users to interact with the rendered object. Besides the basic definitions
|
||||
such as vertices, faces, users can further provide camera parameter, lighting condition, etc.
|
||||
Please check https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene for
|
||||
advanced usage. Note that currently this depends on tb-nightly to show.
|
||||
|
||||
Args:
|
||||
tag (string): Data identifier
|
||||
vertices (torch.Tensor): List of the 3D coordinates of vertices.
|
||||
colors (torch.Tensor): Colors for each vertex
|
||||
faces (torch.Tensor): Indices of vertices within each triangle. (Optional)
|
||||
config_dict: Dictionary with ThreeJS classes names and configuration.
|
||||
global_step (int): Global step value to record
|
||||
walltime (float): Optional override default walltime (time.time())
|
||||
seconds after epoch of event
|
||||
|
||||
Shape:
|
||||
vertices: :math:`(B, N, 3)`. (batch, number_of_vertices, channels)
|
||||
|
||||
colors: :math:`(B, N, 3)`. The values should lie in [0, 255] for type `uint8` or [0, 1] for type `float`.
|
||||
|
||||
faces: :math:`(B, N, 3)`. The values should lie in [0, number_of_vertices] for type `uint8`.
|
||||
|
||||
Examples::
|
||||
|
||||
from torch.utils.tensorboard import SummaryWriter
|
||||
vertices_tensor = torch.as_tensor([
|
||||
[1, 1, 1],
|
||||
[-1, -1, 1],
|
||||
[1, -1, -1],
|
||||
[-1, 1, -1],
|
||||
], dtype=torch.float).unsqueeze(0)
|
||||
colors_tensor = torch.as_tensor([
|
||||
[255, 0, 0],
|
||||
[0, 255, 0],
|
||||
[0, 0, 255],
|
||||
[255, 0, 255],
|
||||
], dtype=torch.int).unsqueeze(0)
|
||||
faces_tensor = torch.as_tensor([
|
||||
[0, 2, 3],
|
||||
[0, 3, 1],
|
||||
[0, 1, 2],
|
||||
[1, 3, 2],
|
||||
], dtype=torch.int).unsqueeze(0)
|
||||
|
||||
writer = SummaryWriter()
|
||||
writer.add_mesh('my_mesh', vertices=vertices_tensor, colors=colors_tensor, faces=faces_tensor)
|
||||
|
||||
writer.close()
|
||||
"""
|
||||
self._get_file_writer().add_summary(mesh(tag, vertices, colors, faces, config_dict), global_step, walltime)
|
||||
|
||||
def flush(self):
|
||||
"""Flushes the event file to disk.
|
||||
Call this method to make sure that all pending events have been written to
|
||||
|
|
|
|||
Loading…
Reference in a new issue