ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
RigidOptimizer.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 <FileSystem.h>
11 #include <ImageIO.h>
12 #include <Optional.h>
13 #include <Parallel.h>
14 #include <PinholeCameraTrajectoryIO.h>
15 
16 #include <memory>
17 #include <vector>
18 
19 #include "io/TriangleMeshIO.h"
22 
23 namespace cloudViewer {
24 namespace pipelines {
25 namespace color_map {
26 
32  int row,
33  Eigen::Vector6d& J_r,
34  double& r,
35  double& w,
36  const ccMesh& mesh,
37  const std::vector<double>& proxy_intensity,
38  const geometry::Image& images_gray,
39  const geometry::Image& images_dx,
40  const geometry::Image& images_dy,
41  const Eigen::Matrix4d& intrinsic,
42  const Eigen::Matrix4d& extrinsic,
43  const std::vector<int>& visibility_image_to_vertex,
44  const int image_boundary_margin) {
45  J_r.setZero();
46  r = 0;
47  int vid = visibility_image_to_vertex[row];
48  Eigen::Vector3d x = mesh.getVertice(vid);
49  Eigen::Vector4d g = extrinsic * Eigen::Vector4d(x(0), x(1), x(2), 1);
50  Eigen::Vector4d uv = intrinsic * g;
51  double u = uv(0) / uv(2);
52  double v = uv(1) / uv(2);
53  if (!images_gray.TestImageBoundary(u, v, image_boundary_margin)) return;
54  bool valid;
55  double gray, dIdx, dIdy;
56  std::tie(valid, gray) = images_gray.FloatValueAt(u, v);
57  std::tie(valid, dIdx) = images_dx.FloatValueAt(u, v);
58  std::tie(valid, dIdy) = images_dy.FloatValueAt(u, v);
59  if (gray == -1.0) return;
60  double invz = 1. / g(2);
61  double v0 = dIdx * intrinsic(0, 0) * invz;
62  double v1 = dIdy * intrinsic(1, 1) * invz;
63  double v2 = -(v0 * g(0) + v1 * g(1)) * invz;
64  J_r(0) = (-g(2) * v1 + g(1) * v2);
65  J_r(1) = (g(2) * v0 - g(0) * v2);
66  J_r(2) = (-g(1) * v0 + g(0) * v1);
67  J_r(3) = v0;
68  J_r(4) = v1;
69  J_r(5) = v2;
70  r = (gray - proxy_intensity[vid]);
71  w = 1.0; // Dummy.
72 }
73 
74 std::pair<ccMesh, camera::PinholeCameraTrajectory> RunRigidOptimizer(
75  const ccMesh& mesh,
76  const std::vector<geometry::RGBDImage>& images_rgbd,
77  const camera::PinholeCameraTrajectory& camera_trajectory,
78  const RigidOptimizerOption& option) {
79  // The following properties will change during optimization.
80  ccMesh opt_mesh = mesh;
81  camera::PinholeCameraTrajectory opt_camera_trajectory = camera_trajectory;
82 
83  // The following properties remain unchanged during optimization.
84  std::vector<geometry::Image> images_gray;
85  std::vector<geometry::Image> images_dx;
86  std::vector<geometry::Image> images_dy;
87  std::vector<geometry::Image> images_color;
88  std::vector<geometry::Image> images_depth;
89  std::vector<geometry::Image> images_mask;
90  std::vector<std::vector<int>> visibility_vertex_to_image;
91  std::vector<std::vector<int>> visibility_image_to_vertex;
92 
93  // Create all debugging directories. We don't delete any existing files but
94  // will overwrite them if the names are the same.
95  if (!option.debug_output_dir_.empty()) {
96  std::vector<std::string> dirs{
97  option.debug_output_dir_, option.debug_output_dir_ + "/rigid",
98  option.debug_output_dir_ + "/rigid/images_mask",
99  option.debug_output_dir_ + "/rigid/opt_mesh",
100  option.debug_output_dir_ + "/rigid/opt_camera_trajectory"};
101  for (const std::string& dir : dirs) {
103  cloudViewer::utility::LogInfo("Directory exists: {}.", dir);
104  } else {
106  dir)) {
107  cloudViewer::utility::LogInfo("Directory created: {}.",
108  dir);
109  } else {
111  "Making directory failed: {}.", dir);
112  }
113  }
114  }
115  }
116 
118  "[ColorMapOptimization] CreateUtilImagesFromRGBD");
119  std::tie(images_gray, images_dx, images_dy, images_color, images_depth) =
120  CreateUtilImagesFromRGBD(images_rgbd);
121 
123  "[ColorMapOptimization] CreateDepthBoundaryMasks");
124  images_mask = CreateDepthBoundaryMasks(
125  images_depth, option.depth_threshold_for_discontinuity_check_,
127  if (!option.debug_output_dir_.empty()) {
128  for (size_t i = 0; i < images_mask.size(); ++i) {
129  std::string file_name = fmt::format(
130  "{}/{}.png",
131  option.debug_output_dir_ + "/rigid/images_mask", i);
132  io::WriteImage(file_name, images_mask[i]);
133  }
134  }
135 
137  "[ColorMapOptimization] CreateVertexAndImageVisibility");
138  std::tie(visibility_vertex_to_image, visibility_image_to_vertex) =
140  opt_mesh, images_depth, images_mask, opt_camera_trajectory,
143 
144  cloudViewer::utility::LogDebug("[ColorMapOptimization] Rigid Optimization");
145  std::vector<double> proxy_intensity;
146  int total_num_ = 0;
147  int n_camera = int(opt_camera_trajectory.parameters_.size());
149  opt_mesh, images_gray, cloudViewer::utility::nullopt,
150  opt_camera_trajectory, visibility_vertex_to_image, proxy_intensity,
151  option.image_boundary_margin_);
152  for (int itr = 0; itr < option.maximum_iteration_; itr++) {
153  cloudViewer::utility::LogDebug("[Iteration {:04d}] ", itr + 1);
154  double residual = 0.0;
155  total_num_ = 0;
156 #pragma omp parallel for schedule(static) \
157  num_threads(utility::EstimateMaxThreads())
158  for (int c = 0; c < n_camera; c++) {
159  Eigen::Matrix4d pose;
160  pose = opt_camera_trajectory.parameters_[c].extrinsic_;
161 
162  auto intrinsic = opt_camera_trajectory.parameters_[c]
163  .intrinsic_.intrinsic_matrix_;
164  auto extrinsic = opt_camera_trajectory.parameters_[c].extrinsic_;
165  Eigen::Matrix4d intr = Eigen::Matrix4d::Zero();
166  intr.block<3, 3>(0, 0) = intrinsic;
167  intr(3, 3) = 1.0;
168 
169  auto f_lambda = [&](int i, Eigen::Vector6d& J_r, double& r,
170  double& w) {
171  w = 1.0; // Dummy.
173  i, J_r, r, w, opt_mesh, proxy_intensity, images_gray[c],
174  images_dx[c], images_dy[c], intr, extrinsic,
175  visibility_image_to_vertex[c],
176  option.image_boundary_margin_);
177  };
178  Eigen::Matrix6d JTJ;
179  Eigen::Vector6d JTr;
180  double r2;
181  std::tie(JTJ, JTr, r2) =
184  f_lambda, int(visibility_image_to_vertex[c].size()),
185  false);
186 
187  bool is_success;
188  Eigen::Matrix4d delta;
189  std::tie(is_success, delta) = cloudViewer::utility::
191  pose = delta * pose;
192  opt_camera_trajectory.parameters_[c].extrinsic_ = pose;
193 #pragma omp critical(RunRigidOptimizer)
194  {
195  residual += r2;
196  total_num_ += int(visibility_image_to_vertex[c].size());
197  }
198  }
199  cloudViewer::utility::LogDebug("Residual error : {:.6f} (avg : {:.6f})",
200  residual, residual / total_num_);
202  opt_mesh, images_gray, cloudViewer::utility::nullopt,
203  opt_camera_trajectory, visibility_vertex_to_image,
204  proxy_intensity, option.image_boundary_margin_);
205 
206  if (!option.debug_output_dir_.empty()) {
207  // Save opt_mesh.
209  opt_mesh, images_color, cloudViewer::utility::nullopt,
210  opt_camera_trajectory, visibility_vertex_to_image,
211  option.image_boundary_margin_,
213  std::string file_name = fmt::format(
214  "{}/iter_{}.ply",
215  option.debug_output_dir_ + "/rigid/opt_mesh", itr);
216  io::WriteTriangleMesh(file_name, opt_mesh);
217 
218  // Save opt_camera_trajectory.
219  file_name = fmt::format(
220  "{}/iter_{}.json",
221  option.debug_output_dir_ + "/rigid/opt_camera_trajectory",
222  itr);
223  io::WritePinholeCameraTrajectory(file_name, opt_camera_trajectory);
224  }
225  }
226 
227  cloudViewer::utility::LogDebug("[ColorMapOptimization] Set Mesh Color");
229  opt_mesh, images_color, cloudViewer::utility::nullopt,
230  opt_camera_trajectory, visibility_vertex_to_image,
232 
233  return std::make_pair(opt_mesh, opt_camera_trajectory);
234 }
235 
236 } // namespace color_map
237 } // namespace pipelines
238 } // namespace cloudViewer
filament::Texture::InternalFormat format
int size
math::float2 uv
Triangular mesh.
Definition: ecvMesh.h:35
Eigen::Vector3d getVertice(size_t index) const
std::vector< PinholeCameraParameters > parameters_
List of PinholeCameraParameters objects.
The Image class stores image with customizable width, height, num of channels and bytes per channel.
Definition: Image.h:33
bool TestImageBoundary(double u, double v, double inner_margin=0.0) const
Test if coordinate (u, v) is located in the inner_marge of the image.
std::pair< bool, double > FloatValueAt(double u, double v) const
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
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
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
bool WriteTriangleMesh(const std::string &filename, const ccMesh &mesh, bool write_ascii, bool compressed, bool write_vertex_normals, bool write_vertex_colors, bool write_triangle_uvs, bool print_progress)
bool WriteImage(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
bool WritePinholeCameraTrajectory(const std::string &filename, const camera::PinholeCameraTrajectory &trajectory)
void SetProxyIntensityForVertex(const ccMesh &mesh, const std::vector< geometry::Image > &images_gray, const utility::optional< std::vector< ImageWarpingField >> &warping_fields, const camera::PinholeCameraTrajectory &camera_trajectory, const std::vector< std::vector< int >> &visibility_vertex_to_image, std::vector< double > &proxy_intensity, int image_boundary_margin)
std::pair< ccMesh, camera::PinholeCameraTrajectory > RunRigidOptimizer(const ccMesh &mesh, const std::vector< geometry::RGBDImage > &images_rgbd, const camera::PinholeCameraTrajectory &camera_trajectory, const RigidOptimizerOption &option)
std::tuple< std::vector< std::vector< int > >, std::vector< std::vector< int > > > CreateVertexAndImageVisibility(const ccMesh &mesh, const std::vector< geometry::Image > &images_depth, const std::vector< geometry::Image > &images_mask, const camera::PinholeCameraTrajectory &camera_trajectory, double maximum_allowable_depth, double depth_threshold_for_visibility_check)
std::tuple< std::vector< geometry::Image >, std::vector< geometry::Image >, std::vector< geometry::Image >, std::vector< geometry::Image >, std::vector< geometry::Image > > CreateUtilImagesFromRGBD(const std::vector< geometry::RGBDImage > &images_rgbd)
std::vector< geometry::Image > CreateDepthBoundaryMasks(const std::vector< geometry::Image > &images_depth, double depth_threshold_for_discontinuity_check, int half_dilation_kernel_size_for_discontinuity_map)
void SetGeometryColorAverage(ccMesh &mesh, const std::vector< geometry::Image > &images_color, const utility::optional< std::vector< ImageWarpingField >> &warping_fields, const camera::PinholeCameraTrajectory &camera_trajectory, const std::vector< std::vector< int >> &visibility_vertex_to_image, int image_boundary_margin, int invisible_vertex_color_knn)
static void ComputeJacobianAndResidualRigid(int row, Eigen::Vector6d &J_r, double &r, double &w, const ccMesh &mesh, const std::vector< double > &proxy_intensity, const geometry::Image &images_gray, const geometry::Image &images_dx, const geometry::Image &images_dy, const Eigen::Matrix4d &intrinsic, const Eigen::Matrix4d &extrinsic, const std::vector< int > &visibility_image_to_vertex, const int image_boundary_margin)
bool MakeDirectoryHierarchy(const std::string &directory)
Definition: FileSystem.cpp:499
bool DirectoryExists(const std::string &directory)
Definition: FileSystem.cpp:473
std::tuple< bool, Eigen::Matrix4d > SolveJacobianSystemAndObtainExtrinsicMatrix(const Eigen::Matrix6d &JTJ, const Eigen::Vector6d &JTr)
Definition: Eigen.cpp:105
constexpr nullopt_t nullopt
Definition: Optional.h:136
std::tuple< MatType, VecType, double > ComputeJTJandJTr(std::function< void(int, VecType &, double &, double &)> f, int iteration_num, bool verbose=true)
Definition: Eigen.cpp:148
Generic file read and write utility for python interface.
int maximum_iteration_
Number of iterations for optimization steps.