class_docstr: Volumetric TSDF Fusion of RGB-D Images.
      
class_docstr_tok: ["Volumetric","TSDF","Fusion","of","RGB-D","Images","."]
docstr: Constructor.
  Args:
  vol_bnds (ndarray): An ndarray of shape (3, 2). Specifies the
  xyz bounds (min/max) in meters.
  voxel_size (float): The volume discretization in meters.
docstr_tok: ["Constructor",".","Args",":","vol_bnds","(","ndarray",")",":","An","ndarray","of","shape","(","3",",","2",")",".","Specifies","the","xyz","bounds","(","min\/max",")","in","meters",".","voxel_size","(","float",")",":","The","volume","discretization","in","meters","."]
code: def __init__(self, vol_bnds, voxel_size, use_gpu=True):
      """Constructor.
      Args:
      vol_bnds (ndarray): An ndarray of shape (3, 2). Specifies the
      xyz bounds (min/max) in meters.
      voxel_size (float): The volume discretization in meters.
      """
      vol_bnds = np.asarray(vol_bnds)
      assert vol_bnds.shape == (
          3,
          2,
      ), "[!] `vol_bnds` should be of shape (3, 2)."
  
      # Define voxel volume parameters
      self._vol_bnds = vol_bnds
      self._voxel_size = float(voxel_size)
      self._trunc_margin = 5 * self._voxel_size  # truncation on SDF
  
      # Adjust volume bounds and ensure C-order contiguous
      self._vol_dim = (
          np.ceil(
              (self._vol_bnds[:, 1] - self._vol_bnds[:, 0])
              / self._voxel_size
          )
          .copy(order="C")
          .astype(int)
      )
      self._vol_bnds[:, 1] = (
          self._vol_bnds[:, 0] + self._vol_dim * self._voxel_size
      )
      self._vol_origin = (
          self._vol_bnds[:, 0].copy(order="C").astype(np.float32)
      )
  
      print(
          "Voxel volume size: {} x {} x {} - # points: {:,}".format(
              self._vol_dim[0],
              self._vol_dim[1],
              self._vol_dim[2],
              self._vol_dim[0] * self._vol_dim[1] * self._vol_dim[2],
          )
      )
  
      # Initialize pointers to voxel volume in CPU memory
      self._tsdf_vol_cpu = np.ones(self._vol_dim).astype(np.float32)
      # for computing the cumulative moving average of observations per voxel
      self._weight_vol_cpu = np.zeros(self._vol_dim).astype(np.float32)
      self.gpu_mode = use_gpu and FUSION_GPU_MODE
  
      # Copy voxel volumes to GPU
      if self.gpu_mode:
          self._tsdf_vol_gpu = cuda.mem_alloc(self._tsdf_vol_cpu.nbytes)
          cuda.memcpy_htod(self._tsdf_vol_gpu, self._tsdf_vol_cpu)
          self._weight_vol_gpu = cuda.mem_alloc(
              self._weight_vol_cpu.nbytes
          )
          cuda.memcpy_htod(self._weight_vol_gpu, self._weight_vol_cpu)
  
          # Cuda kernel function (C++)
          self._cuda_src_mod = SourceModule(
              """
              __global__ void integrate(float * tsdf_vol,
                                    float * weight_vol,
                                    float * vol_dim,
                                    float * vol_origin,
                                    float * cam_intr,
                                    float * cam_pose,
                                    float * other_params,
                                    float * depth_im) {
            // Get voxel index
            int gpu_loop_idx = (int) other_params[0];
            int max_threads_per_block = blockDim.x;
            int block_idx = blockIdx.z*gridDim.y*gridDim.x+blockIdx.y*gridDim.x+blockIdx.x;
            int voxel_idx = gpu_loop_idx*gridDim.x*gridDim.y*gridDim.z*max_threads_per_block+block_idx*max_threads_per_block+threadIdx.x;
            int vol_dim_x = (int) vol_dim[0];
            int vol_dim_y = (int) vol_dim[1];
            int vol_dim_z = (int) vol_dim[2];
            if (voxel_idx > vol_dim_x*vol_dim_y*vol_dim_z)
                return;
            // Get voxel grid coordinates (note: be careful when casting)
            float voxel_x = floorf(((float)voxel_idx)/((float)(vol_dim_y*vol_dim_z)));
            float voxel_y = floorf(((float)(voxel_idx-((int)voxel_x)*vol_dim_y*vol_dim_z))/((float)vol_dim_z));
            float voxel_z = (float)(voxel_idx-((int)voxel_x)*vol_dim_y*vol_dim_z-((int)voxel_y)*vol_dim_z);
            // Voxel grid coordinates to world coordinates
            float voxel_size = other_params[1];
            float pt_x = vol_origin[0]+voxel_x*voxel_size;
            float pt_y = vol_origin[1]+voxel_y*voxel_size;
            float pt_z = vol_origin[2]+voxel_z*voxel_size;
            // World coordinates to camera coordinates
            float tmp_pt_x = pt_x-cam_pose[0*4+3];
            float tmp_pt_y = pt_y-cam_pose[1*4+3];
            float tmp_pt_z = pt_z-cam_pose[2*4+3];
            float cam_pt_x = cam_pose[0*4+0]*tmp_pt_x+cam_pose[1*4+0]*tmp_pt_y+cam_pose[2*4+0]*tmp_pt_z;
            float cam_pt_y = cam_pose[0*4+1]*tmp_pt_x+cam_pose[1*4+1]*tmp_pt_y+cam_pose[2*4+1]*tmp_pt_z;
            float cam_pt_z = cam_pose[0*4+2]*tmp_pt_x+cam_pose[1*4+2]*tmp_pt_y+cam_pose[2*4+2]*tmp_pt_z;
            // Camera coordinates to image pixels
            int pixel_x = (int) roundf(cam_intr[0*3+0]*(cam_pt_x/cam_pt_z)+cam_intr[0*3+2]);
            int pixel_y = (int) roundf(cam_intr[1*3+1]*(cam_pt_y/cam_pt_z)+cam_intr[1*3+2]);
            // Skip if outside view frustum
            int im_h = (int) other_params[2];
            int im_w = (int) other_params[3];
            if (pixel_x < 0 || pixel_x >= im_w || pixel_y < 0 || pixel_y >= im_h || cam_pt_z<0)
                return;
            // Skip invalid depth
            float depth_value = depth_im[pixel_y*im_w+pixel_x];
            if (depth_value == 0)
                return;
            // Integrate TSDF
            float trunc_margin = other_params[4];
            float depth_diff = depth_value-cam_pt_z;
            if (depth_diff < -trunc_margin)
                return;
            float dist = fmin(1.0f,depth_diff/trunc_margin);
            float w_old = weight_vol[voxel_idx];
            float obs_weight = other_params[5];
            float w_new = w_old + obs_weight;
            weight_vol[voxel_idx] = w_new;
            tsdf_vol[voxel_idx] = (tsdf_vol[voxel_idx]*w_old+obs_weight*dist)/w_new;
  
              }"""
          )
  
          self._cuda_integrate = self._cuda_src_mod.get_function(
              "integrate"
          )
  
          # Determine block/grid size on GPU
          gpu_dev = cuda.Device(0)
          self._max_gpu_threads_per_block = (
              gpu_dev.MAX_THREADS_PER_BLOCK
          )
          n_blocks = int(
              np.ceil(
                  float(np.prod(self._vol_dim))
                  / float(self._max_gpu_threads_per_block)
              )
          )
          grid_dim_x = min(
              gpu_dev.MAX_GRID_DIM_X, int(np.floor(np.cbrt(n_blocks)))
          )
          grid_dim_y = min(
              gpu_dev.MAX_GRID_DIM_Y,
              int(np.floor(np.sqrt(n_blocks / grid_dim_x))),
          )
          grid_dim_z = min(
              gpu_dev.MAX_GRID_DIM_Z,
              int(
                  np.ceil(
                      float(n_blocks) / float(grid_dim_x * grid_dim_y)
                  )
              ),
          )
          self._max_gpu_grid_dim = np.array(
              [grid_dim_x, grid_dim_y, grid_dim_z]
          ).astype(int)
          self._n_gpu_loops = int(
              np.ceil(
                  float(np.prod(self._vol_dim))
                  / float(
                      np.prod(self._max_gpu_grid_dim)
                      * self._max_gpu_threads_per_block
                  )
              )
          )
      else:
          # Get voxel grid coordinates
          xv, yv, zv = np.meshgrid(
              range(self._vol_dim[0]),
              range(self._vol_dim[1]),
              range(self._vol_dim[2]),
              indexing="ij",
          )
          self.vox_coords = (
              np.concatenate(
                  [
                      xv.reshape(1, -1),
                      yv.reshape(1, -1),
                      zv.reshape(1, -1),
                  ],
                  axis=0,
              )
              .astype(int)
              .T
          )
  
file_path: torch-points3d/torch_points3d/datasets/registration/fusion.py
imports: import numpy
  from numba import njit, prange
  from skimage import measure
label_desc: Use image node_id 1 to create a new TSDFVolume object  with example: obj = TSDFVolume(vol_bnds, voxel_size, use_gpu)