ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ManuallyAlignPointCloud.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 
10 
11 #include <thread>
12 
13 #include "VisualizerForAlignment.h"
14 
15 void PrintTransformation(const Eigen::Matrix4d &transformation) {
16  using namespace cloudViewer;
17  CVLib::utility::LogInfo("Current transformation is:");
18  CVLib::utility::LogInfo("\t{:.6f} {:.6f} {:.6f} {:.6f}",
19  transformation(0, 0), transformation(0, 1),
20  transformation(0, 2), transformation(0, 3));
21  CVLib::utility::LogInfo("\t{:.6f} {:.6f} {:.6f} {:.6f}",
22  transformation(1, 0), transformation(1, 1),
23  transformation(1, 2), transformation(1, 3));
24  CVLib::utility::LogInfo("\t{:.6f} {:.6f} {:.6f} {:.6f}",
25  transformation(2, 0), transformation(2, 1),
26  transformation(2, 2), transformation(2, 3));
27  CVLib::utility::LogInfo("\t{:.6f} {:.6f} {:.6f} {:.6f}",
28  transformation(3, 0), transformation(3, 1),
29  transformation(3, 2), transformation(3, 3));
30 }
31 
32 void PrintHelp() {
33  using namespace cloudViewer;
34  // clang-format off
35  utility::LogInfo("Usage:");
36  utility::LogInfo(" > ManuallyAlignPointCloud source_file target_file [options]");
37  utility::LogInfo(" Manually align point clouds in source_file and target_file.");
38  utility::LogInfo("");
39  utility::LogInfo("Options:");
40  utility::LogInfo(" --help, -h : Print help information.");
41  utility::LogInfo(" --verbose n : Set verbose level (0-4).");
42  utility::LogInfo(" --voxel_size d : Set downsample voxel size.");
43  utility::LogInfo(" --max_corres_distance d : Set max correspondence distance.");
44  utility::LogInfo(" --without_scaling : Disable scaling in transformations.");
45  utility::LogInfo(" --without_dialog : Disable dialogs. Default files will be used.");
46  utility::LogInfo(" --without_gui_icp file : The program runs as a console command. No window");
47  utility::LogInfo(" will be created. The program reads an alignment");
48  utility::LogInfo(" from file. It does cropping, downsample, ICP,");
49  utility::LogInfo(" then saves the alignment session into file.");
50  utility::LogInfo(" --without_gui_eval file : The program runs as a console command. No window");
51  utility::LogInfo(" will be created. The program reads an alignment");
52  utility::LogInfo(" from file. It does cropping, downsample,");
53  utility::LogInfo(" evaluation, then saves everything.");
54  // clang-format on
55 }
56 
57 int main(int argc, char **argv) {
58  using namespace cloudViewer;
59 
60  if (argc < 3 || utility::ProgramOptionExists(argc, argv, "--help") ||
61  utility::ProgramOptionExists(argc, argv, "-h")) {
62  PrintHelp();
63  return 0;
64  }
65 
66  int verbose = utility::GetProgramOptionAsInt(argc, argv, "--verbose", 2);
68  double voxel_size =
69  utility::GetProgramOptionAsDouble(argc, argv, "--voxel_size", -1.0);
70  double max_corres_distance = utility::GetProgramOptionAsDouble(
71  argc, argv, "--max_corres_distance", -1.0);
72  bool with_scaling =
73  !utility::ProgramOptionExists(argc, argv, "--without_scaling");
74  bool with_dialog =
75  !utility::ProgramOptionExists(argc, argv, "--without_dialog");
76  std::string default_polygon_filename =
78  std::string alignment_filename = utility::GetProgramOptionAsString(
79  argc, argv, "--without_gui_icp", "");
80  std::string eval_filename = utility::GetProgramOptionAsString(
81  argc, argv, "--without_gui_eval", "");
82  std::string default_directory =
84 
85  auto source_ptr = io::CreatePointCloudFromFile(argv[1]);
86  auto target_ptr = io::CreatePointCloudFromFile(argv[2]);
87  if (source_ptr == nullptr || target_ptr == nullptr ||
88  source_ptr->IsEmpty() || target_ptr->IsEmpty()) {
89  utility::LogWarning("Failed to read one of the point clouds.");
90  return 1;
91  }
92 
93  if (!alignment_filename.empty()) {
94  AlignmentSession session;
95  if (!io::ReadIJsonConvertible(alignment_filename, session)) {
96  return 0;
97  }
98  session.voxel_size_ = voxel_size;
99  session.max_correspondence_distance_ = max_corres_distance;
100  source_ptr->Transform(session.transformation_);
101  auto polygon_volume =
102  std::make_shared<visualization::SelectionPolygonVolume>();
103  if (io::ReadIJsonConvertible(default_polygon_filename,
104  *polygon_volume)) {
105  utility::LogInfo("Crop point cloud.");
106  source_ptr = polygon_volume->CropPointCloud(*source_ptr);
107  }
108  if (voxel_size > 0.0) {
109  utility::LogInfo("Downsample point cloud with voxel size {:.4f}.",
110  voxel_size);
111  source_ptr = source_ptr->VoxelDownSample(voxel_size);
112  }
113  if (max_corres_distance > 0.0) {
114  utility::LogInfo("ICP with max correspondence distance {:.4f}.",
115  max_corres_distance);
117  *source_ptr, *target_ptr, max_corres_distance,
118  Eigen::Matrix4d::Identity(),
119  pipelines::registration::
120  TransformationEstimationPointToPoint(true),
122  30));
124  "Registration finished with fitness {:.4f} and RMSE "
125  "{:.4f}.",
126  result.fitness_, result.inlier_rmse_);
127  if (result.fitness_ > 0.0) {
128  session.transformation_ =
129  result.transformation_ * session.transformation_;
131  source_ptr->Transform(result.transformation_);
132  }
133  }
134  io::WriteIJsonConvertible(alignment_filename, session);
135  return 1;
136  }
137 
138  if (!eval_filename.empty()) {
139  AlignmentSession session;
140  if (!io::ReadIJsonConvertible(eval_filename, session)) {
141  return 0;
142  }
143  source_ptr->Transform(session.transformation_);
144  auto polygon_volume =
145  std::make_shared<visualization::SelectionPolygonVolume>();
146  if (io::ReadIJsonConvertible(default_polygon_filename,
147  *polygon_volume)) {
148  utility::LogInfo("Crop point cloud.");
149  source_ptr = polygon_volume->CropPointCloud(*source_ptr);
150  }
151  if (voxel_size > 0.0) {
152  utility::LogInfo("Downsample point cloud with voxel size {:.4f}.",
153  voxel_size);
154  source_ptr = source_ptr->VoxelDownSample(voxel_size);
155  }
156  std::string source_filename =
158  eval_filename) +
159  ".source.ply";
160  std::string target_filename =
162  eval_filename) +
163  ".target.ply";
164  std::string source_binname =
166  eval_filename) +
167  ".source.bin";
168  std::string target_binname =
170  eval_filename) +
171  ".target.bin";
172  FILE *f;
173 
174  io::WritePointCloud(source_filename, *source_ptr);
175  auto source_dis = source_ptr->ComputePointCloudDistance(*target_ptr);
176  if ((f = utility::filesystem::FOpen(source_binname, "wb"))) {
177  fwrite(source_dis.data(), sizeof(double), source_dis.size(), f);
178  fclose(f);
179  } else {
180  utility::LogError("Failed to open {} for writing.", source_binname);
181  }
182 
183  io::WritePointCloud(target_filename, *target_ptr);
184  auto target_dis = target_ptr->ComputePointCloudDistance(*source_ptr);
185  if ((f = utility::filesystem::FOpen(target_binname, "wb"))) {
186  fwrite(target_dis.data(), sizeof(double), target_dis.size(), f);
187  fclose(f);
188  } else {
189  utility::LogError("Failed to open {} for writing.", target_binname);
190  }
191 
192  return 1;
193  }
194 
195  visualization::VisualizerWithEditing vis_source, vis_target;
196  VisualizerForAlignment vis_main(vis_source, vis_target, voxel_size,
197  max_corres_distance, with_scaling,
198  with_dialog, default_polygon_filename,
199  default_directory);
200 
201  vis_source.CreateVisualizerWindow("Source Point Cloud", 1280, 720, 10, 100);
202  vis_source.AddGeometry(source_ptr);
203  if (source_ptr->points_.size() > 5000000) {
204  vis_source.GetRenderOption().point_size_ = 1.0;
205  }
206  vis_source.BuildUtilities();
207  vis_target.CreateVisualizerWindow("Target Point Cloud", 1280, 720, 10, 880);
208  vis_target.AddGeometry(target_ptr);
209  if (target_ptr->points_.size() > 5000000) {
210  vis_target.GetRenderOption().point_size_ = 1.0;
211  }
212  vis_target.BuildUtilities();
213  vis_main.CreateVisualizerWindow("Alignment", 1280, 1440, 1300, 100);
214  vis_main.AddSourceAndTarget(source_ptr, target_ptr);
215  vis_main.BuildUtilities();
216 
217  while (vis_source.PollEvents() && vis_target.PollEvents() &&
218  vis_main.PollEvents()) {
219  }
220 
221  vis_source.DestroyVisualizerWindow();
222  vis_target.DestroyVisualizerWindow();
223  vis_main.DestroyVisualizerWindow();
224  return 0;
225 }
void PrintTransformation(const Eigen::Matrix4d &transformation)
int main(int argc, char **argv)
void PrintHelp()
core::Tensor result
Definition: VtkUtils.cpp:76
Eigen::Matrix4d_u transformation_
bool AddSourceAndTarget(std::shared_ptr< ccPointCloud > source, std::shared_ptr< ccPointCloud > target)
Class that defines the convergence criteria of ICP.
Definition: Registration.h:36
double point_size_
Point size for PointCloud.
Definition: RenderOption.h:178
bool AddGeometry(std::shared_ptr< const ccHObject > geometry_ptr, bool reset_bounding_box=true) override
Function to add geometry to the scene and create corresponding shaders.
RenderOption & GetRenderOption()
Function to retrieve the associated RenderOption.
Definition: Visualizer.h:177
void DestroyVisualizerWindow()
Function to destroy a window.
Definition: Visualizer.cpp:235
bool CreateVisualizerWindow(const std::string &window_name="CloudViewer", const int width=640, const int height=480, const int left=50, const int top=50, const bool visible=true)
Function to create a window and initialize GLFW.
Definition: Visualizer.cpp:95
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
bool WriteIJsonConvertible(const std::string &filename, const cloudViewer::utility::IJsonConvertible &object)
bool WritePointCloud(const std::string &filename, const ccPointCloud &pointcloud, const WritePointCloudOption &params)
std::shared_ptr< ccPointCloud > CreatePointCloudFromFile(const std::string &filename, const std::string &format, bool print_progress)
bool ReadIJsonConvertible(const std::string &filename, cloudViewer::utility::IJsonConvertible &object)
RegistrationResult RegistrationICP(const ccPointCloud &source, const ccPointCloud &target, double max_correspondence_distance, const Eigen::Matrix4d &init, const TransformationEstimation &estimation, const ICPConvergenceCriteria &criteria)
Functions for ICP registration.
std::string GetFileParentDirectory(const std::string &filename)
Definition: FileSystem.cpp:314
std::string GetFileNameWithoutExtension(const std::string &filename)
Definition: FileSystem.cpp:295
FILE * FOpen(const std::string &filename, const std::string &mode)
Definition: FileSystem.cpp:609
int GetProgramOptionAsInt(int argc, char **argv, const std::string &option, const int default_value=0)
Definition: Console.cpp:31
void SetVerbosityLevel(VerbosityLevel level)
Definition: Logging.cpp:89
bool ProgramOptionExists(int argc, char **argv, const std::string &option)
Definition: Console.cpp:100
std::string GetProgramOptionAsString(int argc, char **argv, const std::string &option, const std::string &default_value="")
Definition: Console.cpp:19
double GetProgramOptionAsDouble(int argc, char **argv, const std::string &option, const double default_value=0.0)
Definition: Console.cpp:52
Generic file read and write utility for python interface.