ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
PCAPartition.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 <Eigen.h>
11 
12 #include <Eigen/Eigenvalues>
13 #include <numeric>
14 
16 
17 namespace cloudViewer {
18 namespace t {
19 namespace geometry {
20 namespace kernel {
21 namespace pcapartition {
22 
23 namespace {
24 typedef std::vector<size_t> IdxVec;
25 
33 template <class TReal>
34 void Split(const TReal* const points,
35  const size_t num_points,
36  const IdxVec& indices,
37  std::vector<IdxVec>& output) {
38  Eigen::Vector3d mean;
39  Eigen::Matrix3d cov;
40  std::tie(mean, cov) = utility::ComputeMeanAndCovariance(points, indices);
41 
42  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver;
43  solver.computeDirect(cov, Eigen::ComputeEigenvectors);
44  Eigen::Matrix<TReal, 3, 1> ax = solver.eigenvectors().col(2).cast<TReal>();
45 
46  Eigen::Matrix<TReal, 3, 1> mu = mean.cast<TReal>();
47  Eigen::Map<const Eigen::Matrix<TReal, 3, Eigen::Dynamic>> pts(points, 3,
48  num_points);
49  TReal mindot = ax.dot(pts.col(indices.front()) - mu);
50  TReal maxdot = mindot;
51  for (auto idx : indices) {
52  TReal dot = ax.dot(pts.col(idx) - mu);
53  mindot = std::min(dot, mindot);
54  maxdot = std::max(dot, maxdot);
55  }
56  TReal center = TReal(0.5) * (mindot + maxdot);
57  IdxVec part1, part2;
58  for (auto idx : indices) {
59  TReal dot = ax.dot(pts.col(idx) - mu);
60  if (dot < center) {
61  part1.push_back(idx);
62  } else {
63  part2.push_back(idx);
64  }
65  }
66 
67  if (part1.empty() || part2.empty()) {
68  // this should not happen make sure that we return two partitions that
69  // are both smaller than the input indices
70  part1.clear();
71  part2.clear();
72  part1.insert(part1.begin(), indices.begin(),
73  indices.begin() + indices.size() / 2);
74  part2.insert(part2.begin(), indices.begin() + indices.size() / 2,
75  indices.end());
76  }
77 
78  output.emplace_back(IdxVec(std::move(part1)));
79  output.emplace_back(IdxVec(std::move(part2)));
80 }
81 } // namespace
82 
83 std::tuple<int, core::Tensor> PCAPartition(core::Tensor& points,
84  int max_points) {
85  if (max_points <= 0) {
86  utility::LogError("max_points must be > 0 but is {}", max_points);
87  }
90  const size_t max_points_(max_points);
91  const size_t num_points = points.GetLength();
92  if (num_points == 0) {
93  utility::LogError("number of points must be greater zero");
94  }
95 
96  auto points_cpu = points.To(core::Device()).Contiguous();
98 
99  std::vector<IdxVec> partitions_to_process;
100  std::vector<IdxVec> partitions_result;
101  {
102  partitions_to_process.emplace_back(IdxVec(num_points));
103  IdxVec& indices = partitions_to_process.back();
104  std::iota(std::begin(indices), std::end(indices), 0);
105  }
106 
107  while (partitions_to_process.size()) {
108  std::vector<IdxVec> tmp;
109  for (auto& indices : partitions_to_process) {
110  if (indices.size() <= max_points_) {
111  partitions_result.emplace_back(IdxVec(std::move(indices)));
112  } else {
113  if (points.GetDtype() == core::Float32) {
114  Split(points_cpu.GetDataPtr<float>(), num_points, indices,
115  tmp);
116  } else {
117  Split(points_cpu.GetDataPtr<double>(), num_points, indices,
118  tmp);
119  }
120  }
121  }
122  // tmp contains the partitions for the next iteration
123  partitions_to_process.clear();
124  partitions_to_process.swap(tmp);
125  }
126 
127  auto partition_id = core::Tensor::Empty({int64_t(num_points)}, core::Int32);
128  int32_t* pid_ptr = partition_id.GetDataPtr<int32_t>();
129  for (size_t i = 0; i < partitions_result.size(); ++i) {
130  IdxVec& indices = partitions_result[i];
131  for (const auto idx : indices) {
132  pid_ptr[idx] = int32_t(i);
133  }
134  }
135 
136  int num_partitions = partitions_result.size();
137  return std::tie(num_partitions, partition_id);
138 }
139 
140 } // namespace pcapartition
141 } // namespace kernel
142 } // namespace geometry
143 } // namespace t
144 } // namespace cloudViewer
int points
#define AssertTensorDtypes(tensor,...)
Definition: TensorCheck.h:33
#define AssertTensorShape(tensor,...)
Definition: TensorCheck.h:61
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
Definition: Tensor.cpp:400
#define LogError(...)
Definition: Logging.h:60
__host__ __device__ float dot(float2 a, float2 b)
Definition: cutil_math.h:1119
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
const Dtype Float64
Definition: Dtype.cpp:43
const Dtype Int32
Definition: Dtype.cpp:46
const Dtype Float32
Definition: Dtype.cpp:42
std::tuple< int, core::Tensor > PCAPartition(core::Tensor &points, int max_points)
std::tuple< Eigen::Vector3d, Eigen::Matrix3d > ComputeMeanAndCovariance(const std::vector< Eigen::Vector3d > &points, const std::vector< IdxType > &indices)
Function to compute the mean and covariance matrix of a set of points.
Definition: Eigen.cpp:321
constexpr nullopt_t nullopt
Definition: Optional.h:136
Generic file read and write utility for python interface.