• COLMAP简明教程 重建 转化深度图 导出相机参数 导入相机参数 命令行


    COLMAP简明教程 导入指定参数 命令行 导出深度图

    COLMAP是经典的3D重建、SfM、深度估计开源工作,配置和安装按下不表,本文主要从命令行的角度,对COLMAP的基本用法做教程,并备收藏和笔记。

    对指定图像进行重建和深度估计

    准备好一些多视角图像,放入一个文件夹中,如下所示:

    ├── images/
        ├── 0.png
        ├── 1.png
        ......
        ├── 12.png
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果图像是针孔相机拍摄的,就在根目录下执行以下命令:

    colmap feature_extractor --database_path database.db --image_path images --ImageReader.camera_model PINHOLE
    colmap exhaustive_matcher --database_path database.db
    mkdir sparse
    colmap mapper --database_path database.db --image_path images --output_path sparse
    mkdir dense
    colmap image_undistorter --image_path images --input_path sparse/0 --output_path dense --output_type COLMAP
    colmap patch_match_stereo --workspace_path dense --workspace_format COLMAP --PatchMatchStereo.geom_consistency true
    colmap stereo_fusion --workspace_path dense --workspace_format COLMAP --input_type geometric --output_path dense/fused.ply
    colmap poisson_mesher --input_path dense/fused.ply --output_path dense/meshed-poisson.ply
    colmap delaunay_mesher --input_path dense --output_path dense/meshed-delaunay.ply
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这些命令分别用于提取图片特征、特征匹配、稀疏重建、去畸变、稠密重建、点云构建、mesh文件构建。如果图像不是针孔相机拍摄的,请自行更改colmap feature_extractor命令。
    在执行完这些命令后,可以得到如下目录结构:

    ├── dense/
    	├── images/
    	├── sparse/
    	├── stereo/
    ├── images/
    ├── sparse/
    	├── 0/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中在dense文件夹下还有几个.ply文件,是COLMAP得到的mesh场景重建结果,可以用MeshLab等软件打开观看重建结果。

    转化深度图格式

    dense\stereo\depth_maps文件夹下有很多.bin文件,这些文件是COLMAP得到的深度图,COLMAP官方代码库提供了多种方式将其转化为彩色的可视的深度图,如这份代码
    基于此代码,我提供一份代码,用于将.bin文件转化为灰度的单通道图像,符合大家一般对深度图的认知:

    import argparse
    import numpy as np
    import os
    import struct
    from PIL import Image
    import warnings
    import os
    
    warnings.filterwarnings('ignore') # 屏蔽nan与min_depth比较时产生的警告
    
    camnum = 12
    fB = 32504;
    min_depth_percentile = 2
    max_depth_percentile = 98
    depthmapsdir = '.\\dense\\stereo\\depth_maps\\'
    outputdir = '.\\dense\\stereo\\depth_maps\\'
    
    def read_array(path):
        with open(path, "rb") as fid:
            width, height, channels = np.genfromtxt(fid, delimiter="&", max_rows=1,
                                                    usecols=(0, 1, 2), dtype=int)
            fid.seek(0)
            num_delimiter = 0
            byte = fid.read(1)
            while True:
                if byte == b"&":
                    num_delimiter += 1
                    if num_delimiter >= 3:
                        break
                byte = fid.read(1)
            array = np.fromfile(fid, np.float32)
        array = array.reshape((width, height, channels), order="F")
        return np.transpose(array, (1, 0, 2)).squeeze()
    
    def bin2depth(i, depth_map, depthdir):
        # depth_map = '0.png.geometric.bin'
        # print(depthdir)
        # if min_depth_percentile > max_depth_percentile:
        #     raise ValueError("min_depth_percentile should be less than or equal "
        #                      "to the max_depth_perceintile.")
    
        # Read depth and normal maps corresponding to the same image.
        if not os.path.exists(depth_map):
            raise fileNotFoundError("file not found: {}".format(depth_map))
    
        depth_map = read_array(depth_map)
    
        min_depth, max_depth = np.percentile(depth_map[depth_map>0], [min_depth_percentile, max_depth_percentile])
        depth_map[depth_map <= 0] = np.nan # 把0和负数都设置为nan,防止被min_depth取代
        depth_map[depth_map < min_depth] = min_depth
        depth_map[depth_map > max_depth] = max_depth
    
        maxdisp = fB / min_depth;
        mindisp = fB / max_depth;
        depth_map = (fB/depth_map - mindisp) * 255 / (maxdisp - mindisp);
        depth_map = np.nan_to_num(depth_map) # nan全都变为0
        depth_map = depth_map.astype(int)
    
        image = Image.fromarray(depth_map).convert('L')
        # image = image.resize((1920, 1080), Image.ANTIALIAS) # 保证resize为1920*1080
        image.save(depthdir + str(i) + '.png')
    
    for j in range(camnum):
    	binjdir = depthmapsdir + str(j) + '.png.' + 'geometric' + '.bin'
    	# binjdir = depthmapsdir + str(j) + '.png.' + 'photometric' + '.bin'
    	if os.path.exists(binjdir):
    		read_write_dense.bin2depth(j, binjdir, outputdir)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    这份代码可以把depthmapsdir .bin文件转化成.png图片并保存到outputdir,具体参数大家可以自行调整。
    注意在代码中,depth_map其实就已经把.bin文件变成COLMAP估计得的距离了,这份代码和官方代码思路一样,都是把估计得的距离值的2百分位数至98百分位数范围内的值保存下来,其他值替换掉。
    在得到depth_map后,大家可以自定义自己喜欢和需要的可视化方法。我这里是用fB可视化为视差图了,大家根据需要自己更改代码,这里仅作参考。

    导出估计得的相机参数

    在根目录下运行命令:

    colmap model_converter --input_path dense/sparse --output_path dense/sparse --output_type TXT
    
    • 1

    即可把dense/sparse文件夹中的.bin格式的文件转化为.txt格式的文件。
    其中cameras.txt文件中保存的是内参,形如:

    # Camera list with one line of data per camera:
    #   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]
    # Number of cameras: 30
    1 PINHOLE 1920 1080 1987.52 2254.34 960 540
    2 PINHOLE 1920 1080 2039.08 2320.3 960 540
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里的MODEL是跟之提取特征时选的相机模型一致的。每行的6个数字中,除了图像长宽之外分别是图像对应的相机内参矩阵中的fx,fy,cx,cy,具体含义参见这里
    另一个文件images.txt保存的是外参,形如:

    # Image list with two lines of data per image:
    #   IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME
    #   POINTS2D[] as (X, Y, POINT3D_ID)
    # Number of images: 30, mean observations per image: 86.566666666666663
    1 0.927395 0.0306001 -0.367019 -0.065565 5.03061 -0.487973 2.93529 1 0.png
    1617.59 4.99193 -1 1831.38 5.76693 -1 1527.2 9.43552 -1 1490.19 10.4907 -1 367.424 11.5043 -1 775.228 11.182 637 1653.98 11.5989 -1 1896.38 11.4442 -1 30.5403 16.7052 -1 52.5616 18.6398 -1 7.04345 21.7672 1467 7.04345 21.7672 -1 43.2921 23.8136 -1 133.284 24.7492 -1 1249.04 24.1695 -1 3.80868 26.8114 -1 157.906 26.5587 75 173.966 27.2299 -1 22.0715 28.2147 -1 72.7796 29.7689 -1 278.952 29.9326 841 81.3182 30.1362 -1 1242.21 30.9173 -1 1242.21 30.9173 -1 1608.52 30.874 -1 110.63 32.399 -1 110.63 32.399 -1 178.573 32.1621 -1 178.573 32.1621 639 852.426 33.4657 76 201.459 34.3237 640 201.459 34.3237 -1 44.155 35.6634 -1 44.155 35.6634 -1 65.9694 37.3953 -1 65.9694 37.3953 -1 92.186 37.6702 -1 92.186 37.6702 -1 489.638 37.2811 -1 1014.71 37.6817 -1 165.665 40.7318 -1 80.8138 43.304 -1 200.238 43.746 -1 401.959 44.322 -1 473.235 44.9557 -1 473.235 44.9557 -1 5.5055 46.3413 -1 26.7574 47.175 1453 506.037 47.7838 -1 506.037 47.7838 -1 628.451 47.4269 -1 148.813 51.3945 -1 308.002 52.2475 642 71.4106 53.5389 -1
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    每张图像有两行,第一行是对应相机外参,七个浮点数中的QW,QX,QY,QZ是四元数的形式,可以转换为旋转矩阵或者其他形式,转化办法可参考这里,注意几个参数调用顺序,与COLMAP给的顺序是不同的。
    TX,TY,TZ可直接构成外参中的t矩阵。
    注意,COLMAP提供的相机参数的含义与这里定义的相同。

    导入相机参数进行重建和深度估计

    有时候用COLMAP做批量重建和深度估计,需要使用统一的相机参数,这个时候就需要统一导入相同的一组相机参数。这里介绍使用导入的相机参数的方法,和使用导入的参数重建、深度估计的方法。
    准备一个cameras.txt,很容易,在得到上一步的cameras.txt的基础上,只需要把最前面的三行删掉就好了:

    1 PINHOLE 1920 1080 1987.52 2254.34 960 540
    2 PINHOLE 1920 1080 2039.08 2320.3 960 540
    ...
    
    • 1
    • 2
    • 3

    再准备一个images.txt,也容易,在得到上一步的images.txt之后,最前面的三行删掉,并且只保留每台相机的第一行:

    1 0.927395 0.0306001 -0.367019 -0.065565 5.03061 -0.487973 2.93529 1 0.png
    
    2 0.938886 0.0294343 -0.338403 -0.0557617 4.86882 -0.403504 2.77603 2 1.png
    
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    每台相机后面都要空一行,注意。
    然后准备一个完全空的points3D.txt,将cameras.txtimages.txtpoints3D.txt放入新建的created\sparse文件夹中,形成如下目录:

    ├── created/
      	├── sparse/
       		├── cameras.txt
       		├── images.txt
       		├── points3D.txt
    ├── images/
        ├── 0.png
        ├── 1.png
        ......
        ├── 12.png
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后在根目录下运行以下命令:

    colmap feature_extractor --database_path database.db --image_path images
    colmap exhaustive_matcher --database database.db
    mkdir triangulated/sparse
    colmap point_triangulator --database_path database.db --image_path images --input_path created/sparse --output_path triangulated/sparse
    mkdir dense
    colmap image_undistorter --image_path images --input_path triangulated/sparse --output_path dense
    colmap patch_match_stereo --workspace_path dense --workspace_format COLMAP --PatchMatchStereo.geom_consistency true
    colmap stereo_fusion --workspace_path dense --output_path dense/fused.ply
    colmap model_converter --input_path dense/sparse --output_path dense/sparse --output_type TXT
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    即可完成重建。然后参考前面的步骤进行深度图转换即可。
    如果你的相机参数不是从COLMAP得到的,就需要自己转换参数形式了,其实也容易,参考上面的几个文件的解读即可。

  • 相关阅读:
    2023校招美团笔试
    Ubuntu16.04编译测试LVI_SAM过程
    mysql连接池和redis连接池
    操作系统运行环境
    十进制转换为二进制
    聚观早报 | 脸书泄露数据被罚20亿;iPhone15将全系支持灵动岛
    思腾云计算
    第三篇 RBAC权限管理 数据库设计详解
    【TA】Unity角色二次元风格渲染
    爬虫_爬虫守则,反爬,反反爬和爬虫开发流程
  • 原文地址:https://blog.csdn.net/qq_30565883/article/details/127907414