DROID-SLAM工程项目实践

Demos

sfm_bench数据为例介绍。

运行命令:

python demo.py --imagedir=data/sfm_bench/rgb --calib=calib/eth.txt

脚本文件:demo.py

参数:

--imagedir=data/sfm_bench/rgb (指定数据集的目录)

--calib=calib/eth.txt (相机参数数据文件)

分析

1、data/sfm_bench/rgb

1540481046.653665.png:

2、calib/tum3.txt

726.21081542969 726.21081542969 359.2048034668 202.47247314453

fx = 726.21081542969

fy = 726.21081542969

cx = 359.2048034668

cy = 202.47247314453

3、demo.py

最后看看demo.py文件

首先看main函数:

'''
节选main()主函数
'''

if __name__ == '__main__':
    # 1.解析参数
    parser = argparse.ArgumentParser()
    parser.add_argument("--imagedir", type=str, help="path to image directory")
    parser.add_argument("--calib", type=str, help="path to calibration file")  # 校准文件
    parser.add_argument("--t0", default=0, type=int, help="starting frame")
    parser.add_argument("--stride", default=3, type=int, help="frame stride")

    parser.add_argument("--weights", default="droid.pth")
    parser.add_argument("--buffer", type=int, default=512)
    parser.add_argument("--image_size", default=[240, 320])
    parser.add_argument("--disable_vis", action="store_true")

    parser.add_argument("--beta", type=float, default=0.3, help="weight for translation / rotation components of flow")
    parser.add_argument("--filter_thresh", type=float, default=2.4, help="how much motion before considering new keyframe")
    parser.add_argument("--warmup", type=int, default=8, help="number of warmup frames")
    parser.add_argument("--keyframe_thresh", type=float, default=4.0, help="threshold to create a new keyframe")
    parser.add_argument("--frontend_thresh", type=float, default=16.0, help="add edges between frames whithin this distance")
    parser.add_argument("--frontend_window", type=int, default=25, help="frontend optimization window")
    parser.add_argument("--frontend_radius", type=int, default=2, help="force edges between frames within radius")
    parser.add_argument("--frontend_nms", type=int, default=1, help="non-maximal supression of edges")

    parser.add_argument("--backend_thresh", type=float, default=22.0)
    parser.add_argument("--backend_radius", type=int, default=2)
    parser.add_argument("--backend_nms", type=int, default=3)
    parser.add_argument("--upsample", action="store_true")
    parser.add_argument("--reconstruction_path", help="path to saved reconstruction")
    args = parser.parse_args()

    args.stereo = False
    # 创建多线程
    # 新的多进程时使用spawn启动方法。这通常在使用PyTorch进行多进程训练或并行计算时使用,可以确保每个子进程都有独立的内存空间,避免共享状态带来的潜在问题。
    torch.multiprocessing.set_start_method('spawn')

    droid = None

    # need high resolution depths
    if args.reconstruction_path is not None:
        args.upsample = True

    tstamps = []

    # 2.通过image_stream函数读取数据集,并使用tqdm包进行可视化进度
    print("\n开始跟踪......")

    image_counter = 0
    for (t, image, intrinsics) in tqdm(image_stream(args.imagedir, args.calib, args.stride)):
        image_counter += 1
        # 2.1 判断是否到了采样点
        # 当图像i < 用户设置的t0,表示还没有达到采样的时间,则继续
        if t < args.t0:
            continue

        # 2.2 判断是否可视化
        if not args.disable_vis:
            show_image(image[0])

        # 2.3 实例化Droid模型
        if droid is None:
            args.image_size = [image.shape[2], image.shape[3]]
            droid = Droid(args)  # 实例化模型

        #print(droid)

        # 2.4 开始跟踪建图
        #调用模型中的track函数,对每一帧t中的图像image,按照内参矩阵intrinsics进行跟踪,获取轨迹
        droid.track(t, image, intrinsics=intrinsics)
    print("共计{}帧图像",image_counter)
    # 3. 如果用户需要重建,则将结果通过save_reconstruction()进行重建
    if args.reconstruction_path is not None:
        save_reconstruction(droid, args.reconstruction_path)

    # 4. 对跟踪结果进行评估
    print("\n开始BA操作....")
    traj_est = droid.terminate(image_stream(args.imagedir, args.calib, args.stride))
    print("The shape of traj_est is:",np.shape(traj_est))
    print("traj_est = ",traj_est)
    print("demo running is over ... ")

