7
7
8
8
#include " open3d/t/pipelines/registration/Feature.h"
9
9
10
+ #include " open3d/core/ParallelFor.h"
10
11
#include " open3d/core/nns/NearestNeighborSearch.h"
11
12
#include " open3d/t/geometry/PointCloud.h"
12
13
#include " open3d/t/pipelines/kernel/Feature.h"
@@ -43,58 +44,140 @@ core::Tensor ComputeFPFHFeature(
43
44
core::Int32);
44
45
bool tree_set = false ;
45
46
47
+ const bool filter_fpfh = indices.has_value ();
48
+ // If we are computing a subset of the FPFH feature,
49
+ // cache some information to speed up the computation
50
+ // if the ratio of the indices to the total number of points is high.
51
+ const int cache_info_indices_ratio_thresh = 0.1 ;
52
+ bool cache_fpfh_info = true ;
53
+
46
54
core::Tensor mask_fpfh_points;
47
- core::Tensor mask_required_points;
48
- if (indices.has_value ()) {
55
+ core::Tensor indices_fpfh_points;
56
+ core::Tensor map_point_idx_to_required_point_idx;
57
+ core::Tensor map_required_point_idx_to_point_idx;
58
+ core::Tensor save_p_indices, save_p_distance2, save_p_counts;
59
+ core::Tensor mask_spfh_points;
60
+
61
+ // If we are computing a subset of the FPFH feature, we need to find
62
+ // the subset of points (neighbors) required to compute the FPFH features.
63
+ if (filter_fpfh) {
64
+ if (indices.value ().GetLength () == 0 ) {
65
+ return core::Tensor::Zeros ({0 , 33 }, dtype, device);
66
+ }
49
67
mask_fpfh_points =
50
68
core::Tensor::Zeros ({num_points}, core::Bool, device);
51
- mask_required_points =
52
- core::Tensor::Zeros ({num_points}, core::Bool, device);
53
- core::Tensor indices_tmp, distance2_tmp, counts_tmp;
54
69
mask_fpfh_points.IndexSet ({indices.value ()},
55
70
core::Tensor::Ones ({1 }, core::Bool, device));
56
71
const core::Tensor query_point_positions =
57
- input.GetPointPositions ().IndexGet ({indices.value ()});
72
+ input.GetPointPositions ().IndexGet ({mask_fpfh_points});
73
+ core::Tensor p_indices, p_distance2, p_counts;
58
74
if (radius.has_value () && max_nn.has_value ()) {
59
75
tree_set = tree.HybridIndex (radius.value ());
60
76
if (!tree_set) {
61
77
utility::LogError (" Building HybridIndex failed." );
62
78
}
63
- std::tie (indices_tmp, distance2_tmp, counts_tmp) =
64
- tree.HybridSearch (query_point_positions, radius.value (),
65
- max_nn.value ());
79
+ std::tie (p_indices, p_distance2, p_counts) = tree.HybridSearch (
80
+ query_point_positions, radius.value (), max_nn.value ());
66
81
} else if (!radius.has_value () && max_nn.has_value ()) {
67
82
tree_set = tree.KnnIndex ();
68
83
if (!tree_set) {
69
84
utility::LogError (" Building KnnIndex failed." );
70
85
}
71
- std::tie (indices_tmp, distance2_tmp ) =
86
+ std::tie (p_indices, p_distance2 ) =
72
87
tree.KnnSearch (query_point_positions, max_nn.value ());
88
+
89
+ // Make counts full with min(max_nn, num_points).
90
+ const int fill_value =
91
+ max_nn.value () > num_points ? num_points : max_nn.value ();
92
+ p_counts = core::Tensor::Full ({query_point_positions.GetLength ()},
93
+ fill_value, core::Int32, device);
73
94
} else if (radius.has_value () && !max_nn.has_value ()) {
74
95
tree_set = tree.FixedRadiusIndex (radius.value ());
75
96
if (!tree_set) {
76
97
utility::LogError (" Building RadiusIndex failed." );
77
98
}
78
- std::tie (indices_tmp, distance2_tmp, counts_tmp) =
79
- tree.FixedRadiusSearch (query_point_positions,
80
- radius.value ());
99
+ std::tie (p_indices, p_distance2, p_counts) = tree.FixedRadiusSearch (
100
+ query_point_positions, radius.value ());
81
101
} else {
82
102
utility::LogError (" Both max_nn and radius are none." );
83
103
}
84
104
85
- indices_tmp = indices_tmp.To (core::Int64).View ({-1 });
105
+ core::Tensor mask_required_points =
106
+ core::Tensor::Zeros ({num_points}, core::Bool, device);
86
107
mask_required_points.IndexSet (
87
- {indices_tmp}, core::Tensor::Ones ({1 }, core::Bool, device));
108
+ {p_indices.To (core::Int64).View ({-1 })},
109
+ core::Tensor::Ones ({1 }, core::Bool, device));
110
+ map_required_point_idx_to_point_idx =
111
+ mask_required_points.NonZero ().GetItem (
112
+ {core::TensorKey::Index (0 )});
113
+ indices_fpfh_points =
114
+ mask_fpfh_points.NonZero ().GetItem ({core::TensorKey::Index (0 )});
88
115
89
- } else {
90
- mask_fpfh_points = core::Tensor::Zeros ({0 }, core::Bool, device);
91
- mask_required_points = core::Tensor::Zeros ({0 }, core::Bool, device);
116
+ const bool is_radius_search = p_indices.GetShape ().size () == 1 ;
117
+
118
+ // Cache the info if the ratio of the indices to the total number of
119
+ // points is high and we are not doing a radius search. Radius search
120
+ // requires a different pipeline since tensor output p_counts is a
121
+ // prefix sum.
122
+ cache_fpfh_info =
123
+ !is_radius_search &&
124
+ (static_cast <double >(indices_fpfh_points.GetLength ()) >=
125
+ cache_info_indices_ratio_thresh *
126
+ static_cast <double >(num_points));
127
+
128
+ if (cache_fpfh_info) {
129
+ map_point_idx_to_required_point_idx =
130
+ core::Tensor::Full ({num_points}, -1 , core::Int32, device);
131
+ map_point_idx_to_required_point_idx.IndexSet (
132
+ {map_required_point_idx_to_point_idx},
133
+ core::Tensor::Arange (
134
+ 0 , map_required_point_idx_to_point_idx.GetLength (),
135
+ 1 , core::Int32, device));
136
+
137
+ core::SizeVector save_p_indices_shape = p_indices.GetShape ();
138
+ save_p_indices_shape[0 ] =
139
+ map_required_point_idx_to_point_idx.GetLength ();
140
+ save_p_indices = core::Tensor::Zeros (save_p_indices_shape,
141
+ core::Int32, device);
142
+ save_p_distance2 = core::Tensor::Zeros (save_p_indices.GetShape (),
143
+ dtype, device);
144
+ save_p_counts = core::Tensor::Zeros (
145
+ {map_required_point_idx_to_point_idx.GetLength () +
146
+ (is_radius_search ? 1 : 0 )},
147
+ core::Int32, device);
148
+
149
+ core::Tensor map_fpfh_point_idx_to_required_point_idx =
150
+ map_point_idx_to_required_point_idx
151
+ .IndexGet ({indices_fpfh_points})
152
+ .To (core::Int64);
153
+
154
+ save_p_indices.IndexSet ({map_fpfh_point_idx_to_required_point_idx},
155
+ p_indices);
156
+ save_p_distance2.IndexSet (
157
+ {map_fpfh_point_idx_to_required_point_idx}, p_distance2);
158
+ save_p_counts.IndexSet ({map_fpfh_point_idx_to_required_point_idx},
159
+ p_counts);
160
+
161
+ // If we are filtering FPFH features, we have already computed some
162
+ // info about the FPFH points' neighbors. Now we just need to
163
+ // compute the info for the remaining required points, so skip the
164
+ // computation for the already computed info.
165
+ mask_spfh_points =
166
+ core::Tensor::Zeros ({num_points}, core::Bool, device);
167
+ mask_spfh_points.IndexSet (
168
+ {map_required_point_idx_to_point_idx},
169
+ core::Tensor::Ones ({1 }, core::Bool, device));
170
+ mask_spfh_points.IndexSet (
171
+ {indices_fpfh_points},
172
+ core::Tensor::Zeros ({1 }, core::Bool, device));
173
+ } else {
174
+ mask_spfh_points = mask_required_points;
175
+ }
92
176
}
93
177
94
178
const core::Tensor query_point_positions =
95
- mask_required_points.GetShape ()[0 ] > 0
96
- ? input.GetPointPositions ().IndexGet ({mask_required_points})
97
- : input.GetPointPositions ();
179
+ filter_fpfh ? input.GetPointPositions ().IndexGet ({mask_spfh_points})
180
+ : input.GetPointPositions ();
98
181
99
182
// Compute nearest neighbors and squared distances.
100
183
core::Tensor p_indices, p_distance2, p_counts;
@@ -119,14 +202,25 @@ core::Tensor ComputeFPFHFeature(
119
202
utility::LogError (" Building KnnIndex failed." );
120
203
}
121
204
}
122
- std::tie (p_indices, p_distance2) =
123
- tree.KnnSearch (query_point_positions, max_nn.value ());
124
-
125
- // Make counts full with min(max_nn, num_points).
126
- const int fill_value =
127
- max_nn.value () > num_points ? num_points : max_nn.value ();
128
- p_counts = core::Tensor::Full ({query_point_positions.GetLength ()},
129
- fill_value, core::Int32, device);
205
+
206
+ // tree.KnnSearch complains if the query point cloud is empty.
207
+ if (query_point_positions.GetLength () > 0 ) {
208
+ std::tie (p_indices, p_distance2) =
209
+ tree.KnnSearch (query_point_positions, max_nn.value ());
210
+
211
+ const int fill_value =
212
+ max_nn.value () > num_points ? num_points : max_nn.value ();
213
+
214
+ p_counts = core::Tensor::Full ({query_point_positions.GetLength ()},
215
+ fill_value, core::Int32, device);
216
+ } else {
217
+ p_indices = core::Tensor::Zeros ({0 , max_nn.value ()}, core::Int32,
218
+ device);
219
+ p_distance2 =
220
+ core::Tensor::Zeros ({0 , max_nn.value ()}, dtype, device);
221
+ p_counts = core::Tensor::Zeros ({0 }, core::Int32, device);
222
+ }
223
+
130
224
utility::LogDebug (
131
225
" Use KNNSearch [max_nn: {}] for computing FPFH feature." ,
132
226
max_nn.value ());
@@ -147,18 +241,33 @@ core::Tensor ComputeFPFHFeature(
147
241
}
148
242
149
243
core::Tensor fpfh;
150
- if (indices.has_value ()) {
151
- const auto mask_fpfh_points_indices =
152
- mask_fpfh_points.NonZero ().GetItem ({core::TensorKey::Index (0 )});
153
- const auto map_batch_info_idx_to_point_idx =
154
- mask_required_points.NonZero ().GetItem (
155
- {core::TensorKey::Index (0 )});
156
- fpfh = core::Tensor::Zeros ({mask_fpfh_points_indices.GetLength (), 33 },
157
- dtype, device);
244
+ if (filter_fpfh) {
245
+ const int64_t size = indices_fpfh_points.GetLength ();
246
+ fpfh = core::Tensor::Zeros ({size, 33 }, dtype, device);
247
+ core::Tensor final_p_indices, final_p_distance2, final_p_counts;
248
+ if (cache_fpfh_info) {
249
+ core::Tensor map_spfh_idx_to_required_point_idx =
250
+ map_point_idx_to_required_point_idx
251
+ .IndexGet ({mask_spfh_points})
252
+ .To (core::Int64);
253
+ save_p_indices.IndexSet ({map_spfh_idx_to_required_point_idx},
254
+ p_indices);
255
+ save_p_distance2.IndexSet ({map_spfh_idx_to_required_point_idx},
256
+ p_distance2);
257
+ save_p_counts.IndexSet ({map_spfh_idx_to_required_point_idx},
258
+ p_counts);
259
+ final_p_indices = save_p_indices;
260
+ final_p_distance2 = save_p_distance2;
261
+ final_p_counts = save_p_counts;
262
+ } else {
263
+ final_p_indices = p_indices;
264
+ final_p_distance2 = p_distance2;
265
+ final_p_counts = p_counts;
266
+ }
158
267
pipelines::kernel::ComputeFPFHFeature (
159
- input.GetPointPositions (), input.GetPointNormals (), p_indices,
160
- p_distance2, p_counts, fpfh, mask_fpfh_points ,
161
- map_batch_info_idx_to_point_idx );
268
+ input.GetPointPositions (), input.GetPointNormals (),
269
+ final_p_indices, final_p_distance2, final_p_counts, fpfh ,
270
+ mask_fpfh_points, map_required_point_idx_to_point_idx );
162
271
} else {
163
272
const int64_t size = input.GetPointPositions ().GetLength ();
164
273
fpfh = core::Tensor::Zeros ({size, 33 }, dtype, device);
0 commit comments