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,138 @@ 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 double cache_info_indices_ratio_thresh = 0.01 ;
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 = !is_radius_search &&
123
+ (indices_fpfh_points.GetLength () >=
124
+ cache_info_indices_ratio_thresh * num_points);
125
+
126
+ if (cache_fpfh_info) {
127
+ map_point_idx_to_required_point_idx =
128
+ core::Tensor::Full ({num_points}, -1 , core::Int32, device);
129
+ map_point_idx_to_required_point_idx.IndexSet (
130
+ {map_required_point_idx_to_point_idx},
131
+ core::Tensor::Arange (
132
+ 0 , map_required_point_idx_to_point_idx.GetLength (),
133
+ 1 , core::Int32, device));
134
+
135
+ core::SizeVector save_p_indices_shape = p_indices.GetShape ();
136
+ save_p_indices_shape[0 ] =
137
+ map_required_point_idx_to_point_idx.GetLength ();
138
+ save_p_indices = core::Tensor::Zeros (save_p_indices_shape,
139
+ core::Int32, device);
140
+ save_p_distance2 = core::Tensor::Zeros (save_p_indices.GetShape (),
141
+ dtype, device);
142
+ save_p_counts = core::Tensor::Zeros (
143
+ {map_required_point_idx_to_point_idx.GetLength () +
144
+ (is_radius_search ? 1 : 0 )},
145
+ core::Int32, device);
146
+
147
+ core::Tensor map_fpfh_point_idx_to_required_point_idx =
148
+ map_point_idx_to_required_point_idx
149
+ .IndexGet ({indices_fpfh_points})
150
+ .To (core::Int64);
151
+
152
+ save_p_indices.IndexSet ({map_fpfh_point_idx_to_required_point_idx},
153
+ p_indices);
154
+ save_p_distance2.IndexSet (
155
+ {map_fpfh_point_idx_to_required_point_idx}, p_distance2);
156
+ save_p_counts.IndexSet ({map_fpfh_point_idx_to_required_point_idx},
157
+ p_counts);
158
+
159
+ // If we are filtering FPFH features, we have already computed some
160
+ // info about the FPFH points' neighbors. Now we just need to
161
+ // compute the info for the remaining required points, so skip the
162
+ // computation for the already computed info.
163
+ mask_spfh_points =
164
+ core::Tensor::Zeros ({num_points}, core::Bool, device);
165
+ mask_spfh_points.IndexSet (
166
+ {map_required_point_idx_to_point_idx},
167
+ core::Tensor::Ones ({1 }, core::Bool, device));
168
+ mask_spfh_points.IndexSet (
169
+ {indices_fpfh_points},
170
+ core::Tensor::Zeros ({1 }, core::Bool, device));
171
+ } else {
172
+ mask_spfh_points = mask_required_points;
173
+ }
92
174
}
93
175
94
176
const core::Tensor query_point_positions =
95
- mask_required_points.GetShape ()[0 ] > 0
96
- ? input.GetPointPositions ().IndexGet ({mask_required_points})
97
- : input.GetPointPositions ();
177
+ filter_fpfh ? input.GetPointPositions ().IndexGet ({mask_spfh_points})
178
+ : input.GetPointPositions ();
98
179
99
180
// Compute nearest neighbors and squared distances.
100
181
core::Tensor p_indices, p_distance2, p_counts;
@@ -119,14 +200,25 @@ core::Tensor ComputeFPFHFeature(
119
200
utility::LogError (" Building KnnIndex failed." );
120
201
}
121
202
}
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);
203
+
204
+ // tree.KnnSearch complains if the query point cloud is empty.
205
+ if (query_point_positions.GetLength () > 0 ) {
206
+ std::tie (p_indices, p_distance2) =
207
+ tree.KnnSearch (query_point_positions, max_nn.value ());
208
+
209
+ const int fill_value =
210
+ max_nn.value () > num_points ? num_points : max_nn.value ();
211
+
212
+ p_counts = core::Tensor::Full ({query_point_positions.GetLength ()},
213
+ fill_value, core::Int32, device);
214
+ } else {
215
+ p_indices = core::Tensor::Zeros ({0 , max_nn.value ()}, core::Int32,
216
+ device);
217
+ p_distance2 =
218
+ core::Tensor::Zeros ({0 , max_nn.value ()}, dtype, device);
219
+ p_counts = core::Tensor::Zeros ({0 }, core::Int32, device);
220
+ }
221
+
130
222
utility::LogDebug (
131
223
" Use KNNSearch [max_nn: {}] for computing FPFH feature." ,
132
224
max_nn.value ());
@@ -147,18 +239,33 @@ core::Tensor ComputeFPFHFeature(
147
239
}
148
240
149
241
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);
242
+ if (filter_fpfh) {
243
+ const int64_t size = indices_fpfh_points.GetLength ();
244
+ fpfh = core::Tensor::Zeros ({size, 33 }, dtype, device);
245
+ core::Tensor final_p_indices, final_p_distance2, final_p_counts;
246
+ if (cache_fpfh_info) {
247
+ core::Tensor map_spfh_idx_to_required_point_idx =
248
+ map_point_idx_to_required_point_idx
249
+ .IndexGet ({mask_spfh_points})
250
+ .To (core::Int64);
251
+ save_p_indices.IndexSet ({map_spfh_idx_to_required_point_idx},
252
+ p_indices);
253
+ save_p_distance2.IndexSet ({map_spfh_idx_to_required_point_idx},
254
+ p_distance2);
255
+ save_p_counts.IndexSet ({map_spfh_idx_to_required_point_idx},
256
+ p_counts);
257
+ final_p_indices = save_p_indices;
258
+ final_p_distance2 = save_p_distance2;
259
+ final_p_counts = save_p_counts;
260
+ } else {
261
+ final_p_indices = p_indices;
262
+ final_p_distance2 = p_distance2;
263
+ final_p_counts = p_counts;
264
+ }
158
265
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 );
266
+ input.GetPointPositions (), input.GetPointNormals (),
267
+ final_p_indices, final_p_distance2, final_p_counts, fpfh ,
268
+ mask_fpfh_points, map_required_point_idx_to_point_idx );
162
269
} else {
163
270
const int64_t size = input.GetPointPositions ().GetLength ();
164
271
fpfh = core::Tensor::Zeros ({size, 33 }, dtype, device);
0 commit comments