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.
- pypatchworkpp-1.0.4/CMakeLists.txt +44 -0
- pypatchworkpp-1.0.4/COLCON_IGNORE +0 -0
- pypatchworkpp-1.0.4/PKG-INFO +8 -0
- pypatchworkpp-1.0.4/README.md +52 -0
- pypatchworkpp-1.0.4/examples/demo_sequential.py +83 -0
- pypatchworkpp-1.0.4/examples/demo_visualize.py +82 -0
- pypatchworkpp-1.0.4/patchworkpp/pybinding.cpp +57 -0
- pypatchworkpp-1.0.4/pyproject.toml +21 -0
|
@@ -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,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> • </span>
|
|
12
|
+
<a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/README.md###Python">Install</a>
|
|
13
|
+
<span> • </span>
|
|
14
|
+
<a href="https://github.com/url-kaist/patchwork-plusplus/tree/master/ros">ROS2</a>
|
|
15
|
+
<span> • </span>
|
|
16
|
+
<a href=https://www.youtube.com/watch?v=fogCM159GRk>Paper</a>
|
|
17
|
+
<span> • </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
|
+

|
|
@@ -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
|
+
|