欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

BundleFusion那些事儿

程序员文章站 2022-07-11 15:37:17
...

背景:前面几篇博客中写了很多关于BundleFusion的东西,主要包括bundlefusion的论文阅读笔记,.sens数据集的生成等,经过最近几天的工作,我对bundlefusion又有了新的技术积累,在这里整理一下,也算是理一下思路,然后开始新的工作。

1. 生成.sens文件

根据在https://graphics.stanford.edu/projects/bundlefusion/下载的.zip的数据集可知,一个数据集中包含,以frame-xxxxxx.color.png的彩色图像,有以frame-xxxxxx.depth.png命名的深度图像,以及,以frame-xxxxxx.pose.txt命名的位姿文件,该文件中存储的是一帧位姿,一个4x4的矩阵。如下图所示,所以首先你需要想办法得到这种标准格式的数据。我曾经把我使用kinectV2相机获取的深度图和彩色图对齐后的数据编辑为如下格式,所有的.pose.txt文件中我都写入单位阵。我还试着将TUM数据集和ICL-NUIM数据集编辑成下面这种格式,并且我将数据集提供的groundtruth写入到.pose.txt文件中,目的是想让bundlefusion按照groundtruth位姿来重建。由于数据集提供的groundtruth是用四元数表示的旋转,所以需要将四元数转换为旋转矩阵,由于我使用的是python脚本,这个是时候,scipy库就派上用场了,这里面有很多转换,包括,四元数,旋转矩阵,旋转向量,欧拉角之间的转换。

BundleFusion那些事儿

当然不要忘了,在目录的最后还有一个info.txt文件, 除了彩色图的尺寸,相机的内参数,要按照实际情况改写之后,还有很关键的一个参数就是m_depthShift,在BundleFusion官网上下载的数据集,info.txt中设置的m_depthShift =1000,而在我生成TUM和ICL的数据集时,这个值就得设置为10000,否则生成的数据集,重建不出来模型,具体的分析请阅读我的这篇博客,https://blog.csdn.net/weixin_38636815/article/details/107563959

BundleFusion那些事儿 

 下面是我将ICL数据集整理成上述格式的python脚本

import numpy as np
from scipy.spatial.transform import Rotation as R
import associate
import os
import shutil

def copy_files(sourcefile):
    rgb_path = sourcefile+"rgb/"
    depth_path = sourcefile + "depth/"
    bf_path = sourcefile + "bf_dataset/"
    print(rgb_path)
    print(depth_path)

    rgb_images = os.listdir(rgb_path)
    depth_images = os.listdir(depth_path)
    rgb_int = []
    depth_int = []
    for rgb in rgb_images:
        rgb = rgb.replace('.png', '')
        # print(rgb)
        rgb_int.append(int(rgb))
    for depth in depth_images:
        depth = depth.replace('.png', '')
        depth_int.append(int(depth))

    rgb_id = 0
    for rgb_name in sorted(rgb_int):
        print("frame-" + str(rgb_id).zfill(6) + ".color.png" + " is writing into bf_dataset" )
        shutil.copyfile(rgb_path + "/" + str(rgb_name) + ".png",
                        bf_path + "frame-" + str(rgb_id).zfill(6) + ".color.png")
        rgb_id += 1

    depth_id = 0
    for depth_name in sorted(depth_int):
        print("frame-" + str(depth_id).zfill(6) + ".depth.png" + " is writing into bf_dataset")
        shutil.copyfile(depth_path + "/" + str(depth_name) + ".png",
                        bf_path + "frame-" + str(depth_id).zfill(6) + ".depth.png")
        depth_id += 1

    """
    transform quaternion into rotation and write them into separated pose files
    """
    gt_file = open(sourcefile + "livingRoom3n.gt.freiburg")
    gt_data =gt_file.read()
    lines = gt_data.replace(",", " ").replace("\t", " ").split("\n")
    positions = []
    quaternions = []
    lines_data = []
    for line in lines:
        line_data = []
        if len(line) > 0 and line[0] != '#':
            for v in line.split(' '):
                line_data.append(v)
            lines_data.append(line_data)
            # print(line_data)
    share_vect = np.array([0, 0, 0, 1], dtype=np.float32)[np.newaxis, :]
    pose_id = 0
    for line in lines_data:
        single_position = np.array([line[1], line[2], line[3]], dtype=np.float32)[:, np.newaxis]
        single_quaternion = np.array([line[4], line[5], line[6], line[7]], dtype=np.float32)
        positions.append(single_position)
        quaternions.append(single_quaternion)

        rotation = R.from_quat(single_quaternion)
        m34 = np.concatenate((rotation.as_matrix(), single_position), axis=1)
        m44 = np.concatenate((m34, share_vect), axis=0)
        print("frame-" + str(pose_id).zfill(6) + ".pose.txt" + "is writing into bf_dataset")
        fp = open(bf_path + "frame-" + str(pose_id).zfill(6) + ".pose.txt", 'w')
        for row in m44:
            # fp.write(" ".join(row) + "\n")
            fp.write(' '.join(str(i) for i in row) + '\n')
        pose_id += 1
    print(len(positions))
    print(len(quaternions))

