diff --git a/CHANGELOG.md b/CHANGELOG.md index c43e26450e7..34bbefdd216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - Fix geometry picker Error when LineSet objects are presented (PR #6499) - Fix mis-configured application .desktop link for the Open3D viewer when installing to a custom path (PR #6599) - Fix regression in printing cuda tensor from PR #6444 🐛 +- Add Python pathlib support for file IO (PR #6619) ## 0.13 diff --git a/cpp/pybind/io/class_io.cpp b/cpp/pybind/io/class_io.cpp index 0602f93e577..2ba0f6fad88 100644 --- a/cpp/pybind/io/class_io.cpp +++ b/cpp/pybind/io/class_io.cpp @@ -105,10 +105,10 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::Image m_io.def( "read_image", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; geometry::Image image; - ReadImage(filename, image); + ReadImage(filename.string(), image); return image; }, "Function to read Image from file", "filename"_a); @@ -117,10 +117,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_image", - [](const std::string &filename, const geometry::Image &image, + [](const fs::path &filename, const geometry::Image &image, int quality) { py::gil_scoped_release release; - return WriteImage(filename, image, quality); + return WriteImage(filename.string(), image, quality); }, "Function to write Image to file", "filename"_a, "image"_a, "quality"_a = kOpen3DImageIODefaultQuality); @@ -130,11 +130,12 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::LineSet m_io.def( "read_line_set", - [](const std::string &filename, const std::string &format, + [](const fs::path &filename, const std::string &format, bool print_progress) { py::gil_scoped_release release; geometry::LineSet line_set; - ReadLineSet(filename, line_set, format, print_progress); + ReadLineSet(filename.string(), line_set, format, + print_progress); return line_set; }, "Function to read LineSet from file", "filename"_a, @@ -144,11 +145,11 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_line_set", - [](const std::string &filename, const geometry::LineSet &line_set, + [](const fs::path &filename, const geometry::LineSet &line_set, bool write_ascii, bool compressed, bool print_progress) { py::gil_scoped_release release; - return WriteLineSet(filename, line_set, write_ascii, compressed, - print_progress); + return WriteLineSet(filename.string(), line_set, write_ascii, + compressed, print_progress); }, "Function to write LineSet to file", "filename"_a, "line_set"_a, "write_ascii"_a = false, "compressed"_a = false, @@ -159,12 +160,12 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::PointCloud m_io.def( "read_point_cloud", - [](const std::string &filename, const std::string &format, + [](const fs::path &filename, const std::string &format, bool remove_nan_points, bool remove_infinite_points, bool print_progress) { py::gil_scoped_release release; geometry::PointCloud pcd; - ReadPointCloud(filename, pcd, + ReadPointCloud(filename.string(), pcd, {format, remove_nan_points, remove_infinite_points, print_progress}); return pcd; @@ -202,13 +203,12 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_point_cloud", - [](const std::string &filename, - const geometry::PointCloud &pointcloud, + [](const fs::path &filename, const geometry::PointCloud &pointcloud, const std::string &format, bool write_ascii, bool compressed, bool print_progress) { py::gil_scoped_release release; return WritePointCloud( - filename, pointcloud, + filename.string(), pointcloud, {format, write_ascii, compressed, print_progress}); }, "Function to write PointCloud to file", "filename"_a, @@ -246,14 +246,14 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::TriangleMesh m_io.def( "read_triangle_mesh", - [](const std::string &filename, bool enable_post_processing, + [](const fs::path &filename, bool enable_post_processing, bool print_progress) { py::gil_scoped_release release; geometry::TriangleMesh mesh; ReadTriangleMeshOptions opt; opt.enable_post_processing = enable_post_processing; opt.print_progress = print_progress; - ReadTriangleMesh(filename, mesh, opt); + ReadTriangleMesh(filename.string(), mesh, opt); return mesh; }, "Function to read TriangleMesh from file", "filename"_a, @@ -263,12 +263,12 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_triangle_mesh", - [](const std::string &filename, const geometry::TriangleMesh &mesh, + [](const fs::path &filename, const geometry::TriangleMesh &mesh, bool write_ascii, bool compressed, bool write_vertex_normals, bool write_vertex_colors, bool write_triangle_uvs, bool print_progress) { py::gil_scoped_release release; - return WriteTriangleMesh(filename, mesh, write_ascii, + return WriteTriangleMesh(filename.string(), mesh, write_ascii, compressed, write_vertex_normals, write_vertex_colors, write_triangle_uvs, print_progress); @@ -283,12 +283,12 @@ void pybind_class_io(py::module &m_io) { // open3d::visualization::rendering::TriangleMeshModel (Model.h) m_io.def( "read_triangle_model", - [](const std::string &filename, bool print_progress) { + [](const fs::path &filename, bool print_progress) { py::gil_scoped_release release; visualization::rendering::TriangleMeshModel model; ReadTriangleModelOptions opt; opt.print_progress = print_progress; - ReadTriangleModel(filename, model, opt); + ReadTriangleModel(filename.string(), model, opt); return model; }, "Function to read visualization.rendering.TriangleMeshModel from " @@ -300,11 +300,11 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::VoxelGrid m_io.def( "read_voxel_grid", - [](const std::string &filename, const std::string &format, + [](const fs::path &filename, const std::string &format, bool print_progress) { py::gil_scoped_release release; geometry::VoxelGrid voxel_grid; - ReadVoxelGrid(filename, voxel_grid, format); + ReadVoxelGrid(filename.string(), voxel_grid, format); return voxel_grid; }, "Function to read VoxelGrid from file", "filename"_a, @@ -314,12 +314,11 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_voxel_grid", - [](const std::string &filename, - const geometry::VoxelGrid &voxel_grid, bool write_ascii, - bool compressed, bool print_progress) { + [](const fs::path &filename, const geometry::VoxelGrid &voxel_grid, + bool write_ascii, bool compressed, bool print_progress) { py::gil_scoped_release release; - return WriteVoxelGrid(filename, voxel_grid, write_ascii, - compressed, print_progress); + return WriteVoxelGrid(filename.string(), voxel_grid, + write_ascii, compressed, print_progress); }, "Function to write VoxelGrid to file", "filename"_a, "voxel_grid"_a, "write_ascii"_a = false, "compressed"_a = false, @@ -330,10 +329,10 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::Octree m_io.def( "read_octree", - [](const std::string &filename, const std::string &format) { + [](const fs::path &filename, const std::string &format) { py::gil_scoped_release release; geometry::Octree octree; - ReadOctree(filename, octree, format); + ReadOctree(filename.string(), octree, format); return octree; }, "Function to read Octree from file", "filename"_a, @@ -343,9 +342,9 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_octree", - [](const std::string &filename, const geometry::Octree &octree) { + [](const fs::path &filename, const geometry::Octree &octree) { py::gil_scoped_release release; - return WriteOctree(filename, octree); + return WriteOctree(filename.string(), octree); }, "Function to write Octree to file", "filename"_a, "octree"_a); docstring::FunctionDocInject(m_io, "write_octree", @@ -354,10 +353,10 @@ void pybind_class_io(py::module &m_io) { // open3d::camera m_io.def( "read_pinhole_camera_intrinsic", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; camera::PinholeCameraIntrinsic intrinsic; - ReadIJsonConvertible(filename, intrinsic); + ReadIJsonConvertible(filename.string(), intrinsic); return intrinsic; }, "Function to read PinholeCameraIntrinsic from file", "filename"_a); @@ -366,10 +365,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_pinhole_camera_intrinsic", - [](const std::string &filename, + [](const fs::path &filename, const camera::PinholeCameraIntrinsic &intrinsic) { py::gil_scoped_release release; - return WriteIJsonConvertible(filename, intrinsic); + return WriteIJsonConvertible(filename.string(), intrinsic); }, "Function to write PinholeCameraIntrinsic to file", "filename"_a, "intrinsic"_a); @@ -378,10 +377,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "read_pinhole_camera_parameters", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; camera::PinholeCameraParameters parameters; - ReadIJsonConvertible(filename, parameters); + ReadIJsonConvertible(filename.string(), parameters); return parameters; }, "Function to read PinholeCameraParameters from file", "filename"_a); @@ -390,10 +389,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_pinhole_camera_parameters", - [](const std::string &filename, + [](const fs::path &filename, const camera::PinholeCameraParameters ¶meters) { py::gil_scoped_release release; - return WriteIJsonConvertible(filename, parameters); + return WriteIJsonConvertible(filename.string(), parameters); }, "Function to write PinholeCameraParameters to file", "filename"_a, "parameters"_a); @@ -402,10 +401,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "read_pinhole_camera_trajectory", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; camera::PinholeCameraTrajectory trajectory; - ReadPinholeCameraTrajectory(filename, trajectory); + ReadPinholeCameraTrajectory(filename.string(), trajectory); return trajectory; }, "Function to read PinholeCameraTrajectory from file", "filename"_a); @@ -414,10 +413,11 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_pinhole_camera_trajectory", - [](const std::string &filename, + [](const fs::path &filename, const camera::PinholeCameraTrajectory &trajectory) { py::gil_scoped_release release; - return WritePinholeCameraTrajectory(filename, trajectory); + return WritePinholeCameraTrajectory(filename.string(), + trajectory); }, "Function to write PinholeCameraTrajectory to file", "filename"_a, "trajectory"_a); @@ -427,10 +427,10 @@ void pybind_class_io(py::module &m_io) { // open3d::registration m_io.def( "read_feature", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; pipelines::registration::Feature feature; - ReadFeature(filename, feature); + ReadFeature(filename.string(), feature); return feature; }, "Function to read registration.Feature from file", "filename"_a); @@ -439,10 +439,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_feature", - [](const std::string &filename, + [](const fs::path &filename, const pipelines::registration::Feature &feature) { py::gil_scoped_release release; - return WriteFeature(filename, feature); + return WriteFeature(filename.string(), feature); }, "Function to write Feature to file", "filename"_a, "feature"_a); docstring::FunctionDocInject(m_io, "write_feature", @@ -450,10 +450,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "read_pose_graph", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; pipelines::registration::PoseGraph pose_graph; - ReadPoseGraph(filename, pose_graph); + ReadPoseGraph(filename.string(), pose_graph); return pose_graph; }, "Function to read PoseGraph from file", "filename"_a); @@ -462,10 +462,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_pose_graph", - [](const std::string &filename, + [](const fs::path &filename, const pipelines::registration::PoseGraph pose_graph) { py::gil_scoped_release release; - WritePoseGraph(filename, pose_graph); + WritePoseGraph(filename.string(), pose_graph); }, "Function to write PoseGraph to file", "filename"_a, "pose_graph"_a); @@ -475,13 +475,14 @@ void pybind_class_io(py::module &m_io) { #ifdef BUILD_AZURE_KINECT m_io.def( "read_azure_kinect_sensor_config", - [](const std::string &filename) { + [](const fs::path &filename) { AzureKinectSensorConfig config; - bool success = ReadIJsonConvertibleFromJSON(filename, config); + bool success = + ReadIJsonConvertibleFromJSON(filename.string(), config); if (!success) { utility::LogWarning( "Invalid sensor config {}, using default instead", - filename); + filename.string()); return AzureKinectSensorConfig(); } return config; @@ -493,9 +494,8 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_azure_kinect_sensor_config", - [](const std::string &filename, - const AzureKinectSensorConfig config) { - return WriteIJsonConvertibleToJSON(filename, config); + [](const fs::path &filename, const AzureKinectSensorConfig config) { + return WriteIJsonConvertibleToJSON(filename.string(), config); }, "Function to write Azure Kinect sensor config to file", "filename"_a, "config"_a); @@ -504,13 +504,14 @@ void pybind_class_io(py::module &m_io) { m_io.def( "read_azure_kinect_mkv_metadata", - [](const std::string &filename) { + [](const fs::path &filename) { MKVMetadata metadata; - bool success = ReadIJsonConvertibleFromJSON(filename, metadata); + bool success = ReadIJsonConvertibleFromJSON(filename.string(), + metadata); if (!success) { utility::LogWarning( "Invalid mkv metadata {}, using default instead", - filename); + filename.string()); return MKVMetadata(); } return metadata; @@ -521,8 +522,8 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_azure_kinect_mkv_metadata", - [](const std::string &filename, const MKVMetadata metadata) { - return WriteIJsonConvertibleToJSON(filename, metadata); + [](const fs::path &filename, const MKVMetadata metadata) { + return WriteIJsonConvertibleToJSON(filename.string(), metadata); }, "Function to write Azure Kinect metadata to file", "filename"_a, "config"_a); diff --git a/cpp/pybind/open3d_pybind.h b/cpp/pybind/open3d_pybind.h index 32380c5af9c..0d6caaa4b02 100644 --- a/cpp/pybind/open3d_pybind.h +++ b/cpp/pybind/open3d_pybind.h @@ -40,6 +40,9 @@ // every compilation unit. #include "pybind/core/tensor_type_caster.h" +// Replace with when we require C++17. +#include "pybind_filesystem.h" + namespace py = pybind11; using namespace py::literals; diff --git a/cpp/pybind/pybind_filesystem.h b/cpp/pybind/pybind_filesystem.h new file mode 100644 index 00000000000..85028761057 --- /dev/null +++ b/cpp/pybind/pybind_filesystem.h @@ -0,0 +1,109 @@ +// ---------------------------------------------------------------------------- +// - Open3D: www.open3d.org - +// ---------------------------------------------------------------------------- +// Copyright (c) 2018-2023 www.open3d.org +// SPDX-License-Identifier: MIT +// ---------------------------------------------------------------------------- + +// Adapted from to support C++14. +// Original attribution: +// Copyright (c) 2021 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license. + +#pragma once + +#include +#include +#include +#include +#include + +#include + +#ifdef WIN32 +#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING +#endif +#ifdef __APPLE__ +#include +namespace fs = std::__fs::filesystem; +#else +#include +namespace fs = std::experimental::filesystem; +#endif + +namespace pybind11 { +namespace detail { + +template +struct path_caster { +private: + static PyObject *unicode_from_fs_native(const std::string &w) { +#if !defined(PYPY_VERSION) + return PyUnicode_DecodeFSDefaultAndSize(w.c_str(), ssize_t(w.size())); +#else + // PyPy mistakenly declares the first parameter as non-const. + return PyUnicode_DecodeFSDefaultAndSize(const_cast(w.c_str()), + ssize_t(w.size())); +#endif + } + + static PyObject *unicode_from_fs_native(const std::wstring &w) { + return PyUnicode_FromWideChar(w.c_str(), ssize_t(w.size())); + } + +public: + static handle cast(const T &path, return_value_policy, handle) { + if (auto py_str = unicode_from_fs_native(path.native())) { + return module_::import("pathlib") + .attr("Path")(reinterpret_steal(py_str)) + .release(); + } + return nullptr; + } + + bool load(handle handle, bool) { + // PyUnicode_FSConverter and PyUnicode_FSDecoder normally take care of + // calling PyOS_FSPath themselves, but that's broken on PyPy (PyPy + // issue #3168) so we do it ourselves instead. + PyObject *buf = PyOS_FSPath(handle.ptr()); + if (!buf) { + PyErr_Clear(); + return false; + } + PyObject *native = nullptr; + if (std::is_same::value) { + if (PyUnicode_FSConverter(buf, &native) != 0) { + if (auto *c_str = PyBytes_AsString(native)) { + // AsString returns a pointer to the internal buffer, which + // must not be free'd. + value = c_str; + } + } + } else if (std::is_same::value) { + if (PyUnicode_FSDecoder(buf, &native) != 0) { + if (auto *c_str = PyUnicode_AsWideCharString(native, nullptr)) { + // AsWideCharString returns a new string that must be + // free'd. + value = c_str; // Copies the string. + PyMem_Free(c_str); + } + } + } + Py_XDECREF(native); + Py_DECREF(buf); + if (PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + return true; + } + + PYBIND11_TYPE_CASTER(T, const_name("os.PathLike")); +}; + +template <> +struct type_caster : public path_caster {}; + +} // namespace detail +} // namespace pybind11 diff --git a/cpp/pybind/t/io/class_io.cpp b/cpp/pybind/t/io/class_io.cpp index e91b6d35095..9ec88f08475 100644 --- a/cpp/pybind/t/io/class_io.cpp +++ b/cpp/pybind/t/io/class_io.cpp @@ -71,10 +71,10 @@ void pybind_class_io(py::module &m_io) { // open3d::t::geometry::Image m_io.def( "read_image", - [](const std::string &filename) { + [](const fs::path &filename) { py::gil_scoped_release release; geometry::Image image; - ReadImage(filename, image); + ReadImage(filename.string(), image); return image; }, "Function to read image from file.", "filename"_a); @@ -83,10 +83,10 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_image", - [](const std::string &filename, const geometry::Image &image, + [](const fs::path &filename, const geometry::Image &image, int quality) { py::gil_scoped_release release; - return WriteImage(filename, image, quality); + return WriteImage(filename.string(), image, quality); }, "Function to write Image to file.", "filename"_a, "image"_a, "quality"_a = kOpen3DImageIODefaultQuality); @@ -96,12 +96,12 @@ void pybind_class_io(py::module &m_io) { // open3d::t::geometry::PointCloud m_io.def( "read_point_cloud", - [](const std::string &filename, const std::string &format, + [](const fs::path &filename, const std::string &format, bool remove_nan_points, bool remove_infinite_points, bool print_progress) { py::gil_scoped_release release; t::geometry::PointCloud pcd; - ReadPointCloud(filename, pcd, + ReadPointCloud(filename.string(), pcd, {format, remove_nan_points, remove_infinite_points, print_progress}); return pcd; @@ -114,12 +114,12 @@ void pybind_class_io(py::module &m_io) { m_io.def( "write_point_cloud", - [](const std::string &filename, + [](const fs::path &filename, const t::geometry::PointCloud &pointcloud, bool write_ascii, bool compressed, bool print_progress) { py::gil_scoped_release release; return WritePointCloud( - filename, pointcloud, + filename.string(), pointcloud, {write_ascii, compressed, print_progress}); }, "Function to write PointCloud with tensor attributes to file.", @@ -131,14 +131,14 @@ void pybind_class_io(py::module &m_io) { // open3d::geometry::TriangleMesh m_io.def( "read_triangle_mesh", - [](const std::string &filename, bool enable_post_processing, + [](const fs::path &filename, bool enable_post_processing, bool print_progress) { py::gil_scoped_release release; t::geometry::TriangleMesh mesh; open3d::io::ReadTriangleMeshOptions opt; opt.enable_post_processing = enable_post_processing; opt.print_progress = print_progress; - ReadTriangleMesh(filename, mesh, opt); + ReadTriangleMesh(filename.string(), mesh, opt); return mesh; }, "Function to read TriangleMesh from file", "filename"_a, @@ -178,13 +178,12 @@ The following example reads a triangle mesh with the .ply extension:: m_io.def( "write_triangle_mesh", - [](const std::string &filename, - const t::geometry::TriangleMesh &mesh, bool write_ascii, - bool compressed, bool write_vertex_normals, + [](const fs::path &filename, const t::geometry::TriangleMesh &mesh, + bool write_ascii, bool compressed, bool write_vertex_normals, bool write_vertex_colors, bool write_triangle_uvs, bool print_progress) { py::gil_scoped_release release; - return WriteTriangleMesh(filename, mesh, write_ascii, + return WriteTriangleMesh(filename.string(), mesh, write_ascii, compressed, write_vertex_normals, write_vertex_colors, write_triangle_uvs, print_progress); @@ -222,7 +221,9 @@ Example:: # Save noisy depth image (uint16) o3d.t.io.write_image("noisy_depth.png", im_dst) )"); - depth_noise_simulator.def(py::init(), + depth_noise_simulator.def(py::init([](const fs::path &fielname) { + return DepthNoiseSimulator(fielname.string()); + }), "noise_model_path"_a); depth_noise_simulator.def("simulate", &DepthNoiseSimulator::Simulate, "im_src"_a, "depth_scale"_a = 1000.0f, diff --git a/cpp/pybind/t/io/sensor.cpp b/cpp/pybind/t/io/sensor.cpp index 880342eb4b6..241d763d3cc 100644 --- a/cpp/pybind/t/io/sensor.cpp +++ b/cpp/pybind/t/io/sensor.cpp @@ -130,8 +130,12 @@ void pybind_sensor(py::module &m) { std::unique_ptr> rgbd_video_reader(m, "RGBDVideoReader", "RGBD Video file reader."); rgbd_video_reader.def(py::init<>()) - .def_static("create", &RGBDVideoReader::Create, "filename"_a, - "Create RGBD video reader based on filename") + .def_static( + "create", + [](const fs::path &filename) { + return RGBDVideoReader::Create(filename.string()); + }, + "filename"_a, "Create RGBD video reader based on filename") .def("save_frames", &RGBDVideoReader::SaveFrames, "frame_path"_a, "start_time_us"_a = 0, "end_time_us"_a = UINT64_MAX, "Save synchronized and aligned individual frames to " diff --git a/cpp/pybind/visualization/renderoption.cpp b/cpp/pybind/visualization/renderoption.cpp index daf88cc72de..903e2db6135 100644 --- a/cpp/pybind/visualization/renderoption.cpp +++ b/cpp/pybind/visualization/renderoption.cpp @@ -27,16 +27,16 @@ void pybind_renderoption(py::module &m) { }) .def( "load_from_json", - [](RenderOption &ro, const std::string &filename) { - io::ReadIJsonConvertible(filename, ro); + [](RenderOption &ro, const fs::path &filename) { + io::ReadIJsonConvertible(filename.string(), ro); }, "Function to load RenderOption from a JSON " "file.", "filename"_a) .def( "save_to_json", - [](RenderOption &ro, const std::string &filename) { - io::WriteIJsonConvertible(filename, ro); + [](RenderOption &ro, const fs::path &filename) { + io::WriteIJsonConvertible(filename.string(), ro); }, "Function to save RenderOption to a JSON " "file.", diff --git a/cpp/pybind/visualization/utility.cpp b/cpp/pybind/visualization/utility.cpp index 417438b1bff..603e78bb947 100644 --- a/cpp/pybind/visualization/utility.cpp +++ b/cpp/pybind/visualization/utility.cpp @@ -154,12 +154,12 @@ void pybind_visualization_utility_methods(py::module &m) { [](const std::vector> &geometry_ptrs, const std::string &window_name, int width, int height, int left, - int top, const std::string &json_filename) { + int top, const fs::path &json_filename) { std::string current_dir = utility::filesystem::GetWorkingDirectory(); DrawGeometriesWithCustomAnimation(geometry_ptrs, window_name, width, height, left, top, - json_filename); + json_filename.string()); utility::filesystem::ChangeWorkingDirectory(current_dir); }, "Function to draw a list of geometry::Geometry objects with a GUI " @@ -251,9 +251,9 @@ void pybind_visualization_utility_methods(py::module &m) { m.def( "read_selection_polygon_volume", - [](const std::string &filename) { + [](const fs::path &filename) { SelectionPolygonVolume vol; - io::ReadIJsonConvertible(filename, vol); + io::ReadIJsonConvertible(filename.string(), vol); return vol; }, "Function to read SelectionPolygonVolume from file", "filename"_a); diff --git a/cpp/pybind/visualization/visualizer.cpp b/cpp/pybind/visualization/visualizer.cpp index dba049503f9..9370612d06c 100644 --- a/cpp/pybind/visualization/visualizer.cpp +++ b/cpp/pybind/visualization/visualizer.cpp @@ -104,20 +104,39 @@ void pybind_visualizer(py::module &m) { &Visualizer::CaptureScreenFloatBuffer, "Function to capture screen and store RGB in a float buffer", "do_render"_a = false) - .def("capture_screen_image", &Visualizer::CaptureScreenImage, - "Function to capture and save a screen image", "filename"_a, - "do_render"_a = false) + .def( + "capture_screen_image", + [](Visualizer &self, const fs::path &filename, + bool do_render) { + return self.CaptureScreenImage(filename.string(), + do_render); + }, + "Function to capture and save a screen image", "filename"_a, + "do_render"_a = false) .def("capture_depth_float_buffer", &Visualizer::CaptureDepthFloatBuffer, "Function to capture depth in a float buffer", "do_render"_a = false) - .def("capture_depth_image", &Visualizer::CaptureDepthImage, - "Function to capture and save a depth image", "filename"_a, - "do_render"_a = false, "depth_scale"_a = 1000.0) - .def("capture_depth_point_cloud", - &Visualizer::CaptureDepthPointCloud, - "Function to capture and save local point cloud", "filename"_a, - "do_render"_a = false, "convert_to_world_coordinate"_a = false) + .def( + "capture_depth_image", + [](Visualizer &self, const fs::path &filename, + bool do_render, double depth_scale) { + self.CaptureDepthImage(filename.string(), do_render, + depth_scale); + }, + "Function to capture and save a depth image", "filename"_a, + "do_render"_a = false, "depth_scale"_a = 1000.0) + .def( + "capture_depth_point_cloud", + [](Visualizer &self, const fs::path &filename, + bool do_render, bool convert_to_world_coordinate) { + self.CaptureDepthPointCloud( + filename.string(), do_render, + convert_to_world_coordinate); + }, + "Function to capture and save local point cloud", + "filename"_a, "do_render"_a = false, + "convert_to_world_coordinate"_a = false) .def("get_window_name", &Visualizer::GetWindowName) .def("get_view_status", &Visualizer::GetViewStatus, "Get the current view status as a json string of " diff --git a/examples/python/visualization/mitsuba_material_estimation.py b/examples/python/visualization/mitsuba_material_estimation.py index 79783edcf3f..fc8d26c9ecf 100644 --- a/examples/python/visualization/mitsuba_material_estimation.py +++ b/examples/python/visualization/mitsuba_material_estimation.py @@ -118,7 +118,7 @@ def mse(image, ref_img): def load_input_mesh(model_path, tex_dim): - mesh = o3d.t.io.read_triangle_mesh(str(model_path)) + mesh = o3d.t.io.read_triangle_mesh(model_path) mesh.material.set_default_properties() mesh.material.material_name = 'defaultLit' # note: ignored by Mitsuba, just used to visualize in Open3D mesh.material.texture_maps['albedo'] = o3d.t.geometry.Image(0.5 + np.zeros( diff --git a/python/test/io/test_pathlib.py b/python/test/io/test_pathlib.py new file mode 100644 index 00000000000..ad3313a6067 --- /dev/null +++ b/python/test/io/test_pathlib.py @@ -0,0 +1,21 @@ +# ---------------------------------------------------------------------------- +# - Open3D: www.open3d.org - +# ---------------------------------------------------------------------------- +# Copyright (c) 2018-2023 www.open3d.org +# SPDX-License-Identifier: MIT +# ---------------------------------------------------------------------------- + +from pathlib import Path + +import open3d as o3d + + +def test_pathlib_support(): + pcd_pointcloud = o3d.data.PCDPointCloud() + assert isinstance(pcd_pointcloud.path, str) + + pcd = o3d.io.read_point_cloud(pcd_pointcloud.path) + assert pcd.has_points() + + pcd = o3d.io.read_point_cloud(Path(pcd_pointcloud.path)) + assert pcd.has_points() \ No newline at end of file