Open3D (C++ API)  0.13.0
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TSDFVoxelGridImpl.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - Open3D: www.open3d.org -
3 // ----------------------------------------------------------------------------
4 // The MIT License (MIT)
5 //
6 // Copyright (c) 2018 www.open3d.org
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 // ----------------------------------------------------------------------------
26 
27 #include <atomic>
28 #include <cmath>
29 
30 #include "open3d/core/Dispatch.h"
31 #include "open3d/core/Dtype.h"
33 #include "open3d/core/SizeVector.h"
34 #include "open3d/core/Tensor.h"
40 #include "open3d/utility/Console.h"
41 #include "open3d/utility/Timer.h"
42 
43 namespace open3d {
44 namespace t {
45 namespace geometry {
46 namespace kernel {
47 namespace tsdf {
48 
49 #if defined(__CUDACC__)
50 void IntegrateCUDA
51 #else
52 void IntegrateCPU
53 #endif
54  (const core::Tensor& depth,
55  const core::Tensor& color,
56  const core::Tensor& indices,
57  const core::Tensor& block_keys,
58  core::Tensor& block_values,
59  // Transforms
60  const core::Tensor& intrinsics,
61  const core::Tensor& extrinsics,
62  // Parameters
63  int64_t resolution,
64  float voxel_size,
65  float sdf_trunc,
66  float depth_scale,
67  float depth_max) {
68  // Parameters
69  int64_t resolution3 = resolution * resolution * resolution;
70 
71  // Shape / transform indexers, no data involved
72  NDArrayIndexer voxel_indexer({resolution, resolution, resolution});
73  TransformIndexer transform_indexer(intrinsics, extrinsics, voxel_size);
74 
75  // Real data indexer
76  NDArrayIndexer depth_indexer(depth, 2);
77  NDArrayIndexer block_keys_indexer(block_keys, 1);
78  NDArrayIndexer voxel_block_buffer_indexer(block_values, 4);
79 
80  // Optional color integration
81  NDArrayIndexer color_indexer;
82  bool integrate_color = false;
83  if (color.NumElements() != 0) {
84  color_indexer = NDArrayIndexer(color, 2);
85  integrate_color = true;
86  }
87 
88  // Plain arrays that does not require indexers
89  const int* indices_ptr = indices.GetDataPtr<int>();
90 
91  int64_t n = indices.GetLength() * resolution3;
92 
93 #if defined(__CUDACC__)
94  core::kernel::CUDALauncher launcher;
95 #else
97 #endif
98 
100  voxel_block_buffer_indexer.ElementByteSize(), [&]() {
101  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(
102  int64_t workload_idx) {
103  // Natural index (0, N) -> (block_idx, voxel_idx)
104  int block_idx = indices_ptr[workload_idx / resolution3];
105  int voxel_idx = workload_idx % resolution3;
106 
108  // block_idx -> (x_block, y_block, z_block)
109  int* block_key_ptr =
110  block_keys_indexer.GetDataPtr<int>(block_idx);
111  int64_t xb = static_cast<int64_t>(block_key_ptr[0]);
112  int64_t yb = static_cast<int64_t>(block_key_ptr[1]);
113  int64_t zb = static_cast<int64_t>(block_key_ptr[2]);
114 
115  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
116  int64_t xv, yv, zv;
117  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
118 
119  // coordinate in world (in voxel)
120  int64_t x = (xb * resolution + xv);
121  int64_t y = (yb * resolution + yv);
122  int64_t z = (zb * resolution + zv);
123 
124  // coordinate in camera (in voxel -> in meter)
125  float xc, yc, zc, u, v;
126  transform_indexer.RigidTransform(
127  static_cast<float>(x), static_cast<float>(y),
128  static_cast<float>(z), &xc, &yc, &zc);
129 
130  // coordinate in image (in pixel)
131  transform_indexer.Project(xc, yc, zc, &u, &v);
132  if (!depth_indexer.InBoundary(u, v)) {
133  return;
134  }
135 
136  // Associate image workload and compute SDF and TSDF.
137  float depth = *depth_indexer.GetDataPtr<float>(
138  static_cast<int64_t>(u),
139  static_cast<int64_t>(v)) /
140  depth_scale;
141 
142  float sdf = (depth - zc);
143  if (depth <= 0 || depth > depth_max || zc <= 0 ||
144  sdf < -sdf_trunc) {
145  return;
146  }
147  sdf = sdf < sdf_trunc ? sdf : sdf_trunc;
148  sdf /= sdf_trunc;
149 
150  // Associate voxel workload and update TSDF/Weights
151  voxel_t* voxel_ptr =
152  voxel_block_buffer_indexer.GetDataPtr<voxel_t>(
153  xv, yv, zv, block_idx);
154 
155  if (integrate_color) {
156  float* color_ptr = color_indexer.GetDataPtr<float>(
157  static_cast<int64_t>(u),
158  static_cast<int64_t>(v));
159 
160  voxel_ptr->Integrate(sdf, color_ptr[0], color_ptr[1],
161  color_ptr[2]);
162  } else {
163  voxel_ptr->Integrate(sdf);
164  }
165  });
166  });
167 #if defined(__CUDACC__)
168  OPEN3D_CUDA_CHECK(cudaDeviceSynchronize());
169 #endif
170 }
171 
172 #if defined(__CUDACC__)
173 void ExtractSurfacePointsCUDA
174 #else
176 #endif
177  (const core::Tensor& indices,
178  const core::Tensor& nb_indices,
179  const core::Tensor& nb_masks,
180  const core::Tensor& block_keys,
181  const core::Tensor& block_values,
185  int64_t resolution,
186  float voxel_size,
187  float weight_threshold,
188  int& valid_size) {
189  // Parameters
190  int64_t resolution3 = resolution * resolution * resolution;
191 
192  // Shape / transform indexers, no data involved
193  NDArrayIndexer voxel_indexer({resolution, resolution, resolution});
194 
195  // Real data indexer
196  NDArrayIndexer voxel_block_buffer_indexer(block_values, 4);
197  NDArrayIndexer block_keys_indexer(block_keys, 1);
198  NDArrayIndexer nb_block_masks_indexer(nb_masks, 2);
199  NDArrayIndexer nb_block_indices_indexer(nb_indices, 2);
200 
201  // Plain arrays that does not require indexers
202  const int64_t* indices_ptr = indices.GetDataPtr<int64_t>();
203 
204  int64_t n_blocks = indices.GetLength();
205  int64_t n = n_blocks * resolution3;
206 
207  // Output
208 #if defined(__CUDACC__)
209  core::Tensor count(std::vector<int>{0}, {1}, core::Dtype::Int32,
210  block_values.GetDevice());
211  int* count_ptr = count.GetDataPtr<int>();
212 #else
213  std::atomic<int> count_atomic(0);
214  std::atomic<int>* count_ptr = &count_atomic;
215 #endif
216 
217 #if defined(__CUDACC__)
218  core::kernel::CUDALauncher launcher;
219 #else
220  core::kernel::CPULauncher launcher;
221 #endif
222  if (valid_size < 0) {
224  "No estimated max point cloud size provided, using a 2-pass "
225  "estimation. Surface extraction could be slow.");
226  // This pass determines valid number of points.
228  voxel_block_buffer_indexer.ElementByteSize(), [&]() {
229  launcher.LaunchGeneralKernel(
230  n, [=] OPEN3D_DEVICE(int64_t workload_idx) {
231  auto GetVoxelAt = [&] OPEN3D_DEVICE(
232  int xo, int yo,
233  int zo,
234  int curr_block_idx)
235  -> voxel_t* {
236  return DeviceGetVoxelAt<voxel_t>(
237  xo, yo, zo, curr_block_idx,
238  static_cast<int>(resolution),
239  nb_block_masks_indexer,
240  nb_block_indices_indexer,
241  voxel_block_buffer_indexer);
242  };
243 
244  // Natural index (0, N) -> (block_idx,
245  // voxel_idx)
246  int64_t workload_block_idx =
247  workload_idx / resolution3;
248  int64_t block_idx =
249  indices_ptr[workload_block_idx];
250  int64_t voxel_idx = workload_idx % resolution3;
251 
252  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
253  int64_t xv, yv, zv;
254  voxel_indexer.WorkloadToCoord(voxel_idx, &xv,
255  &yv, &zv);
256 
257  voxel_t* voxel_ptr =
258  voxel_block_buffer_indexer
259  .GetDataPtr<voxel_t>(xv, yv, zv,
260  block_idx);
261  float tsdf_o = voxel_ptr->GetTSDF();
262  float weight_o = voxel_ptr->GetWeight();
263  if (weight_o <= weight_threshold) return;
264 
265  // Enumerate x-y-z directions
266  for (int i = 0; i < 3; ++i) {
267  voxel_t* ptr = GetVoxelAt(
268  static_cast<int>(xv) + (i == 0),
269  static_cast<int>(yv) + (i == 1),
270  static_cast<int>(zv) + (i == 2),
271  static_cast<int>(
272  workload_block_idx));
273  if (ptr == nullptr) continue;
274 
275  float tsdf_i = ptr->GetTSDF();
276  float weight_i = ptr->GetWeight();
277 
278  if (weight_i > weight_threshold &&
279  tsdf_i * tsdf_o < 0) {
280  OPEN3D_ATOMIC_ADD(count_ptr, 1);
281  }
282  }
283  });
284  });
285 
286 #if defined(__CUDACC__)
287  valid_size = count[0].Item<int>();
288  count[0] = 0;
289 #else
290  valid_size = (*count_ptr).load();
291  (*count_ptr) = 0;
292 #endif
293  }
294 
295  int max_count = valid_size;
296  if (points.GetLength() == 0) {
297  points = core::Tensor({max_count, 3}, core::Dtype::Float32,
298  block_values.GetDevice());
299  }
300  NDArrayIndexer point_indexer(points, 1);
301 
302  // Normals
303  bool extract_normal = false;
304  NDArrayIndexer normal_indexer;
305  if (normals.has_value()) {
306  extract_normal = true;
307  if (normals.value().get().GetLength() == 0) {
308  normals.value().get() =
309  core::Tensor({max_count, 3}, core::Dtype::Float32,
310  block_values.GetDevice());
311  }
312  normal_indexer = NDArrayIndexer(normals.value().get(), 1);
313  }
314 
315  // This pass extracts exact surface points.
317  voxel_block_buffer_indexer.ElementByteSize(), [&]() {
318  // Colors
319  bool extract_color = false;
320  NDArrayIndexer color_indexer;
321  if (voxel_t::HasColor() && colors.has_value()) {
322  extract_color = true;
323  if (colors.value().get().GetLength() == 0) {
324  colors.value().get() = core::Tensor(
325  {max_count, 3}, core::Dtype::Float32,
326  block_values.GetDevice());
327  }
328  color_indexer = NDArrayIndexer(colors.value().get(), 1);
329  }
330 
331  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(
332  int64_t workload_idx) {
333  auto GetVoxelAt = [&] OPEN3D_DEVICE(
334  int xo, int yo, int zo,
335  int curr_block_idx) -> voxel_t* {
336  return DeviceGetVoxelAt<voxel_t>(
337  xo, yo, zo, curr_block_idx,
338  static_cast<int>(resolution),
339  nb_block_masks_indexer,
340  nb_block_indices_indexer,
341  voxel_block_buffer_indexer);
342  };
343  auto GetNormalAt = [&] OPEN3D_DEVICE(int xo, int yo, int zo,
344  int curr_block_idx,
345  float* n) {
346  return DeviceGetNormalAt<voxel_t>(
347  xo, yo, zo, curr_block_idx, n,
348  static_cast<int>(resolution), voxel_size,
349  nb_block_masks_indexer,
350  nb_block_indices_indexer,
351  voxel_block_buffer_indexer);
352  };
353 
354  // Natural index (0, N) -> (block_idx, voxel_idx)
355  int64_t workload_block_idx = workload_idx / resolution3;
356  int64_t block_idx = indices_ptr[workload_block_idx];
357  int64_t voxel_idx = workload_idx % resolution3;
358 
360  // block_idx -> (x_block, y_block, z_block)
361  int* block_key_ptr =
362  block_keys_indexer.GetDataPtr<int>(block_idx);
363  int64_t xb = static_cast<int64_t>(block_key_ptr[0]);
364  int64_t yb = static_cast<int64_t>(block_key_ptr[1]);
365  int64_t zb = static_cast<int64_t>(block_key_ptr[2]);
366 
367  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
368  int64_t xv, yv, zv;
369  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
370 
371  voxel_t* voxel_ptr =
372  voxel_block_buffer_indexer.GetDataPtr<voxel_t>(
373  xv, yv, zv, block_idx);
374  float tsdf_o = voxel_ptr->GetTSDF();
375  float weight_o = voxel_ptr->GetWeight();
376 
377  if (weight_o <= weight_threshold) return;
378 
379  int64_t x = xb * resolution + xv;
380  int64_t y = yb * resolution + yv;
381  int64_t z = zb * resolution + zv;
382 
383  float no[3] = {0}, ni[3] = {0};
384  if (extract_normal) {
385  GetNormalAt(static_cast<int>(xv), static_cast<int>(yv),
386  static_cast<int>(zv),
387  static_cast<int>(workload_block_idx), no);
388  }
389 
390  // Enumerate x-y-z axis
391  for (int i = 0; i < 3; ++i) {
392  voxel_t* ptr = GetVoxelAt(
393  static_cast<int>(xv) + (i == 0),
394  static_cast<int>(yv) + (i == 1),
395  static_cast<int>(zv) + (i == 2),
396  static_cast<int>(workload_block_idx));
397  if (ptr == nullptr) continue;
398 
399  float tsdf_i = ptr->GetTSDF();
400  float weight_i = ptr->GetWeight();
401 
402  if (weight_i > weight_threshold &&
403  tsdf_i * tsdf_o < 0) {
404  float ratio = (0 - tsdf_o) / (tsdf_i - tsdf_o);
405 
406  int idx = OPEN3D_ATOMIC_ADD(count_ptr, 1);
407  if (idx >= valid_size) {
408  printf("Point cloud size larger than "
409  "estimated, please increase the "
410  "estimation!\n");
411  return;
412  }
413 
414  float* point_ptr =
415  point_indexer.GetDataPtr<float>(idx);
416  point_ptr[0] =
417  voxel_size * (x + ratio * int(i == 0));
418  point_ptr[1] =
419  voxel_size * (y + ratio * int(i == 1));
420  point_ptr[2] =
421  voxel_size * (z + ratio * int(i == 2));
422 
423  if (extract_color) {
424  float* color_ptr =
425  color_indexer.GetDataPtr<float>(idx);
426 
427  float r_o = voxel_ptr->GetR();
428  float g_o = voxel_ptr->GetG();
429  float b_o = voxel_ptr->GetB();
430 
431  float r_i = ptr->GetR();
432  float g_i = ptr->GetG();
433  float b_i = ptr->GetB();
434 
435  color_ptr[0] =
436  ((1 - ratio) * r_o + ratio * r_i) /
437  255.0f;
438  color_ptr[1] =
439  ((1 - ratio) * g_o + ratio * g_i) /
440  255.0f;
441  color_ptr[2] =
442  ((1 - ratio) * b_o + ratio * b_i) /
443  255.0f;
444  }
445 
446  if (extract_normal) {
447  GetNormalAt(
448  static_cast<int>(xv) + (i == 0),
449  static_cast<int>(yv) + (i == 1),
450  static_cast<int>(zv) + (i == 2),
451  static_cast<int>(workload_block_idx),
452  ni);
453 
454  float* normal_ptr =
455  normal_indexer.GetDataPtr<float>(idx);
456  float nx = (1 - ratio) * no[0] + ratio * ni[0];
457  float ny = (1 - ratio) * no[1] + ratio * ni[1];
458  float nz = (1 - ratio) * no[2] + ratio * ni[2];
459  float norm = static_cast<float>(
460  sqrt(nx * nx + ny * ny + nz * nz) +
461  1e-5);
462  normal_ptr[0] = nx / norm;
463  normal_ptr[1] = ny / norm;
464  normal_ptr[2] = nz / norm;
465  }
466  }
467  }
468  });
469  });
470 #if defined(__CUDACC__)
471  int total_count = count.Item<int>();
472 #else
473  int total_count = (*count_ptr).load();
474 #endif
475 
476  utility::LogDebug("{} vertices extracted", total_count);
477  valid_size = total_count;
478 
479 #if defined(BUILD_CUDA_MODULE) && defined(__CUDACC__)
480  OPEN3D_CUDA_CHECK(cudaDeviceSynchronize());
481 #endif
482 }
483 
484 #if defined(__CUDACC__)
485 void ExtractSurfaceMeshCUDA
486 #else
488 #endif
489  (const core::Tensor& indices,
490  const core::Tensor& inv_indices,
491  const core::Tensor& nb_indices,
492  const core::Tensor& nb_masks,
493  const core::Tensor& block_keys,
494  const core::Tensor& block_values,
495  core::Tensor& vertices,
496  core::Tensor& triangles,
499  int64_t resolution,
500  float voxel_size,
501  float weight_threshold,
502  int& vertex_count) {
503 
504  int64_t resolution3 = resolution * resolution * resolution;
505 
506  // Shape / transform indexers, no data involved
507  NDArrayIndexer voxel_indexer({resolution, resolution, resolution});
508  int n_blocks = static_cast<int>(indices.GetLength());
509 
510 #if defined(__CUDACC__)
512 #endif
513  // TODO(wei): profile performance by replacing the table to a hashmap.
514  // Voxel-wise mesh info. 4 channels correspond to:
515  // 3 edges' corresponding vertex index + 1 table index.
516  core::Tensor mesh_structure;
517  try {
518  mesh_structure = core::Tensor::Zeros(
519  {n_blocks, resolution, resolution, resolution, 4},
520  core::Dtype::Int32, block_keys.GetDevice());
521  } catch (const std::runtime_error&) {
523  "[MeshExtractionKernel] Unable to allocate assistance mesh "
524  "structure for Marching "
525  "Cubes with {} active voxel blocks. Please consider using a "
526  "larger voxel size (currently {}) for TSDF "
527  "integration, or using tsdf_volume.cpu() to perform mesh "
528  "extraction on CPU.",
529  n_blocks, voxel_size);
530  }
531 
532  // Real data indexer
533  NDArrayIndexer voxel_block_buffer_indexer(block_values, 4);
534  NDArrayIndexer mesh_structure_indexer(mesh_structure, 4);
535  NDArrayIndexer nb_block_masks_indexer(nb_masks, 2);
536  NDArrayIndexer nb_block_indices_indexer(nb_indices, 2);
537 
538  // Plain arrays that does not require indexers
539  const int64_t* indices_ptr = indices.GetDataPtr<int64_t>();
540  const int64_t* inv_indices_ptr = inv_indices.GetDataPtr<int64_t>();
541  int64_t n = n_blocks * resolution3;
542 
543 #if defined(__CUDACC__)
544  core::kernel::CUDALauncher launcher;
545 #else
546  core::kernel::CPULauncher launcher;
547 #endif
548  int64_t voxel_bytesize = voxel_block_buffer_indexer.ElementByteSize();
549  // Pass 0: analyze mesh structure, set up one-on-one correspondences
550  // from edges to vertices.
551  DISPATCH_BYTESIZE_TO_VOXEL(voxel_bytesize, [&]() {
552  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(int64_t widx) {
553  auto GetVoxelAt = [&] OPEN3D_DEVICE(
554  int xo, int yo, int zo,
555  int curr_block_idx) -> voxel_t* {
556  return DeviceGetVoxelAt<voxel_t>(
557  xo, yo, zo, curr_block_idx,
558  static_cast<int>(resolution), nb_block_masks_indexer,
559  nb_block_indices_indexer, voxel_block_buffer_indexer);
560  };
561 
562  // Natural index (0, N) -> (block_idx, voxel_idx)
563  int64_t workload_block_idx = widx / resolution3;
564  int64_t voxel_idx = widx % resolution3;
565 
566  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
567  int64_t xv, yv, zv;
568  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
569 
570  // Check per-vertex sign in the cube to determine cube
571  // type
572  int table_idx = 0;
573  for (int i = 0; i < 8; ++i) {
574  voxel_t* voxel_ptr_i =
575  GetVoxelAt(static_cast<int>(xv) + vtx_shifts[i][0],
576  static_cast<int>(yv) + vtx_shifts[i][1],
577  static_cast<int>(zv) + vtx_shifts[i][2],
578  static_cast<int>(workload_block_idx));
579  if (voxel_ptr_i == nullptr) return;
580 
581  float tsdf_i = voxel_ptr_i->GetTSDF();
582  float weight_i = voxel_ptr_i->GetWeight();
583  if (weight_i <= weight_threshold) return;
584 
585  table_idx |= ((tsdf_i < 0) ? (1 << i) : 0);
586  }
587 
588  int* mesh_struct_ptr = mesh_structure_indexer.GetDataPtr<int>(
589  xv, yv, zv, workload_block_idx);
590  mesh_struct_ptr[3] = table_idx;
591 
592  if (table_idx == 0 || table_idx == 255) return;
593 
594  // Check per-edge sign determine the cube type
595  int edges_with_vertices = edge_table[table_idx];
596  for (int i = 0; i < 12; ++i) {
597  if (edges_with_vertices & (1 << i)) {
598  int64_t xv_i = xv + edge_shifts[i][0];
599  int64_t yv_i = yv + edge_shifts[i][1];
600  int64_t zv_i = zv + edge_shifts[i][2];
601  int edge_i = edge_shifts[i][3];
602 
603  int dxb = static_cast<int>(xv_i / resolution);
604  int dyb = static_cast<int>(yv_i / resolution);
605  int dzb = static_cast<int>(zv_i / resolution);
606 
607  int nb_idx = (dxb + 1) + (dyb + 1) * 3 + (dzb + 1) * 9;
608 
609  int64_t block_idx_i =
610  *nb_block_indices_indexer.GetDataPtr<int64_t>(
611  workload_block_idx, nb_idx);
612  int* mesh_ptr_i = mesh_structure_indexer.GetDataPtr<int>(
613  xv_i - dxb * resolution, yv_i - dyb * resolution,
614  zv_i - dzb * resolution,
615  inv_indices_ptr[block_idx_i]);
616 
617  // Non-atomic write, but we are safe
618  mesh_ptr_i[edge_i] = -1;
619  }
620  }
621  });
622  });
623 
624  // Pass 1: determine valid number of vertices (if not preset)
625 #if defined(__CUDACC__)
626  core::Tensor count(std::vector<int>{0}, {}, core::Dtype::Int32,
627  block_values.GetDevice());
628 
629  int* count_ptr = count.GetDataPtr<int>();
630 #else
631  std::atomic<int> count_atomic(0);
632  std::atomic<int>* count_ptr = &count_atomic;
633 #endif
634 
635  if (vertex_count < 0) {
636  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(int64_t widx) {
637  // Natural index (0, N) -> (block_idx, voxel_idx)
638  int64_t workload_block_idx = widx / resolution3;
639  int64_t voxel_idx = widx % resolution3;
640 
641  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
642  int64_t xv, yv, zv;
643  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
644 
645  // Obtain voxel's mesh struct ptr
646  int* mesh_struct_ptr = mesh_structure_indexer.GetDataPtr<int>(
647  xv, yv, zv, workload_block_idx);
648 
649  // Early quit -- no allocated vertex to compute
650  if (mesh_struct_ptr[0] != -1 && mesh_struct_ptr[1] != -1 &&
651  mesh_struct_ptr[2] != -1) {
652  return;
653  }
654 
655  // Enumerate 3 edges in the voxel
656  for (int e = 0; e < 3; ++e) {
657  int vertex_idx = mesh_struct_ptr[e];
658  if (vertex_idx != -1) continue;
659 
660  OPEN3D_ATOMIC_ADD(count_ptr, 1);
661  }
662  });
663 
664 #if defined(__CUDACC__)
665  vertex_count = count.Item<int>();
666 #else
667  vertex_count = (*count_ptr).load();
668 #endif
669  }
670 
671  utility::LogDebug("Total vertex count = {}", vertex_count);
672  vertices = core::Tensor({vertex_count, 3}, core::Dtype::Float32,
673  block_values.GetDevice());
674 
675  bool extract_normal = false;
676  NDArrayIndexer normal_indexer;
677  if (normals.has_value()) {
678  extract_normal = true;
679  normals.value().get() =
680  core::Tensor({vertex_count, 3}, core::Dtype::Float32,
681  block_values.GetDevice());
682  normal_indexer = NDArrayIndexer(normals.value().get(), 1);
683  }
684 
685  NDArrayIndexer block_keys_indexer(block_keys, 1);
686  NDArrayIndexer vertex_indexer(vertices, 1);
687 
688 #if defined(__CUDACC__)
689  count = core::Tensor(std::vector<int>{0}, {}, core::Dtype::Int32,
690  block_values.GetDevice());
691  count_ptr = count.GetDataPtr<int>();
692 #else
693  (*count_ptr) = 0;
694 #endif
695 
696  // Pass 2: extract vertices.
697  DISPATCH_BYTESIZE_TO_VOXEL(voxel_bytesize, [&]() {
698  bool extract_color = false;
699  NDArrayIndexer color_indexer;
700  if (voxel_t::HasColor() && colors.has_value()) {
701  extract_color = true;
702  colors.value().get() =
703  core::Tensor({vertex_count, 3}, core::Dtype::Float32,
704  block_values.GetDevice());
705  color_indexer = NDArrayIndexer(colors.value().get(), 1);
706  }
707 
708  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(int64_t widx) {
709  auto GetVoxelAt = [&] OPEN3D_DEVICE(
710  int xo, int yo, int zo,
711  int curr_block_idx) -> voxel_t* {
712  return DeviceGetVoxelAt<voxel_t>(
713  xo, yo, zo, curr_block_idx,
714  static_cast<int>(resolution), nb_block_masks_indexer,
715  nb_block_indices_indexer, voxel_block_buffer_indexer);
716  };
717 
718  auto GetNormalAt = [&] OPEN3D_DEVICE(int xo, int yo, int zo,
719  int curr_block_idx, float* n) {
720  return DeviceGetNormalAt<voxel_t>(
721  xo, yo, zo, curr_block_idx, n,
722  static_cast<int>(resolution), voxel_size,
723  nb_block_masks_indexer, nb_block_indices_indexer,
724  voxel_block_buffer_indexer);
725  };
726 
727  // Natural index (0, N) -> (block_idx, voxel_idx)
728  int64_t workload_block_idx = widx / resolution3;
729  int64_t block_idx = indices_ptr[workload_block_idx];
730  int64_t voxel_idx = widx % resolution3;
731 
732  // block_idx -> (x_block, y_block, z_block)
733  int* block_key_ptr = block_keys_indexer.GetDataPtr<int>(block_idx);
734  int64_t xb = static_cast<int64_t>(block_key_ptr[0]);
735  int64_t yb = static_cast<int64_t>(block_key_ptr[1]);
736  int64_t zb = static_cast<int64_t>(block_key_ptr[2]);
737 
738  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
739  int64_t xv, yv, zv;
740  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
741 
742  // global coordinate (in voxels)
743  int64_t x = xb * resolution + xv;
744  int64_t y = yb * resolution + yv;
745  int64_t z = zb * resolution + zv;
746 
747  // Obtain voxel's mesh struct ptr
748  int* mesh_struct_ptr = mesh_structure_indexer.GetDataPtr<int>(
749  xv, yv, zv, workload_block_idx);
750 
751  // Early quit -- no allocated vertex to compute
752  if (mesh_struct_ptr[0] != -1 && mesh_struct_ptr[1] != -1 &&
753  mesh_struct_ptr[2] != -1) {
754  return;
755  }
756 
757  // Obtain voxel ptr
758  voxel_t* voxel_ptr = voxel_block_buffer_indexer.GetDataPtr<voxel_t>(
759  xv, yv, zv, block_idx);
760  float tsdf_o = voxel_ptr->GetTSDF();
761  float no[3] = {0}, ne[3] = {0};
762 
763  if (extract_normal) {
764  GetNormalAt(static_cast<int>(xv), static_cast<int>(yv),
765  static_cast<int>(zv),
766  static_cast<int>(workload_block_idx), no);
767  }
768 
769  // Enumerate 3 edges in the voxel
770  for (int e = 0; e < 3; ++e) {
771  int vertex_idx = mesh_struct_ptr[e];
772  if (vertex_idx != -1) continue;
773 
774  voxel_t* voxel_ptr_e =
775  GetVoxelAt(static_cast<int>(xv) + (e == 0),
776  static_cast<int>(yv) + (e == 1),
777  static_cast<int>(zv) + (e == 2),
778  static_cast<int>(workload_block_idx));
779  float tsdf_e = voxel_ptr_e->GetTSDF();
780  float ratio = (0 - tsdf_o) / (tsdf_e - tsdf_o);
781 
782  int idx = OPEN3D_ATOMIC_ADD(count_ptr, 1);
783  mesh_struct_ptr[e] = idx;
784 
785  float ratio_x = ratio * int(e == 0);
786  float ratio_y = ratio * int(e == 1);
787  float ratio_z = ratio * int(e == 2);
788 
789  float* vertex_ptr = vertex_indexer.GetDataPtr<float>(idx);
790  vertex_ptr[0] = voxel_size * (x + ratio_x);
791  vertex_ptr[1] = voxel_size * (y + ratio_y);
792  vertex_ptr[2] = voxel_size * (z + ratio_z);
793 
794  if (extract_normal) {
795  float* normal_ptr = normal_indexer.GetDataPtr<float>(idx);
796  GetNormalAt(static_cast<int>(xv) + (e == 0),
797  static_cast<int>(yv) + (e == 1),
798  static_cast<int>(zv) + (e == 2),
799  static_cast<int>(workload_block_idx), ne);
800  float nx = (1 - ratio) * no[0] + ratio * ne[0];
801  float ny = (1 - ratio) * no[1] + ratio * ne[1];
802  float nz = (1 - ratio) * no[2] + ratio * ne[2];
803  float norm = static_cast<float>(
804  sqrt(nx * nx + ny * ny + nz * nz) + 1e-5);
805  normal_ptr[0] = nx / norm;
806  normal_ptr[1] = ny / norm;
807  normal_ptr[2] = nz / norm;
808  }
809 
810  if (extract_color) {
811  float* color_ptr = color_indexer.GetDataPtr<float>(idx);
812  float r_o = voxel_ptr->GetR();
813  float g_o = voxel_ptr->GetG();
814  float b_o = voxel_ptr->GetB();
815 
816  float r_e = voxel_ptr_e->GetR();
817  float g_e = voxel_ptr_e->GetG();
818  float b_e = voxel_ptr_e->GetB();
819  color_ptr[0] = ((1 - ratio) * r_o + ratio * r_e) / 255.0f;
820  color_ptr[1] = ((1 - ratio) * g_o + ratio * g_e) / 255.0f;
821  color_ptr[2] = ((1 - ratio) * b_o + ratio * b_e) / 255.0f;
822  }
823  }
824  });
825  });
826 
827  // Pass 3: connect vertices and form triangles.
828  int triangle_count = vertex_count * 3;
829  triangles = core::Tensor({triangle_count, 3}, core::Dtype::Int64,
830  block_values.GetDevice());
831  NDArrayIndexer triangle_indexer(triangles, 1);
832 
833 #if defined(__CUDACC__)
834  count = core::Tensor(std::vector<int>{0}, {}, core::Dtype::Int32,
835  block_values.GetDevice());
836  count_ptr = count.GetDataPtr<int>();
837 #else
838  (*count_ptr) = 0;
839 #endif
840  launcher.LaunchGeneralKernel(n, [=] OPEN3D_DEVICE(int64_t widx) {
841  // Natural index (0, N) -> (block_idx, voxel_idx)
842  int64_t workload_block_idx = widx / resolution3;
843  int64_t voxel_idx = widx % resolution3;
844 
845  // voxel_idx -> (x_voxel, y_voxel, z_voxel)
846  int64_t xv, yv, zv;
847  voxel_indexer.WorkloadToCoord(voxel_idx, &xv, &yv, &zv);
848 
849  // Obtain voxel's mesh struct ptr
850  int* mesh_struct_ptr = mesh_structure_indexer.GetDataPtr<int>(
851  xv, yv, zv, workload_block_idx);
852 
853  int table_idx = mesh_struct_ptr[3];
854  if (tri_count[table_idx] == 0) return;
855 
856  for (size_t tri = 0; tri < 16; tri += 3) {
857  if (tri_table[table_idx][tri] == -1) return;
858 
859  int tri_idx = OPEN3D_ATOMIC_ADD(count_ptr, 1);
860 
861  for (size_t vertex = 0; vertex < 3; ++vertex) {
862  int edge = tri_table[table_idx][tri + vertex];
863 
864  int64_t xv_i = xv + edge_shifts[edge][0];
865  int64_t yv_i = yv + edge_shifts[edge][1];
866  int64_t zv_i = zv + edge_shifts[edge][2];
867  int64_t edge_i = edge_shifts[edge][3];
868 
869  int dxb = static_cast<int>(xv_i / resolution);
870  int dyb = static_cast<int>(yv_i / resolution);
871  int dzb = static_cast<int>(zv_i / resolution);
872 
873  int nb_idx = (dxb + 1) + (dyb + 1) * 3 + (dzb + 1) * 9;
874 
875  int64_t block_idx_i =
876  *nb_block_indices_indexer.GetDataPtr<int64_t>(
877  workload_block_idx, nb_idx);
878  int* mesh_struct_ptr_i = mesh_structure_indexer.GetDataPtr<int>(
879  xv_i - dxb * resolution, yv_i - dyb * resolution,
880  zv_i - dzb * resolution, inv_indices_ptr[block_idx_i]);
881 
882  int64_t* triangle_ptr =
883  triangle_indexer.GetDataPtr<int64_t>(tri_idx);
884  triangle_ptr[2 - vertex] = mesh_struct_ptr_i[edge_i];
885  }
886  }
887  });
888 
889 #if defined(__CUDACC__)
890  triangle_count = count.Item<int>();
891 #else
892  triangle_count = (*count_ptr).load();
893 #endif
894  utility::LogInfo("Total triangle count = {}", triangle_count);
895  triangles = triangles.Slice(0, 0, triangle_count);
896 }
897 
898 #if defined(__CUDACC__)
899 void EstimateRangeCUDA
900 #else
901 void EstimateRangeCPU
902 #endif
903  (const core::Tensor& block_keys,
904  core::Tensor& range_minmax_map,
905  const core::Tensor& intrinsics,
906  const core::Tensor& extrinsics,
907  int h,
908  int w,
909  int down_factor,
910  int64_t block_resolution,
911  float voxel_size,
912  float depth_min,
913  float depth_max) {
914 
915  // TODO(wei): reserve it in a reusable buffer
916 
917  // Every 2 channels: (min, max)
918  int h_down = h / down_factor;
919  int w_down = w / down_factor;
920  range_minmax_map = core::Tensor({h_down, w_down, 2}, core::Dtype::Float32,
921  block_keys.GetDevice());
922  NDArrayIndexer range_map_indexer(range_minmax_map, 2);
923 
924  // Every 6 channels: (v_min, u_min, v_max, u_max, z_min, z_max)
925  const int fragment_size = 16;
926  const int frag_buffer_size = 65535;
927 
928  // TODO(wei): explicit buffer
929  core::Tensor fragment_buffer =
930  core::Tensor({frag_buffer_size, 6}, core::Dtype::Float32,
931  block_keys.GetDevice());
932 
933  NDArrayIndexer frag_buffer_indexer(fragment_buffer, 1);
934  NDArrayIndexer block_keys_indexer(block_keys, 1);
935  TransformIndexer w2c_transform_indexer(intrinsics, extrinsics);
936 #if defined(__CUDACC__)
937  core::Tensor count(std::vector<int>{0}, {1}, core::Dtype::Int32,
938  block_keys.GetDevice());
939  int* count_ptr = count.GetDataPtr<int>();
940 #else
941  std::atomic<int> count_atomic(0);
942  std::atomic<int>* count_ptr = &count_atomic;
943 #endif
944 #if defined(__CUDACC__)
945  core::kernel::CUDALauncher launcher;
946 #else
947  core::kernel::CPULauncher launcher;
948  using std::max;
949  using std::min;
950 #endif
951 
952  // Pass 0: iterate over blocks, fill-in an rendering fragment array
953  launcher.LaunchGeneralKernel(
954  block_keys.GetLength(), [=] OPEN3D_DEVICE(int64_t workload_idx) {
955  int* key = block_keys_indexer.GetDataPtr<int>(workload_idx);
956 
957  int u_min = w_down - 1, v_min = h_down - 1, u_max = 0,
958  v_max = 0;
959  float z_min = depth_max, z_max = depth_min;
960 
961  float xc, yc, zc, u, v;
962 
963  // Project 8 corners to low-res image and form a rectangle
964  for (int i = 0; i < 8; ++i) {
965  float xw = (key[0] + ((i & 1) > 0)) * block_resolution *
966  voxel_size;
967  float yw = (key[1] + ((i & 2) > 0)) * block_resolution *
968  voxel_size;
969  float zw = (key[2] + ((i & 4) > 0)) * block_resolution *
970  voxel_size;
971 
972  w2c_transform_indexer.RigidTransform(xw, yw, zw, &xc, &yc,
973  &zc);
974  if (zc <= 0) continue;
975 
976  // Project to the down sampled image buffer
977  w2c_transform_indexer.Project(xc, yc, zc, &u, &v);
978  u /= down_factor;
979  v /= down_factor;
980 
981  v_min = min(static_cast<int>(floorf(v)), v_min);
982  v_max = max(static_cast<int>(ceilf(v)), v_max);
983 
984  u_min = min(static_cast<int>(floorf(u)), u_min);
985  u_max = max(static_cast<int>(ceilf(u)), u_max);
986 
987  z_min = min(z_min, zc);
988  z_max = max(z_max, zc);
989  }
990 
991  v_min = max(0, v_min);
992  v_max = min(h_down - 1, v_max);
993 
994  u_min = max(0, u_min);
995  u_max = min(w_down - 1, u_max);
996 
997  if (v_min >= v_max || u_min >= u_max || z_min >= z_max) return;
998 
999  // Divide the rectangle into small 16x16 fragments
1000  int frag_v_count =
1001  ceilf(float(v_max - v_min + 1) / float(fragment_size));
1002  int frag_u_count =
1003  ceilf(float(u_max - u_min + 1) / float(fragment_size));
1004 
1005  int frag_count = frag_v_count * frag_u_count;
1006  int frag_count_start = OPEN3D_ATOMIC_ADD(count_ptr, 1);
1007  int frag_count_end = frag_count_start + frag_count;
1008  if (frag_count_end >= frag_buffer_size) {
1009  printf("Fragment count exceeding buffer size, abort!\n");
1010  }
1011 
1012  int offset = 0;
1013  for (int frag_v = 0; frag_v < frag_v_count; ++frag_v) {
1014  for (int frag_u = 0; frag_u < frag_u_count;
1015  ++frag_u, ++offset) {
1016  float* frag_ptr = frag_buffer_indexer.GetDataPtr<float>(
1017  frag_count_start + offset);
1018  // zmin, zmax
1019  frag_ptr[0] = z_min;
1020  frag_ptr[1] = z_max;
1021 
1022  // vmin, umin
1023  frag_ptr[2] = v_min + frag_v * fragment_size;
1024  frag_ptr[3] = u_min + frag_u * fragment_size;
1025 
1026  // vmax, umax
1027  frag_ptr[4] = min(frag_ptr[2] + fragment_size - 1,
1028  static_cast<float>(v_max));
1029  frag_ptr[5] = min(frag_ptr[3] + fragment_size - 1,
1030  static_cast<float>(u_max));
1031  }
1032  }
1033  });
1034 #if defined(__CUDACC__)
1035  int frag_count = count[0].Item<int>();
1036 #else
1037  int frag_count = (*count_ptr).load();
1038 #endif
1039 
1040  // Pass 0.5: Fill in range map to prepare for atomic min/max
1041  launcher.LaunchGeneralKernel(
1042  h_down * w_down, [=] OPEN3D_DEVICE(int64_t workload_idx) {
1043  int v = workload_idx / w_down;
1044  int u = workload_idx % w_down;
1045  float* range_ptr = range_map_indexer.GetDataPtr<float>(u, v);
1046  range_ptr[0] = depth_max;
1047  range_ptr[1] = depth_min;
1048  });
1049 
1050  // Pass 1: iterate over rendering fragment array, fill-in range
1051  launcher.LaunchGeneralKernel(
1052  frag_count * fragment_size * fragment_size,
1053  [=] OPEN3D_DEVICE(int64_t workload_idx) {
1054  int frag_idx = workload_idx / (fragment_size * fragment_size);
1055  int local_idx = workload_idx % (fragment_size * fragment_size);
1056  int dv = local_idx / fragment_size;
1057  int du = local_idx % fragment_size;
1058 
1059  float* frag_ptr =
1060  frag_buffer_indexer.GetDataPtr<float>(frag_idx);
1061  int v_min = static_cast<int>(frag_ptr[2]);
1062  int u_min = static_cast<int>(frag_ptr[3]);
1063  int v_max = static_cast<int>(frag_ptr[4]);
1064  int u_max = static_cast<int>(frag_ptr[5]);
1065 
1066  int v = v_min + dv;
1067  int u = u_min + du;
1068  if (v > v_max || u > u_max) return;
1069 
1070  float z_min = frag_ptr[0];
1071  float z_max = frag_ptr[1];
1072  float* range_ptr = range_map_indexer.GetDataPtr<float>(u, v);
1073 #ifdef __CUDACC__
1074  atomicMinf(&(range_ptr[0]), z_min);
1075  atomicMaxf(&(range_ptr[1]), z_max);
1076 #else
1077 #pragma omp critical
1078  {
1079  range_ptr[0] = min(z_min, range_ptr[0]);
1080  range_ptr[1] = max(z_max, range_ptr[1]);
1081  }
1082 #endif
1083  });
1084 #if defined(__CUDACC__)
1085  OPEN3D_CUDA_CHECK(cudaDeviceSynchronize());
1086 #endif
1087 }
1088 
1089 struct BlockCache {
1090  int x;
1091  int y;
1092  int z;
1094 
1095  inline int OPEN3D_DEVICE Check(int xin, int yin, int zin) {
1096  return (xin == x && yin == y && zin == z) ? block_idx : -1;
1097  }
1098 
1099  inline void OPEN3D_DEVICE Update(int xin,
1100  int yin,
1101  int zin,
1102  int block_idx_in) {
1103  x = xin;
1104  y = yin;
1105  z = zin;
1106  block_idx = block_idx_in;
1107  }
1108 };
1109 
1110 #if defined(__CUDACC__)
1111 void RayCastCUDA
1112 #else
1113 void RayCastCPU
1114 #endif
1115  (std::shared_ptr<core::DeviceHashmap>& hashmap,
1116  const core::Tensor& block_values,
1117  const core::Tensor& range_map,
1118  core::Tensor& vertex_map,
1119  core::Tensor& depth_map,
1120  core::Tensor& color_map,
1121  core::Tensor& normal_map,
1122  const core::Tensor& intrinsics,
1123  const core::Tensor& extrinsics,
1124  int h,
1125  int w,
1126  int64_t block_resolution,
1127  float voxel_size,
1128  float sdf_trunc,
1129  float depth_scale,
1130  float depth_min,
1131  float depth_max,
1132  float weight_threshold) {
1133  using Key = core::Block<int, 3>;
1134  using Hash = core::BlockHash<int, 3>;
1135 
1136 #if defined(BUILD_CUDA_MODULE) && defined(__CUDACC__)
1137  auto cuda_hashmap =
1138  std::dynamic_pointer_cast<core::StdGPUHashmap<Key, Hash>>(hashmap);
1139  if (cuda_hashmap == nullptr) {
1141  "Unsupported backend: CUDA raycasting only supports STDGPU.");
1142  }
1143  auto hashmap_impl = cuda_hashmap->GetImpl();
1144 #else
1145  auto cpu_hashmap =
1146  std::dynamic_pointer_cast<core::TBBHashmap<Key, Hash>>(hashmap);
1147  auto hashmap_impl = *cpu_hashmap->GetImpl();
1148 #endif
1149 
1150  NDArrayIndexer voxel_block_buffer_indexer(block_values, 4);
1151  NDArrayIndexer range_map_indexer(range_map, 2);
1152 
1153  NDArrayIndexer vertex_map_indexer;
1154  NDArrayIndexer depth_map_indexer;
1155  NDArrayIndexer color_map_indexer;
1156  NDArrayIndexer normal_map_indexer;
1157 
1158  bool enable_vertex = (vertex_map.GetLength() != 0);
1159  bool enable_depth = (depth_map.GetLength() != 0);
1160  bool enable_color = (color_map.GetLength() != 0);
1161  bool enable_normal = (normal_map.GetLength() != 0);
1162  if (!enable_vertex && !enable_depth && !enable_color && !enable_normal) {
1163  utility::LogWarning("No output specified for ray casting, exit.");
1164  return;
1165  }
1166 
1167  if (enable_vertex) {
1168  vertex_map_indexer = NDArrayIndexer(vertex_map, 2);
1169  }
1170  if (enable_depth) {
1171  depth_map_indexer = NDArrayIndexer(depth_map, 2);
1172  }
1173  if (enable_color) {
1174  color_map_indexer = NDArrayIndexer(color_map, 2);
1175  }
1176  if (enable_normal) {
1177  normal_map_indexer = NDArrayIndexer(normal_map, 2);
1178  }
1179 
1180  TransformIndexer c2w_transform_indexer(
1181  intrinsics, t::geometry::InverseTransformation(extrinsics));
1182  TransformIndexer w2c_transform_indexer(intrinsics, extrinsics);
1183 
1184  int64_t rows = h;
1185  int64_t cols = w;
1186 
1187  float block_size = voxel_size * block_resolution;
1188 #if defined(BUILD_CUDA_MODULE) && defined(__CUDACC__)
1189  core::kernel::CUDALauncher launcher;
1190 #else
1191  core::kernel::CPULauncher launcher;
1192  using std::max;
1193 #endif
1194 
1195  DISPATCH_BYTESIZE_TO_VOXEL(voxel_block_buffer_indexer.ElementByteSize(), [&]() {
1196  launcher.LaunchGeneralKernel(
1197  rows * cols, [=] OPEN3D_DEVICE(int64_t workload_idx) {
1198  auto GetVoxelAtP = [&] OPEN3D_DEVICE(
1199  int x_b, int y_b, int z_b,
1200  int x_v, int y_v, int z_v,
1201  core::addr_t block_addr,
1202  BlockCache& cache) -> voxel_t* {
1203  int x_vn = (x_v + block_resolution) % block_resolution;
1204  int y_vn = (y_v + block_resolution) % block_resolution;
1205  int z_vn = (z_v + block_resolution) % block_resolution;
1206 
1207  int dx_b = Sign(x_v - x_vn);
1208  int dy_b = Sign(y_v - y_vn);
1209  int dz_b = Sign(z_v - z_vn);
1210 
1211  if (dx_b == 0 && dy_b == 0 && dz_b == 0) {
1212  return voxel_block_buffer_indexer
1213  .GetDataPtr<voxel_t>(x_v, y_v, z_v,
1214  block_addr);
1215  } else {
1216  Key key;
1217  key.Set(0, x_b + dx_b);
1218  key.Set(1, y_b + dy_b);
1219  key.Set(2, z_b + dz_b);
1220 
1221  int block_addr = cache.Check(key.Get(0), key.Get(1),
1222  key.Get(2));
1223  if (block_addr < 0) {
1224  auto iter = hashmap_impl.find(key);
1225  if (iter == hashmap_impl.end()) return nullptr;
1226  block_addr = iter->second;
1227  cache.Update(key.Get(0), key.Get(1), key.Get(2),
1228  block_addr);
1229  }
1230 
1231  return voxel_block_buffer_indexer
1232  .GetDataPtr<voxel_t>(x_vn, y_vn, z_vn,
1233  block_addr);
1234  }
1235  };
1236 
1237  auto GetVoxelAtT = [&] OPEN3D_DEVICE(
1238  float x_o, float y_o, float z_o,
1239  float x_d, float y_d, float z_d,
1240  float t,
1241  BlockCache& cache) -> voxel_t* {
1242  float x_g = x_o + t * x_d;
1243  float y_g = y_o + t * y_d;
1244  float z_g = z_o + t * z_d;
1245 
1246  // Block coordinate and look up
1247  int x_b = static_cast<int>(floorf(x_g / block_size));
1248  int y_b = static_cast<int>(floorf(y_g / block_size));
1249  int z_b = static_cast<int>(floorf(z_g / block_size));
1250 
1251  Key key;
1252  key.Set(0, x_b);
1253  key.Set(1, y_b);
1254  key.Set(2, z_b);
1255 
1256  int block_addr = cache.Check(x_b, y_b, z_b);
1257  if (block_addr < 0) {
1258  auto iter = hashmap_impl.find(key);
1259  if (iter == hashmap_impl.end()) return nullptr;
1260  block_addr = iter->second;
1261  cache.Update(x_b, y_b, z_b, block_addr);
1262  }
1263 
1264  // Voxel coordinate and look up
1265  int x_v = int((x_g - x_b * block_size) / voxel_size);
1266  int y_v = int((y_g - y_b * block_size) / voxel_size);
1267  int z_v = int((z_g - z_b * block_size) / voxel_size);
1268  return voxel_block_buffer_indexer.GetDataPtr<voxel_t>(
1269  x_v, y_v, z_v, block_addr);
1270  };
1271 
1272  int64_t y = workload_idx / cols;
1273  int64_t x = workload_idx % cols;
1274 
1275  float *depth_ptr = nullptr, *vertex_ptr = nullptr,
1276  *normal_ptr = nullptr, *color_ptr = nullptr;
1277  if (enable_depth) {
1278  depth_ptr = depth_map_indexer.GetDataPtr<float>(x, y);
1279  *depth_ptr = 0;
1280  }
1281  if (enable_vertex) {
1282  vertex_ptr = vertex_map_indexer.GetDataPtr<float>(x, y);
1283  vertex_ptr[0] = 0;
1284  vertex_ptr[1] = 0;
1285  vertex_ptr[2] = 0;
1286  }
1287  if (enable_color) {
1288  color_ptr = color_map_indexer.GetDataPtr<float>(x, y);
1289  color_ptr[0] = 0;
1290  color_ptr[1] = 0;
1291  color_ptr[2] = 0;
1292  }
1293  if (enable_normal) {
1294  normal_ptr = normal_map_indexer.GetDataPtr<float>(x, y);
1295  normal_ptr[0] = 0;
1296  normal_ptr[1] = 0;
1297  normal_ptr[2] = 0;
1298  }
1299 
1300  const float* range =
1301  range_map_indexer.GetDataPtr<float>(x / 8, y / 8);
1302  float t = range[0];
1303  const float t_max = range[1];
1304  if (t >= t_max) return;
1305 
1306  // Coordinates in camera and global
1307  float x_c = 0, y_c = 0, z_c = 0;
1308  float x_g = 0, y_g = 0, z_g = 0;
1309  float x_o = 0, y_o = 0, z_o = 0;
1310 
1311  // Iterative ray intersection check
1312  float t_prev = t;
1313 
1314  float tsdf_prev = -1.0f;
1315  float tsdf = 1.0;
1316  float w = 0.0;
1317 
1318  // Camera origin
1319  c2w_transform_indexer.RigidTransform(0, 0, 0, &x_o, &y_o,
1320  &z_o);
1321 
1322  // Direction
1323  c2w_transform_indexer.Unproject(static_cast<float>(x),
1324  static_cast<float>(y), 1.0f,
1325  &x_c, &y_c, &z_c);
1326  c2w_transform_indexer.RigidTransform(x_c, y_c, z_c, &x_g,
1327  &y_g, &z_g);
1328  float x_d = (x_g - x_o);
1329  float y_d = (y_g - y_o);
1330  float z_d = (z_g - z_o);
1331 
1332  BlockCache cache{0, 0, 0, -1};
1333  bool surface_found = false;
1334  while (t < t_max) {
1335  voxel_t* voxel_ptr = GetVoxelAtT(x_o, y_o, z_o, x_d,
1336  y_d, z_d, t, cache);
1337 
1338  if (!voxel_ptr) {
1339  t_prev = t;
1340  t += block_size;
1341  } else {
1342  tsdf_prev = tsdf;
1343  tsdf = voxel_ptr->GetTSDF();
1344  w = voxel_ptr->GetWeight();
1345  if (tsdf_prev > 0 && w >= weight_threshold &&
1346  tsdf <= 0) {
1347  surface_found = true;
1348  break;
1349  }
1350  t_prev = t;
1351  float delta = tsdf * sdf_trunc;
1352  t += delta < voxel_size ? voxel_size : delta;
1353  }
1354  }
1355 
1356  if (surface_found) {
1357  float t_intersect = (t * tsdf_prev - t_prev * tsdf) /
1358  (tsdf_prev - tsdf);
1359  x_g = x_o + t_intersect * x_d;
1360  y_g = y_o + t_intersect * y_d;
1361  z_g = z_o + t_intersect * z_d;
1362 
1363  // Trivial vertex assignment
1364  if (enable_depth) {
1365  *depth_ptr = t_intersect * depth_scale;
1366  }
1367  if (enable_vertex) {
1368  w2c_transform_indexer.RigidTransform(
1369  x_g, y_g, z_g, vertex_ptr + 0,
1370  vertex_ptr + 1, vertex_ptr + 2);
1371  }
1372 
1373  // Trilinear interpolation
1374  // TODO(wei): simplify the flow by splitting the
1375  // functions given what is enabled
1376  if (enable_color || enable_normal) {
1377  int x_b =
1378  static_cast<int>(floorf(x_g / block_size));
1379  int y_b =
1380  static_cast<int>(floorf(y_g / block_size));
1381  int z_b =
1382  static_cast<int>(floorf(z_g / block_size));
1383  float x_v = (x_g - float(x_b) * block_size) /
1384  voxel_size;
1385  float y_v = (y_g - float(y_b) * block_size) /
1386  voxel_size;
1387  float z_v = (z_g - float(z_b) * block_size) /
1388  voxel_size;
1389 
1390  Key key;
1391  key.Set(0, x_b);
1392  key.Set(1, y_b);
1393  key.Set(2, z_b);
1394 
1395  int block_addr = cache.Check(x_b, y_b, z_b);
1396  if (block_addr < 0) {
1397  auto iter = hashmap_impl.find(key);
1398  if (iter == hashmap_impl.end()) return;
1399  block_addr = iter->second;
1400  cache.Update(x_b, y_b, z_b, block_addr);
1401  }
1402 
1403  int x_v_floor = static_cast<int>(floorf(x_v));
1404  int y_v_floor = static_cast<int>(floorf(y_v));
1405  int z_v_floor = static_cast<int>(floorf(z_v));
1406 
1407  float ratio_x = x_v - float(x_v_floor);
1408  float ratio_y = y_v - float(y_v_floor);
1409  float ratio_z = z_v - float(z_v_floor);
1410 
1411  float sum_weight_color = 0.0;
1412  float sum_weight_normal = 0.0;
1413  for (int k = 0; k < 8; ++k) {
1414  int dx_v = (k & 1) > 0 ? 1 : 0;
1415  int dy_v = (k & 2) > 0 ? 1 : 0;
1416  int dz_v = (k & 4) > 0 ? 1 : 0;
1417  float ratio = (dx_v * (ratio_x) +
1418  (1 - dx_v) * (1 - ratio_x)) *
1419  (dy_v * (ratio_y) +
1420  (1 - dy_v) * (1 - ratio_y)) *
1421  (dz_v * (ratio_z) +
1422  (1 - dz_v) * (1 - ratio_z));
1423 
1424  voxel_t* voxel_ptr_k = GetVoxelAtP(
1425  x_b, y_b, z_b, x_v_floor + dx_v,
1426  y_v_floor + dy_v, z_v_floor + dz_v,
1427  block_addr, cache);
1428 
1429  if (enable_color && voxel_ptr_k &&
1430  voxel_ptr_k->GetWeight() > 0) {
1431  sum_weight_color += ratio;
1432  color_ptr[0] += ratio * voxel_ptr_k->GetR();
1433  color_ptr[1] += ratio * voxel_ptr_k->GetG();
1434  color_ptr[2] += ratio * voxel_ptr_k->GetB();
1435  }
1436 
1437  if (enable_normal) {
1438  for (int dim = 0; dim < 3; ++dim) {
1439  voxel_t* voxel_ptr_k_plus = GetVoxelAtP(
1440  x_b, y_b, z_b,
1441  x_v_floor + dx_v + (dim == 0),
1442  y_v_floor + dy_v + (dim == 1),
1443  z_v_floor + dz_v + (dim == 2),
1444  block_addr, cache);
1445  voxel_t* voxel_ptr_k_minus =
1446  GetVoxelAtP(x_b, y_b, z_b,
1447  x_v_floor + dx_v -
1448  (dim == 0),
1449  y_v_floor + dy_v -
1450  (dim == 1),
1451  z_v_floor + dz_v -
1452  (dim == 2),
1453  block_addr, cache);
1454 
1455  bool valid = false;
1456  if (voxel_ptr_k_plus &&
1457  voxel_ptr_k_plus->GetWeight() > 0) {
1458  normal_ptr[dim] +=
1459  ratio *
1460  voxel_ptr_k_plus
1461  ->GetTSDF() /
1462  (2 * voxel_size);
1463  valid = true;
1464  }
1465 
1466  if (voxel_ptr_k_minus &&
1467  voxel_ptr_k_minus->GetWeight() >
1468  0) {
1469  normal_ptr[dim] -=
1470  ratio *
1471  voxel_ptr_k_minus
1472  ->GetTSDF() /
1473  (2 * voxel_size);
1474  valid = true;
1475  }
1476  sum_weight_normal += valid ? ratio : 0;
1477  }
1478  } // if (enable_normal)
1479  } // loop over 8 neighbors
1480 
1481  if (enable_color && sum_weight_color > 0) {
1482  sum_weight_color *= 255.0;
1483  color_ptr[0] /= sum_weight_color;
1484  color_ptr[1] /= sum_weight_color;
1485  color_ptr[2] /= sum_weight_color;
1486  }
1487  if (enable_normal && sum_weight_normal > 0) {
1488  normal_ptr[0] /= sum_weight_normal;
1489  normal_ptr[1] /= sum_weight_normal;
1490  normal_ptr[2] /= sum_weight_normal;
1491  float norm =
1492  sqrt(normal_ptr[0] * normal_ptr[0] +
1493  normal_ptr[1] * normal_ptr[1] +
1494  normal_ptr[2] * normal_ptr[2]);
1495  w2c_transform_indexer.Rotate(
1496  normal_ptr[0] / norm,
1497  normal_ptr[1] / norm,
1498  normal_ptr[2] / norm, normal_ptr + 0,
1499  normal_ptr + 1, normal_ptr + 2);
1500  }
1501  } // if (color or normal)
1502  } // if (tsdf < 0)
1503  });
1504  });
1505 
1506 #if defined(__CUDACC__)
1507  OPEN3D_CUDA_CHECK(cudaDeviceSynchronize());
1508 #endif
1509 }
1510 
1511 } // namespace tsdf
1512 } // namespace kernel
1513 } // namespace geometry
1514 } // namespace t
1515 } // namespace open3d
OPEN3D_HOST_DEVICE void Rotate(float x_in, float y_in, float z_in, float *x_out, float *y_out, float *z_out) const
Transform a 3D coordinate in camera coordinate to world coordinate.
Definition: GeometryIndexer.h:98
Definition: TSDFVoxelGridImpl.h:1089
void ReleaseCache()
Definition: CUDAUtils.cpp:56
Definition: GeometryIndexer.h:168
OPEN3D_HOST_DEVICE void Project(float x_in, float y_in, float z_in, float *u_out, float *v_out) const
Project a 3D coordinate in camera coordinate to a 2D uv coordinate.
Definition: GeometryIndexer.h:117
Definition: CPULauncher.h:42
void ExtractSurfaceMeshCPU(const core::Tensor &block_indices, const core::Tensor &inv_block_indices, const core::Tensor &nb_block_indices, const core::Tensor &nb_block_masks, const core::Tensor &block_keys, const core::Tensor &block_values, core::Tensor &vertices, core::Tensor &triangles, utility::optional< std::reference_wrapper< core::Tensor >> vertex_normals, utility::optional< std::reference_wrapper< core::Tensor >> vertex_colors, int64_t block_resolution, float voxel_size, float weight_threshold, int &vertex_count)
Definition: TSDFVoxelGridImpl.h:489
OPEN3D_HOST_DEVICE int Sign(int x)
Definition: GeometryMacros.h:62
#define LogWarning(...)
Definition: Console.h:95
int block_idx
Definition: TSDFVoxelGridImpl.h:1093
int offset
Definition: FilePCD.cpp:64
Helper class for converting coordinates/indices between 3D/3D, 3D/2D, 2D/3D.
Definition: GeometryIndexer.h:42
#define OPEN3D_CUDA_CHECK(err)
Definition: CUDAUtils.h:59
std::shared_ptr< tbb::concurrent_unordered_map< Key, addr_t, Hash > > GetImpl() const
Definition: TBBHashmap.h:79
Definition: Dispatch.h:115
void EstimateRangeCPU(const core::Tensor &block_keys, core::Tensor &range_minmax_map, const core::Tensor &intrinsics, const core::Tensor &extrinsics, int h, int w, int down_factor, int64_t block_resolution, float voxel_size, float depth_min, float depth_max)
Definition: TSDFVoxelGridImpl.h:903
Device GetDevice() const
Definition: Tensor.cpp:1098
Definition: StdGPUHashmap.h:46
#define LogError(...)
Definition: Console.h:79
void OPEN3D_DEVICE Update(int xin, int yin, int zin, int block_idx_in)
Definition: TSDFVoxelGridImpl.h:1099
#define OPEN3D_DEVICE
Definition: CUDAUtils.h:57
int x
Definition: TSDFVoxelGridImpl.h:1090
OPEN3D_HOST_DEVICE void Unproject(float u_in, float v_in, float d_in, float *x_out, float *y_out, float *z_out) const
Unproject a 2D uv coordinate with depth to 3D in camera coordinate.
Definition: GeometryIndexer.h:128
#define OPEN3D_ATOMIC_ADD(X, Y)
Definition: GeometryMacros.h:37
static const Dtype Int32
Definition: Dtype.h:46
OPEN3D_HOST_DEVICE void RigidTransform(float x_in, float y_in, float z_in, float *x_out, float *y_out, float *z_out) const
Transform a 3D coordinate in camera coordinate to world coordinate.
Definition: GeometryIndexer.h:79
OPEN3D_HOST_DEVICE T * GetDataPtr(int64_t x) const
Definition: GeometryIndexer.h:324
math::float4 color
Definition: LineSetBuffers.cpp:64
int y
Definition: TSDFVoxelGridImpl.h:1091
core::Tensor InverseTransformation(const core::Tensor &T)
TODO(wei): find a proper place for such functionalities.
Definition: Utility.h:36
Definition: Dispatch.h:72
const char const char value recording_handle imu_sample recording_handle uint8_t size_t data_size k4a_record_configuration_t config target_format k4a_capture_t capture_handle k4a_imu_sample_t imu_sample playback_handle k4a_logging_message_cb_t void min_level device_handle k4a_imu_sample_t timeout_in_ms capture_handle capture_handle capture_handle image_handle temperature_c int
Definition: K4aPlugin.cpp:479
static Tensor Zeros(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with zeros.
Definition: Tensor.cpp:240
#define LogInfo(...)
Definition: Console.h:108
int count
Definition: FilePCD.cpp:61
static const Dtype Float32
Definition: Dtype.h:42
void ExtractSurfacePointsCPU(const core::Tensor &block_indices, const core::Tensor &nb_block_indices, const core::Tensor &nb_block_masks, const core::Tensor &block_keys, const core::Tensor &block_values, core::Tensor &points, utility::optional< std::reference_wrapper< core::Tensor >> normals, utility::optional< std::reference_wrapper< core::Tensor >> colors, int64_t block_resolution, float voxel_size, float weight_threshold, int &valid_size)
Definition: TSDFVoxelGridImpl.h:177
Definition: Optional.h:54
static void LaunchGeneralKernel(int64_t n, func_t element_kernel)
General kernels with non-conventional indexers.
Definition: CPULauncher.h:176
int OPEN3D_DEVICE Check(int xin, int yin, int zin)
Definition: TSDFVoxelGridImpl.h:1095
int points
Definition: FilePCD.cpp:73
static const Dtype Int64
Definition: Dtype.h:47
#define DISPATCH_BYTESIZE_TO_VOXEL(BYTESIZE,...)
Definition: TSDFVoxel.h:33
Definition: PinholeCameraIntrinsic.cpp:35
#define LogDebug(...)
Definition: Console.h:121
Definition: Tensor.h:50
const char const char value recording_handle imu_sample recording_handle uint8_t size_t data_size k4a_record_configuration_t config target_format k4a_capture_t capture_handle k4a_imu_sample_t imu_sample playback_handle k4a_logging_message_cb_t void min_level device_handle k4a_imu_sample_t timeout_in_ms capture_handle capture_handle capture_handle image_handle float
Definition: K4aPlugin.cpp:465
uint32_t addr_t
Definition: HashmapBuffer.h:58
OPEN3D_HOST_DEVICE int64_t ElementByteSize()
Definition: GeometryIndexer.h:234
void RayCastCPU(std::shared_ptr< core::DeviceHashmap > &hashmap, const core::Tensor &block_values, const core::Tensor &range_map, core::Tensor &vertex_map, core::Tensor &depth_map, core::Tensor &color_map, core::Tensor &normal_map, const core::Tensor &intrinsics, const core::Tensor &extrinsics, int h, int w, int64_t block_resolution, float voxel_size, float sdf_trunc, float depth_scale, float depth_min, float depth_max, float weight_threshold)
Definition: TSDFVoxelGridImpl.h:1115
T * GetDataPtr()
Definition: Tensor.h:1005
void IntegrateCPU(const core::Tensor &depth, const core::Tensor &color, const core::Tensor &block_indices, const core::Tensor &block_keys, core::Tensor &block_values, const core::Tensor &intrinsics, const core::Tensor &extrinsics, int64_t resolution, float voxel_size, float sdf_trunc, float depth_scale, float depth_max)
Definition: TSDFVoxelGridImpl.h:54
OPEN3D_HOST_DEVICE bool InBoundary(float x, float y) const
Definition: GeometryIndexer.h:302
Definition: TBBHashmap.h:40
int64_t GetLength() const
Definition: Tensor.h:986
int z
Definition: TSDFVoxelGridImpl.h:1092
#define max(x, y)
Definition: SVD3x3CPU.h:38