Open3D (C++ API)  0.19.0
Loading...
Searching...
No Matches
FeatureImpl.h
Go to the documentation of this file.
1// ----------------------------------------------------------------------------
2// - Open3D: www.open3d.org -
3// ----------------------------------------------------------------------------
4// Copyright (c) 2018-2024 www.open3d.org
5// SPDX-License-Identifier: MIT
6// ----------------------------------------------------------------------------
7
13
14namespace open3d {
15namespace t {
16namespace pipelines {
17namespace kernel {
18
19#ifndef __CUDACC__
20using std::max;
21using std::min;
22#endif
23
24template <typename scalar_t>
25OPEN3D_HOST_DEVICE void ComputePairFeature(const scalar_t *p1,
26 const scalar_t *n1,
27 const scalar_t *p2,
28 const scalar_t *n2,
29 scalar_t *feature) {
30 scalar_t dp2p1[3], n1_copy[3], n2_copy[3];
31 dp2p1[0] = p2[0] - p1[0];
32 dp2p1[1] = p2[1] - p1[1];
33 dp2p1[2] = p2[2] - p1[2];
34 feature[3] = sqrt(dp2p1[0] * dp2p1[0] + dp2p1[1] * dp2p1[1] +
35 dp2p1[2] * dp2p1[2]);
36 if (feature[3] == 0) {
37 feature[0] = 0;
38 feature[1] = 0;
39 feature[2] = 0;
40 feature[3] = 0;
41 return;
42 }
43
44 scalar_t angle1 = core::linalg::kernel::dot_3x1(n1, dp2p1) / feature[3];
45 scalar_t angle2 = core::linalg::kernel::dot_3x1(n2, dp2p1) / feature[3];
46 if (acos(fabs(angle1)) > acos(fabs(angle2))) {
47 n1_copy[0] = n2[0];
48 n1_copy[1] = n2[1];
49 n1_copy[2] = n2[2];
50 n2_copy[0] = n1[0];
51 n2_copy[1] = n1[1];
52 n2_copy[2] = n1[2];
53 dp2p1[0] *= -1;
54 dp2p1[1] *= -1;
55 dp2p1[2] *= -1;
56 feature[2] = -angle2;
57 } else {
58 n1_copy[0] = n1[0];
59 n1_copy[1] = n1[1];
60 n1_copy[2] = n1[2];
61 n2_copy[0] = n2[0];
62 n2_copy[1] = n2[1];
63 n2_copy[2] = n2[2];
64 feature[2] = angle1;
65 }
66
67 scalar_t v[3];
68 core::linalg::kernel::cross_3x1(dp2p1, n1_copy, v);
69 const scalar_t v_norm = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
70 if (v_norm == 0.0) {
71 feature[0] = 0.0;
72 feature[1] = 0.0;
73 feature[2] = 0.0;
74 feature[3] = 0.0;
75 return;
76 }
77 v[0] /= v_norm;
78 v[1] /= v_norm;
79 v[2] /= v_norm;
80 scalar_t w[3];
82 feature[1] = core::linalg::kernel::dot_3x1(v, n2_copy);
83 feature[0] = atan2(core::linalg::kernel::dot_3x1(w, n2_copy),
84 core::linalg::kernel::dot_3x1(n1_copy, n2_copy));
85}
86
87template <typename scalar_t>
88OPEN3D_HOST_DEVICE void UpdateSPFHFeature(const scalar_t *feature,
89 int64_t idx,
90 scalar_t hist_incr,
91 scalar_t *spfh) {
92 int h_index1 =
93 static_cast<int>(floor(11 * (feature[0] + M_PI) / (2.0 * M_PI)));
94 h_index1 = h_index1 >= 11 ? 10 : max(0, h_index1);
95
96 int h_index2 = static_cast<int>(floor(11 * (feature[1] + 1.0) * 0.5));
97 h_index2 = h_index2 >= 11 ? 10 : max(0, h_index2);
98
99 int h_index3 = static_cast<int>(floor(11 * (feature[2] + 1.0) * 0.5));
100 h_index3 = h_index3 >= 11 ? 10 : max(0, h_index3);
101
102 spfh[idx * 33 + h_index1] += hist_incr;
103 spfh[idx * 33 + h_index2 + 11] += hist_incr;
104 spfh[idx * 33 + h_index3 + 22] += hist_incr;
105}
106
107#if defined(__CUDACC__)
108void ComputeFPFHFeatureCUDA
109#elif defined(SYCL_LANGUAGE_VERSION)
110void ComputeFPFHFeatureSYCL
111#else
113#endif
114 (const core::Tensor &points,
115 const core::Tensor &normals,
116 const core::Tensor &indices,
117 const core::Tensor &distance2,
118 const core::Tensor &counts,
119 core::Tensor &fpfhs,
120 const std::optional<core::Tensor> &mask,
121 const std::optional<core::Tensor> &map_info_idx_to_point_idx) {
122 const core::Dtype dtype = points.GetDtype();
123 const core::Device device = points.GetDevice();
124 const int64_t n_points = points.GetLength();
125
126 const bool filter_fpfh =
127 mask.has_value() && map_info_idx_to_point_idx.has_value();
128 if (mask.has_value() ^ map_info_idx_to_point_idx.has_value()) {
129 utility::LogError(
130 "Parameters mask and map_info_idx_to_point_idx must "
131 "either be both provided or both not provided.");
132 }
133 if (filter_fpfh) {
134 if (mask.value().GetShape()[0] != n_points) {
135 utility::LogError(
136 "Parameter mask was provided, but its size {:d} should"
137 "be equal to the number of points {:d}.",
138 (int)mask.value().GetShape()[0], n_points);
139 }
140 if (map_info_idx_to_point_idx.value().GetShape()[0] !=
141 counts.GetShape()[0] - (indices.GetShape().size() == 1 ? 1 : 0)) {
142 utility::LogError(
143 "Parameter map_info_idx_to_point_idx was provided, "
144 "but its size"
145 "{:d} should be equal to the size of counts {:d}.",
146 (int)map_info_idx_to_point_idx.value().GetShape()[0],
147 (int)counts.GetShape()[0]);
148 }
149 }
150
151 core::Tensor map_spfh_info_idx_to_point_idx =
152 map_info_idx_to_point_idx.value_or(
153 core::Tensor::Empty({0}, core::Int64, device));
154
155 const core::Tensor map_fpfh_idx_to_point_idx =
156 filter_fpfh ? mask.value().NonZero().GetItem(
158 : core::Tensor::Empty({0}, core::Int64, device);
159
160 const int32_t n_fpfh =
161 filter_fpfh ? map_fpfh_idx_to_point_idx.GetLength() : n_points;
162 const int32_t n_spfh =
163 filter_fpfh ? map_spfh_info_idx_to_point_idx.GetLength() : n_points;
164
165 core::Tensor spfhs =
166 core::Tensor::Zeros({n_spfh, 33}, dtype, fpfhs.GetDevice());
167
168 core::Tensor map_point_idx_to_spfh_idx;
169 if (filter_fpfh) {
170 map_point_idx_to_spfh_idx = core::Tensor::Full(
171 {n_points}, -1, core::Int64, fpfhs.GetDevice());
172 map_point_idx_to_spfh_idx.IndexSet(
173 {map_spfh_info_idx_to_point_idx},
174 core::Tensor::Arange(0, n_spfh, 1, core::Int64,
175 fpfhs.GetDevice()));
176 } else {
177 map_point_idx_to_spfh_idx =
178 core::Tensor::Empty({0}, core::Int64, fpfhs.GetDevice());
179 }
180
181 // Check the nns type (knn = hybrid = false, radius = true).
182 // The nns radius search mode will resulting a prefix sum 1D tensor.
183 bool is_radius_search;
184 int nn_size = 0;
185 if (indices.GetShape().size() == 1) {
186 is_radius_search = true;
187 } else {
188 is_radius_search = false;
189 nn_size = indices.GetShape()[1];
190 }
191
193 const scalar_t *points_ptr = points.GetDataPtr<scalar_t>();
194 const scalar_t *normals_ptr = normals.GetDataPtr<scalar_t>();
195 const int32_t *indices_ptr = indices.GetDataPtr<int32_t>();
196 const scalar_t *distance2_ptr = distance2.GetDataPtr<scalar_t>();
197 const int32_t *counts_ptr = counts.GetDataPtr<int32_t>();
198 scalar_t *spfhs_ptr = spfhs.GetDataPtr<scalar_t>();
199 scalar_t *fpfhs_ptr = fpfhs.GetDataPtr<scalar_t>();
200 const int64_t *map_spfh_info_idx_to_point_idx_ptr =
201 map_spfh_info_idx_to_point_idx.GetDataPtr<int64_t>();
202 const int64_t *map_fpfh_idx_to_point_idx_ptr =
203 map_fpfh_idx_to_point_idx.GetDataPtr<int64_t>();
204 const int64_t *map_point_idx_to_spfh_idx_ptr =
205 map_point_idx_to_spfh_idx.GetDataPtr<int64_t>();
206
207 // Compute SPFH features for the points.
209 points.GetDevice(), n_spfh,
210 [=] OPEN3D_DEVICE(int64_t workload_idx) {
211 int64_t workload_point_idx =
212 filter_fpfh ? map_spfh_info_idx_to_point_idx_ptr
213 [workload_idx]
214 : workload_idx;
215 int64_t idx = 3 * workload_point_idx;
216 const scalar_t *point = points_ptr + idx;
217 const scalar_t *normal = normals_ptr + idx;
218
219 const int indice_size =
220 is_radius_search ? (counts_ptr[workload_idx + 1] -
221 counts_ptr[workload_idx])
222 : counts_ptr[workload_idx];
223
224 if (indice_size > 1) {
225 const scalar_t hist_incr =
226 100.0 / static_cast<scalar_t>(indice_size - 1);
227 for (int i = 1; i < indice_size; i++) {
228 const int point_idx =
229 is_radius_search
230 ? indices_ptr
231 [i +
232 counts_ptr[workload_idx]]
233 : indices_ptr[workload_idx *
234 nn_size +
235 i];
236
237 const scalar_t *point_ref =
238 points_ptr + 3 * point_idx;
239 const scalar_t *normal_ref =
240 normals_ptr + 3 * point_idx;
241 scalar_t fea[4] = {0};
242 ComputePairFeature<scalar_t>(
243 point, normal, point_ref, normal_ref, fea);
244 UpdateSPFHFeature<scalar_t>(fea, workload_idx,
245 hist_incr, spfhs_ptr);
246 }
247 }
248 });
249
250 // Compute FPFH features for the points.
252 points.GetDevice(), n_fpfh,
253 [=] OPEN3D_DEVICE(int64_t workload_idx) {
254 int64_t workload_spfh_idx =
255 filter_fpfh ? map_point_idx_to_spfh_idx_ptr
256 [map_fpfh_idx_to_point_idx_ptr
257 [workload_idx]]
258 : workload_idx;
259 const int indice_size =
260 is_radius_search
261 ? (counts_ptr[workload_spfh_idx + 1] -
262 counts_ptr[workload_spfh_idx])
263 : counts_ptr[workload_spfh_idx];
264 if (indice_size > 1) {
265 scalar_t sum[3] = {0.0, 0.0, 0.0};
266 for (int i = 1; i < indice_size; i++) {
267 const int idx =
268 is_radius_search
269 ? i + counts_ptr[workload_spfh_idx]
270 : workload_spfh_idx * nn_size + i;
271 const scalar_t dist = distance2_ptr[idx];
272 if (dist == 0.0) continue;
273 const int32_t spfh_idx =
274 filter_fpfh ? map_point_idx_to_spfh_idx_ptr
275 [indices_ptr[idx]]
276 : indices_ptr[idx];
277 for (int j = 0; j < 33; j++) {
278 const scalar_t val =
279 spfhs_ptr[spfh_idx * 33 + j] / dist;
280 sum[j / 11] += val;
281 fpfhs_ptr[workload_idx * 33 + j] += val;
282 }
283 }
284 for (int j = 0; j < 3; j++) {
285 sum[j] = sum[j] != 0.0 ? 100.0 / sum[j] : 0.0;
286 }
287 for (int j = 0; j < 33; j++) {
288 fpfhs_ptr[workload_idx * 33 + j] *= sum[j / 11];
289 fpfhs_ptr[workload_idx * 33 + j] +=
290 spfhs_ptr[workload_spfh_idx * 33 + j];
291 }
292 }
293 });
294 });
295}
296
297} // namespace kernel
298} // namespace pipelines
299} // namespace t
300} // namespace open3d
Common CUDA utilities.
#define OPEN3D_HOST_DEVICE
Definition CUDAUtils.h:43
#define OPEN3D_DEVICE
Definition CUDAUtils.h:44
#define DISPATCH_FLOAT_DTYPE_TO_TEMPLATE(DTYPE,...)
Definition Dispatch.h:77
T dist
Definition FixedRadiusSearchSYCLImpl.h:163
double t
Definition SurfaceReconstructionPoisson.cpp:172
Point< Real, 3 > point
Definition SurfaceReconstructionPoisson.cpp:163
Definition Device.h:18
Definition Dtype.h:20
Definition Tensor.h:32
Tensor NonZero() const
Definition Tensor.cpp:1876
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
Definition Tensor.cpp:399
static Tensor Arange(const Scalar start, const Scalar stop, const Scalar step=1, const Dtype dtype=core::Int64, const Device &device=core::Device("CPU:0"))
Create a 1D tensor with evenly spaced values in the given interval.
Definition Tensor.cpp:435
int64_t GetLength() const
Definition Tensor.h:1182
T * GetDataPtr()
Definition Tensor.h:1201
static Tensor Full(const SizeVector &shape, T fill_value, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with specified value.
Definition Tensor.h:253
static Tensor Zeros(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with zeros.
Definition Tensor.cpp:405
Tensor GetItem(const TensorKey &tk) const
Definition Tensor.cpp:518
void IndexSet(const std::vector< Tensor > &index_tensors, const Tensor &src_tensor)
Advanced indexing getter.
Definition Tensor.cpp:1009
int points
Definition FilePCD.cpp:55
#define M_PI
Definition mikktspace.c:37
OPEN3D_HOST_DEVICE OPEN3D_FORCE_INLINE void cross_3x1(const scalar_t *A_3x1_input, const scalar_t *B_3x1_input, scalar_t *C_3x1_output)
Definition Matrix.h:63
OPEN3D_HOST_DEVICE OPEN3D_FORCE_INLINE scalar_t dot_3x1(const scalar_t *A_3x1_input, const scalar_t *B_3x1_input)
Definition Matrix.h:89
const Dtype Int64
Definition Dtype.cpp:47
void ParallelFor(const Device &device, int64_t n, const func_t &func)
Definition ParallelFor.h:135
OPEN3D_HOST_DEVICE void ComputePairFeature(const scalar_t *p1, const scalar_t *n1, const scalar_t *p2, const scalar_t *n2, scalar_t *feature)
Definition FeatureImpl.h:25
void ComputeFPFHFeatureCPU(const core::Tensor &points, const core::Tensor &normals, const core::Tensor &indices, const core::Tensor &distance2, const core::Tensor &counts, core::Tensor &fpfhs, const std::optional< core::Tensor > &mask=std::nullopt, const std::optional< core::Tensor > &map_batch_info_idx_to_point_idx=std::nullopt)
Definition FeatureImpl.h:114
OPEN3D_HOST_DEVICE void UpdateSPFHFeature(const scalar_t *feature, int64_t idx, scalar_t hist_incr, scalar_t *spfh)
Definition FeatureImpl.h:88
Definition PinholeCameraIntrinsic.cpp:16
const core::Tensor * normals
Definition TriangleMesh.cpp:2126
const core::Tensor * indices
Definition TriangleMesh.cpp:2128