From f4051a15085ab47f52968c4a2daf443bd95d70d8 Mon Sep 17 00:00:00 2001 From: Owen Burns Date: Wed, 11 Sep 2024 10:27:22 -0400 Subject: [PATCH 1/6] Fixed missing brackets in tensorboard summary.py (#6960) --- python/open3d/visualization/tensorboard_plugin/summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/open3d/visualization/tensorboard_plugin/summary.py b/python/open3d/visualization/tensorboard_plugin/summary.py index 0b6e254b754..4bb35fa5da7 100644 --- a/python/open3d/visualization/tensorboard_plugin/summary.py +++ b/python/open3d/visualization/tensorboard_plugin/summary.py @@ -424,7 +424,7 @@ def get_or_check_shape(prop, tensor_tuple, exp_shape): raise ValueError( f"Property {prop} tensor should have shape[{k}]" f"={s} for all elements but is " - f"{tensor.shape[k-1] for tensor in tensor_tuple}.") + f"{[tensor.shape[k-1] for tensor in tensor_tuple]}.") return shape[:2] From dbb3c7d58b7af5819540dacd8ea4e0a11ab605dc Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:14:16 -0700 Subject: [PATCH 2/6] Always pre-transform vertices while reading with Assimp. (#6959) Better error messages. --- cpp/open3d/io/TriangleMeshIO.h | 1 + cpp/open3d/io/file_format/FileASSIMP.cpp | 24 ++++++++++++++-------- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 19 ++++++++++------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/cpp/open3d/io/TriangleMeshIO.h b/cpp/open3d/io/TriangleMeshIO.h index 3ee4cb0f492..32ec2aa751a 100644 --- a/cpp/open3d/io/TriangleMeshIO.h +++ b/cpp/open3d/io/TriangleMeshIO.h @@ -31,6 +31,7 @@ struct ReadTriangleMeshOptions { /// `aiProcessPreset_TargetRealtime_Fast, /// aiProcess_RemoveRedundantMaterials, aiProcess_OptimizeMeshes, /// aiProcess_PreTransformVertices`. + /// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h /// /// Note that identical vertices will always be joined regardless of whether /// post-processing is enabled or not, which changes the number of vertices diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index 92a2fda8077..15bc3ffd26a 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -36,13 +36,16 @@ FileGeometry ReadFileGeometryTypeFBX(const std::string& path) { return FileGeometry(CONTAINS_TRIANGLES | CONTAINS_POINTS); } +// Ref: +// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h const unsigned int kPostProcessFlags_compulsory = - aiProcess_JoinIdenticalVertices; + aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | + aiProcess_PreTransformVertices; const unsigned int kPostProcessFlags_fast = - aiProcessPreset_TargetRealtime_Fast | - aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes | - aiProcess_PreTransformVertices; + kPostProcessFlags_compulsory | aiProcess_GenNormals | + aiProcess_Triangulate | aiProcess_GenUVCoords | + aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes; struct TextureImages { std::shared_ptr albedo; @@ -65,7 +68,7 @@ void LoadTextures(const std::string& filename, std::string base_path = utility::filesystem::GetFileParentDirectory(filename); - auto texture_loader = [&base_path, &scene, &mat]( + auto texture_loader = [&base_path, &scene, &mat, &filename]( aiTextureType type, std::shared_ptr& img) { if (mat->GetTextureCount(type) > 0) { @@ -94,7 +97,10 @@ void LoadTextures(const std::string& filename, } } else { utility::LogWarning( - "This format of image is not supported."); + "Unsupported texture format for texture {} for " + "file {}: Only jpg and " + "png textures are supported.", + path.C_Str(), filename); } } // Else, build the path to it. @@ -170,7 +176,8 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } @@ -326,7 +333,8 @@ bool ReadModelUsingAssimp(const std::string& filename, const auto* scene = importer.ReadFile(filename.c_str(), kPostProcessFlags_fast); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 514d59fca1b..da23953fe79 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -35,14 +35,16 @@ namespace t { namespace io { // Split all polygons with more than 3 edges into triangles. +// Ref: +// https://github.com/assimp/assimp/blob/master/include/assimp/postprocess.h const unsigned int kPostProcessFlags_compulsory = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | - aiProcess_SortByPType; + aiProcess_SortByPType | aiProcess_PreTransformVertices; const unsigned int kPostProcessFlags_fast = - aiProcessPreset_TargetRealtime_Fast | - aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes | - aiProcess_PreTransformVertices; + kPostProcessFlags_compulsory | aiProcess_GenNormals | + aiProcess_GenUVCoords | aiProcess_RemoveRedundantMaterials | + aiProcess_OptimizeMeshes; bool ReadTriangleMeshUsingASSIMP( const std::string& filename, @@ -58,7 +60,8 @@ bool ReadTriangleMeshUsingASSIMP( const auto* scene = importer.ReadFile(filename.c_str(), post_process_flags); if (!scene) { - utility::LogWarning("Unable to load file {} with ASSIMP", filename); + utility::LogWarning("Unable to load file {} with ASSIMP: {}", filename, + importer.GetErrorString()); return false; } @@ -219,7 +222,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Sanity checks... if (write_ascii) { utility::LogWarning( - "TriangleMesh can't be saved in ASCII fromat as .glb"); + "TriangleMesh can't be saved in ASCII format as .glb"); return false; } if (compressed) { @@ -470,7 +473,9 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Export if (exporter.Export(ai_scene.get(), "glb2", filename.c_str()) == AI_FAILURE) { - utility::LogWarning("Got error: {}", exporter.GetErrorString()); + utility::LogWarning( + "Got error: ({}) while writing TriangleMesh to file {}.", + exporter.GetErrorString(), filename); return false; } From 165ff2b15b095ed8c0be72a626bdf30e042b6ec4 Mon Sep 17 00:00:00 2001 From: daizhirui Date: Mon, 16 Sep 2024 16:41:53 -0700 Subject: [PATCH 3/6] fix build with fmt-v11 (#6969) --- cpp/open3d/t/geometry/RaycastingScene.cpp | 2 +- cpp/open3d/utility/IJsonConvertible.h | 2 +- cpp/open3d/visualization/rendering/RendererHandle.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/open3d/t/geometry/RaycastingScene.cpp b/cpp/open3d/t/geometry/RaycastingScene.cpp index 31fdbeef856..bc003f8cbed 100644 --- a/cpp/open3d/t/geometry/RaycastingScene.cpp +++ b/cpp/open3d/t/geometry/RaycastingScene.cpp @@ -1179,7 +1179,7 @@ namespace fmt { template <> struct formatter { template - auto format(const RTCError& c, FormatContext& ctx) { + auto format(const RTCError& c, FormatContext& ctx) const { const char* name = rtcGetErrorString(c); return format_to(ctx.out(), name); } diff --git a/cpp/open3d/utility/IJsonConvertible.h b/cpp/open3d/utility/IJsonConvertible.h index 8febffa566f..e18e2948a23 100644 --- a/cpp/open3d/utility/IJsonConvertible.h +++ b/cpp/open3d/utility/IJsonConvertible.h @@ -86,7 +86,7 @@ namespace fmt { template <> struct formatter { template - auto format(const Json::Value &value, FormatContext &ctx) + auto format(const Json::Value &value, FormatContext &ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "{}", open3d::utility::JsonToString(value)); } diff --git a/cpp/open3d/visualization/rendering/RendererHandle.h b/cpp/open3d/visualization/rendering/RendererHandle.h index dc8b06382e7..be69a3d99a8 100644 --- a/cpp/open3d/visualization/rendering/RendererHandle.h +++ b/cpp/open3d/visualization/rendering/RendererHandle.h @@ -164,7 +164,7 @@ struct formatter< char>> { template auto format(const open3d::visualization::rendering::REHandle_abstract& uid, - FormatContext& ctx) -> decltype(ctx.out()) { + FormatContext& ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "[{}, {}, hash: {}]", open3d::visualization::rendering::REHandle_abstract:: TypeToString(uid.type), From a71ab5eecaa07afd04ef50fca38cedd554025bf1 Mon Sep 17 00:00:00 2001 From: Daniel Simon Date: Tue, 17 Sep 2024 07:28:10 -0700 Subject: [PATCH 4/6] Add missing build byproducts to enable support for Ninja on Windows (#6971) * Added missing build byproduct for BoringSSL * Added missing build byproduct for libcurl * Added missing build byproduct for ipp * Fixed incorrect build byproduct for UVAtlas * Added missing build byproduct for VTK --- 3rdparty/boringssl/boringssl.cmake | 4 +++- 3rdparty/curl/curl.cmake | 5 ++++- 3rdparty/ipp/ipp.cmake | 28 ++++++++++++++++++---------- 3rdparty/uvatlas/uvatlas.cmake | 2 +- 3rdparty/vtk/vtk_build.cmake | 15 ++++++++++----- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/3rdparty/boringssl/boringssl.cmake b/3rdparty/boringssl/boringssl.cmake index 5f9aeea9e33..5048b76648f 100644 --- a/3rdparty/boringssl/boringssl.cmake +++ b/3rdparty/boringssl/boringssl.cmake @@ -48,7 +48,9 @@ ExternalProject_Add( CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + /$<$:$,Debug,Release>/>lib/${CMAKE_STATIC_LIBRARY_PREFIX}ssl${CMAKE_STATIC_LIBRARY_SUFFIX} + /$<$:$,Debug,Release>/>lib/${CMAKE_STATIC_LIBRARY_PREFIX}crypto${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_boringssl SOURCE_DIR) diff --git a/3rdparty/curl/curl.cmake b/3rdparty/curl/curl.cmake index c45dbea0dd9..7a73051c1c0 100644 --- a/3rdparty/curl/curl.cmake +++ b/3rdparty/curl/curl.cmake @@ -51,6 +51,7 @@ if(BUILD_CURL_FROM_SOURCE) ${ExternalProject_CMAKE_ARGS_hidden} BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} + /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}-d${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_curl INSTALL_DIR) @@ -79,7 +80,9 @@ else() CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + /lib/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}${CMAKE_STATIC_LIBRARY_SUFFIX} + /lib/${CMAKE_STATIC_LIBRARY_PREFIX}${curl_lib_name}-d${CMAKE_STATIC_LIBRARY_SUFFIX} ) ExternalProject_Get_Property(ext_curl SOURCE_DIR) diff --git a/3rdparty/ipp/ipp.cmake b/3rdparty/ipp/ipp.cmake index ffc9b320512..deaed2ab179 100755 --- a/3rdparty/ipp/ipp.cmake +++ b/3rdparty/ipp/ipp.cmake @@ -35,6 +35,20 @@ else() set(IPP_SUBPATH "") endif() +# Threading layer libs must be linked first. +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/ipp-performace-benefits-with-tl-functions.html +# Library dependency order: +# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/library-dependencies-by-domain.html +if (WIN32) + set(IPP_LIBRARIES ipp_iw ippcvmt_tl_tbb ippcvmt ippimt_tl_tbb ippimt ippccmt_tl_tbb ippccmt ippsmt ippvmmt ippcoremt_tl_tbb ippcoremt) +else() + set(IPP_LIBRARIES ipp_iw ippcv_tl_tbb ippcv ippi_tl_tbb ippi ippcc_tl_tbb ippcc ipps ippvm ippcore_tl_tbb ippcore) +endif() + +foreach(item IN LISTS IPP_LIBRARIES) + list(APPEND IPP_BUILD_BYPRODUCTS /${IPP_SUBPATH}lib/${CMAKE_STATIC_LIBRARY_PREFIX}${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) +endforeach() + ExternalProject_Add(ext_ipp PREFIX ipp URL ${IPP_URL} @@ -45,16 +59,10 @@ ExternalProject_Add(ext_ipp CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - ) + BUILD_BYPRODUCTS + ${IPP_BUILD_BYPRODUCTS} +) + ExternalProject_Get_Property(ext_ipp SOURCE_DIR) set(IPP_INCLUDE_DIR "${SOURCE_DIR}/${IPP_SUBPATH}include/") -# Threading layer libs must be linked first. -# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/ipp-performace-benefits-with-tl-functions.html -# Library dependency order: -# https://www.intel.com/content/www/us/en/docs/ipp/developer-guide-reference/2021-11/library-dependencies-by-domain.html -if (WIN32) - set(IPP_LIBRARIES ipp_iw ippcvmt_tl_tbb ippcvmt ippimt_tl_tbb ippimt ippccmt_tl_tbb ippccmt ippsmt ippvmmt ippcoremt_tl_tbb ippcoremt) -else() - set(IPP_LIBRARIES ipp_iw ippcv_tl_tbb ippcv ippi_tl_tbb ippi ippcc_tl_tbb ippcc ipps ippvm ippcore_tl_tbb ippcore) -endif() set(IPP_LIB_DIR "${SOURCE_DIR}/${IPP_SUBPATH}lib") \ No newline at end of file diff --git a/3rdparty/uvatlas/uvatlas.cmake b/3rdparty/uvatlas/uvatlas.cmake index 51831059941..63642f80e3d 100644 --- a/3rdparty/uvatlas/uvatlas.cmake +++ b/3rdparty/uvatlas/uvatlas.cmake @@ -52,7 +52,7 @@ ExternalProject_Add( -Ddirectxmath_DIR= DEPENDS ext_directxheaders ext_directxmath BUILD_BYPRODUCTS - /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}uvatlas${CMAKE_STATIC_LIBRARY_SUFFIX} + /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}UVAtlas${CMAKE_STATIC_LIBRARY_SUFFIX} ) diff --git a/3rdparty/vtk/vtk_build.cmake b/3rdparty/vtk/vtk_build.cmake index 271d3789667..f9217f2301e 100644 --- a/3rdparty/vtk/vtk_build.cmake +++ b/3rdparty/vtk/vtk_build.cmake @@ -26,13 +26,13 @@ set(VTK_LIBRARIES vtksys-${VTK_VERSION}${VTK_LIB_SUFFIX} ) -foreach(item IN LISTS VTK_LIBRARIES) - list(APPEND VTK_BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) -endforeach() - if(BUILD_VTK_FROM_SOURCE) + foreach(item IN LISTS VTK_LIBRARIES) + list(APPEND VTK_BUILD_BYPRODUCTS /${Open3D_INSTALL_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endforeach() + ExternalProject_Add( ext_vtk PREFIX vtk @@ -294,6 +294,10 @@ if(BUILD_VTK_FROM_SOURCE) else() #### download prebuilt vtk + foreach(item IN LISTS VTK_LIBRARIES) + list(APPEND VTK_BUILD_BYPRODUCTS /lib/${item}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endforeach() + if(LINUX_AARCH64) message(FATAL "No precompiled vtk for platform. Enable BUILD_VTK_FROM_SOURCE") elseif(APPLE_AARCH64) @@ -334,7 +338,8 @@ else() #### download prebuilt vtk CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" - BUILD_BYPRODUCTS "" + BUILD_BYPRODUCTS + ${VTK_BUILD_BYPRODUCTS} ) ExternalProject_Get_Property(ext_vtk SOURCE_DIR) From 85981ff8b902669899a4b9e4f6029647a1ee7f97 Mon Sep 17 00:00:00 2001 From: Robin <102535177+rxba@users.noreply.github.com> Date: Wed, 18 Sep 2024 03:12:23 +0200 Subject: [PATCH 5/6] fix visualization/draw ICP example and add warnings (#6933) * fix mismatch of selection sets, no target points in set and overall mismatch in number of correspondences. --------- Co-authored-by: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> --- CHANGELOG.md | 1 + examples/python/visualization/draw.py | 57 +++++++++++++++++++-------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c58729b74da..e5803ea862f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - Fix minimal oriented bounding box of MeshBase derived classes and add new unit tests (PR #6898) - Fix projection of point cloud to Depth/RGBD image if no position attribute is provided (PR #6880) - Support lowercase types when reading PCD files (PR #6930) +- Fix visualization/draw ICP example and add warnings (PR #6933) ## 0.13 diff --git a/examples/python/visualization/draw.py b/examples/python/visualization/draw.py index f354f55783e..231a0af8bb0 100644 --- a/examples/python/visualization/draw.py +++ b/examples/python/visualization/draw.py @@ -11,6 +11,7 @@ import open3d.visualization as vis import os import random +import warnings pyexample_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) test_data_path = os.path.join(os.path.dirname(pyexample_path), 'test_data') @@ -142,30 +143,46 @@ def selections(): source_name = "Source (yellow)" target_name = "Target (blue)" - def do_icp_one_set(o3dvis): + def _prep_correspondences(o3dvis, two_set=False): # sets: [name: [{ "index": int, "order": int, "point": (x, y, z)}, ...], # ...] sets = o3dvis.get_selection_sets() - source_picked = sorted(list(sets[0][source_name]), - key=lambda x: x.order) - target_picked = sorted(list(sets[0][target_name]), - key=lambda x: x.order) - source_indices = [idx.index for idx in source_picked] - target_indices = [idx.index for idx in target_picked] - - t = get_icp_transform(source, target, source_indices, target_indices) - source.transform(t) - - # Update the source geometry - o3dvis.remove_geometry(source_name) - o3dvis.add_geometry({"name": source_name, "geometry": source}) + if not sets: + warnings.warn( + "Empty selection sets. Select point correspondences for initial rough transform.", + RuntimeWarning) + return [], [] + if source_name not in sets[0]: + warnings.warn( + "First selection set should contain Source (yellow) points.", + RuntimeWarning) + return [], [] - def do_icp_two_sets(o3dvis): - sets = o3dvis.get_selection_sets() source_set = sets[0][source_name] - target_set = sets[1][target_name] + if two_set: + if not len(sets) == 2: + warnings.warn( + "Two set registration requires exactly two selection sets of corresponding points.", + RuntimeWarning) + return [], [] + target_set = sets[1][target_name] + else: + if target_name not in sets[0]: + warnings.warn( + "Selection set should contain Target (blue) points.", + RuntimeWarning) + return [], [] + target_set = sets[0][target_name] source_picked = sorted(list(source_set), key=lambda x: x.order) target_picked = sorted(list(target_set), key=lambda x: x.order) + if len(source_picked) != len(target_picked): + warnings.warn( + f"Registration requires equal number of corresponding points (current selection: {len(source_picked)} source, {len(target_picked)} target).", + RuntimeWarning) + return [], [] + return source_picked, target_picked + + def _do_icp(o3dvis, source_picked, target_picked): source_indices = [idx.index for idx in source_picked] target_indices = [idx.index for idx in target_picked] @@ -176,6 +193,12 @@ def do_icp_two_sets(o3dvis): o3dvis.remove_geometry(source_name) o3dvis.add_geometry({"name": source_name, "geometry": source}) + def do_icp_one_set(o3dvis): + _do_icp(o3dvis, *_prep_correspondences(o3dvis)) + + def do_icp_two_sets(o3dvis): + _do_icp(o3dvis, *_prep_correspondences(o3dvis, two_set=True)) + vis.draw([{ "name": source_name, "geometry": source From f4e1fa94f6b8385092eaddc721d6dfe9799f9a68 Mon Sep 17 00:00:00 2001 From: Sameer Sheorey <41028320+ssheorey@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:27:45 -0700 Subject: [PATCH 6/6] Add remote visualizer example. Allow changing rpc address in draw. (#6973) --- .../python/visualization/remote_visualizer.py | 71 +++++++++++++++++++ .../visualization/_external_visualizer.py | 7 ++ python/open3d/visualization/draw.py | 11 +-- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 examples/python/visualization/remote_visualizer.py diff --git a/examples/python/visualization/remote_visualizer.py b/examples/python/visualization/remote_visualizer.py new file mode 100644 index 00000000000..e360e55272b --- /dev/null +++ b/examples/python/visualization/remote_visualizer.py @@ -0,0 +1,71 @@ +# ---------------------------------------------------------------------------- +# - Open3D: www.open3d.org - +# ---------------------------------------------------------------------------- +# Copyright (c) 2018-2023 www.open3d.org +# SPDX-License-Identifier: MIT +# ---------------------------------------------------------------------------- +"""This example shows Open3D's remote visualization feature using RPC +communication. To run this example, start the client first by running + +python remote_visualizer.py client + +and then run the server by running + +python remote_visualizer.py server + +Port 51454 is used by default for communication. For remote visualization (client +and server running on different machines), use ssh to forward the remote server +port to your local computer: + + ssh -N -R 51454:localhost:51454 user@remote_host + +See documentation for more details (e.g. to use a different port). +""" +import sys +import numpy as np +import open3d as o3d +import open3d.visualization as vis + + +def make_point_cloud(npts, center, radius, colorize): + pts = np.random.uniform(-radius, radius, size=[npts, 3]) + center + cloud = o3d.geometry.PointCloud() + cloud.points = o3d.utility.Vector3dVector(pts) + if colorize: + colors = np.random.uniform(0.0, 1.0, size=[npts, 3]) + cloud.colors = o3d.utility.Vector3dVector(colors) + return cloud + + +def server_time_animation(): + orig = make_point_cloud(200, (0, 0, 0), 1.0, True) + clouds = [{"name": "t=0", "geometry": orig, "time": 0}] + drift_dir = (1.0, 0.0, 0.0) + expand = 1.0 + n = 20 + ev = o3d.visualization.ExternalVisualizer() + for i in range(1, n): + amount = float(i) / float(n - 1) + cloud = o3d.geometry.PointCloud() + pts = np.asarray(orig.points) + pts = pts * (1.0 + amount * expand) + [amount * v for v in drift_dir] + cloud.points = o3d.utility.Vector3dVector(pts) + cloud.colors = orig.colors + ev.set(obj=cloud, time=i, path=f"points at t={i}") + print('.', end='', flush=True) + print() + + +def client_time_animation(): + o3d.visualization.draw(title="Open3D - Remote Visualizer Client", + show_ui=True, + rpc_interface=True) + + +if __name__ == "__main__": + assert len(sys.argv) == 2 and sys.argv[1] in ('client', 'server'), ( + "Usage: python remote_visualizer.py [client|server]") + if sys.argv[1] == "client": + client_time_animation() + elif sys.argv[1] == "server": + server_time_animation() diff --git a/python/open3d/visualization/_external_visualizer.py b/python/open3d/visualization/_external_visualizer.py index ac7209127f0..774ddbaebe1 100644 --- a/python/open3d/visualization/_external_visualizer.py +++ b/python/open3d/visualization/_external_visualizer.py @@ -37,17 +37,23 @@ def set(self, obj=None, path='', time=0, layer='', connection=None): Example: To quickly send a single object just write:: + ev.set(point_cloud) To place the object at a specific location in the scene tree do:: + ev.set(point_cloud, path='group/mypoints', time=42, layer='') + Note that depending on the visualizer some arguments like time or layer may not be supported and will be ignored. To set multiple objects use a list to pass multiple objects:: + ev.set([point_cloud, mesh, camera]) + Each entry in the list can be a tuple specifying all or some of the location parameters:: + ev.set(objs=[(point_cloud,'group/mypoints', 1, 'layer1'), (mesh, 'group/mymesh'), camera @@ -147,6 +153,7 @@ def draw(self, geometry=None, *args, **kwargs): Example: Here we use draw with the default external visualizer:: + import open3d as o3d torus = o3d.geometry.TriangleMesh.create_torus() diff --git a/python/open3d/visualization/draw.py b/python/open3d/visualization/draw.py index f7764b41400..aa6e29e2b2c 100644 --- a/python/open3d/visualization/draw.py +++ b/python/open3d/visualization/draw.py @@ -83,9 +83,10 @@ def draw(geometry=None, animation_time_step (float): Duration in seconds for each animation frame. animation_duration (float): Total animation duration in seconds. - rpc_interface (bool): Start an RPC interface at http://localhost:51454 and - listen for drawing requests. The requests can be made with - :class:`open3d.visualization.ExternalVisualizer`. + rpc_interface (bool or str): Start an RPC interface at this local + address and listen for drawing requests. If rpc_interface is True, the + default address "tcp://localhost:51454" is used. The requests can be + made with :class:`open3d.visualization.ExternalVisualizer`. on_init (Callable): Extra initialization procedure for the underlying GUI window. The procedure receives a single argument of type :class:`open3d.visualization.O3DVisualizer`. @@ -202,7 +203,9 @@ def add(g, n): w.show_skybox(show_skybox) if rpc_interface: - w.start_rpc_interface(address="tcp://127.0.0.1:51454", timeout=10000) + if not isinstance(rpc_interface, str): + rpc_interface = "tcp://127.0.0.1:51454" + w.start_rpc_interface(address=rpc_interface, timeout=10000) def stop_rpc(): w.stop_rpc_interface()