拆解KinectFusion算法之TSDF

TSDF的全称是truncated signed distance function,这个算法的主要功能是为了融合(fusion)相机新观测到的深度信息或者说几何信息,当然也可以融合颜色信息甚至是其他的信息比如法向量和材质等等,总的来讲就是把时序上不断观测到的信息加权平均。

首先,要定义一个体素块的集合,这个体素就类比像素,体素是三维空间中承载一些数量值的最小单元。比如说每个体素块的尺寸选择为4mm,这里假定体素块是立方体,当然也可以是小长方体,另外假定每个坐标轴方向都是256个体素块,那么所有体素块就构成一个1.024m的大立方体。之所以选择256这样的数值是为了方便CUDA加速,一般是32的整数倍,32是一个Warp里面的线程数。常见的体素的维度有128,256,512,具体要看下显卡的显存能支持多大。

接下来我们还是从一段CPU代码来具体了解下TSDF算法的执行过程,实际上一个256x256x256的TSDF融合在CUDA加速下可能不到1ms就算完了,CPU代码写个三重的for循环大概可能是秒级算完,不做realtime应用也还可以接受。这段tsdf的代码同样是参考自Intrinsic3D,稍有改动。

for(int z = 0; z < grid_dim; z++){
    for(int y = 0; y < grid_dim; y++){
        for(int x = 0; x < grid_dim; x++){
            int idx = z * grid_dim * grid_dim + y * grid_dim + x;
            Vec3f pos_world(x * voxel_size, y * voxel_size, z * voxel_size);
            //这里的grid_pose实际上是一个平移,就是把位于第一象限的体素栅格平移到第一帧相机的坐标系下。
            //具体地是分别沿x轴和y轴负向移动一段距离,一般就选择0.5 * grid_dim * voxel_size就可以。
            //另外向z轴正向移动一点距离,这个距离具体看你重建的场景在相机z轴正向的距离,以保证大部分场景可以落到体素栅格内部即可。
            pos_world = grid_pose.topLeftCorner(3, 3) * pos_world + grid_pose.topRightCorner(3, 1);
			//这里选择第一帧相机的坐标系为世界坐标系,world_to_cam是把世界坐标系变换到当前相机世界坐标系的刚体变换。
			//这个刚体变换用ICP进行求解得到。
            Vec3f p = world_to_cam.topLeftCorner(3, 3) * pos_world + world_to_cam.topRightCorner(3, 1);
            
            if(p[2] < 0.0f)
                continue;
			//变换到当前相机坐标系下的体素块根据相机内参进行投影;
            int u = (p[0] / p[2]) * fx + cx + 0.5f;
            int v = (p[1] / p[2]) * fy + cy + 0.5f;
            
            //投出图像范围的体素块不更新
            if(u < 0 || v < 0 || u >= depth_imgae.cols || v >= depth_imgae.rows)
                continue;
            
            const float d = depth_imgae.at<float>(v, u);
            if (d <= 0.0f)
                continue;

            // compute signed distance; positive in front of the observation
            const float sdf = d - p[2];
			
			//体素块相比相机观测到的深度值更靠近相机,sdf是正的,否则就是负的
            if (sdf <= -truncation)
                continue;
            if (sdf >= truncation)
                continue;
            valid_voxel_num++;
            
            const float tsdf = sdf >= 0.0f ? std::min(truncation, sdf) : std::max(-truncation, sdf);

            float weight_update = 1.0f;
           
            // update sdf
            const float w_old = grid_weight[idx];
            const float w_new = w_old + weight_update;
            grid_tsdf[idx] = (grid_tsdf[idx] * w_old + tsdf * weight_update) / w_new;
            // update weight
            grid_weight[idx] = w_new;
        }
    }
}

TSDF就写这么多,下一节写MarchingCubes算法。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值