1
+ #include < pybind11/pybind11.h>
2
+ #include < pybind11/numpy.h>
3
+ #include < pybind11/eigen.h>
4
+ #include < pybind11/functional.h>
5
+
6
+ #include < boost/filesystem.hpp>
7
+ #include < fast_gicp/gicp/fast_gicp.hpp>
8
+ #include < fast_gicp/gicp/fast_vgicp.hpp>
9
+
10
+ #ifdef USE_VGICP_CUDA
11
+ #include < fast_gicp/ndt/ndt_cuda.hpp>
12
+ #include < fast_gicp/gicp/fast_vgicp_cuda.hpp>
13
+ #endif
14
+
15
+ #include < pcl/point_types.h>
16
+ #include < pcl/point_cloud.h>
17
+ #include < pcl/filters/approximate_voxel_grid.h>
18
+
19
+ namespace py = pybind11;
20
+
21
+ fast_gicp::NeighborSearchMethod search_method (const std::string& neighbor_search_method) {
22
+ if (neighbor_search_method == " DIRECT1" ) {
23
+ return fast_gicp::NeighborSearchMethod::DIRECT1;
24
+ } else if (neighbor_search_method == " DIRECT7" ) {
25
+ return fast_gicp::NeighborSearchMethod::DIRECT7;
26
+ } else if (neighbor_search_method == " DIRECT27" ) {
27
+ return fast_gicp::NeighborSearchMethod::DIRECT27;
28
+ } else if (neighbor_search_method == " DIRECT_RADIUS" ) {
29
+ return fast_gicp::NeighborSearchMethod::DIRECT_RADIUS;
30
+ }
31
+
32
+ std::cerr << " error: unknown neighbor search method " << neighbor_search_method << std::endl;
33
+ return fast_gicp::NeighborSearchMethod::DIRECT1;
34
+ }
35
+
36
+ pcl::PointCloud<pcl::PointXYZ>::Ptr eigen2pcl (const Eigen::Matrix<double , -1 , 3 >& points) {
37
+ pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
38
+ cloud->resize (points.rows ());
39
+
40
+ for (int i=0 ; i<points.rows (); i++) {
41
+ cloud->at (i).getVector3fMap () = points.row (i).cast <float >();
42
+ }
43
+ return cloud;
44
+ }
45
+
46
+ Eigen::Matrix<double , -1 , 3 > downsample (const Eigen::Matrix<double , -1 , 3 >& points, double downsample_resolution) {
47
+ auto cloud = eigen2pcl (points);
48
+
49
+ pcl::ApproximateVoxelGrid<pcl::PointXYZ> voxelgrid;
50
+ voxelgrid.setLeafSize (downsample_resolution, downsample_resolution, downsample_resolution);
51
+
52
+ pcl::PointCloud<pcl::PointXYZ>::Ptr filtered (new pcl::PointCloud<pcl::PointXYZ>);
53
+ voxelgrid.filter (*filtered);
54
+
55
+ Eigen::Matrix<float , -1 , 3 > filtered_points (filtered->size (), 3 );
56
+ for (int i=0 ; i<filtered->size (); i++) {
57
+ filtered_points.row (i) = filtered->at (i).getVector3fMap ();
58
+ }
59
+
60
+ return filtered_points.cast <double >();
61
+ }
62
+
63
+ Eigen::Matrix4d align_points (
64
+ const Eigen::Matrix<double , -1 , 3 >& target,
65
+ const Eigen::Matrix<double , -1 , 3 >& source,
66
+ const std::string& method,
67
+ double downsample_resolution,
68
+ int k_correspondences,
69
+ double max_correspondence_distance,
70
+ double voxel_resolution,
71
+ int num_threads,
72
+ const std::string& neighbor_search_method,
73
+ double neighbor_search_radius,
74
+ const Eigen::Matrix4f& initial_guess
75
+ ) {
76
+ pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud = eigen2pcl (target);
77
+ pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud = eigen2pcl (source);
78
+
79
+ if (downsample_resolution > 0.0 ) {
80
+ pcl::ApproximateVoxelGrid<pcl::PointXYZ> voxelgrid;
81
+ voxelgrid.setLeafSize (downsample_resolution, downsample_resolution, downsample_resolution);
82
+
83
+ pcl::PointCloud<pcl::PointXYZ>::Ptr filtered (new pcl::PointCloud<pcl::PointXYZ>);
84
+ voxelgrid.setInputCloud (target_cloud);
85
+ voxelgrid.filter (*filtered);
86
+ target_cloud.swap (filtered);
87
+
88
+ voxelgrid.setInputCloud (source_cloud);
89
+ voxelgrid.filter (*filtered);
90
+ source_cloud.swap (filtered);
91
+ }
92
+
93
+ boost::shared_ptr<fast_gicp::LsqRegistration<pcl::PointXYZ, pcl::PointXYZ>> reg;
94
+
95
+ if (method == " GICP" ) {
96
+ pcl::shared_ptr<fast_gicp::FastGICP<pcl::PointXYZ, pcl::PointXYZ>> gicp (new fast_gicp::FastGICP<pcl::PointXYZ, pcl::PointXYZ>);
97
+ gicp->setMaxCorrespondenceDistance (max_correspondence_distance);
98
+ gicp->setCorrespondenceRandomness (k_correspondences);
99
+ gicp->setNumThreads (num_threads);
100
+ reg = gicp;
101
+ } else if (method == " VGICP" ) {
102
+ pcl::shared_ptr<fast_gicp::FastVGICP<pcl::PointXYZ, pcl::PointXYZ>> vgicp (new fast_gicp::FastVGICP<pcl::PointXYZ, pcl::PointXYZ>);
103
+ vgicp->setCorrespondenceRandomness (k_correspondences);
104
+ vgicp->setResolution (voxel_resolution);
105
+ vgicp->setNeighborSearchMethod (search_method (neighbor_search_method));
106
+ vgicp->setNumThreads (num_threads);
107
+ reg = vgicp;
108
+ } else if (method == " VGICP_CUDA" ) {
109
+ #ifdef USE_VGICP_CUDA
110
+ pcl::shared_ptr<fast_gicp::FastVGICPCuda<pcl::PointXYZ, pcl::PointXYZ>> vgicp (new fast_gicp::FastVGICPCuda<pcl::PointXYZ, pcl::PointXYZ>);
111
+ vgicp->setCorrespondenceRandomness (k_correspondences);
112
+ vgicp->setNeighborSearchMethod (search_method (neighbor_search_method), neighbor_search_radius);
113
+ vgicp->setResolution (voxel_resolution);
114
+ reg = vgicp;
115
+ #else
116
+ std::cerr << " error: you need to build fast_gicp with BUILD_VGICP_CUDA=ON"
117
+ return Eigen::Matrix4d::Identity ();
118
+ #endif
119
+ } else if (method == " NDT_CUDA" ) {
120
+ #ifdef USE_VGICP_CUDA
121
+ pcl::shared_ptr<fast_gicp::NDTCuda<pcl::PointXYZ, pcl::PointXYZ>> ndt (new fast_gicp::NDTCuda<pcl::PointXYZ, pcl::PointXYZ>);
122
+ ndt->setResolution (voxel_resolution);
123
+ ndt->setNeighborSearchMethod (search_method (neighbor_search_method), neighbor_search_radius);
124
+ reg = ndt;
125
+ #else
126
+ std::cerr << " error: you need to build fast_gicp with BUILD_VGICP_CUDA=ON"
127
+ return Eigen::Matrix4d::Identity ();
128
+ #endif
129
+ } else {
130
+ std::cerr << " error: unknown registration method " << method << std::endl;
131
+ return Eigen::Matrix4d::Identity ();
132
+ }
133
+
134
+ reg->setInputTarget (target_cloud);
135
+ reg->setInputSource (source_cloud);
136
+
137
+ pcl::PointCloud<pcl::PointXYZ>::Ptr aligned (new pcl::PointCloud<pcl::PointXYZ>);
138
+ reg->align (*aligned, initial_guess);
139
+
140
+ return reg->getFinalTransformation ().cast <double >();
141
+ }
142
+
143
+ using LsqRegistration = fast_gicp::LsqRegistration<pcl::PointXYZ, pcl::PointXYZ>;
144
+ using FastGICP = fast_gicp::FastGICP<pcl::PointXYZ, pcl::PointXYZ>;
145
+ using FastVGICP = fast_gicp::FastVGICP<pcl::PointXYZ, pcl::PointXYZ>;
146
+ #ifdef USE_VGICP_CUDA
147
+ using FastVGICPCuda = fast_gicp::FastVGICPCuda<pcl::PointXYZ, pcl::PointXYZ>;
148
+ using NDTCuda = fast_gicp::NDTCuda<pcl::PointXYZ, pcl::PointXYZ>;
149
+ #endif
150
+
151
+ PYBIND11_MODULE (pygicp, m) {
152
+ m.def (" downsample" , &downsample, " downsample points" );
153
+
154
+ m.def (" align_points" , &align_points, " align two point sets" ,
155
+ py::arg (" target" ),
156
+ py::arg (" source" ),
157
+ py::arg (" method" ) = " GICP" ,
158
+ py::arg (" downsample_resolution" ) = -1.0 ,
159
+ py::arg (" k_correspondences" ) = 15 ,
160
+ py::arg (" max_correspondence_distance" ) = std::numeric_limits<double >::max (),
161
+ py::arg (" voxel_resolution" ) = 1.0 ,
162
+ py::arg (" num_threads" ) = 0 ,
163
+ py::arg (" neighbor_search_method" ) = " DIRECT1" ,
164
+ py::arg (" neighbor_search_radius" ) = 1.5 ,
165
+ py::arg (" initial_guess" ) = Eigen::Matrix4f::Identity ()
166
+ );
167
+
168
+ py::class_<LsqRegistration, std::shared_ptr<LsqRegistration>>(m, " LsqRegistration" )
169
+ .def (" set_input_target" , [] (LsqRegistration& reg, const Eigen::Matrix<double , -1 , 3 >& points) { reg.setInputTarget (eigen2pcl (points)); })
170
+ .def (" set_input_source" , [] (LsqRegistration& reg, const Eigen::Matrix<double , -1 , 3 >& points) { reg.setInputSource (eigen2pcl (points)); })
171
+ .def (" swap_source_and_target" , &LsqRegistration::swapSourceAndTarget)
172
+ .def (" get_final_hessian" , &LsqRegistration::getFinalHessian)
173
+ .def (" get_final_transformation" , &LsqRegistration::getFinalTransformation)
174
+ .def (" align" ,
175
+ [] (LsqRegistration& reg, const Eigen::Matrix4f& initial_guess) {
176
+ pcl::PointCloud<pcl::PointXYZ> aligned;
177
+ reg.align (aligned, initial_guess);
178
+ return reg.getFinalTransformation ();
179
+ }, py::arg (" initial_guess" ) = Eigen::Matrix4f::Identity ()
180
+ )
181
+ ;
182
+
183
+ py::class_<FastGICP, LsqRegistration, std::shared_ptr<FastGICP>>(m, " FastGICP" )
184
+ .def (py::init ())
185
+ .def (" set_num_threads" , &FastGICP::setNumThreads)
186
+ .def (" set_correspondence_randomness" , &FastGICP::setCorrespondenceRandomness)
187
+ .def (" set_max_correspondence_distance" , &FastGICP::setMaxCorrespondenceDistance)
188
+ ;
189
+
190
+ py::class_<FastVGICP, FastGICP, std::shared_ptr<FastVGICP>>(m, " FastVGICP" )
191
+ .def (py::init ())
192
+ .def (" set_resolution" , &FastVGICP::setResolution)
193
+ .def (" set_neighbor_search_method" , [](FastVGICP& vgicp, const std::string& method) { vgicp.setNeighborSearchMethod (search_method (method)); })
194
+ ;
195
+
196
+ #ifdef USE_VGICP_CUDA
197
+ py::class_<FastVGICPCuda, LsqRegistration, std::shared_ptr<FastVGICPCuda>>(m, " FastVGICPCuda" )
198
+ .def (py::init ())
199
+ .def (" set_resolution" , &FastVGICPCuda::setResolution)
200
+ .def (" set_neighbor_search_method" ,
201
+ [](FastVGICPCuda& vgicp, const std::string& method, double radius) { vgicp.setNeighborSearchMethod (search_method (method), radius); }
202
+ , py::arg (" method" ) = " DIRECT1" , py::arg (" radius" ) = 1.5
203
+ )
204
+ .def (" set_correspondence_randomness" , &FastVGICPCuda::setCorrespondenceRandomness)
205
+ ;
206
+
207
+ py::class_<NDTCuda, LsqRegistration, std::shared_ptr<NDTCuda>>(m, " NDTCuda" )
208
+ .def (py::init ())
209
+ .def (" set_neighbor_search_method" ,
210
+ [](NDTCuda& ndt, const std::string& method, double radius) { ndt.setNeighborSearchMethod (search_method (method), radius); }
211
+ , py::arg (" method" ) = " DIRECT1" , py::arg (" radius" ) = 1.5
212
+ )
213
+ .def (" set_resolution" , &NDTCuda::setResolution)
214
+ ;
215
+ #endif
216
+
217
+ #ifdef VERSION_INFO
218
+ m.attr (" __version__" ) = MACRO_STRINGIFY (VERSION_INFO);
219
+ #else
220
+ m.attr (" __version__" ) = " dev" ;
221
+ #endif
222
+ }
0 commit comments