pypatchworkpp 1.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ cmake_minimum_required(VERSION 3.16...3.26)
2
+ project(patchworkpp_python_wrapper LANGUAGES CXX)
3
+
4
+ # Set build type
5
+ set(CMAKE_BUILD_TYPE Release)
6
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
8
+
9
+ # Parameters used in `patchworkpp` subdirectory.
10
+ # Thus, link should be `patchworkpp::ground_seg_cores`
11
+ # See https://github.com/url-kaist/patchwork-plusplus/tree/master/cpp/CMakeLists.txt#L21
12
+ set(PARENT_PROJECT_NAME patchworkpp)
13
+ set(TARGET_NAME ground_seg_cores)
14
+
15
+ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
16
+ find_package(pybind11 CONFIG REQUIRED)
17
+
18
+ # See our `pyproject.toml` file. We use `scikit_build_core`, which turns on `SKBUILD`
19
+ if (DEFINED SKBUILD)
20
+ message(STATUS "Building with Scikit-Build")
21
+ endif ()
22
+
23
+ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../cpp/)
24
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../cpp ${CMAKE_CURRENT_BINARY_DIR}/patchworkpp_cpp)
25
+ else()
26
+ cmake_minimum_required(VERSION 3.18)
27
+ message(STATUS "Performing out-of-tree build, fetching Patchwork++ v${CMAKE_PROJECT_VERSION} Release from Github")
28
+ include(FetchContent)
29
+ FetchContent_Declare(
30
+ ext_ground_seg_cores PREFIX ${PARENT_PROJECT_NAME}
31
+ URL https://github.com/url-kaist/patchwork-plusplus/archive/refs/tags/v${CMAKE_PROJECT_VERSION}.tar.gz SOURCE_SUBDIR
32
+ cpp/patchworkpp)
33
+ FetchContent_MakeAvailable(ext_ground_seg_cores)
34
+ endif()
35
+
36
+ pybind11_add_module(pypatchworkpp patchworkpp/pybinding.cpp)
37
+
38
+ target_link_libraries(pypatchworkpp PUBLIC ${PARENT_PROJECT_NAME}::${TARGET_NAME})
39
+
40
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
41
+ target_compile_options(pypatchworkpp PUBLIC -fsized-deallocation)
42
+ endif()
43
+
44
+ install(TARGETS pypatchworkpp DESTINATION .)
File without changes
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.1
2
+ Name: pypatchworkpp
3
+ Version: 1.0.4
4
+ Summary: ground segmentation
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: numpy>=1.23
7
+ Requires-Dist: open3d-cpu>=0.17; extra == "demo"
8
+ Provides-Extra: demo
@@ -0,0 +1,52 @@
1
+ <div align="center">
2
+ <h1>Patchwork++</h1>
3
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/patchworkpp"><img src="https://img.shields.io/badge/-C++-blue?logo=cplusplus" /></a>
4
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master"><img src="https://img.shields.io/badge/Python-3670A0?logo=python&logoColor=ffdd54" /></a>
5
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/ros"><img src="https://img.shields.io/badge/ROS2-Humble-blue" /></a>
6
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master"><img src="https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black" /></a>
7
+ <a href="https://ieeexplore.ieee.org/document/9981561"><img src="https://img.shields.io/badge/DOI-10.1109/IROS47612.2022.9981561-004088.svg"/>
8
+ <br />
9
+ <br />
10
+ <a href=https://www.youtube.com/watch?v=fogCM159GRk>Video</a>
11
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
12
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/README.md###Python">Install</a>
13
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
14
+ <a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/ros">ROS2</a>
15
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
16
+ <a href=https://www.youtube.com/watch?v=fogCM159GRk>Paper</a>
17
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
18
+ <a href=https://github.com/url-kaist/patchwork-plusplus/issues>Contact Us</a>
19
+ <br />
20
+ <br />
21
+ <p align="center"><img src=../pictures/patchwork++.gif alt="animated" /></p>
22
+
23
+ [Patchwork++][arXivlink], an extension of [Patchwork][patchworklink], is **a fast, robust, and self-adaptive ground segmentation algorithm** on 3D point cloud.
24
+ </div>
25
+
26
+ [arXivlink]: https://arxiv.org/abs/2207.11919
27
+ [patchworklink]: https://github.com/LimHyungTae/patchwork
28
+
29
+ ---
30
+
31
+ # Patchwork++ in Python
32
+
33
+ ## :runner: To run the demo codes
34
+ > There are some example codes for your convenience!
35
+ > Please try using Patchwork++ to segment ground points in a 3D point cloud :smiley:
36
+
37
+ * Example 1. Run patchwork++ and visualize ground points (green) and non-ground points (red)
38
+ ```commandline
39
+ python python/examples/demo_visualize.py
40
+ ```
41
+
42
+ * Example 2. Run patchwork++ with sequential point cloud inputs
43
+ ```commandline
44
+ python python/examples/demo_sequential.py
45
+ ```
46
+
47
+ ### Demo Result
48
+ If you execute Patchwork++ with given demo codes well, you can get the following result!
49
+
50
+ It is a ground segmentation result of data/000000.bin file using Open3D visualization. (Ground : Green, Nonground : Red)
51
+
52
+ ![Open3D Visualization of "data/000000.bin"](../pictures/demo_000000.png)
@@ -0,0 +1,83 @@
1
+ import os
2
+ import open3d as o3d
3
+ import numpy as np
4
+ import pypatchworkpp
5
+
6
+ cur_dir = os.path.dirname(os.path.abspath(__file__))
7
+ data_dir = os.path.join(cur_dir, '../../data/')
8
+
9
+
10
+ def read_bin(bin_path):
11
+ scan = np.fromfile(bin_path, dtype=np.float32)
12
+ scan = scan.reshape((-1, 4))
13
+
14
+ return scan
15
+
16
+ if __name__ == "__main__":
17
+
18
+ # Patchwork++ initialization
19
+ params = pypatchworkpp.Parameters()
20
+ params.verbose = True
21
+
22
+ PatchworkPLUSPLUS = pypatchworkpp.patchworkpp(params)
23
+
24
+ for file in sorted(os.listdir(data_dir)):
25
+
26
+ # Load point cloud
27
+ pointcloud = read_bin(data_dir+file)
28
+
29
+ # Estimate Ground
30
+ PatchworkPLUSPLUS.estimateGround(pointcloud)
31
+
32
+ # Get Ground and Nonground
33
+ ground = PatchworkPLUSPLUS.getGround()
34
+ nonground = PatchworkPLUSPLUS.getNonground()
35
+ time_taken = PatchworkPLUSPLUS.getTimeTaken()
36
+
37
+ ground_idx = PatchworkPLUSPLUS.getGroundIndices()
38
+ nonground_idx = PatchworkPLUSPLUS.getNongroundIndices()
39
+
40
+ # Get centers and normals for patches
41
+ centers = PatchworkPLUSPLUS.getCenters()
42
+ normals = PatchworkPLUSPLUS.getNormals()
43
+
44
+ print("Origianl Points #: ", pointcloud.shape[0])
45
+ print("Ground Points #: ", ground.shape[0])
46
+ print("Nonground Points #: ", nonground.shape[0])
47
+ print("Time Taken : ", time_taken / 1000000, "(sec)")
48
+ print("Press ... \n")
49
+ print("\t H : help")
50
+ print("\t N : visualize the surface normals")
51
+ print("\tESC : close the Open3D window")
52
+
53
+ vis = o3d.visualization.VisualizerWithKeyCallback()
54
+ vis.create_window(width = 600, height = 400)
55
+
56
+ mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
57
+
58
+ ground_o3d = o3d.geometry.PointCloud()
59
+ ground_o3d.points = o3d.utility.Vector3dVector(ground)
60
+ ground_o3d.colors = o3d.utility.Vector3dVector(
61
+ np.array([[0.0, 1.0, 0.0] for _ in range(ground.shape[0])], dtype=float) # RGB
62
+ )
63
+
64
+ nonground_o3d = o3d.geometry.PointCloud()
65
+ nonground_o3d.points = o3d.utility.Vector3dVector(nonground)
66
+ nonground_o3d.colors = o3d.utility.Vector3dVector(
67
+ np.array([[1.0, 0.0, 0.0] for _ in range(nonground.shape[0])], dtype=float) #RGB
68
+ )
69
+
70
+ centers_o3d = o3d.geometry.PointCloud()
71
+ centers_o3d.points = o3d.utility.Vector3dVector(centers)
72
+ centers_o3d.normals = o3d.utility.Vector3dVector(normals)
73
+ centers_o3d.colors = o3d.utility.Vector3dVector(
74
+ np.array([[1.0, 1.0, 0.0] for _ in range(centers.shape[0])], dtype=float) #RGB
75
+ )
76
+
77
+ vis.add_geometry(mesh)
78
+ vis.add_geometry(ground_o3d)
79
+ vis.add_geometry(nonground_o3d)
80
+ vis.add_geometry(centers_o3d)
81
+
82
+ vis.run()
83
+ vis.destroy_window()
@@ -0,0 +1,82 @@
1
+ import os
2
+ import open3d as o3d
3
+ import numpy as np
4
+ import pypatchworkpp
5
+
6
+ cur_dir = os.path.dirname(os.path.abspath(__file__))
7
+ input_cloud_filepath = os.path.join(cur_dir, '../../data/000000.bin')
8
+
9
+
10
+ def read_bin(bin_path):
11
+ scan = np.fromfile(bin_path, dtype=np.float32)
12
+ scan = scan.reshape((-1, 4))
13
+
14
+ return scan
15
+
16
+ if __name__ == "__main__":
17
+
18
+ # Patchwork++ initialization
19
+ params = pypatchworkpp.Parameters()
20
+ params.verbose = True
21
+
22
+ PatchworkPLUSPLUS = pypatchworkpp.patchworkpp(params)
23
+
24
+ # Load point cloud
25
+ pointcloud = read_bin(input_cloud_filepath)
26
+
27
+ # Estimate Ground
28
+ PatchworkPLUSPLUS.estimateGround(pointcloud)
29
+
30
+ # Get Ground and Nonground
31
+ ground = PatchworkPLUSPLUS.getGround()
32
+ nonground = PatchworkPLUSPLUS.getNonground()
33
+ time_taken = PatchworkPLUSPLUS.getTimeTaken()
34
+
35
+ ground_idx = PatchworkPLUSPLUS.getGroundIndices()
36
+ nonground_idx = PatchworkPLUSPLUS.getNongroundIndices()
37
+
38
+ # Get centers and normals for patches
39
+ centers = PatchworkPLUSPLUS.getCenters()
40
+ normals = PatchworkPLUSPLUS.getNormals()
41
+
42
+ print("Origianl Points #: ", pointcloud.shape[0])
43
+ print("Ground Points #: ", ground.shape[0])
44
+ print("Nonground Points #: ", nonground.shape[0])
45
+ print("Time Taken : ", time_taken / 1000000, "(sec)")
46
+ print("Press ... \n")
47
+ print("\t H : help")
48
+ print("\t N : visualize the surface normals")
49
+ print("\tESC : close the Open3D window")
50
+
51
+ # Visualize
52
+ vis = o3d.visualization.VisualizerWithKeyCallback()
53
+ vis.create_window(width = 600, height = 400)
54
+
55
+ mesh = o3d.geometry.TriangleMesh.create_coordinate_frame()
56
+
57
+ ground_o3d = o3d.geometry.PointCloud()
58
+ ground_o3d.points = o3d.utility.Vector3dVector(ground)
59
+ ground_o3d.colors = o3d.utility.Vector3dVector(
60
+ np.array([[0.0, 1.0, 0.0] for _ in range(ground.shape[0])], dtype=float) # RGB
61
+ )
62
+
63
+ nonground_o3d = o3d.geometry.PointCloud()
64
+ nonground_o3d.points = o3d.utility.Vector3dVector(nonground)
65
+ nonground_o3d.colors = o3d.utility.Vector3dVector(
66
+ np.array([[1.0, 0.0, 0.0] for _ in range(nonground.shape[0])], dtype=float) #RGB
67
+ )
68
+
69
+ centers_o3d = o3d.geometry.PointCloud()
70
+ centers_o3d.points = o3d.utility.Vector3dVector(centers)
71
+ centers_o3d.normals = o3d.utility.Vector3dVector(normals)
72
+ centers_o3d.colors = o3d.utility.Vector3dVector(
73
+ np.array([[1.0, 1.0, 0.0] for _ in range(centers.shape[0])], dtype=float) #RGB
74
+ )
75
+
76
+ vis.add_geometry(mesh)
77
+ vis.add_geometry(ground_o3d)
78
+ vis.add_geometry(nonground_o3d)
79
+ vis.add_geometry(centers_o3d)
80
+
81
+ vis.run()
82
+ vis.destroy_window()
@@ -0,0 +1,57 @@
1
+ #include <pybind11/eigen.h>
2
+ #include <pybind11/pybind11.h>
3
+ #include <pybind11/stl.h>
4
+
5
+ #include "patchwork/patchworkpp.h"
6
+
7
+ namespace py = pybind11;
8
+
9
+ PYBIND11_MODULE(pypatchworkpp, m) {
10
+
11
+ m.doc() = "Python Patchwork++";
12
+ m.attr("__version__") = "0.0.1";
13
+
14
+ py::class_<patchwork::Params>(m, "Parameters")
15
+ .def(py::init<>())
16
+ .def_readwrite("sensor_height", &patchwork::Params::sensor_height)
17
+ .def_readwrite("verbose", &patchwork::Params::verbose)
18
+ .def_readwrite("enable_RNR", &patchwork::Params::enable_RNR)
19
+ .def_readwrite("enable_RVPF", &patchwork::Params::enable_RVPF)
20
+ .def_readwrite("enable_TGR", &patchwork::Params::enable_TGR)
21
+ .def_readwrite("num_iter", &patchwork::Params::num_iter)
22
+ .def_readwrite("num_lpr", &patchwork::Params::num_lpr)
23
+ .def_readwrite("num_min_pts", &patchwork::Params::num_min_pts)
24
+ .def_readwrite("num_zones", &patchwork::Params::num_zones)
25
+ .def_readwrite("num_rings_of_interest", &patchwork::Params::num_rings_of_interest)
26
+ .def_readwrite("RNR_ver_angle_thr", &patchwork::Params::RNR_ver_angle_thr)
27
+ .def_readwrite("RNR_intensity_thr", &patchwork::Params::RNR_intensity_thr)
28
+ .def_readwrite("sensor_height", &patchwork::Params::sensor_height)
29
+ .def_readwrite("th_seeds", &patchwork::Params::th_seeds)
30
+ .def_readwrite("th_dist", &patchwork::Params::th_dist)
31
+ .def_readwrite("th_seeds_v", &patchwork::Params::th_seeds_v)
32
+ .def_readwrite("th_dist_v", &patchwork::Params::th_dist_v)
33
+ .def_readwrite("max_range", &patchwork::Params::max_range)
34
+ .def_readwrite("min_range", &patchwork::Params::min_range)
35
+ .def_readwrite("uprightness_thr", &patchwork::Params::uprightness_thr)
36
+ .def_readwrite("adaptive_seed_selection_margin", &patchwork::Params::adaptive_seed_selection_margin)
37
+ .def_readwrite("intensity_thr", &patchwork::Params::intensity_thr)
38
+ .def_readwrite("num_sectors_each_zone", &patchwork::Params::num_sectors_each_zone)
39
+ .def_readwrite("num_rings_each_zone", &patchwork::Params::num_rings_each_zone)
40
+ .def_readwrite("max_flatness_storage", &patchwork::Params::max_flatness_storage)
41
+ .def_readwrite("max_elevation_storage", &patchwork::Params::max_elevation_storage)
42
+ .def_readwrite("elevation_thr", &patchwork::Params::elevation_thr)
43
+ .def_readwrite("flatness_thr", &patchwork::Params::flatness_thr);
44
+
45
+ py::class_<patchwork::PatchWorkpp>(m, "patchworkpp")
46
+ .def(py::init<patchwork::Params>())
47
+ .def("getHeight", &patchwork::PatchWorkpp::getHeight)
48
+ .def("getTimeTaken", &patchwork::PatchWorkpp::getTimeTaken)
49
+ .def("getGround", &patchwork::PatchWorkpp::getGround)
50
+ .def("getNonground", &patchwork::PatchWorkpp::getNonground)
51
+ .def("getCenters", &patchwork::PatchWorkpp::getCenters)
52
+ .def("getGroundIndices", &patchwork::PatchWorkpp::getGroundIndices)
53
+ .def("getNongroundIndices", &patchwork::PatchWorkpp::getNongroundIndices)
54
+ .def("getNormals", &patchwork::PatchWorkpp::getNormals)
55
+ .def("estimateGround", &patchwork::PatchWorkpp::estimateGround);
56
+ // .def_readwrite("sensor_height_", &PatchWorkpp::sensor_height_);
57
+ }
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["scikit_build_core", "pybind11"]
3
+ build-backend = "scikit_build_core.build"
4
+
5
+ [project]
6
+ name = "pypatchworkpp"
7
+ version = "1.0.4"
8
+ requires-python = ">=3.8"
9
+ description = "ground segmentation"
10
+ dependencies = [
11
+ "numpy>=1.23"
12
+ ]
13
+
14
+ [project.optional-dependencies]
15
+ demo = [
16
+ "open3d-cpu>=0.17"
17
+ ]
18
+
19
+ [tool.scikit-build]
20
+ cmake.args = ["-DINCLUDE_PYTHON_WRAPPER=true"]
21
+