image_stream(imagedir, calib, stride)函数

'''
imagedir:图片路径
calib:校准文件(参数文件)
stride:采样步幅
'''
def image_stream(imagedir, calib, stride):
    """ image generator """

    # 加载相机参数文件calib,并用" "进行分割
    calib = np.loadtxt(calib, delimiter=" ")

    # 读取的数据取前四列
    fx, fy, cx, cy = calib[:4]

    # 构建内参矩阵K
    K = np.eye(3)
    K[0,0] = fx
    K[0,2] = cx
    K[1,1] = fy
    K[1,2] = cy

    # 按照stride步进行视频帧采样
    image_list = sorted(os.listdir(imagedir))[::stride]

    # 对于采样之后的图像,帧进行循环遍历
    for t, imfile in enumerate(image_list):

        # 首先去读内一张图像
        image = cv2.imread(os.path.join(imagedir, imfile))

        # 图像畸变校正
        # calib文件格式为:458.654 457.296 367.215 248.375 -0.28340811 0.07395907 0.00019359 1.76187114e-05
        # 如果calib长度>4的话,说明文件中有畸变参数。需要根据畸变参数进行校准
        if len(calib) > 4:
            image = cv2.undistort(image, K, calib[4:])

        # 获取原始图像的尺寸数据
        h0, w0, _ = image.shape

        # 将图片变成384 * 512,并且保持宽高比例不变
        h1 = int(h0 * np.sqrt((384 * 512) / (h0 * w0)))
        w1 = int(w0 * np.sqrt((384 * 512) / (h0 * w0)))

        # 利用cv2.resize函数将图片裁剪成固定尺寸
        image = cv2.resize(image, (w1, h1))

        # 通过裁剪操作,确保图像的高度和宽度是8的倍数
        image = image[:h1-h1%8, :w1-w1%8]

        # 调整图像的维度,最终的张量形状将变为(通道数, 高度, 宽度),方便计算
        image = torch.as_tensor(image).permute(2, 0, 1)

        # 将内参转化成张量
        intrinsics = torch.as_tensor([fx, fy, cx, cy])

        #因为上面进行了裁剪,所以相机内参数据需要根据裁剪比例进行缩放
        intrinsics[0::2] *= (w1 / w0)
        intrinsics[1::2] *= (h1 / h0)

        # 返回的值作为一个迭代元组,包含三个元素:t、image[None] 和 intrinsics。
        yield t, image[None], intrinsics

save_reconstruction(droid, reconstruction_path)

def save_reconstruction(droid, reconstruction_path):

    from pathlib import Path
    import random
    import string

    # 获取调用该函数时,droid.video.counter记录的次数
    t = droid.video.counter.value

    # 获取不同对象,索引0到索引t-1的元素的元素
    tstamps = droid.video.tstamp[:t].cpu().numpy()
    images = droid.video.images[:t].cpu().numpy()
    disps = droid.video.disps_up[:t].cpu().numpy()
    poses = droid.video.poses[:t].cpu().numpy()
    intrinsics = droid.video.intrinsics[:t].cpu().numpy()

    # 确保reconstructions路径存在
    Path("reconstructions/{}".format(reconstruction_path)).mkdir(parents=True, exist_ok=True)

    # 在reconstructions/路径下存放tstamps数据,并使用npy格式
    np.save("reconstructions/{}/tstamps.npy".format(reconstruction_path), tstamps)

    # 在reconstructions/路径下存放images数据,并使用npy格式
    np.save("reconstructions/{}/images.npy".format(reconstruction_path), images)

    # 在reconstructions/路径下存放disps数据,并使用npy格式
    np.save("reconstructions/{}/disps.npy".format(reconstruction_path), disps)

    # 在reconstructions/路径下存放poses数据,并使用npy格式
    np.save("reconstructions/{}/poses.npy".format(reconstruction_path), poses)

    # 在reconstructions/路径下存放intrinsics数据,并使用npy格式
    np.save("reconstructions/{}/intrinsics.npy".format(reconstruction_path), intrinsics)

实验结果

1、debug输出

这里可以看出:traj_est的输出就是一个220*7的矩阵。其中220表示220帧图像,7代表位姿向量(4维旋转+3维平移)。

2、视频帧界面

3、俯视图

4、正视图 

5、侧视图

但是原作者的工程中自带的demo没有结束功能,一直停在“Global BA Iteration #12”的位置,也没有进行评估操作。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值