ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Registration.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
13 
14 namespace cloudViewer {
15 namespace t {
16 namespace pipelines {
17 namespace kernel {
18 
20  const core::Tensor &target_points,
21  const core::Tensor &target_normals,
22  const core::Tensor &correspondence_indices,
23  const registration::RobustKernel &kernel) {
24  const core::Device device = source_points.GetDevice();
25 
26  // Pose {6,} tensor [output].
27  core::Tensor pose = core::Tensor::Empty({6}, core::Float64, device);
28 
29  float residual = 0;
30  int inlier_count = 0;
31 
32  if (source_points.IsCPU()) {
34  source_points.Contiguous(), target_points.Contiguous(),
35  target_normals.Contiguous(),
36  correspondence_indices.Contiguous(), pose, residual,
37  inlier_count, source_points.GetDtype(), device, kernel);
38  } else if (source_points.IsCUDA()) {
39  core::CUDAScopedDevice scoped_device(source_points.GetDevice());
40  CUDA_CALL(ComputePosePointToPlaneCUDA, source_points.Contiguous(),
41  target_points.Contiguous(), target_normals.Contiguous(),
42  correspondence_indices.Contiguous(), pose, residual,
43  inlier_count, source_points.GetDtype(), device, kernel);
44  } else {
45  utility::LogError("Unimplemented device.");
46  }
47 
48  utility::LogDebug("PointToPlane Transform: residual {}, inlier_count {}",
49  residual, inlier_count);
50 
51  return pose;
52 }
53 
55  const core::Tensor &source_colors,
56  const core::Tensor &target_points,
57  const core::Tensor &target_normals,
58  const core::Tensor &target_colors,
59  const core::Tensor &target_color_gradients,
60  const core::Tensor &correspondence_indices,
61  const registration::RobustKernel &kernel,
62  const double &lambda_geometric) {
63  const core::Device device = source_points.GetDevice();
64 
65  // Pose {6,} tensor [output].
67 
68  float residual = 0;
69  int inlier_count = 0;
70 
71  if (source_points.IsCPU()) {
73  source_points.Contiguous(), source_colors.Contiguous(),
74  target_points.Contiguous(), target_normals.Contiguous(),
75  target_colors.Contiguous(), target_color_gradients.Contiguous(),
76  correspondence_indices.Contiguous(), pose, residual,
77  inlier_count, source_points.GetDtype(), device, kernel,
78  lambda_geometric);
79  } else if (source_points.IsCUDA()) {
80  core::CUDAScopedDevice scoped_device(source_points.GetDevice());
81  CUDA_CALL(ComputePoseColoredICPCUDA, source_points.Contiguous(),
82  source_colors.Contiguous(), target_points.Contiguous(),
83  target_normals.Contiguous(), target_colors.Contiguous(),
84  target_color_gradients.Contiguous(),
85  correspondence_indices.Contiguous(), pose, residual,
86  inlier_count, source_points.GetDtype(), device, kernel,
87  lambda_geometric);
88  } else {
89  utility::LogError("Unimplemented device.");
90  }
91 
92  utility::LogDebug("PointToPlane Transform: residual {}, inlier_count {}",
93  residual, inlier_count);
94 
95  return pose;
96 }
97 
99  const core::Tensor &source_points,
100  const core::Tensor &source_dopplers,
101  const core::Tensor &source_directions,
102  const core::Tensor &target_points,
103  const core::Tensor &target_normals,
104  const core::Tensor &correspondence_indices,
105  const core::Tensor &current_transform,
106  const core::Tensor &transform_vehicle_to_sensor,
107  const std::size_t iteration,
108  const double period,
109  const double lambda_doppler,
110  const bool reject_dynamic_outliers,
111  const double doppler_outlier_threshold,
112  const std::size_t outlier_rejection_min_iteration,
113  const std::size_t geometric_robust_loss_min_iteration,
114  const std::size_t doppler_robust_loss_min_iteration,
115  const registration::RobustKernel &geometric_kernel,
116  const registration::RobustKernel &doppler_kernel) {
117  const core::Device device = source_points.GetDevice();
118  const core::Dtype dtype = source_points.GetDtype();
119 
120  // Pose {6,} tensor [ouput].
121  core::Tensor output_pose =
123 
124  float residual = 0;
125  int inlier_count = 0;
126 
127  // Use robust kernels only after a specified minimum number of iterations.
128  const auto kernel_default = registration::RobustKernel(
130  const auto kernel_geometric =
131  (iteration >= geometric_robust_loss_min_iteration)
132  ? geometric_kernel
133  : kernel_default;
134  const auto kernel_doppler = (iteration >= doppler_robust_loss_min_iteration)
135  ? doppler_kernel
136  : kernel_default;
137 
138  // Enable outlier rejection based on the current iteration count.
139  const bool reject_outliers = reject_dynamic_outliers &&
140  (iteration >= outlier_rejection_min_iteration);
141 
142  // Extract the rotation and translation parts from the matrix.
143  const core::Tensor R_S_to_V =
144  transform_vehicle_to_sensor
145  .GetItem({core::TensorKey::Slice(0, 3, 1),
146  core::TensorKey::Slice(0, 3, 1)})
147  .Inverse()
148  .Flatten()
149  .To(device, dtype);
150  const core::Tensor r_v_to_s_in_V =
151  transform_vehicle_to_sensor
152  .GetItem({core::TensorKey::Slice(0, 3, 1),
153  core::TensorKey::Slice(3, 4, 1)})
154  .Flatten()
155  .To(device, dtype);
156 
157  // Compute the pose (rotation + translation) vector.
158  const core::Tensor state_vector =
159  pipelines::kernel::TransformationToPose(current_transform)
160  .To(device, dtype);
161 
162  // Compute the linear and angular velocity from the pose vector.
163  const core::Tensor w_v_in_V =
164  (state_vector.GetItem(core::TensorKey::Slice(0, 3, 1)).Neg() /
165  period)
166  .To(device, dtype);
167  const core::Tensor v_v_in_V =
168  (state_vector.GetItem(core::TensorKey::Slice(3, 6, 1)).Neg() /
169  period)
170  .To(device, dtype);
171 
172  core::Device::DeviceType device_type = device.GetType();
173  if (device_type == core::Device::DeviceType::CPU) {
175  source_points.Contiguous(), source_dopplers.Contiguous(),
176  source_directions.Contiguous(), target_points.Contiguous(),
177  target_normals.Contiguous(),
178  correspondence_indices.Contiguous(), output_pose, residual,
179  inlier_count, dtype, device, R_S_to_V.Contiguous(),
180  r_v_to_s_in_V.Contiguous(), w_v_in_V.Contiguous(),
181  v_v_in_V.Contiguous(), period, reject_outliers,
182  doppler_outlier_threshold, kernel_geometric, kernel_doppler,
183  lambda_doppler);
184  } else if (device_type == core::Device::DeviceType::CUDA) {
185  CUDA_CALL(ComputePoseDopplerICPCUDA, source_points.Contiguous(),
186  source_dopplers.Contiguous(), source_directions.Contiguous(),
187  target_points.Contiguous(), target_normals.Contiguous(),
188  correspondence_indices.Contiguous(), output_pose, residual,
189  inlier_count, dtype, device, R_S_to_V.Contiguous(),
190  r_v_to_s_in_V.Contiguous(), w_v_in_V.Contiguous(),
191  v_v_in_V.Contiguous(), period, reject_outliers,
192  doppler_outlier_threshold, kernel_geometric, kernel_doppler,
193  lambda_doppler);
194  } else {
195  utility::LogError("Unimplemented device.");
196  }
197 
199  "DopplerPointToPlane Transform: residual {}, inlier_count {}",
200  residual, inlier_count);
201 
202  return output_pose;
203 }
204 
205 std::tuple<core::Tensor, core::Tensor> ComputeRtPointToPoint(
206  const core::Tensor &source_points,
207  const core::Tensor &target_points,
208  const core::Tensor &correspondence_indices) {
209  const core::Device device = source_points.GetDevice();
210 
211  // [Output] Rotation and translation tensor of type Float64.
212  core::Tensor R, t;
213 
214  int inlier_count = 0;
215 
216  if (source_points.IsCPU()) {
217  // Pointer to point cloud data - indexed according to correspondences.
219  source_points.Contiguous(), target_points.Contiguous(),
220  correspondence_indices.Contiguous(), R, t, inlier_count,
221  source_points.GetDtype(), device);
222  } else if (source_points.IsCUDA()) {
223 #ifdef BUILD_CUDA_MODULE
224  core::CUDAScopedDevice scoped_device(source_points.GetDevice());
225  // TODO: Implement optimized CUDA reduction kernel.
226  core::Tensor valid = correspondence_indices.Ne(-1).Reshape({-1});
227  // correpondence_set : (i, corres[i]).
228 
229  if (valid.GetLength() == 0) {
230  utility::LogError("No valid correspondence present.");
231  }
232 
233  // source[i] and target[corres[i]] is a correspondence.
234  core::Tensor source_indices =
235  core::Tensor::Arange(0, source_points.GetShape()[0], 1,
236  core::Int64, device)
237  .IndexGet({valid});
238  // Only take valid indices.
239  core::Tensor target_indices =
240  correspondence_indices.IndexGet({valid}).Reshape({-1});
241 
242  // Number of good correspondences (C).
243  inlier_count = source_indices.GetLength();
244 
245  core::Tensor source_select = source_points.IndexGet({source_indices});
246  core::Tensor target_select = target_points.IndexGet({target_indices});
247 
248  // https://ieeexplore.ieee.org/document/88573
249  core::Tensor mean_s = source_select.Mean({0}, true);
250  core::Tensor mean_t = target_select.Mean({0}, true);
251 
252  // Compute linear system on CPU as Float64.
253  core::Device host("CPU:0");
254  core::Tensor Sxy = (target_select - mean_t)
255  .T()
256  .Matmul(source_select - mean_s)
257  .Div_(static_cast<float>(inlier_count))
258  .To(host, core::Float64);
259 
260  mean_s = mean_s.To(host, core::Float64);
261  mean_t = mean_t.To(host, core::Float64);
262 
263  core::Tensor U, D, VT;
264  std::tie(U, D, VT) = Sxy.SVD();
266  if (U.Det() * (VT.T()).Det() < 0) {
267  S[-1][-1] = -1;
268  }
269  R = U.Matmul(S.Matmul(VT));
270  t = mean_t.Reshape({-1}) - R.Matmul(mean_s.T()).Reshape({-1});
271 #else
272  utility::LogError("Not compiled with CUDA, but CUDA device is used.");
273 #endif
274  } else {
275  utility::LogError("Unimplemented device.");
276  }
277  return std::make_tuple(R, t);
278 }
279 
281  const core::Tensor &target_points,
282  const core::Tensor &correspondence_indices) {
283  const core::Device device = target_points.GetDevice();
284 
285  core::Tensor information_matrix =
287 
288  if (target_points.IsCPU()) {
290  target_points.Contiguous(), correspondence_indices.Contiguous(),
291  information_matrix, target_points.GetDtype(), device);
292  } else if (target_points.IsCUDA()) {
293  core::CUDAScopedDevice scoped_device(target_points.GetDevice());
294  CUDA_CALL(ComputeInformationMatrixCUDA, target_points.Contiguous(),
295  correspondence_indices.Contiguous(), information_matrix,
296  target_points.GetDtype(), device);
297  } else {
298  utility::LogError("Unimplemented device.");
299  }
300 
301  return information_matrix;
302 }
303 
304 } // namespace kernel
305 } // namespace pipelines
306 } // namespace t
307 } // namespace cloudViewer
#define CUDA_CALL(cuda_function,...)
Definition: CUDAUtils.h:49
When CUDA is not enabled, this is a dummy class.
Definition: CUDAUtils.h:214
DeviceType GetType() const
Returns type of the device, e.g. DeviceType::CPU, DeviceType::CUDA.
Definition: Device.h:58
DeviceType
Type for device.
Definition: Device.h:21
static const Dtype Float64
Definition: Dtype.h:25
bool IsCUDA() const
Definition: Device.h:99
bool IsCPU() const
Definition: Device.h:95
static TensorKey Slice(utility::optional< int64_t > start, utility::optional< int64_t > stop, utility::optional< int64_t > step)
Definition: TensorKey.cpp:138
double Det() const
Compute the determinant of a 2D square tensor.
Definition: Tensor.cpp:1092
Tensor Contiguous() const
Definition: Tensor.cpp:772
Tensor Matmul(const Tensor &rhs) const
Definition: Tensor.cpp:1919
Tensor Neg() const
Element-wise negation of a tensor, returning a new tensor.
Definition: Tensor.cpp:1329
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:436
int64_t GetLength() const
Definition: Tensor.h:1125
Tensor Ne(const Tensor &value) const
Element-wise not-equals-to of tensors, returning a new boolean tensor.
Definition: Tensor.cpp:1714
Dtype GetDtype() const
Definition: Tensor.h:1164
Tensor GetItem(const TensorKey &tk) const
Definition: Tensor.cpp:473
static Tensor Eye(int64_t n, Dtype dtype, const Device &device)
Create an identity matrix of size n x n.
Definition: Tensor.cpp:418
Tensor IndexGet(const std::vector< Tensor > &index_tensors) const
Advanced indexing getter. This will always allocate a new Tensor.
Definition: Tensor.cpp:905
Device GetDevice() const override
Definition: Tensor.cpp:1435
Tensor Reshape(const SizeVector &dst_shape) const
Definition: Tensor.cpp:671
Tensor Mean(const SizeVector &dims, bool keepdim=false) const
Definition: Tensor.cpp:1247
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
Definition: Tensor.cpp:400
Tensor Div_(const Tensor &value)
Definition: Tensor.cpp:1225
SizeVector GetShape() const
Definition: Tensor.h:1127
std::tuple< Tensor, Tensor, Tensor > SVD() const
Definition: Tensor.cpp:1990
Tensor T() const
Expects input to be <= 2-D Tensor by swapping dimension 0 and 1.
Definition: Tensor.cpp:1079
Tensor To(Dtype dtype, bool copy=false) const
Definition: Tensor.cpp:739
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
const Dtype Int64
Definition: Dtype.cpp:47
void Inverse(const Tensor &A, Tensor &output)
Computes A^{-1} with LU factorization, where A is a N x N square matrix.
Definition: Inverse.cpp:18
const Dtype Float64
Definition: Dtype.cpp:43
void To(const core::Tensor &src, core::Tensor &dst, double scale, double offset)
Definition: Image.cpp:17
void ComputePoseColoredICPCPU(const core::Tensor &source_points, const core::Tensor &source_colors, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &target_colors, const core::Tensor &target_color_gradients, const core::Tensor &correspondence_indices, core::Tensor &pose, float &residual, int &inlier_count, const core::Dtype &dtype, const core::Device &device, const registration::RobustKernel &kernel, const double &lambda_geometric)
void ComputePosePointToPlaneCPU(const core::Tensor &source_points, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &correspondence_indices, core::Tensor &pose, float &residual, int &inlier_count, const core::Dtype &dtype, const core::Device &device, const registration::RobustKernel &kernel)
void ComputeRtPointToPointCPU(const core::Tensor &source_points, const core::Tensor &target_points, const core::Tensor &corres, core::Tensor &R, core::Tensor &t, int &inlier_count, const core::Dtype &dtype, const core::Device &device)
core::Tensor ComputePoseDopplerICP(const core::Tensor &source_points, const core::Tensor &source_dopplers, const core::Tensor &source_directions, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &correspondence_indices, const core::Tensor &current_transform, const core::Tensor &transform_vehicle_to_sensor, const std::size_t iteration, const double period, const double lambda_doppler, const bool reject_dynamic_outliers, const double doppler_outlier_threshold, const std::size_t outlier_rejection_min_iteration, const std::size_t geometric_robust_loss_min_iteration, const std::size_t doppler_robust_loss_min_iteration, const registration::RobustKernel &geometric_kernel, const registration::RobustKernel &doppler_kernel)
Computes pose for DopplerICP registration method.
std::tuple< core::Tensor, core::Tensor > ComputeRtPointToPoint(const core::Tensor &source_points, const core::Tensor &target_points, const core::Tensor &correspondence_indices)
Computes (R) Rotation {3,3} and (t) translation {3,} for point to point registration method.
core::Tensor ComputePosePointToPlane(const core::Tensor &source_points, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &correspondence_indices, const registration::RobustKernel &kernel)
Computes pose for point to plane registration method.
void ComputePoseDopplerICPCPU(const core::Tensor &source_points, const core::Tensor &source_dopplers, const core::Tensor &source_directions, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &correspondence_indices, core::Tensor &output_pose, float &residual, int &inlier_count, const core::Dtype &dtype, const core::Device &device, const core::Tensor &R_S_to_V, const core::Tensor &r_v_to_s_in_V, const core::Tensor &w_v_in_V, const core::Tensor &v_v_in_V, const double period, const bool reject_dynamic_outliers, const double doppler_outlier_threshold, const registration::RobustKernel &kernel_geometric, const registration::RobustKernel &kernel_doppler, const double lambda_doppler)
void ComputeInformationMatrixCPU(const core::Tensor &target_points, const core::Tensor &correspondence_indices, core::Tensor &information_matrix, const core::Dtype &dtype, const core::Device &device)
core::Tensor ComputeInformationMatrix(const core::Tensor &target_points, const core::Tensor &correspondence_indices)
Computes Information Matrix of shape {6, 6}, of dtype Float64 on device CPU:0, from the target point ...
core::Tensor ComputePoseColoredICP(const core::Tensor &source_points, const core::Tensor &source_colors, const core::Tensor &target_points, const core::Tensor &target_normals, const core::Tensor &target_colors, const core::Tensor &target_color_gradients, const core::Tensor &correspondence_indices, const registration::RobustKernel &kernel, const double &lambda_geometric)
Computes pose for colored-icp registration method.
core::Tensor TransformationToPose(const core::Tensor &transformation)
Convert transformation matrix to pose.
Generic file read and write utility for python interface.