if __name__ == '__main__':
    print('test')
    # copy_files("/media/yunlei/YL/DATASETS/ICL_DATABASE/lr_kt0/living_room_traj0_frei_png/")
    # copy_files("/media/yunlei/YL/DATASETS/ICL_DATABASE/lr_kt1/living_room_traj1n_frei_png/")
    # copy_files("/media/yunlei/YL/DATASETS/ICL_DATABASE/lr_kt2/living_room_traj2n_frei_png/")
    copy_files("/media/yunlei/YL/DATASETS/ICL_DATABASE/lr_kt3/living_room_traj3n_frei_png/")

假设现在已经 整理出的规范的文件格式,现在我们需要一个工具,将彩色图,深度图和位姿数据写入到.sens文件中,在BundleFusion工程中有实现将图像写入到.sens文件的函数,只需要用下面的mian函数替换你原工程 BundleFusion/FriedLiver/Source下FriedLiver.cpp中的主函数,在新的主函数中写入你自己数据的路径和名称,使用ctrl+F5的方式在vs2013中运行此时的工程,可以在saveToFile函数中加上一些打印信息,这样可以更清楚的看到数据的生成状态。不出意外的话上述方式就可以成功生成.sens文件。

int main()
{
	ml::SensorData sd;
	sd.initDefault(640, 480, 640, 480, sd.m_calibrationColor, sd.m_calibrationDepth);
	sd.loadFromImages("E:/DATASETS/ICL_DATABASE/lr_kt1/living_room_traj1n_frei_png/bf_dataset", "frame-", "png");
	sd.saveToFile("E:/DATASETS/ICL_DATABASE/lr_kt1/living_room_traj1n_frei_png.sens");
	std::cout << "generate sens data" << std::endl;
	return 0;
}

二、使用自己生成的.sens文件运行BundleFusion

如果你想通过ctrl+F5的方式运行代码,那么你就修改,位于BundleFusion-master/FriedLiver下的zParametersDefault.txt下的s_binaryDumpSensorFile,当然你也可以先在vs2013上编译工程,然后双击 BundleFusion-master/FriedLiver/x64/Release下的FriedLiver.exe可执行文件,这个时候你需要修改,FriedLiver.exe同目录下的zParametersDefault.txt文件中的s_binaryDumpSensorFile变量。两个配置文件中的其他一些参数也要调整,尤其是有关图像的宽度和高度的变量,要根据你数据集的实际大小来修改。需要注意的是,在zParametersBundingDefault.txt文件中的s_downsampledWidth和s_downsampledHeight这两变量的值设置不当,会导致系统很容易跟踪失败,大面积的跟踪失败会导致重建的模型不完整。

如果不出什么意外,自己生成的数据集就可以在bundlefusion上运行了。

三、如何使用写入的groundtruth进行重建

我希望系统可以按照我写入到.pose.txt文件中的pose重建,但是我在工程中没有找到直接的开关变量,所以我就开始研究如何实现这个功能。

未完待续