ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Odometry.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 
10 #include <Image.h>
11 #include <RGBDImage.h>
12 #include <Timer.h>
13 
14 #include <Eigen/Dense>
15 #include <memory>
16 
18 
19 namespace cloudViewer {
20 namespace pipelines {
21 namespace odometry {
22 
23 static std::tuple<geometry::Image, geometry::Image> InitializeCorrespondenceMap(
24  int width, int height) {
25  // initialization: filling with any (u,v) to (-1,-1)
26  geometry::Image correspondence_map;
27  geometry::Image depth_buffer;
28  correspondence_map.Prepare(width, height, 2, 4);
29  depth_buffer.Prepare(width, height, 1, 4);
30  for (int v = 0; v < correspondence_map.height_; v++) {
31  for (int u = 0; u < correspondence_map.width_; u++) {
32  *correspondence_map.PointerAt<int>(u, v, 0) = -1;
33  *correspondence_map.PointerAt<int>(u, v, 1) = -1;
34  *depth_buffer.PointerAt<float>(u, v, 0) = -1.0f;
35  }
36  }
37  return std::make_tuple(correspondence_map, depth_buffer);
38 }
39 
40 static inline void AddElementToCorrespondenceMap(
41  geometry::Image &correspondence_map,
42  geometry::Image &depth_buffer,
43  int u_s,
44  int v_s,
45  int u_t,
46  int v_t,
47  float transformed_d_t) {
48  int exist_u_t, exist_v_t;
49  double exist_d_t;
50  exist_u_t = *correspondence_map.PointerAt<int>(u_s, v_s, 0);
51  exist_v_t = *correspondence_map.PointerAt<int>(u_s, v_s, 1);
52  if (exist_u_t != -1 && exist_v_t != -1) {
53  exist_d_t = *depth_buffer.PointerAt<float>(u_s, v_s);
54  if (transformed_d_t <
55  exist_d_t) { // update nearer point as correspondence
56  *correspondence_map.PointerAt<int>(u_s, v_s, 0) = u_t;
57  *correspondence_map.PointerAt<int>(u_s, v_s, 1) = v_t;
58  *depth_buffer.PointerAt<float>(u_s, v_s) = transformed_d_t;
59  }
60  } else { // register correspondence
61  *correspondence_map.PointerAt<int>(u_s, v_s, 0) = u_t;
62  *correspondence_map.PointerAt<int>(u_s, v_s, 1) = v_t;
63  *depth_buffer.PointerAt<float>(u_s, v_s) = transformed_d_t;
64  }
65 }
66 
67 static void MergeCorrespondenceMaps(geometry::Image &correspondence_map,
68  geometry::Image &depth_buffer,
69  geometry::Image &correspondence_map_part,
70  geometry::Image &depth_buffer_part) {
71  for (int v_s = 0; v_s < correspondence_map.height_; v_s++) {
72  for (int u_s = 0; u_s < correspondence_map.width_; u_s++) {
73  int u_t = *correspondence_map_part.PointerAt<int>(u_s, v_s, 0);
74  int v_t = *correspondence_map_part.PointerAt<int>(u_s, v_s, 1);
75  if (u_t != -1 && v_t != -1) {
76  float transformed_d_t =
77  *depth_buffer_part.PointerAt<float>(u_s, v_s);
78  AddElementToCorrespondenceMap(correspondence_map, depth_buffer,
79  u_s, v_s, u_t, v_t,
80  transformed_d_t);
81  }
82  }
83  }
84 }
85 
86 static int CountCorrespondence(const geometry::Image &correspondence_map) {
87  int correspondence_count = 0;
88  for (int v_s = 0; v_s < correspondence_map.height_; v_s++) {
89  for (int u_s = 0; u_s < correspondence_map.width_; u_s++) {
90  int u_t = *correspondence_map.PointerAt<int>(u_s, v_s, 0);
91  int v_t = *correspondence_map.PointerAt<int>(u_s, v_s, 1);
92  if (u_t != -1 && v_t != -1) {
93  correspondence_count++;
94  }
95  }
96  }
97  return correspondence_count;
98 }
99 
101  const Eigen::Matrix3d intrinsic_matrix,
102  const Eigen::Matrix4d &extrinsic,
103  const geometry::Image &depth_s,
104  const geometry::Image &depth_t,
105  const OdometryOption &option) {
106  const Eigen::Matrix3d K = intrinsic_matrix;
107  const Eigen::Matrix3d K_inv = K.inverse();
108  const Eigen::Matrix3d R = extrinsic.block<3, 3>(0, 0);
109  const Eigen::Matrix3d KRK_inv = K * R * K_inv;
110  Eigen::Vector3d Kt = K * extrinsic.block<3, 1>(0, 3);
111 
112  geometry::Image correspondence_map;
113  geometry::Image depth_buffer;
114  std::tie(correspondence_map, depth_buffer) =
115  InitializeCorrespondenceMap(depth_t.width_, depth_t.height_);
116 
117 #pragma omp parallel
118  {
119  geometry::Image correspondence_map_private;
120  geometry::Image depth_buffer_private;
121  std::tie(correspondence_map_private, depth_buffer_private) =
122  InitializeCorrespondenceMap(depth_t.width_, depth_t.height_);
123 #pragma omp for nowait
124  for (int v_s = 0; v_s < depth_s.height_; v_s++) {
125  for (int u_s = 0; u_s < depth_s.width_; u_s++) {
126  double d_s = *depth_s.PointerAt<float>(u_s, v_s);
127  if (!std::isnan(d_s)) {
128  Eigen::Vector3d uv_in_s =
129  d_s * KRK_inv * Eigen::Vector3d(u_s, v_s, 1.0) + Kt;
130  double transformed_d_s = uv_in_s(2);
131  int u_t = (int)(uv_in_s(0) / transformed_d_s + 0.5);
132  int v_t = (int)(uv_in_s(1) / transformed_d_s + 0.5);
133  if (u_t >= 0 && u_t < depth_t.width_ && v_t >= 0 &&
134  v_t < depth_t.height_) {
135  double d_t = *depth_t.PointerAt<float>(u_t, v_t);
136  if (!std::isnan(d_t) &&
137  std::abs(transformed_d_s - d_t) <=
138  option.max_depth_diff_) {
140  correspondence_map_private,
141  depth_buffer_private, u_s, v_s, u_t, v_t,
142  (float)d_s);
143  }
144  }
145  }
146  }
147  }
148 #pragma omp critical(ComputeCorrespondence)
149  {
150  MergeCorrespondenceMaps(correspondence_map, depth_buffer,
151  correspondence_map_private,
152  depth_buffer_private);
153 
154  } // omp critical
155  } // omp parallel
156 
157  CorrespondenceSetPixelWise correspondence;
158  int correspondence_count = CountCorrespondence(correspondence_map);
159  correspondence.resize(correspondence_count);
160  int cnt = 0;
161  for (int v_s = 0; v_s < correspondence_map.height_; v_s++) {
162  for (int u_s = 0; u_s < correspondence_map.width_; u_s++) {
163  int u_t = *correspondence_map.PointerAt<int>(u_s, v_s, 0);
164  int v_t = *correspondence_map.PointerAt<int>(u_s, v_s, 1);
165  if (u_t != -1 && v_t != -1) {
166  Eigen::Vector4i pixel_correspondence(u_s, v_s, u_t, v_t);
167  correspondence[cnt] = pixel_correspondence;
168  cnt++;
169  }
170  }
171  }
172  return correspondence;
173 }
174 
175 static std::shared_ptr<geometry::Image> ConvertDepthImageToXYZImage(
176  const geometry::Image &depth, const Eigen::Matrix3d &intrinsic_matrix) {
177  auto image_xyz = std::make_shared<geometry::Image>();
178  if (depth.num_of_channels_ != 1 || depth.bytes_per_channel_ != 4) {
180  "[ConvertDepthImageToXYZImage] Unsupported image format.");
181  }
182  const double inv_fx = 1.0 / intrinsic_matrix(0, 0);
183  const double inv_fy = 1.0 / intrinsic_matrix(1, 1);
184  const double ox = intrinsic_matrix(0, 2);
185  const double oy = intrinsic_matrix(1, 2);
186  image_xyz->Prepare(depth.width_, depth.height_, 3, 4);
187 
188  for (int y = 0; y < image_xyz->height_; y++) {
189  for (int x = 0; x < image_xyz->width_; x++) {
190  float *px = image_xyz->PointerAt<float>(x, y, 0);
191  float *py = image_xyz->PointerAt<float>(x, y, 1);
192  float *pz = image_xyz->PointerAt<float>(x, y, 2);
193  float z = *depth.PointerAt<float>(x, y);
194  *px = (float)((x - ox) * z * inv_fx);
195  *py = (float)((y - oy) * z * inv_fy);
196  *pz = z;
197  }
198  }
199  return image_xyz;
200 }
201 
202 static std::vector<Eigen::Matrix3d> CreateCameraMatrixPyramid(
203  const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic,
204  int levels) {
205  std::vector<Eigen::Matrix3d> pyramid_camera_matrix;
206  pyramid_camera_matrix.reserve(levels);
207  for (int i = 0; i < levels; i++) {
208  Eigen::Matrix3d level_camera_matrix;
209  if (i == 0)
210  level_camera_matrix = pinhole_camera_intrinsic.intrinsic_matrix_;
211  else
212  level_camera_matrix = 0.5 * pyramid_camera_matrix[i - 1];
213  level_camera_matrix(2, 2) = 1.;
214  pyramid_camera_matrix.push_back(level_camera_matrix);
215  }
216  return pyramid_camera_matrix;
217 }
218 
220  const Eigen::Matrix4d &extrinsic,
221  const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic,
222  const geometry::Image &depth_s,
223  const geometry::Image &depth_t,
224  const OdometryOption &option) {
225  CorrespondenceSetPixelWise correspondence =
226  ComputeCorrespondence(pinhole_camera_intrinsic.intrinsic_matrix_,
227  extrinsic, depth_s, depth_t, option);
228 
229  auto xyz_t = ConvertDepthImageToXYZImage(
230  depth_t, pinhole_camera_intrinsic.intrinsic_matrix_);
231 
232  // write q^*
233  // see http://redwood-data.org/indoor/registration.html
234  // note: I comes first and q_skew is scaled by factor 2.
235  Eigen::Matrix6d GTG = Eigen::Matrix6d::Identity();
236 #pragma omp parallel
237  {
238  Eigen::Matrix6d GTG_private = Eigen::Matrix6d::Identity();
239  Eigen::Vector6d G_r_private = Eigen::Vector6d::Zero();
240 #pragma omp for nowait
241  for (int row = 0; row < int(correspondence.size()); row++) {
242  int u_t = correspondence[row](2);
243  int v_t = correspondence[row](3);
244  double x = *xyz_t->PointerAt<float>(u_t, v_t, 0);
245  double y = *xyz_t->PointerAt<float>(u_t, v_t, 1);
246  double z = *xyz_t->PointerAt<float>(u_t, v_t, 2);
247  G_r_private.setZero();
248  G_r_private(1) = z;
249  G_r_private(2) = -y;
250  G_r_private(3) = 1.0;
251  GTG_private.noalias() += G_r_private * G_r_private.transpose();
252  G_r_private.setZero();
253  G_r_private(0) = -z;
254  G_r_private(2) = x;
255  G_r_private(4) = 1.0;
256  GTG_private.noalias() += G_r_private * G_r_private.transpose();
257  G_r_private.setZero();
258  G_r_private(0) = y;
259  G_r_private(1) = -x;
260  G_r_private(5) = 1.0;
261  GTG_private.noalias() += G_r_private * G_r_private.transpose();
262  }
263 #pragma omp critical(CreateInformationMatrix)
264  { GTG += GTG_private; }
265  }
266  return GTG;
267 }
268 
269 static void NormalizeIntensity(
270  geometry::Image &image_s,
271  geometry::Image &image_t,
272  const CorrespondenceSetPixelWise &correspondence) {
273  if (image_s.width_ != image_t.width_ ||
274  image_s.height_ != image_t.height_) {
276  "[NormalizeIntensity] Size of two input images should be "
277  "same");
278  }
279  double mean_s = 0.0, mean_t = 0.0;
280  for (size_t row = 0; row < correspondence.size(); row++) {
281  int u_s = correspondence[row](0);
282  int v_s = correspondence[row](1);
283  int u_t = correspondence[row](2);
284  int v_t = correspondence[row](3);
285  mean_s += *image_s.PointerAt<float>(u_s, v_s);
286  mean_t += *image_t.PointerAt<float>(u_t, v_t);
287  }
288  mean_s /= (double)correspondence.size();
289  mean_t /= (double)correspondence.size();
290  image_s.LinearTransform(0.5 / mean_s, 0.0);
291  image_t.LinearTransform(0.5 / mean_t, 0.0);
292 }
293 
294 static inline std::shared_ptr<geometry::RGBDImage> PackRGBDImage(
295  const geometry::Image &color, const geometry::Image &depth) {
296  return std::make_shared<geometry::RGBDImage>(
297  geometry::RGBDImage(color, depth));
298 }
299 
300 static std::shared_ptr<geometry::Image> PreprocessDepth(
301  const geometry::Image &depth_orig, const OdometryOption &option) {
302  std::shared_ptr<geometry::Image> depth_processed =
303  std::make_shared<geometry::Image>();
304  *depth_processed = depth_orig;
305  for (int y = 0; y < depth_processed->height_; y++) {
306  for (int x = 0; x < depth_processed->width_; x++) {
307  float *p = depth_processed->PointerAt<float>(x, y);
308  if ((*p < option.min_depth_ || *p > option.max_depth_ || *p <= 0))
309  *p = std::numeric_limits<float>::quiet_NaN();
310  }
311  }
312  return depth_processed;
313 }
314 
315 static inline bool CheckImagePair(const geometry::Image &image_s,
316  const geometry::Image &image_t) {
317  return (image_s.width_ == image_t.width_ &&
318  image_s.height_ == image_t.height_);
319 }
320 
321 static inline bool IsColorImageRGB(const geometry::Image &image) {
322  return (image.num_of_channels_ == 3);
323 }
324 
325 static inline bool CheckRGBDImagePair(const geometry::RGBDImage &source,
326  const geometry::RGBDImage &target) {
327  if (IsColorImageRGB(source.color_) && IsColorImageRGB(target.color_)) {
328  return (CheckImagePair(source.color_, target.color_) &&
329  CheckImagePair(source.depth_, target.depth_) &&
330  CheckImagePair(source.color_, source.depth_) &&
331  CheckImagePair(target.color_, target.depth_) &&
332  CheckImagePair(source.color_, target.color_) &&
333  source.color_.num_of_channels_ == 3 &&
334  target.color_.num_of_channels_ == 3 &&
335  source.depth_.num_of_channels_ == 1 &&
336  target.depth_.num_of_channels_ == 1 &&
337  source.color_.bytes_per_channel_ == 1 &&
338  target.color_.bytes_per_channel_ == 1 &&
339  source.depth_.bytes_per_channel_ == 4 &&
340  target.depth_.bytes_per_channel_ == 4);
341  }
342  if (!IsColorImageRGB(source.color_) && !IsColorImageRGB(target.color_)) {
343  return (CheckImagePair(source.color_, target.color_) &&
344  CheckImagePair(source.depth_, target.depth_) &&
345  CheckImagePair(source.color_, source.depth_) &&
346  CheckImagePair(target.color_, target.depth_) &&
347  CheckImagePair(source.color_, target.color_) &&
348  source.color_.num_of_channels_ == 1 &&
349  source.depth_.num_of_channels_ == 1 &&
350  target.color_.num_of_channels_ == 1 &&
351  target.depth_.num_of_channels_ == 1 &&
352  source.color_.bytes_per_channel_ == 4 &&
353  target.color_.bytes_per_channel_ == 4 &&
354  source.depth_.bytes_per_channel_ == 4 &&
355  target.depth_.bytes_per_channel_ == 4);
356  }
357  return false;
358 }
359 
360 static std::tuple<std::shared_ptr<geometry::RGBDImage>,
361  std::shared_ptr<geometry::RGBDImage>>
363  const geometry::RGBDImage &source,
364  const geometry::RGBDImage &target,
365  const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic,
366  const Eigen::Matrix4d &odo_init,
367  const OdometryOption &option) {
368  std::shared_ptr<geometry::Image> source_color, target_color;
369  if (IsColorImageRGB(source.color_) && IsColorImageRGB(target.color_)) {
370  source_color = source.color_.CreateFloatImage();
371  target_color = target.color_.CreateFloatImage();
372  } else {
373  source_color = std::make_shared<geometry::Image>(source.color_);
374  target_color = std::make_shared<geometry::Image>(target.color_);
375  }
376 
377  auto source_gray =
378  source_color->Filter(geometry::Image::FilterType::Gaussian3);
379  auto target_gray =
380  target_color->Filter(geometry::Image::FilterType::Gaussian3);
381  auto source_depth_preprocessed = PreprocessDepth(source.depth_, option);
382  auto target_depth_preprocessed = PreprocessDepth(target.depth_, option);
383  auto source_depth = source_depth_preprocessed->Filter(
385  auto target_depth = target_depth_preprocessed->Filter(
387 
389  pinhole_camera_intrinsic.intrinsic_matrix_, odo_init, *source_depth,
390  *target_depth, option);
391  NormalizeIntensity(*source_gray, *target_gray, correspondence);
392 
393  auto source_out = PackRGBDImage(*source_gray, *source_depth);
394  auto target_out = PackRGBDImage(*target_gray, *target_depth);
395  return std::make_tuple(source_out, target_out);
396 }
397 
398 static std::tuple<bool, Eigen::Matrix4d> DoSingleIteration(
399  int iter,
400  int level,
401  const geometry::RGBDImage &source,
402  const geometry::RGBDImage &target,
403  const geometry::Image &source_xyz,
404  const geometry::RGBDImage &target_dx,
405  const geometry::RGBDImage &target_dy,
406  const Eigen::Matrix3d intrinsic,
407  const Eigen::Matrix4d &extrinsic_initial,
408  const RGBDOdometryJacobian &jacobian_method,
409  const OdometryOption &option) {
411  intrinsic, extrinsic_initial, source.depth_, target.depth_, option);
412  int corresps_count = (int)correspondence.size();
413 
414  auto f_lambda =
415  [&](int i,
416  std::vector<Eigen::Vector6d, utility::Vector6d_allocator> &J_r,
417  std::vector<double> &r, std::vector<double> &w) {
418  jacobian_method.ComputeJacobianAndResidual(
419  i, J_r, r, w, source, target, source_xyz, target_dx,
420  target_dy, intrinsic, extrinsic_initial,
421  correspondence);
422  };
423  utility::LogDebug("Iter : {:d}, Level : {:d}, ", iter, level);
424  Eigen::Matrix6d JTJ;
425  Eigen::Vector6d JTr;
426  double r2;
427  std::tie(JTJ, JTr, r2) =
428  utility::ComputeJTJandJTr<Eigen::Matrix6d, Eigen::Vector6d>(
429  f_lambda, corresps_count);
430 
431  bool is_success;
432  Eigen::Matrix4d extrinsic;
433  std::tie(is_success, extrinsic) =
435  if (!is_success) {
436  utility::LogWarning("[ComputeOdometry] no solution!");
437  return std::make_tuple(false, Eigen::Matrix4d::Identity());
438  } else {
439  return std::make_tuple(true, extrinsic);
440  }
441 }
442 
443 static std::tuple<bool, Eigen::Matrix4d> ComputeMultiscale(
444  const geometry::RGBDImage &source,
445  const geometry::RGBDImage &target,
446  const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic,
447  const Eigen::Matrix4d &extrinsic_initial,
448  const RGBDOdometryJacobian &jacobian_method,
449  const OdometryOption &option) {
450  std::vector<int> iter_counts = option.iteration_number_per_pyramid_level_;
451  int num_levels = (int)iter_counts.size();
452 
453  auto source_pyramid = source.CreatePyramid(num_levels);
454  auto target_pyramid = target.CreatePyramid(num_levels);
455  auto target_pyramid_dx = geometry::RGBDImage::FilterPyramid(
456  target_pyramid, geometry::Image::FilterType::Sobel3Dx);
457  auto target_pyramid_dy = geometry::RGBDImage::FilterPyramid(
458  target_pyramid, geometry::Image::FilterType::Sobel3Dy);
459 
460  Eigen::Matrix4d result_odo = extrinsic_initial.isZero()
461  ? Eigen::Matrix4d::Identity()
462  : extrinsic_initial;
463 
464  std::vector<Eigen::Matrix3d> pyramid_camera_matrix =
465  CreateCameraMatrixPyramid(pinhole_camera_intrinsic,
466  (int)iter_counts.size());
467 
468  for (int level = num_levels - 1; level >= 0; level--) {
469  const Eigen::Matrix3d level_camera_matrix =
470  pyramid_camera_matrix[level];
471 
472  auto source_xyz_level = ConvertDepthImageToXYZImage(
473  source_pyramid[level]->depth_, level_camera_matrix);
474  auto source_level = PackRGBDImage(source_pyramid[level]->color_,
475  source_pyramid[level]->depth_);
476  auto target_level = PackRGBDImage(target_pyramid[level]->color_,
477  target_pyramid[level]->depth_);
478  auto target_dx_level = PackRGBDImage(target_pyramid_dx[level]->color_,
479  target_pyramid_dx[level]->depth_);
480  auto target_dy_level = PackRGBDImage(target_pyramid_dy[level]->color_,
481  target_pyramid_dy[level]->depth_);
482 
483  for (int iter = 0; iter < iter_counts[num_levels - level - 1]; iter++) {
484  Eigen::Matrix4d curr_odo;
485  bool is_success;
486  std::tie(is_success, curr_odo) = DoSingleIteration(
487  iter, level, *source_level, *target_level,
488  *source_xyz_level, *target_dx_level, *target_dy_level,
489  level_camera_matrix, result_odo, jacobian_method, option);
490  result_odo = curr_odo * result_odo;
491 
492  if (!is_success) {
493  utility::LogWarning("[ComputeOdometry] no solution!");
494  return std::make_tuple(false, Eigen::Matrix4d::Identity());
495  }
496  }
497  }
498  return std::make_tuple(true, result_odo);
499 }
500 
501 std::tuple<bool, Eigen::Matrix4d, Eigen::Matrix6d> ComputeRGBDOdometry(
502  const geometry::RGBDImage &source,
503  const geometry::RGBDImage &target,
504  const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic
505  /*= camera::PinholeCameraIntrinsic()*/,
506  const Eigen::Matrix4d &odo_init /*= Eigen::Matrix4d::Identity()*/,
507  const RGBDOdometryJacobian &jacobian_method
508  /*=RGBDOdometryJacobianFromHybridTerm*/,
509  const OdometryOption &option /*= OdometryOption()*/) {
510  if (!CheckRGBDImagePair(source, target)) {
512  "[RGBDOdometry] Two RGBD pairs should be same in size.");
513  return std::make_tuple(false, Eigen::Matrix4d::Identity(),
514  Eigen::Matrix6d::Zero());
515  }
516 
517  std::shared_ptr<geometry::RGBDImage> source_processed, target_processed;
518  std::tie(source_processed, target_processed) = InitializeRGBDOdometry(
519  source, target, pinhole_camera_intrinsic, odo_init, option);
520 
521  Eigen::Matrix4d extrinsic;
522  bool is_success;
523  std::tie(is_success, extrinsic) = ComputeMultiscale(
524  *source_processed, *target_processed, pinhole_camera_intrinsic,
525  odo_init, jacobian_method, option);
526 
527  if (is_success) {
528  Eigen::Matrix4d trans_output = extrinsic;
529  Eigen::MatrixXd info_output = CreateInformationMatrix(
530  extrinsic, pinhole_camera_intrinsic, source_processed->depth_,
531  target_processed->depth_, option);
532  return std::make_tuple(true, trans_output, info_output);
533  } else {
534  return std::make_tuple(false, Eigen::Matrix4d::Identity(),
535  Eigen::Matrix6d::Identity());
536  }
537 }
538 
539 } // namespace odometry
540 } // namespace pipelines
541 } // namespace cloudViewer
std::shared_ptr< core::Tensor > image
int width
int height
math::float4 color
Contains the pinhole camera intrinsic parameters.
The Image class stores image with customizable width, height, num of channels and bytes per channel.
Definition: Image.h:33
std::shared_ptr< Image > CreateFloatImage(Image::ColorToIntensityConversionType type=Image::ColorToIntensityConversionType::Weighted) const
Return a gray scaled float type image.
Image & Prepare(int width, int height, int num_of_channels, int bytes_per_channel)
Prepare Image properties and allocate Image buffer.
Definition: Image.h:109
int num_of_channels_
Number of chanels in the image.
Definition: Image.h:223
int height_
Height of the image.
Definition: Image.h:221
int bytes_per_channel_
Number of bytes per channel.
Definition: Image.h:225
int width_
Width of the image.
Definition: Image.h:219
@ Gaussian3
Gaussian filter of size 3 x 3.
@ Sobel3Dx
Sobel filter along X-axis.
@ Sobel3Dy
Sobel filter along Y-axis.
Image & LinearTransform(double scale=1.0, double offset=0.0)
T * PointerAt(int u, int v) const
Function to access the raw data of a single-channel Image.
RGBDImage is for a pair of registered color and depth images,.
Definition: RGBDImage.h:27
Image color_
The color image.
Definition: RGBDImage.h:135
RGBDImagePyramid CreatePyramid(size_t num_of_levels, bool with_gaussian_filter_for_color=true, bool with_gaussian_filter_for_depth=false) const
Image depth_
The depth image.
Definition: RGBDImage.h:137
static RGBDImagePyramid FilterPyramid(const RGBDImagePyramid &rgbd_image_pyramid, Image::FilterType type)
double max_depth_
Pixels that has larger than specified depth values are ignored.
Base class that computes Jacobian from two RGB-D images.
virtual void ComputeJacobianAndResidual(int row, std::vector< Eigen::Vector6d, utility::Vector6d_allocator > &J_r, std::vector< double > &r, std::vector< double > &w, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, const geometry::RGBDImage &target_dx, const geometry::RGBDImage &target_dy, const Eigen::Matrix3d &intrinsic, const Eigen::Matrix4d &extrinsic, const CorrespondenceSetPixelWise &corresps) const =0
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
Eigen::Matrix< double, 6, 1 > Vector6d
Definition: Eigen.h:107
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
Definition: Eigen.h:106
static std::shared_ptr< geometry::RGBDImage > PackRGBDImage(const geometry::Image &color, const geometry::Image &depth)
Definition: Odometry.cpp:294
static void AddElementToCorrespondenceMap(geometry::Image &correspondence_map, geometry::Image &depth_buffer, int u_s, int v_s, int u_t, int v_t, float transformed_d_t)
Definition: Odometry.cpp:40
std::tuple< bool, Eigen::Matrix4d, Eigen::Matrix6d > ComputeRGBDOdometry(const geometry::RGBDImage &source, const geometry::RGBDImage &target, const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, const Eigen::Matrix4d &odo_init, const RGBDOdometryJacobian &jacobian_method, const OdometryOption &option)
Function to estimate 6D rigid motion from two RGBD image pairs.
Definition: Odometry.cpp:501
static void NormalizeIntensity(geometry::Image &image_s, geometry::Image &image_t, const CorrespondenceSetPixelWise &correspondence)
Definition: Odometry.cpp:269
static std::tuple< std::shared_ptr< geometry::RGBDImage >, std::shared_ptr< geometry::RGBDImage > > InitializeRGBDOdometry(const geometry::RGBDImage &source, const geometry::RGBDImage &target, const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, const Eigen::Matrix4d &odo_init, const OdometryOption &option)
Definition: Odometry.cpp:362
static void MergeCorrespondenceMaps(geometry::Image &correspondence_map, geometry::Image &depth_buffer, geometry::Image &correspondence_map_part, geometry::Image &depth_buffer_part)
Definition: Odometry.cpp:67
static Eigen::Matrix6d CreateInformationMatrix(const Eigen::Matrix4d &extrinsic, const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, const geometry::Image &depth_s, const geometry::Image &depth_t, const OdometryOption &option)
Definition: Odometry.cpp:219
static std::tuple< geometry::Image, geometry::Image > InitializeCorrespondenceMap(int width, int height)
Definition: Odometry.cpp:23
static std::shared_ptr< geometry::Image > ConvertDepthImageToXYZImage(const geometry::Image &depth, const Eigen::Matrix3d &intrinsic_matrix)
Definition: Odometry.cpp:175
static std::shared_ptr< geometry::Image > PreprocessDepth(const geometry::Image &depth_orig, const OdometryOption &option)
Definition: Odometry.cpp:300
static std::tuple< bool, Eigen::Matrix4d > ComputeMultiscale(const geometry::RGBDImage &source, const geometry::RGBDImage &target, const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, const Eigen::Matrix4d &extrinsic_initial, const RGBDOdometryJacobian &jacobian_method, const OdometryOption &option)
Definition: Odometry.cpp:443
static bool CheckRGBDImagePair(const geometry::RGBDImage &source, const geometry::RGBDImage &target)
Definition: Odometry.cpp:325
static std::tuple< bool, Eigen::Matrix4d > DoSingleIteration(int iter, int level, const geometry::RGBDImage &source, const geometry::RGBDImage &target, const geometry::Image &source_xyz, const geometry::RGBDImage &target_dx, const geometry::RGBDImage &target_dy, const Eigen::Matrix3d intrinsic, const Eigen::Matrix4d &extrinsic_initial, const RGBDOdometryJacobian &jacobian_method, const OdometryOption &option)
Definition: Odometry.cpp:398
std::vector< Eigen::Vector4i, utility::Vector4i_allocator > CorrespondenceSetPixelWise
static std::vector< Eigen::Matrix3d > CreateCameraMatrixPyramid(const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, int levels)
Definition: Odometry.cpp:202
static bool CheckImagePair(const geometry::Image &image_s, const geometry::Image &image_t)
Definition: Odometry.cpp:315
static bool IsColorImageRGB(const geometry::Image &image)
Definition: Odometry.cpp:321
static CorrespondenceSetPixelWise ComputeCorrespondence(const Eigen::Matrix3d intrinsic_matrix, const Eigen::Matrix4d &extrinsic, const geometry::Image &depth_s, const geometry::Image &depth_t, const OdometryOption &option)
Definition: Odometry.cpp:100
static int CountCorrespondence(const geometry::Image &correspondence_map)
Definition: Odometry.cpp:86
std::tuple< bool, Eigen::Matrix4d > SolveJacobianSystemAndObtainExtrinsicMatrix(const Eigen::Matrix6d &JTJ, const Eigen::Vector6d &JTr)
Definition: Eigen.cpp:105
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 4, 1 > Vector4i
Definition: knncpp.h:31