14 #include <Eigen/Dense>
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;
37 return std::make_tuple(correspondence_map, depth_buffer);
47 float transformed_d_t) {
48 int exist_u_t, exist_v_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);
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;
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;
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);
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++;
97 return correspondence_count;
101 const Eigen::Matrix3d intrinsic_matrix,
102 const Eigen::Matrix4d &extrinsic,
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);
114 std::tie(correspondence_map, depth_buffer) =
121 std::tie(correspondence_map_private, depth_buffer_private) =
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 &&
135 double d_t = *depth_t.
PointerAt<
float>(u_t, v_t);
136 if (!std::isnan(d_t) &&
140 correspondence_map_private,
141 depth_buffer_private, u_s, v_s, u_t, v_t,
148 #pragma omp critical(ComputeCorrespondence)
151 correspondence_map_private,
152 depth_buffer_private);
159 correspondence.resize(correspondence_count);
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) {
167 correspondence[cnt] = pixel_correspondence;
172 return correspondence;
176 const geometry::Image &depth,
const Eigen::Matrix3d &intrinsic_matrix) {
177 auto image_xyz = std::make_shared<geometry::Image>();
180 "[ConvertDepthImageToXYZImage] Unsupported image format.");
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);
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);
194 *px = (float)((x - ox) * z * inv_fx);
195 *
py = (float)((y - oy) * z * inv_fy);
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;
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);
216 return pyramid_camera_matrix;
220 const Eigen::Matrix4d &extrinsic,
227 extrinsic, depth_s, depth_t, option);
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();
250 G_r_private(3) = 1.0;
251 GTG_private.noalias() += G_r_private * G_r_private.transpose();
252 G_r_private.setZero();
255 G_r_private(4) = 1.0;
256 GTG_private.noalias() += G_r_private * G_r_private.transpose();
257 G_r_private.setZero();
260 G_r_private(5) = 1.0;
261 GTG_private.noalias() += G_r_private * G_r_private.transpose();
263 #pragma omp critical(CreateInformationMatrix)
264 { GTG += GTG_private; }
276 "[NormalizeIntensity] Size of two input images should be "
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);
288 mean_s /= (double)correspondence.size();
289 mean_t /= (double)correspondence.size();
296 return std::make_shared<geometry::RGBDImage>(
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();
312 return depth_processed;
322 return (
image.num_of_channels_ == 3);
360 static std::tuple<std::shared_ptr<geometry::RGBDImage>,
361 std::shared_ptr<geometry::RGBDImage>>
366 const Eigen::Matrix4d &odo_init,
368 std::shared_ptr<geometry::Image> source_color, target_color;
373 source_color = std::make_shared<geometry::Image>(source.
color_);
374 target_color = std::make_shared<geometry::Image>(target.
color_);
383 auto source_depth = source_depth_preprocessed->Filter(
385 auto target_depth = target_depth_preprocessed->Filter(
390 *target_depth, option);
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);
406 const Eigen::Matrix3d intrinsic,
407 const Eigen::Matrix4d &extrinsic_initial,
411 intrinsic, extrinsic_initial, source.
depth_, target.
depth_, option);
412 int corresps_count = (int)correspondence.size();
416 std::vector<Eigen::Vector6d, utility::Vector6d_allocator> &J_r,
417 std::vector<double> &r, std::vector<double> &w) {
419 i, J_r, r, w, source, target, source_xyz, target_dx,
420 target_dy, intrinsic, extrinsic_initial,
427 std::tie(JTJ, JTr, r2) =
428 utility::ComputeJTJandJTr<Eigen::Matrix6d, Eigen::Vector6d>(
429 f_lambda, corresps_count);
432 Eigen::Matrix4d extrinsic;
433 std::tie(is_success, extrinsic) =
437 return std::make_tuple(
false, Eigen::Matrix4d::Identity());
439 return std::make_tuple(
true, extrinsic);
447 const Eigen::Matrix4d &extrinsic_initial,
451 int num_levels = (int)iter_counts.size();
460 Eigen::Matrix4d result_odo = extrinsic_initial.isZero()
461 ? Eigen::Matrix4d::Identity()
464 std::vector<Eigen::Matrix3d> pyramid_camera_matrix =
466 (
int)iter_counts.size());
468 for (
int level = num_levels - 1; level >= 0; level--) {
469 const Eigen::Matrix3d level_camera_matrix =
470 pyramid_camera_matrix[level];
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_);
483 for (
int iter = 0; iter < iter_counts[num_levels - level - 1]; iter++) {
484 Eigen::Matrix4d curr_odo;
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;
494 return std::make_tuple(
false, Eigen::Matrix4d::Identity());
498 return std::make_tuple(
true, result_odo);
506 const Eigen::Matrix4d &odo_init ,
512 "[RGBDOdometry] Two RGBD pairs should be same in size.");
513 return std::make_tuple(
false, Eigen::Matrix4d::Identity(),
514 Eigen::Matrix6d::Zero());
517 std::shared_ptr<geometry::RGBDImage> source_processed, target_processed;
519 source, target, pinhole_camera_intrinsic, odo_init, option);
521 Eigen::Matrix4d extrinsic;
524 *source_processed, *target_processed, pinhole_camera_intrinsic,
525 odo_init, jacobian_method, option);
528 Eigen::Matrix4d trans_output = extrinsic;
530 extrinsic, pinhole_camera_intrinsic, source_processed->depth_,
531 target_processed->depth_, option);
532 return std::make_tuple(
true, trans_output, info_output);
534 return std::make_tuple(
false, Eigen::Matrix4d::Identity(),
535 Eigen::Matrix6d::Identity());
std::shared_ptr< core::Tensor > image
Contains the pinhole camera intrinsic parameters.
Eigen::Matrix3d intrinsic_matrix_
The Image class stores image with customizable width, height, num of channels and bytes per channel.
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.
int num_of_channels_
Number of chanels in the image.
int height_
Height of the image.
int bytes_per_channel_
Number of bytes per channel.
int width_
Width of the image.
@ 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,.
Image color_
The color image.
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.
static RGBDImagePyramid FilterPyramid(const RGBDImagePyramid &rgbd_image_pyramid, Image::FilterType type)
std::vector< int > iteration_number_per_pyramid_level_
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
__host__ __device__ int2 abs(int2 v)
Eigen::Matrix< double, 6, 1 > Vector6d
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
static std::shared_ptr< geometry::RGBDImage > PackRGBDImage(const geometry::Image &color, const geometry::Image &depth)
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)
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.
static void NormalizeIntensity(geometry::Image &image_s, geometry::Image &image_t, const CorrespondenceSetPixelWise &correspondence)
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)
static void MergeCorrespondenceMaps(geometry::Image &correspondence_map, geometry::Image &depth_buffer, geometry::Image &correspondence_map_part, geometry::Image &depth_buffer_part)
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)
static std::tuple< geometry::Image, geometry::Image > InitializeCorrespondenceMap(int width, int height)
static std::shared_ptr< geometry::Image > ConvertDepthImageToXYZImage(const geometry::Image &depth, const Eigen::Matrix3d &intrinsic_matrix)
static std::shared_ptr< geometry::Image > PreprocessDepth(const geometry::Image &depth_orig, const OdometryOption &option)
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)
static bool CheckRGBDImagePair(const geometry::RGBDImage &source, const geometry::RGBDImage &target)
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)
std::vector< Eigen::Vector4i, utility::Vector4i_allocator > CorrespondenceSetPixelWise
static std::vector< Eigen::Matrix3d > CreateCameraMatrixPyramid(const camera::PinholeCameraIntrinsic &pinhole_camera_intrinsic, int levels)
static bool CheckImagePair(const geometry::Image &image_s, const geometry::Image &image_t)
static bool IsColorImageRGB(const geometry::Image &image)
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)
static int CountCorrespondence(const geometry::Image &correspondence_map)
std::tuple< bool, Eigen::Matrix4d > SolveJacobianSystemAndObtainExtrinsicMatrix(const Eigen::Matrix6d &JTJ, const Eigen::Vector6d &JTr)
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 4, 1 > Vector4i