ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ConvertPointCloud.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 
8 #include <limits>
9 
10 #include "CloudViewer.h"
11 
12 void PrintHelp() {
13  using namespace cloudViewer;
14  PrintCloudViewerVersion();
15  // clang-format off
16  utility::LogInfo("Usage:");
17  utility::LogInfo(" > ConvertPointCloud source_file target_file [options]");
18  utility::LogInfo(" > ConvertPointCloud source_directory target_directory [options]");
19  utility::LogInfo(" Read point cloud from source file and convert it to target file.");
20  utility::LogInfo("");
21  utility::LogInfo("Options (listed in the order of execution priority):");
22  utility::LogInfo(" --help, -h : Print help information.");
23  utility::LogInfo(" --verbose n : Set verbose level (0-4).");
24  utility::LogInfo(" --clip_x_min x0 : Clip points with x coordinate < x0.");
25  utility::LogInfo(" --clip_x_max x1 : Clip points with x coordinate > x1.");
26  utility::LogInfo(" --clip_y_min y0 : Clip points with y coordinate < y0.");
27  utility::LogInfo(" --clip_y_max y1 : Clip points with y coordinate > y1.");
28  utility::LogInfo(" --clip_z_min z0 : Clip points with z coordinate < z0.");
29  utility::LogInfo(" --clip_z_max z1 : Clip points with z coordinate > z1.");
30  utility::LogInfo(" --filter_mahalanobis d : Filter out points with Mahalanobis distance > d.");
31  utility::LogInfo(" --uniform_sample_every K : Downsample the point cloud uniformly. Keep only");
32  utility::LogInfo(" : one point for every K points.");
33  utility::LogInfo(" --voxel_sample voxel_size : Downsample the point cloud with a voxel.");
34  utility::LogInfo(" --estimate_normals radius : Estimate normals using a search neighborhood of");
35  utility::LogInfo(" radius. The normals are oriented w.r.t. the");
36  utility::LogInfo(" original normals of the pointcloud if they");
37  utility::LogInfo(" exist. Otherwise, they are oriented towards -Z");
38  utility::LogInfo(" direction.");
39  utility::LogInfo(" --estimate_normals_knn k : Estimate normals using a search with k nearest");
40  utility::LogInfo(" neighbors. The normals are oriented w.r.t. the");
41  utility::LogInfo(" original normals of the pointcloud if they");
42  utility::LogInfo(" exist. Otherwise, they are oriented towards -Z");
43  utility::LogInfo(" direction.");
44  utility::LogInfo(" --orient_normals [x,y,z] : Orient the normals w.r.t the direction [x,y,z].");
45  utility::LogInfo(" --camera_location [x,y,z] : Orient the normals w.r.t camera location [x,y,z].");
46  // clang-format on
47 }
48 
49 void convert(int argc,
50  char **argv,
51  const std::string &file_in,
52  const std::string &file_out) {
53  using namespace cloudViewer;
54  using namespace cloudViewer::utility::filesystem;
55  auto pointcloud_ptr =
57  if (!pointcloud_ptr) {
58  utility::LogError("Failed to create point cloud from {}.", file_in);
59  }
60  size_t point_num_in = pointcloud_ptr->size();
61  bool processed = false;
62 
63  // clip
65  argc, argv,
66  {"--clip_x_min", "--clip_x_max", "--clip_y_min", "--clip_y_max",
67  "--clip_z_min", "--clip_z_max"})) {
68  Eigen::Vector3d min_bound, max_bound;
69  min_bound(0) = utility::GetProgramOptionAsDouble(
70  argc, argv, "--clip_x_min",
71  std::numeric_limits<double>::lowest());
72  min_bound(1) = utility::GetProgramOptionAsDouble(
73  argc, argv, "--clip_y_min",
74  std::numeric_limits<double>::lowest());
75  min_bound(2) = utility::GetProgramOptionAsDouble(
76  argc, argv, "--clip_z_min",
77  std::numeric_limits<double>::lowest());
78  max_bound(0) = utility::GetProgramOptionAsDouble(
79  argc, argv, "--clip_x_max", std::numeric_limits<double>::max());
80  max_bound(1) = utility::GetProgramOptionAsDouble(
81  argc, argv, "--clip_y_max", std::numeric_limits<double>::max());
82  max_bound(2) = utility::GetProgramOptionAsDouble(
83  argc, argv, "--clip_z_max", std::numeric_limits<double>::max());
84  pointcloud_ptr = pointcloud_ptr->Crop(ccBBox(min_bound, max_bound));
85  processed = true;
86  }
87 
88  // filter_mahalanobis
89  double mahalanobis_threshold = utility::GetProgramOptionAsDouble(
90  argc, argv, "--filter_mahalanobis", 0.0);
91  if (mahalanobis_threshold > 0.0) {
92  auto mahalanobis = pointcloud_ptr->ComputeMahalanobisDistance();
93  std::vector<size_t> indices;
94  for (size_t i = 0; i < pointcloud_ptr->size(); i++) {
95  if (mahalanobis[i] < mahalanobis_threshold) {
96  indices.push_back(i);
97  }
98  }
99  auto pcd = pointcloud_ptr->SelectByIndex(indices);
101  "Based on Mahalanobis distance, {:d} points were filtered.",
102  (int)(pointcloud_ptr->size() - pcd->size()));
103  pointcloud_ptr = pcd;
104  }
105 
106  // uniform_downsample
107  int every_k = utility::GetProgramOptionAsInt(argc, argv,
108  "--uniform_sample_every", 0);
109  if (every_k > 1) {
110  utility::LogDebug("Downsample point cloud uniformly every {:d} points.",
111  every_k);
113  processed = true;
114  }
115 
116  // voxel_downsample
117  double voxel_size = utility::GetProgramOptionAsDouble(
118  argc, argv, "--voxel_sample", 0.0);
119  if (voxel_size > 0.0) {
120  utility::LogDebug("Downsample point cloud with voxel size {:.4f}.",
121  voxel_size);
123  processed = true;
124  }
125 
126  // estimate_normals
127  double radius = utility::GetProgramOptionAsDouble(
128  argc, argv, "--estimate_normals", 0.0);
129  if (radius > 0.0) {
130  utility::LogDebug("Estimate normals with search radius {:.4f}.",
131  radius);
134  processed = true;
135  }
136 
137  int k = utility::GetProgramOptionAsInt(argc, argv, "--estimate_normals_knn",
138  0);
139  if (k > 0) {
140  utility::LogDebug("Estimate normals with search knn {:d}.", k);
142  processed = true;
143  }
144 
145  // orient_normals
146  Eigen::VectorXd direction = utility::GetProgramOptionAsEigenVectorXd(
147  argc, argv, "--orient_normals");
148  if (direction.size() == 3 && pointcloud_ptr->hasNormals()) {
149  utility::LogDebug("Orient normals to [%.2f, %.2f, %.2f].", direction(0),
150  direction(1), direction(2));
151  Eigen::Vector3d dir(direction);
153  processed = true;
154  }
155  Eigen::VectorXd camera_loc = utility::GetProgramOptionAsEigenVectorXd(
156  argc, argv, "--camera_location");
157  if (camera_loc.size() == 3 && pointcloud_ptr->hasNormals()) {
158  utility::LogDebug("Orient normals towards [%.2f, %.2f, %.2f].",
159  camera_loc(0), camera_loc(1), camera_loc(2));
160  Eigen::Vector3d loc(camera_loc);
162  processed = true;
163  }
164 
165  size_t point_num_out = pointcloud_ptr->size();
166  if (processed) {
168  "Processed point cloud from {:d} points to {:d} points.",
169  (int)point_num_in, (int)point_num_out);
170  }
171  io::WritePointCloud(file_out.c_str(), *pointcloud_ptr, {false, true});
172 }
173 
174 int main(int argc, char **argv) {
175  using namespace cloudViewer;
176  using namespace cloudViewer::utility::filesystem;
177 
178  if (argc < 3 || utility::ProgramOptionExists(argc, argv, "--help") ||
179  utility::ProgramOptionExists(argc, argv, "-h")) {
180  PrintHelp();
181  return 0;
182  }
183 
184  int verbose = utility::GetProgramOptionAsInt(argc, argv, "--verbose", 2);
186 
187  if (FileExists(argv[1])) {
188  convert(argc, argv, argv[1], argv[2]);
189  } else if (DirectoryExists(argv[1])) {
190  MakeDirectoryHierarchy(argv[2]);
191  std::vector<std::string> filenames;
192  ListFilesInDirectory(argv[1], filenames);
193  for (const auto &fn : filenames) {
194  convert(argc, argv, fn,
195  GetRegularizedDirectoryName(argv[2]) +
197  }
198  } else {
199  utility::LogWarning("File or directory does not exist.");
200  return 1;
201  }
202 
203  return 0;
204 }
int main(int argc, char **argv)
void convert(int argc, char **argv, const std::string &file_in, const std::string &file_out)
void PrintHelp()
ccPointCloud * pointcloud_ptr
Bounding box structure.
Definition: ecvBBox.h:25
std::shared_ptr< ccPointCloud > Crop(const ccBBox &bbox) const
Function to crop ccPointCloud into output ccPointCloud.
bool EstimateNormals(const cloudViewer::geometry::KDTreeSearchParam &search_param=cloudViewer::geometry::KDTreeSearchParamKNN(), bool fast_normal_computation=true)
Function to compute the normals of a point cloud.
bool OrientNormalsTowardsCameraLocation(const Eigen::Vector3d &camera_location=Eigen::Vector3d::Zero())
Function to orient the normals of a point cloud.
std::vector< double > ComputeMahalanobisDistance() const
Function to compute the Mahalanobis distance for points in an input point cloud.
bool hasNormals() const override
Returns whether normals are enabled or not.
bool OrientNormalsToAlignWithDirection(const Eigen::Vector3d &orientation_reference=Eigen::Vector3d(0.0, 0.0, 1.0))
Function to orient the normals of a point cloud.
std::shared_ptr< ccPointCloud > VoxelDownSample(double voxel_size)
Function to downsample input ccPointCloud into output ccPointCloud with a voxel.
std::shared_ptr< ccPointCloud > SelectByIndex(const std::vector< size_t > &indices, bool invert=false) const
Function to select points from input ccPointCloud into output ccPointCloud.
std::shared_ptr< ccPointCloud > UniformDownSample(size_t every_k_points) const
Function to downsample input ccPointCloud into output ccPointCloud uniformly.
unsigned size() const override
Definition: PointCloudTpl.h:38
KDTree search parameters for pure KNN search.
KDTree search parameters for pure radius search.
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
int max(int a, int b)
Definition: cutil_math.h:48
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 ListFilesInDirectory(const std::string &directory, std::vector< std::string > &filenames)
Definition: FileSystem.cpp:561
bool MakeDirectoryHierarchy(const std::string &directory)
Definition: FileSystem.cpp:499
bool DirectoryExists(const std::string &directory)
Definition: FileSystem.cpp:473
std::string GetRegularizedDirectoryName(const std::string &directory)
Definition: FileSystem.cpp:323
bool FileExists(const std::string &filename)
Definition: FileSystem.cpp:524
std::string GetFileNameWithoutDirectory(const std::string &filename)
Definition: FileSystem.cpp:301
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 ProgramOptionExistsAny(int argc, char **argv, const std::vector< std::string > &options)
Definition: Console.cpp:104
bool ProgramOptionExists(int argc, char **argv, const std::string &option)
Definition: Console.cpp:100
Eigen::VectorXd GetProgramOptionAsEigenVectorXd(int argc, char **argv, const std::string &option, const Eigen::VectorXd default_value=Eigen::VectorXd::Zero(0))
Definition: Console.cpp:71
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.