2DGS数据集处理实战:COLMAP与自定义数据预处理全解析

2DGS数据集处理实战:COLMAP与自定义数据预处理全解析

【免费下载链接】2d-gaussian-splatting [SIGGRAPH'24] 2D Gaussian Splatting for Geometrically Accurate Radiance Fields 【免费下载链接】2d-gaussian-splatting 项目地址: https://gitcode.com/GitHub_Trending/2d/2d-gaussian-splatting

引言:从图像到三维场景的桥梁

在计算机视觉领域,将二维图像序列转换为三维场景表示是一个核心挑战。2D Gaussian Splatting(2DGS)作为SIGGRAPH'24的最新研究成果,为这一问题提供了高效且精确的解决方案。本文将深入探讨2DGS数据处理流程,重点解析COLMAP相机姿态估计与自定义数据预处理的关键技术,帮助读者掌握从原始图像到训练数据的完整转换过程。

读完本文后,您将能够:

  • 理解2DGS数据处理的整体流程与核心组件
  • 熟练使用COLMAP进行相机标定与稀疏重建
  • 掌握自定义数据集的格式转换与预处理方法
  • 解决数据处理中常见的坐标转换、图像畸变等问题
  • 优化数据质量以提升2DGS模型训练效果

2DGS数据处理全景图

2DGS的数据处理流程涉及多个关键步骤,从原始图像采集到最终可用于训练的数据集,每个环节都对最终重建质量有着重要影响。以下是2DGS数据处理的整体框架:

mermaid

核心数据处理模块

2DGS项目中负责数据处理的核心模块主要集中在scene/目录下,包括:

这些模块协同工作,完成从COLMAP输出文件到2DGS训练数据的转换。其中,colmap_loader.py负责解析COLMAP生成的相机参数和三维点云,dataset_readers.py则将这些数据转换为2DGS所需的格式,并进行数据集划分。

COLMAP相机标定与三维重建

COLMAP(COLMAP - Structure-from-Motion and Multi-View Stereo)是一个广泛使用的开源三维重建工具,它能够从多张二维图像中估计相机姿态并重建场景的三维结构。在2DGS中,COLMAP的输出结果是数据处理的重要输入。

COLMAP支持的相机模型

COLMAP支持多种相机模型,2DGS通过scene/colmap_loader.py中的CAMERA_MODELS字典定义了支持的相机模型及其参数数量:

CAMERA_MODELS = {
    CameraModel(model_id=0, model_name="SIMPLE_PINHOLE", num_params=3),
    CameraModel(model_id=1, model_name="PINHOLE", num_params=4),
    CameraModel(model_id=2, model_name="SIMPLE_RADIAL", num_params=4),
    CameraModel(model_id=3, model_name="RADIAL", num_params=5),
    CameraModel(model_id=4, model_name="OPENCV", num_params=8),
    CameraModel(model_id=5, model_name="OPENCV_FISHEYE", num_params=8),
    CameraModel(model_id=6, model_name="FULL_OPENCV", num_params=12),
    CameraModel(model_id=7, model_name="FOV", num_params=5),
    CameraModel(model_id=8, model_name="SIMPLE_RADIAL_FISHEYE", num_params=4),
    CameraModel(model_id=9, model_name="RADIAL_FISHEYE", num_params=5),
    CameraModel(model_id=10, model_name="THIN_PRISM_FISHEYE", num_params=12)
}

在2DGS中,主要支持PINHOLESIMPLE_PINHOLE两种相机模型,这两种模型假设图像已经经过畸变校正。

COLMAP数据格式解析

COLMAP输出两种格式的数据:文本格式和二进制格式。2DGS的scene/colmap_loader.py提供了读取这两种格式的函数:

  • 文本格式读取:read_extrinsics_text()read_intrinsics_text()read_points3D_text()
  • 二进制格式读取:read_extrinsics_binary()read_intrinsics_binary()read_points3D_binary()

以相机内参读取为例,文本格式的相机内参文件(cameras.txt)格式如下:

# Camera list with one line of data per camera:
#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]
# Number of cameras: 2
1 PINHOLE 1920 1080 1500 1500 960 540
2 PINHOLE 1920 1080 1500 1500 960 540

对应的解析代码在read_intrinsics_text()函数中实现:

def read_intrinsics_text(path):
    """
    Taken from https://github.com/colmap/colmap/blob/dev/scripts/python/read_write_model.py
    """
    cameras = {}
    with open(path, "r") as fid:
        while True:
            line = fid.readline()
            if not line:
                break
            line = line.strip()
            if len(line) > 0 and line[0] != "#":
                elems = line.split()
                camera_id = int(elems[0])
                model = elems[1]
                assert model == "PINHOLE", "While the loader support other types, the rest of the code assumes PINHOLE"
                width = int(elems[2])
                height = int(elems[3])
                params = np.array(tuple(map(float, elems[4:])))
                cameras[camera_id] = Camera(id=camera_id, model=model,
                                            width=width, height=height,
                                            params=params)
    return cameras

四元数到旋转矩阵的转换

COLMAP使用四元数(Quaternion)表示相机旋转,而2DGS在内部使用旋转矩阵(Rotation Matrix)。scene/colmap_loader.py中的qvec2rotmat()函数实现了这一转换:

def qvec2rotmat(qvec):
    return np.array([
        [1 - 2 * qvec[2]**2 - 2 * qvec[3]**2,
         2 * qvec[1] * qvec[2] - 2 * qvec[0] * qvec[3],
         2 * qvec[3] * qvec[1] + 2 * qvec[0] * qvec[2]],
        [2 * qvec[1] * qvec[2] + 2 * qvec[0] * qvec[3],
         1 - 2 * qvec[1]**2 - 2 * qvec[3]**2,
         2 * qvec[2] * qvec[3] - 2 * qvec[0] * qvec[1]],
        [2 * qvec[3] * qvec[1] - 2 * qvec[0] * qvec[2],
         2 * qvec[2] * qvec[3] + 2 * qvec[0] * qvec[1],
         1 - 2 * qvec[1]**2 - 2 * qvec[2]**2]])

这一转换对于正确理解相机姿态至关重要,因为四元数和旋转矩阵在不同的计算场景中各有优势:四元数适合插值和避免万向锁问题,而旋转矩阵则便于进行坐标变换计算。

COLMAP到2DGS的工作流

使用COLMAP处理图像并将结果导入2DGS的完整流程如下:

  1. 图像准备:收集同一场景的多张图像,确保足够的重叠区域
  2. 特征提取与匹配:COLMAP自动提取图像特征并进行匹配
  3. 相机姿态估计:通过运动恢复结构(SfM)估计相机内外参数
  4. 稀疏重建:生成场景的稀疏点云
  5. 数据导入2DGS:使用scene/colmap_loader.py读取COLMAP输出

mermaid

相机参数与坐标转换

相机参数的正确解析和坐标系统的统一是数据处理中的关键环节。不同的软件和库可能采用不同的坐标系统定义,需要进行适当的转换才能确保数据的一致性。

相机内参解析

相机内参(Intrinsic Parameters)描述了相机的光学特性,包括焦距、主点坐标等。对于PINHOLE相机模型,2DGS通过以下代码解析内参:

if intr.model=="SIMPLE_PINHOLE":
    focal_length_x = intr.params[0]
    FovY = focal2fov(focal_length_x, height)
    FovX = focal2fov(focal_length_x, width)
elif intr.model=="PINHOLE":
    focal_length_x = intr.params[0]
    focal_length_y = intr.params[1]
    FovY = focal2fov(focal_length_y, height)
    FovX = focal2fov(focal_length_x, width)

其中,focal2fov()函数将焦距转换为视场角(Field of View):

def focal2fov(focal, pixels):
    return 2 * math.atan(pixels / (2 * focal))

视场角是相机光学系统的重要参数,它决定了相机能够捕捉的场景范围。在2DGS中,视场角用于正确计算高斯分布的投影。

坐标系统转换

不同的三维重建和渲染系统可能采用不同的坐标系统定义。COLMAP和2DGS在坐标系统上存在差异,需要进行适当的转换。

COLMAP采用的是计算机视觉中常用的坐标系统:

  • X轴:向右
  • Y轴:向下
  • Z轴:向前

而在一些图形学系统(如OpenGL、Blender)中,坐标系统通常为:

  • X轴:向右
  • Y轴:向上
  • Z轴:向后

2DGS在处理Nerf合成数据集时,需要进行坐标系统的转换:

# NeRF 'transform_matrix' is a camera-to-world transform
c2w = np.array(frame["transform_matrix"])
# change from OpenGL/Blender camera axes (Y up, Z back) to COLMAP (Y down, Z forward)
c2w[:3, 1:3] *= -1

这一转换确保了不同来源的数据能够在统一的坐标系统下进行处理,避免了因坐标定义不同而导致的模型错误。

相机姿态表示

相机姿态可以用多种方式表示,2DGS中主要使用旋转矩阵(R)和平移向量(T)。相机外参(Extrinsic Parameters)描述了相机在世界坐标系中的位置和朝向。

scene/dataset_readers.py中,相机信息被封装为CameraInfo类:

class CameraInfo(NamedTuple):
    uid: int
    R: np.array
    T: np.array
    FovY: np.array
    FovX: np.array
    image: np.array
    image_path: str
    image_name: str
    width: int
    height: int

其中,R是旋转矩阵,T是平移向量,共同描述了相机在世界坐标系中的姿态。

点云数据处理

点云(Point Cloud)是三维场景的一种离散表示,由大量三维点组成,每个点通常包含三维坐标和颜色信息。在2DGS中,点云数据作为初始的三维场景表示,为高斯分布的初始化提供基础。

COLMAP点云格式

COLMAP输出的点云有两种格式:文本格式(points3D.txt)和二进制格式(points3D.bin)。2DGS提供了读取这两种格式的函数:

  • 文本格式读取:read_points3D_text()
  • 二进制格式读取:read_points3D_binary()

以二进制格式读取为例:

def read_points3D_binary(path_to_model_file):
    with open(path_to_model_file, "rb") as fid:
        num_points = read_next_bytes(fid, 8, "Q")[0]

        xyzs = np.empty((num_points, 3))
        rgbs = np.empty((num_points, 3))
        errors = np.empty((num_points, 1))

        for p_id in range(num_points):
            binary_point_line_properties = read_next_bytes(
                fid, num_bytes=43, format_char_sequence="QdddBBBd")
            xyz = np.array(binary_point_line_properties[1:4])
            rgb = np.array(binary_point_line_properties[4:7])
            error = np.array(binary_point_line_properties[7])
            track_length = read_next_bytes(
                fid, num_bytes=8, format_char_sequence="Q")[0]
            track_elems = read_next_bytes(
                fid, num_bytes=8*track_length,
                format_char_sequence="ii"*track_length)
            xyzs[p_id] = xyz
            rgbs[p_id] = rgb
            errors[p_id] = error
    return xyzs, rgbs, errors

PLY格式转换

为了方便点云的可视化和后续处理,2DGS将COLMAP点云转换为PLY(Polygon File Format)格式:

def storePly(path, xyz, rgb):
    # Define the dtype for the structured array
    dtype = [('x', 'f4'), ('y', 'f4'), ('z', 'f4'),
            ('nx', 'f4'), ('ny', 'f4'), ('nz', 'f4'),
            ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')]
    
    normals = np.zeros_like(xyz)

    elements = np.empty(xyz.shape[0], dtype=dtype)
    attributes = np.concatenate((xyz, normals, rgb), axis=1)
    elements[:] = list(map(tuple, attributes))

    # Create the PlyData object and write to file
    vertex_element = PlyElement.describe(elements, 'vertex')
    ply_data = PlyData([vertex_element])
    ply_data.write(path)

PLY格式是一种通用的三维模型格式,支持点云、网格等多种表示形式,便于使用MeshLab、CloudCompare等软件进行可视化和编辑。

点云统计与过滤

在将点云用于2DGS训练之前,通常需要进行统计分析和过滤,去除噪声点和离群点。虽然2DGS的代码中没有直接提供这些功能,但可以使用Python的点云处理库(如Open3D)进行扩展:

# 示例:使用Open3D进行点云过滤
import open3d as o3d

pcd = o3d.io.read_point_cloud("sparse/0/points3D.ply")
# 统计离群点移除
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
filtered_pcd = pcd.select_by_index(ind)
o3d.io.write_point_cloud("sparse/0/filtered_points3D.ply", filtered_pcd)

这一步骤对于提高模型训练质量非常重要,因为噪声点会导致高斯分布的错误初始化,影响最终的渲染效果。

自定义数据集预处理

除了使用COLMAP处理的数据集外,2DGS还支持自定义数据集的导入。这需要将自定义数据转换为2DGS能够识别的格式,并确保坐标系统和相机参数的一致性。

自定义数据集格式要求

2DGS支持类似NeRF的自定义数据集格式,主要包含:

  • 图像文件:场景的二维图像
  • 变换文件:描述相机姿态和内参的JSON文件

变换文件(如transforms_train.json)的格式如下:

{
    "camera_angle_x": 0.6911112070083618,
    "frames": [
        {
            "file_path": "./train/r_0",
            "transform_matrix": [
                [1.0, 0.0, 0.0, 0.0],
                [0.0, 1.0, 0.0, 0.0],
                [0.0, 0.0, 1.0, 0.0],
                [0.0, 0.0, 0.0, 1.0]
            ]
        },
        // 更多帧...
    ]
}

数据加载与坐标转换

2DGS提供了readCamerasFromTransforms()函数来读取自定义数据集:

def readCamerasFromTransforms(path, transformsfile, white_background, extension=".png"):
    cam_infos = []

    with open(os.path.join(path, transformsfile)) as json_file:
        contents = json.load(json_file)
        fovx = contents["camera_angle_x"]

        frames = contents["frames"]
        for idx, frame in enumerate(frames):
            cam_name = os.path.join(path, frame["file_path"] + extension)

            # NeRF 'transform_matrix' is a camera-to-world transform
            c2w = np.array(frame["transform_matrix"])
            # change from OpenGL/Blender camera axes (Y up, Z back) to COLMAP (Y down, Z forward)
            c2w[:3, 1:3] *= -1

            # get the world-to-camera transform and set R, T
            w2c = np.linalg.inv(c2w)
            R = np.transpose(w2c[:3,:3])  # R is stored transposed due to 'glm' in CUDA code
            T = w2c[:3, 3]

            # 图像加载和处理...

注意这里的坐标转换:NeRF使用的是OpenGL/Blender坐标系统(Y轴向上,Z轴向后),而2DGS采用COLMAP的坐标系统(Y轴向下,Z轴向前),因此需要对变换矩阵进行调整。

数据集划分与归一化

为了进行模型训练和评估,需要将数据集划分为训练集和测试集。2DGS提供了灵活的划分方式:

if eval:
    train_cam_infos = [c for idx, c in enumerate(cam_infos) if idx % llffhold != 0]
    test_cam_infos = [c for idx, c in enumerate(cam_infos) if idx % llffhold == 0]
else:
    train_cam_infos = cam_infos
    test_cam_infos = []

此外,为了提高训练稳定性,2DGS还对相机坐标进行归一化处理,将场景中心平移到原点并缩放到单位球内:

def getNerfppNorm(cam_info):
    def get_center_and_diag(cam_centers):
        cam_centers = np.hstack(cam_centers)
        avg_cam_center = np.mean(cam_centers, axis=1, keepdims=True)
        center = avg_cam_center
        dist = np.linalg.norm(cam_centers - center, axis=0, keepdims=True)
        diagonal = np.max(dist)
        return center.flatten(), diagonal

    cam_centers = []

    for cam in cam_info:
        W2C = getWorld2View2(cam.R, cam.T)
        C2W = np.linalg.inv(W2C)
        cam_centers.append(C2W[:3, 3:4])

    center, diagonal = get_center_and_diag(cam_centers)
    radius = diagonal * 1.1

    translate = -center

    return {"translate": translate, "radius": radius}

这一归一化步骤对于优化器的收敛和模型的泛化能力至关重要。

数据处理常见问题与解决方案

在数据处理过程中,可能会遇到各种问题,如坐标转换错误、图像格式不兼容、相机参数缺失等。以下是一些常见问题的解决方案:

坐标系统混淆

问题:导入自定义数据集后,渲染结果出现严重扭曲或相机位置异常。

原因:不同软件使用的坐标系统不同,导致相机姿态计算错误。

解决方案:确保正确进行坐标系统转换,特别是Y轴方向和Z轴方向的调整:

# 将OpenGL/Blender坐标转换为COLMAP坐标
c2w[:3, 1:3] *= -1  # Y轴和Z轴反转

图像畸变未校正

问题:渲染结果边缘出现明显的畸变。

原因:使用了带有畸变的图像,但未进行畸变校正。

解决方案

  1. 使用COLMAP对图像进行畸变校正
  2. 在自定义数据集中确保提供的是校正后的图像
  3. 对于鱼眼相机等特殊镜头,使用对应的相机模型

点云质量低下

问题:稀疏点云过于稀疏或包含大量噪声点,导致高斯初始化效果差。

解决方案

  1. 增加输入图像数量,提高特征匹配质量
  2. 使用COLMAP的图像对准功能优化相机姿态
  3. 对点云进行后处理,如统计离群点移除
  4. 考虑使用MVS(Multi-View Stereo)生成稠密点云

相机参数不完整

问题:缺少相机内参或外参,导致数据加载失败。

解决方案

  1. 确保COLMAP处理成功完成,生成完整的输出文件
  2. 对于自定义数据集,检查transforms.json文件是否包含所有必要参数
  3. 使用默认参数进行估计,如假设主点位于图像中心
# 示例:假设主点位于图像中心
if principal_point is None:
    cx = width / 2
    cy = height / 2

数据预处理优化策略

为了提高2DGS模型的训练效果和渲染质量,可以采用以下数据预处理优化策略:

图像分辨率调整

过高的图像分辨率会增加计算负担,而过低的分辨率则会丢失细节。根据场景复杂度选择合适的分辨率:

# 示例:调整图像分辨率
image = image.resize((new_width, new_height), Image.LANCZOS)

图像增强

适当的图像增强可以提高模型的泛化能力:

# 示例:简单的图像增强
from PIL import ImageEnhance

enhancer = ImageEnhance.Brightness(image)
image = enhancer.enhance(1.2)  # 增加亮度

相机参数精细化

精确的相机参数对于三维重建至关重要,可以通过以下方法优化:

  1. 使用棋盘格标定板精确标定相机内参
  2. 考虑相机的径向和切向畸变参数
  3. 对于多相机系统,单独标定每个相机

点云密度优化

点云的密度和分布直接影响高斯初始化的质量:

  1. 确保图像覆盖场景的各个角度
  2. 增加图像数量以提高点云密度
  3. 使用稠密重建方法生成更密集的点云

总结与展望

数据处理是2DGS技术 pipeline 中的关键环节,直接影响最终的重建质量和渲染效果。本文详细介绍了2DGS数据处理的完整流程,包括COLMAP相机标定、点云处理、坐标转换和自定义数据集导入等核心内容。

通过掌握这些技术,读者可以:

  1. 熟练使用COLMAP进行相机姿态估计和稀疏重建
  2. 理解并处理不同坐标系统之间的转换
  3. 准备高质量的2DGS训练数据
  4. 解决数据处理中常见的技术难题

未来,随着2DGS技术的不断发展,数据处理流程可能会更加自动化和高效化。例如,集成实时相机标定、自动图像质量评估、智能点云优化等功能,进一步降低数据准备的门槛,提高重建质量。

对于希望深入研究2DGS数据处理的读者,建议重点关注以下方向:

  • 不同相机模型对2DGS重建质量的影响
  • 点云密度与高斯数量的关系优化
  • 动态场景的数据处理策略
  • 多模态数据(如RGB-D)的融合方法

通过不断优化数据处理流程,2DGS技术将在三维重建、增强现实、虚拟现实等领域发挥更大的作用。

附录:数据集处理工具清单

COLMAP相关工具

  • COLMAP官方文档:https://colmap.github.io/
  • COLMAP Python API:提供更灵活的数据处理接口

点云处理工具

  • Open3D:开源点云处理库,支持多种滤波和特征提取算法
  • CloudCompare:可视化和编辑点云数据的强大工具

图像预处理工具

  • OpenCV:计算机视觉库,提供图像畸变校正、特征提取等功能
  • PIL/Pillow:Python图像处理库,用于图像格式转换和基本操作

2DGS数据处理脚本

掌握这些工具将帮助您更高效地进行2DGS数据集的处理和优化,为后续的模型训练和场景重建奠定坚实基础。


希望本文能够帮助您深入理解2DGS的数据处理流程。如果您有任何问题或建议,欢迎在项目GitHub仓库提交issue或PR。感谢您的阅读!

如果觉得本文对您有帮助,请点赞、收藏并关注项目更新,以便获取更多2DGS相关的技术分享。

下一期预告:《2DGS模型训练全解析:从参数调优到训练加速》

【免费下载链接】2d-gaussian-splatting [SIGGRAPH'24] 2D Gaussian Splatting for Geometrically Accurate Radiance Fields 【免费下载链接】2d-gaussian-splatting 项目地址: https://gitcode.com/GitHub_Trending/2d/2d-gaussian-splatting

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值