ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ControlGrid.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 
12 
13 namespace cloudViewer {
14 namespace t {
15 namespace pipelines {
16 namespace slac {
17 
18 const std::string ControlGrid::kGrid8NbIndices = "Grid8NbIndices";
20  "Grid8NbVertexInterpRatios";
22  "Grid8NbNormalInterpRatios";
23 
24 ControlGrid::ControlGrid(float grid_size,
25  int64_t grid_count,
26  const core::Device& device)
27  : grid_size_(grid_size), device_(device) {
28  ctr_hashmap_ = std::make_shared<core::HashMap>(
30  core::SizeVector{3}, device);
31 }
32 
33 ControlGrid::ControlGrid(float grid_size,
34  const core::Tensor& keys,
35  const core::Tensor& values,
36  const core::Device& device)
37  : grid_size_(grid_size), device_(device) {
38  ctr_hashmap_ = std::make_shared<core::HashMap>(
39  2 * keys.GetLength(), core::Int32, core::SizeVector{3},
40  core::Float32, core::SizeVector{3}, device);
41 
42  core::Tensor buf_indices, masks;
43  ctr_hashmap_->Insert(keys, values, buf_indices, masks);
44 }
45 
47  core::Tensor pts = pcd.GetPointPositions();
48  int64_t n = pts.GetLength();
49 
50  // Coordinate in the grid unit.
51  core::Tensor vals = (pts / grid_size_).Floor();
52  core::Tensor keys = vals.To(core::Int32);
53 
54  // Prepare for insertion with 8 neighbors
55  core::Tensor keys_nb({8, n, 3}, core::Int32, device_);
56  core::Tensor vals_nb({8, n, 3}, core::Float32, device_);
57  for (int nb = 0; nb < 8; ++nb) {
58  int x_sel = (nb & 4) >> 2;
59  int y_sel = (nb & 2) >> 1;
60  int z_sel = (nb & 1);
61 
62  core::Tensor dt = core::Tensor(std::vector<int>{x_sel, y_sel, z_sel},
63  {1, 3}, core::Int32, device_);
64  keys_nb[nb] = keys + dt;
65  vals_nb[nb] = vals + dt.To(core::Float32);
66  }
67  keys_nb = keys_nb.View({8 * n, 3});
68 
69  // Convert back to the meter unit.
70  vals_nb = vals_nb.View({8 * n, 3}) * grid_size_;
71 
72  core::HashSet unique_hashset(n, core::Int32, core::SizeVector{3}, device_);
73 
74  core::Tensor buf_indices_unique, masks_unique;
75  unique_hashset.Insert(keys_nb, buf_indices_unique, masks_unique);
76 
77  core::Tensor buf_indices, masks;
78  ctr_hashmap_->Insert(keys_nb.IndexGet({masks_unique}),
79  vals_nb.IndexGet({masks_unique}), buf_indices, masks);
80 }
81 
83  ctr_hashmap_->Reserve(ctr_hashmap_->Size() * 2);
84 
85  core::Tensor active_buf_indices;
86  ctr_hashmap_->GetActiveIndices(active_buf_indices);
87 
88  // Select anchor point
89  core::Tensor active_keys = ctr_hashmap_->GetKeyTensor().IndexGet(
90  {active_buf_indices.To(core::Int64)});
91 
92  std::vector<Eigen::Vector3i> active_keys_vec =
94 
95  std::vector<Eigen::Vector4i> active_keys_indexed(active_keys_vec.size());
96  for (size_t i = 0; i < active_keys_vec.size(); ++i) {
97  active_keys_indexed[i](0) = active_keys_vec[i](0);
98  active_keys_indexed[i](1) = active_keys_vec[i](1);
99  active_keys_indexed[i](2) = active_keys_vec[i](2);
100  active_keys_indexed[i](3) = i;
101  }
102 
103  std::sort(active_keys_indexed.begin(), active_keys_indexed.end(),
104  [=](const Eigen::Vector4i& a, const Eigen::Vector4i& b) -> bool {
105  return (a(2) < b(2)) || (a(2) == b(2) && a(1) < b(1)) ||
106  (a(2) == b(2) && a(1) == b(1) && a(0) < b(0));
107  });
108  anchor_idx_ =
109  active_buf_indices[active_keys_indexed[active_keys_indexed.size() /
110  2](3)]
111  .Item<int>();
112 }
113 
114 std::tuple<core::Tensor, core::Tensor, core::Tensor>
116  core::Tensor active_buf_indices;
117  ctr_hashmap_->GetActiveIndices(active_buf_indices);
118 
119  core::Tensor active_indices = active_buf_indices.To(core::Int64);
120  core::Tensor active_keys =
121  ctr_hashmap_->GetKeyTensor().IndexGet({active_indices});
122 
123  int64_t n = active_indices.GetLength();
124  core::Tensor keys_nb({6, n, 3}, core::Int32, device_);
125 
126  core::Tensor dx = core::Tensor(std::vector<int>{1, 0, 0}, {1, 3},
127  core::Int32, device_);
128  core::Tensor dy = core::Tensor(std::vector<int>{0, 1, 0}, {1, 3},
129  core::Int32, device_);
130  core::Tensor dz = core::Tensor(std::vector<int>{0, 0, 1}, {1, 3},
131  core::Int32, device_);
132  keys_nb[0] = active_keys - dx;
133  keys_nb[1] = active_keys + dx;
134  keys_nb[2] = active_keys - dy;
135  keys_nb[3] = active_keys + dy;
136  keys_nb[4] = active_keys - dz;
137  keys_nb[5] = active_keys + dz;
138 
139  // Obtain nearest neighbors
140  keys_nb = keys_nb.View({6 * n, 3});
141 
142  core::Tensor buf_indices_nb, masks_nb;
143  ctr_hashmap_->Find(keys_nb, buf_indices_nb, masks_nb);
144 
145  return std::make_tuple(active_buf_indices,
146  buf_indices_nb.View({6, n}).T().Contiguous(),
147  masks_nb.View({6, n}).T().Contiguous());
148 }
149 
151  const geometry::PointCloud& pcd) {
152  core::Tensor pts = pcd.GetPointPositions();
153  core::Tensor nms;
154  if (pcd.HasPointNormals()) {
155  nms = pcd.GetPointNormals().T().Contiguous();
156  }
157  int64_t n = pts.GetLength();
158 
159  core::Tensor pts_quantized = pts / grid_size_;
160  core::Tensor pts_quantized_floor = pts_quantized.Floor();
161 
162  // (N x 3) -> [0, 1] for trilinear interpolation
163  core::Tensor residual = pts_quantized - pts_quantized_floor;
164  std::vector<std::vector<core::Tensor>> residuals(3);
165  for (int axis = 0; axis < 3; ++axis) {
166  core::Tensor residual_axis = residual.GetItem(
168  core::TensorKey::Index(axis)});
169 
170  residuals[axis].emplace_back(1.f - residual_axis);
171  residuals[axis].emplace_back(residual_axis);
172  }
173 
174  core::Tensor keys = pts_quantized_floor.To(core::Int32);
175  core::Tensor keys_nb({8, n, 3}, core::Int32, device_);
176  core::Tensor point_ratios_nb({8, n}, core::Float32, device_);
177  core::Tensor normal_ratios_nb({8, n}, core::Float32, device_);
178  for (int nb = 0; nb < 8; ++nb) {
179  int x_sel = (nb & 4) >> 2;
180  int y_sel = (nb & 2) >> 1;
181  int z_sel = (nb & 1);
182 
183  float x_sign = x_sel * 2.0 - 1.0;
184  float y_sign = y_sel * 2.0 - 1.0;
185  float z_sign = z_sel * 2.0 - 1.0;
186 
187  core::Tensor dt = core::Tensor(std::vector<int>{x_sel, y_sel, z_sel},
188  {1, 3}, core::Int32, device_);
189  keys_nb[nb] = keys + dt;
190  point_ratios_nb[nb] =
191  residuals[0][x_sel] * residuals[1][y_sel] * residuals[2][z_sel];
192  if (pcd.HasPointNormals()) {
193  normal_ratios_nb[nb] =
194  x_sign * nms[0] * residuals[1][y_sel] *
195  residuals[2][z_sel] +
196  y_sign * nms[1] * residuals[0][x_sel] *
197  residuals[2][z_sel] +
198  z_sign * nms[2] * residuals[0][x_sel] * residuals[1][y_sel];
199  }
200  }
201 
202  keys_nb = keys_nb.View({8 * n, 3});
203 
204  core::Tensor buf_indices_nb, masks_nb;
205  ctr_hashmap_->Find(keys_nb, buf_indices_nb, masks_nb);
206 
207  // (n, 8)
208  buf_indices_nb = buf_indices_nb.View({8, n}).T().Contiguous();
209  // (n, 8)
210  point_ratios_nb = point_ratios_nb.T().Contiguous();
211 
215  core::Tensor valid_mask =
216  masks_nb.View({8, n}).To(core::Int64).Sum({0}).Eq(8);
217 
218  geometry::PointCloud pcd_with_params = pcd;
219  pcd_with_params.SetPointPositions(
220  pcd.GetPointPositions().IndexGet({valid_mask}));
221  pcd_with_params.SetPointAttr(kGrid8NbIndices,
222  buf_indices_nb.IndexGet({valid_mask}));
224  point_ratios_nb.IndexGet({valid_mask}));
225 
226  if (pcd.HasPointColors()) {
227  pcd_with_params.SetPointColors(
228  pcd.GetPointColors().IndexGet({valid_mask}));
229  }
230  if (pcd.HasPointNormals()) {
231  pcd_with_params.SetPointNormals(
232  pcd.GetPointNormals().IndexGet({valid_mask}));
233  normal_ratios_nb = normal_ratios_nb.T().Contiguous();
235  normal_ratios_nb.IndexGet({valid_mask}));
236  }
237 
238  return pcd_with_params;
239 }
240 
242  if (!pcd.HasPointAttr(kGrid8NbIndices) ||
245  "Please use ControlGrid.Parameterize to obtain attributes "
246  "regarding neighbor grids before calling Deform");
247  }
248 
249  // N x 3
250  core::Tensor grid_positions = ctr_hashmap_->GetValueTensor();
251 
252  // N x 8, we have ensured that every neighbor is valid through
253  // grid.Parameterize
254  core::Tensor nb_grid_indices =
256  core::Tensor nb_grid_positions =
257  grid_positions.IndexGet({nb_grid_indices.View({-1})})
258  .View({-1, 8, 3});
259 
260  // (N, 8, 3) x (N, 8, 1) => Reduce on dim 1 => (N, 3) position
261  // interpolation.
262  core::Tensor nb_grid_point_interp =
264  core::Tensor interp_positions =
265  (nb_grid_positions * nb_grid_point_interp.View({-1, 8, 1}))
266  .Sum({1});
267 
268  geometry::PointCloud interp_pcd(interp_positions);
269 
270  if (pcd.HasPointNormals()) {
271  // (N, 8, 3) x (N, 8, 1) => Reduce on dim 1 => (N, 3) normal
272  // interpolation.
273  core::Tensor nb_grid_normal_interp =
275  core::Tensor interp_normals =
276  (nb_grid_positions * nb_grid_normal_interp.View({-1, 8, 1}))
277  .Sum({1});
278  core::Tensor interp_normals_len =
279  (interp_normals * interp_normals).Sum({1}).Sqrt();
280  interp_normals = interp_normals / interp_normals_len.View({-1, 1});
281  interp_pcd.SetPointNormals(interp_normals);
282  }
283 
284  if (pcd.HasPointColors()) {
285  interp_pcd.SetPointColors(pcd.GetPointColors());
286  }
287  return interp_pcd;
288 }
289 
291  const core::Tensor& intrinsics,
292  const core::Tensor& extrinsics,
293  float depth_scale,
294  float depth_max) {
296  depth, intrinsics, extrinsics, depth_scale, depth_max);
297 
298  geometry::PointCloud pcd_param = Parameterize(pcd);
299  geometry::PointCloud pcd_deformed = Deform(pcd_param);
300 
301  return pcd_deformed.ProjectToDepthImage(depth.GetCols(), depth.GetRows(),
302  intrinsics, extrinsics, depth_scale,
303  depth_max);
304 }
305 
307  const core::Tensor& intrinsics,
308  const core::Tensor& extrinsics,
309  float depth_scale,
310  float depth_max) {
312  rgbd, intrinsics, extrinsics, depth_scale, depth_max);
313 
314  geometry::PointCloud pcd_param = Parameterize(pcd);
315  geometry::PointCloud pcd_deformed = Deform(pcd_param);
316 
317  int cols = rgbd.depth_.GetCols();
318  int rows = rgbd.color_.GetRows();
319 
320  return pcd_deformed.ProjectToRGBDImage(cols, rows, intrinsics, extrinsics,
321  depth_scale, depth_max);
322 }
323 
324 } // namespace slac
325 } // namespace pipelines
326 } // namespace t
327 } // namespace cloudViewer
void Sqrt(const double in[2], double out[2])
Definition: Factor.cpp:124
static TensorKey Index(int64_t index)
Definition: TensorKey.cpp:134
static TensorKey Slice(utility::optional< int64_t > start, utility::optional< int64_t > stop, utility::optional< int64_t > step)
Definition: TensorKey.cpp:138
Tensor Contiguous() const
Definition: Tensor.cpp:772
Tensor Sum(const SizeVector &dims, bool keepdim=false) const
Definition: Tensor.cpp:1240
int64_t GetLength() const
Definition: Tensor.h:1125
Tensor GetItem(const TensorKey &tk) const
Definition: Tensor.cpp:473
Tensor IndexGet(const std::vector< Tensor > &index_tensors) const
Advanced indexing getter. This will always allocate a new Tensor.
Definition: Tensor.cpp:905
Tensor View(const SizeVector &dst_shape) const
Definition: Tensor.cpp:721
Tensor T() const
Expects input to be <= 2-D Tensor by swapping dimension 0 and 1.
Definition: Tensor.cpp:1079
Tensor To(Dtype dtype, bool copy=false) const
Definition: Tensor.cpp:739
Tensor Floor() const
Element-wise floor value of a tensor, returning a new tensor.
Definition: Tensor.cpp:1411
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
int64_t GetCols() const
Get the number of columns of the image.
Definition: Image.h:87
int64_t GetRows() const
Get the number of rows of the image.
Definition: Image.h:84
A point cloud contains a list of 3D points.
Definition: PointCloud.h:82
void SetPointNormals(const core::Tensor &value)
Set the value of the "normals" attribute. Convenience function.
Definition: PointCloud.h:198
core::Tensor & GetPointNormals()
Get the value of the "normals" attribute. Convenience function.
Definition: PointCloud.h:130
void SetPointColors(const core::Tensor &value)
Set the value of the "colors" attribute. Convenience function.
Definition: PointCloud.h:192
bool HasPointAttr(const std::string &key) const
Definition: PointCloud.h:207
void SetPointPositions(const core::Tensor &value)
Set the value of the "positions" attribute. Convenience function.
Definition: PointCloud.h:186
core::Tensor & GetPointPositions()
Get the value of the "positions" attribute. Convenience function.
Definition: PointCloud.h:124
static PointCloud CreateFromDepthImage(const Image &depth, const core::Tensor &intrinsics, const core::Tensor &extrinsics=core::Tensor::Eye(4, core::Float32, core::Device("CPU:0")), float depth_scale=1000.0f, float depth_max=3.0f, int stride=1, bool with_normals=false)
Factory function to create a point cloud from a depth image and a camera model.
Definition: PointCloud.cpp:956
geometry::RGBDImage ProjectToRGBDImage(int width, int height, const core::Tensor &intrinsics, const core::Tensor &extrinsics=core::Tensor::Eye(4, core::Float32, core::Device("CPU:0")), float depth_scale=1000.0f, float depth_max=3.0f)
Project a point cloud to an RGBD image.
geometry::Image ProjectToDepthImage(int width, int height, const core::Tensor &intrinsics, const core::Tensor &extrinsics=core::Tensor::Eye(4, core::Float32, core::Device("CPU:0")), float depth_scale=1000.0f, float depth_max=3.0f)
Project a point cloud to a depth image.
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: PointCloud.h:111
void SetPointAttr(const std::string &key, const core::Tensor &value)
Definition: PointCloud.h:177
core::Tensor & GetPointColors()
Get the value of the "colors" attribute. Convenience function.
Definition: PointCloud.h:127
static PointCloud CreateFromRGBDImage(const RGBDImage &rgbd_image, const core::Tensor &intrinsics, const core::Tensor &extrinsics=core::Tensor::Eye(4, core::Float32, core::Device("CPU:0")), float depth_scale=1000.0f, float depth_max=3.0f, int stride=1, bool with_normals=false)
Factory function to create a point cloud from an RGB-D image and a camera model.
Definition: PointCloud.cpp:986
RGBDImage A pair of color and depth images.
Definition: RGBDImage.h:21
Image depth_
The depth image.
Definition: RGBDImage.h:106
Image color_
The color image.
Definition: RGBDImage.h:104
static const std::string kGrid8NbVertexInterpRatios
8 neighbor grid interpolation ratio for vertex per point.
Definition: ControlGrid.h:36
ControlGrid()=default
Default constructor.
static const std::string kGrid8NbIndices
Definition: ControlGrid.h:34
geometry::PointCloud Deform(const geometry::PointCloud &pcd)
Non-rigidly deform a point cloud using the control grid.
void Touch(const geometry::PointCloud &pcd)
Allocate control grids in the shared camera space.
Definition: ControlGrid.cpp:46
std::tuple< core::Tensor, core::Tensor, core::Tensor > GetNeighborGridMap()
geometry::PointCloud Parameterize(const geometry::PointCloud &pcd)
static const std::string kGrid8NbNormalInterpRatios
8 neighbor grid interpolation ratio for normal per point.
Definition: ControlGrid.h:38
#define LogError(...)
Definition: Logging.h:60
std::vector< Eigen::Vector3i > TensorToEigenVector3iVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 3) to std::vector<Eigen::Vector3i>. An exception will be thrown if the...
const Dtype Int64
Definition: Dtype.cpp:47
constexpr utility::nullopt_t None
Definition: TensorKey.h:20
const Dtype Int32
Definition: Dtype.cpp:46
const Dtype Float32
Definition: Dtype.cpp:42
void To(const core::Tensor &src, core::Tensor &dst, double scale, double offset)
Definition: Image.cpp:17
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 4, 1 > Vector4i
Definition: knncpp.h:31