4
0
mirror of https://github.com/nerfstudio-project/nerfstudio.git synced 2026-11-11 10:34:29 +08:00
nerfstudio/tests/viewer/test_drawing.py
Ethan Weber 1e22984d87
Initial viewer (visualizer) code (#52)
* some initial code for viewer. copying from other repos

* adding electron support

* moving some test files around. need to rewrite these

* camera traj. test cases. starting to transmit images

* still working on vis

* reorganized viewer and figured out websocket sending from client. need to do with camera pose

* black

* temporarily ignore pylinting for viewer

* ignore viewer

* fixed array

* license and fixed ignore in pyproject.toml

* improved syntax
2022-06-16 12:11:40 -07:00

326 lines
12 KiB
Python

from __future__ import absolute_import, division, print_function
import unittest
import subprocess
import sys
import tempfile
import os
if sys.version_info >= (3, 0):
from io import StringIO, BytesIO
else:
from StringIO import StringIO
BytesIO = StringIO
import io
import numpy as np
import meshcat
import meshcat.geometry as g
import meshcat.cameras as c
import meshcat.transformations as tf
class VisualizerTest(unittest.TestCase):
def setUp(self):
self.vis = meshcat.Visualizer()
if "CI" in os.environ:
port = self.vis.url().split(":")[-1].split("/")[0]
self.dummy_proc = subprocess.Popen(
[sys.executable, "-m", "meshcat.tests.dummy_websocket_client", str(port)]
)
else:
self.vis.open()
self.dummy_proc = None
self.vis.wait()
def tearDown(self):
if self.dummy_proc is not None:
self.dummy_proc.kill()
class TestDrawing(VisualizerTest):
def runTest(self):
self.vis.delete()
v = self.vis["shapes"]
v.set_transform(tf.translation_matrix([1.0, 0, 0]))
v["box"].set_object(g.Box([1.0, 0.2, 0.3]))
v["box"].delete()
v["box"].set_object(g.Box([0.1, 0.2, 0.3]))
v["box"].set_transform(tf.translation_matrix([0.05, 0.1, 0.15]))
v["cylinder"].set_object(g.Cylinder(0.2, 0.1), g.MeshLambertMaterial(color=0x22DD22))
v["cylinder"].set_transform(tf.translation_matrix([0, 0.5, 0.1]).dot(tf.rotation_matrix(-np.pi / 2, [1, 0, 0])))
v["sphere"].set_object(g.Mesh(g.Sphere(0.15), g.MeshLambertMaterial(color=0xFF11DD)))
v["sphere"].set_transform(tf.translation_matrix([0, 1, 0.15]))
v["ellipsoid"].set_object(g.Ellipsoid([0.3, 0.1, 0.1]))
v["ellipsoid"].set_transform(tf.translation_matrix([0, 1.5, 0.1]))
v["transparent_ellipsoid"].set_object(
g.Mesh(g.Ellipsoid([0.3, 0.1, 0.1]), g.MeshLambertMaterial(color=0xFFFFFF, opacity=0.5))
)
v["transparent_ellipsoid"].set_transform(tf.translation_matrix([0, 2.0, 0.1]))
v = self.vis["meshes/valkyrie/head"]
v.set_object(
g.Mesh(
g.ObjMeshGeometry.from_file(os.path.join(meshcat.viewer_assets_path(), "data/head_multisense.obj")),
g.MeshLambertMaterial(
map=g.ImageTexture(
image=g.PngImage.from_file(
os.path.join(meshcat.viewer_assets_path(), "data/HeadTextureMultisense.png")
)
)
),
)
)
v.set_transform(tf.translation_matrix([0, 0.5, 0.5]))
v = self.vis["meshes/convex"]
v["obj"].set_object(
g.Mesh(
g.ObjMeshGeometry.from_file(
os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.obj")
)
)
)
v["stl_ascii"].set_object(
g.Mesh(
g.StlMeshGeometry.from_file(
os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.stl_ascii")
)
)
)
v["stl_ascii"].set_transform(tf.translation_matrix([0, -0.5, 0]))
v["stl_binary"].set_object(
g.Mesh(
g.StlMeshGeometry.from_file(
os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.stl_binary")
)
)
)
v["stl_binary"].set_transform(tf.translation_matrix([0, -1, 0]))
v["dae"].set_object(
g.Mesh(
g.DaeMeshGeometry.from_file(
os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.dae")
)
)
)
v["dae"].set_transform(tf.translation_matrix([0, -1.5, 0]))
v = self.vis["points"]
v.set_transform(tf.translation_matrix([0, 2, 0]))
verts = np.random.rand(3, 1000000)
colors = verts
v["random"].set_object(g.PointCloud(verts, colors))
v["random"].set_transform(tf.translation_matrix([-0.5, -0.5, 0]))
v = self.vis["lines"]
v.set_transform(tf.translation_matrix(([-2, -3, 0])))
vertices = np.random.random((3, 10)).astype(np.float32)
v["line_segments"].set_object(g.LineSegments(g.PointsGeometry(vertices)))
v["line"].set_object(g.Line(g.PointsGeometry(vertices)))
v["line"].set_transform(tf.translation_matrix([0, 1, 0]))
v["line_loop"].set_object(g.LineLoop(g.PointsGeometry(vertices)))
v["line_loop"].set_transform(tf.translation_matrix([0, 2, 0]))
v["line_loop_with_material"].set_object(
g.LineLoop(g.PointsGeometry(vertices), g.LineBasicMaterial(color=0xFF0000))
)
v["line_loop_with_material"].set_transform(tf.translation_matrix([0, 3, 0]))
colors = vertices # Color each line by treating its xyz coordinates as RGB colors
v["line_with_vertex_colors"].set_object(
g.Line(g.PointsGeometry(vertices, colors), g.LineBasicMaterial(vertexColors=True))
)
v["line_with_vertex_colors"].set_transform(tf.translation_matrix([0, 4, 0]))
v["triad"].set_object(
g.LineSegments(
g.PointsGeometry(
position=np.array([[0, 0, 0], [1, 0, 0], [0, 0, 0], [0, 1, 0], [0, 0, 0], [0, 0, 1]])
.astype(np.float32)
.T,
color=np.array([[1, 0, 0], [1, 0.6, 0], [0, 1, 0], [0.6, 1, 0], [0, 0, 1], [0, 0.6, 1]])
.astype(np.float32)
.T,
),
g.LineBasicMaterial(vertexColors=True),
)
)
v["triad"].set_transform(tf.translation_matrix(([0, 5, 0])))
v["triad_function"].set_object(g.triad(0.5))
v["triad_function"].set_transform(tf.translation_matrix([0, 6, 0]))
class TestMeshStreams(VisualizerTest):
def runTest(self):
"""Applications using meshcat may already have meshes loaded in memory. It is
more efficient to load these meshes with streams rather than going to and then
from a file on disk. To test this we are importing meshes from disk and
converting them into streams so it kind of defeats the intended purpose! But at
least it tests the functionality.
"""
self.vis.delete()
v = self.vis["meshes/convex"]
# Obj file
filename = os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.obj")
with open(filename, "r") as f:
fio = StringIO(f.read())
v["stream_obj"].set_object(g.Mesh(g.ObjMeshGeometry.from_stream(fio)))
v["stream_stl_ascii"].set_transform(tf.translation_matrix([0, 0.0, 0]))
# STL ASCII
filename = os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.stl_ascii")
with open(filename, "r") as f:
fio = StringIO(f.read())
v["stream_stl_ascii"].set_object(g.Mesh(g.StlMeshGeometry.from_stream(fio)))
v["stream_stl_ascii"].set_transform(tf.translation_matrix([0, -0.5, 0]))
# STL Binary
filename = os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.stl_binary")
with open(filename, "rb") as f:
fio = BytesIO(f.read())
v["stream_stl_binary"].set_object(g.Mesh(g.StlMeshGeometry.from_stream(fio)))
v["stream_stl_binary"].set_transform(tf.translation_matrix([0, -1.0, 0]))
# DAE
filename = os.path.join(meshcat.viewer_assets_path(), "../tests/data/mesh_0_convex_piece_0.dae")
with open(filename, "r") as f:
fio = StringIO(f.read())
v["stream_dae"].set_object(g.Mesh(g.DaeMeshGeometry.from_stream(fio)))
v["stream_dae"].set_transform(tf.translation_matrix([0, -1.5, 0]))
class TestStandaloneServer(unittest.TestCase):
def setUp(self):
self.zmq_url = "tcp://127.0.0.1:5560"
args = ["meshcat-server", "--zmq-url", self.zmq_url]
if "CI" not in os.environ:
args.append("--open")
self.server_proc = subprocess.Popen(args)
self.vis = meshcat.Visualizer(self.zmq_url)
# self.vis = meshcat.Visualizer()
# self.vis.open()
if "CI" in os.environ:
port = self.vis.url().split(":")[-1].split("/")[0]
self.dummy_proc = subprocess.Popen(
[sys.executable, "-m", "meshcat.tests.dummy_websocket_client", str(port)]
)
else:
# self.vis.open()
self.dummy_proc = None
self.vis.wait()
def runTest(self):
v = self.vis["shapes"]
v["cube"].set_object(g.Box([0.1, 0.2, 0.3]))
v.set_transform(tf.translation_matrix([1.0, 0, 0]))
v.set_transform(tf.translation_matrix([1.0, 1.0, 0]))
def tearDown(self):
if self.dummy_proc is not None:
self.dummy_proc.kill()
self.server_proc.kill()
class TestAnimation(VisualizerTest):
def runTest(self):
v = self.vis["shapes"]
v.set_transform(tf.translation_matrix([1.0, 0, 0]))
v["cube"].set_object(g.Box([0.1, 0.2, 0.3]))
animation = meshcat.animation.Animation()
with animation.at_frame(v, 0) as frame_vis:
frame_vis.set_transform(tf.translation_matrix([0, 0, 0]))
with animation.at_frame(v, 30) as frame_vis:
frame_vis.set_transform(tf.translation_matrix([2, 0, 0]).dot(tf.rotation_matrix(np.pi / 2, [0, 0, 1])))
v.set_animation(animation)
class TestCameraAnimation(VisualizerTest):
def runTest(self):
v = self.vis["shapes"]
v.set_transform(tf.translation_matrix([1.0, 0, 0]))
v["cube"].set_object(g.Box([0.1, 0.2, 0.3]))
animation = meshcat.animation.Animation()
with animation.at_frame(v, 0) as frame_vis:
frame_vis.set_transform(tf.translation_matrix([0, 0, 0]))
with animation.at_frame(v, 30) as frame_vis:
frame_vis.set_transform(tf.translation_matrix([2, 0, 0]).dot(tf.rotation_matrix(np.pi / 2, [0, 0, 1])))
with animation.at_frame(v, 0) as frame_vis:
frame_vis["/Cameras/default/rotated/<object>"].set_property("zoom", "number", 1)
with animation.at_frame(v, 30) as frame_vis:
frame_vis["/Cameras/default/rotated/<object>"].set_property("zoom", "number", 0.5)
v.set_animation(animation)
class TestStaticHTML(TestDrawing):
def runTest(self):
"""Test that we can generate a static HTML file from the Drawing test case and view it."""
super(TestStaticHTML, self).runTest()
res = self.vis.static_html()
# save to a file
temp = tempfile.mkstemp(suffix=".html")
with open(temp[1], "w") as f:
f.write(res)
class TestSetProperty(VisualizerTest):
def runTest(self):
self.vis["/Background"].set_property("top_color", [255, 0, 0])
class TestTriangularMesh(VisualizerTest):
def runTest(self):
"""
Test that we can render meshes from raw vertices and faces as
numpy arrays
"""
v = self.vis["triangular_mesh"]
v.set_transform(tf.rotation_matrix(np.pi / 2, [0.0, 0, 1]))
vertices = np.array([[0, 0, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1]])
faces = np.array([[0, 1, 2], [3, 0, 2]])
v.set_object(g.TriangularMeshGeometry(vertices, faces), g.MeshLambertMaterial(color=0xEEDD22, wireframe=True))
class TestOrthographicCamera(VisualizerTest):
def runTest(self):
"""
Test that we can set_object with an OrthographicCamera.
"""
self.vis.set_object(g.Box([0.5, 0.5, 0.5]))
camera = c.OrthographicCamera(left=-1, right=1, bottom=-1, top=1, near=-1000, far=1000)
self.vis["/Cameras/default/rotated"].set_object(camera)
self.vis["/Cameras/default"].set_transform(tf.translation_matrix([0, -1, 0]))
self.vis["/Cameras/default/rotated/<object>"].set_property("position", [0, 0, 0])
self.vis["/Grid"].set_property("visible", False)
class TestPerspectiveCamera(VisualizerTest):
def runTest(self):
"""
Test that we can set_object with a PerspectiveCamera.
"""
self.vis.set_object(g.Box([0.5, 0.5, 0.5]))
camera = g.PerspectiveCamera(fov=90)
self.vis["/Cameras/default/rotated"].set_object(camera)
self.vis["/Cameras/default"].set_transform(tf.translation_matrix([1, -1, 0.5]))
self.vis["/Cameras/default/rotated/<object>"].set_property("position", [0, 0, 0])