3D高斯点云CUDA版本数据制作与demo运行

news2025/1/7 9:56:34

0. 简介

关于UCloud(优刻得)旗下的compshare算力共享平台
UCloud(优刻得)是中国知名的中立云计算服务商,科创板上市,中国云计算第一股。
Compshare GPU算力平台隶属于UCloud,专注于提供高性价4090算力资源,配备独立IP,支持按时、按天、按月灵活计费,支持github、huggingface访问加速。
使用下方链接注册可获得20元算力金,免费体验10小时4090云算力
https://www.compshare.cn/?ytag=GPU_lovelyyoshino_Lcsdn_csdn_display

在计算机图形学领域,3D高斯点云(3D Gaussian Splatting)技术因其在实时渲染中的应用而备受关注。之前我们在3D Gaussian Splatting是什么以及为什么这么火作为第一篇对3DGS做了一系列的科普。然后在《如何搭建RGBD GS-ICP SLAM环境以及如何与自己编的pcl并存》以及《Ubuntu20.04安装LibTorch并完成高斯溅射环境搭建》中介绍了GS相关的一些拓展操作和实机项目使用。同时在《认识3D Gaussian Splatting以及如何和ROS结合完成实时建图》进一步给读者大概展示了怎么样将点云数据和相机keyframe数据结合在一起,然后做高斯渲染。我们这一篇继续和优刻得合作,来带着大家看一看gaussian-splatting-cuda这个项目,以及如何进一步和LIVO融合的

1. 3D高斯点云技术的主要融合手段

在3D高斯点云(3D Gaussian Splatting,3DGS)技术的发展中,前端和后端的有效结合成为了实现高效且高质量渲染的关键。以下是3DGS主要融合的手段,包括前端点云图像融合、后端高斯训练以及混合高斯模型的介绍。

1.1 前端点云图像融合

前端部分主要负责从传感器(如相机、激光雷达等)获取数据,并通过相应的算法将这些数据融合成点云图像。以下是一些常用的前端融合方法:

  1. CoCo-LIC:这是一种协同点云和图像传感器融合的方法,旨在通过结合多种传感器的数据,提高环境感知的精度。CoCo-LIC利用深度学习技术优化数据融合过程,从而提升SLAM(Simultaneous Localization and Mapping)系统的表现。

  2. FAST-LVIO:该方法采用紧耦合的视觉惯性里程计(Visual-Inertial Odometry)技术,能够实现实时的点云生成和地图构建。通过将视觉数据与惯性测量单元(IMU)数据结合,FAST-LVIO在动态环境中仍能保持高精度。

  3. FAST-LIO2:作为FAST-LVIO的改进版本,FAST-LIO2引入了更高效的优化算法,能够在更复杂的环境中进行实时点云图像融合。其核心思想是通过局部优化来提高全局地图的精度。

  4. DLIO:深度学习视觉惯性里程计(Deep Learning Visual-Inertial Odometry)利用深度学习模型对传感器数据进行处理,以增强对动态场景的适应能力,进而提升点云图像的质量和准确性。

这些前端方法通过结合不同的传感器数据,为后端的高斯训练提供了高质量的点云输入。然后再+后端高斯训练来完成整个训练。后端部分主要负责对前端生成的点云进行训练与处理,以实现高效的渲染与表示。以下是一些重要的后端高斯训练方法:

  1. Gaussian-Splatting:这一方法通过将点云表示为高斯分布,能够有效处理大规模点云数据。Gaussian-Splatting通过将点云中的每个点视为一个高斯核,利用其光照和颜色信息,实现高质量的渲染效果。

  2. Mip-Splatting:Mip-Splatting是对Gaussian-Splatting的扩展,考虑了多分辨率的高斯核,使得在不同视距下的渲染效果更加平滑。通过采用多级别的高斯核,Mip-Splatting能够在渲染时动态选择合适的细节层次,提高性能。

  3. Street Gaussians:这一方法专注于街道场景的高斯表示,特别适用于城市环境中的点云数据。Street Gaussians利用场景的几何特征,优化了高斯的分布和密度,从而提升渲染的真实感。

  4. OpenSplat:OpenSplat是一个开源项目,旨在提供高效的高斯点云处理框架。它集成了多种高斯处理技术,支持用户根据具体需求进行灵活配置。

  5. gaussian-splatting-cuda:这是一个基于CUDA实现的高斯点云处理库,利用GPU加速高斯渲染和训练过程。通过并行计算,gaussian-splatting-cuda能够显著提高渲染速度,适用于实时应用场景。

1.2 混合高斯模型

混合高斯模型(Mixture of Gaussians)在3DGS中具有重要的应用,特别是在处理复杂场景时,能够提供更灵活的表示。

  1. LOG-3DGS:这是一个基于对数高斯点云表示的模型,旨在解决传统高斯模型在动态场景中的局限性。通过对数变换,LOG-3DGS能够更好地捕捉场景的细节变化,提高渲染的稳定性和准确性。

  2. GS-LIVM:该方法结合了高斯点云和视觉惯性里程计(LIVM)技术,能够在动态环境中高效地进行点云生成与处理。GS-LIVM利用高斯模型的优势,在保证渲染质量的同时,提升了系统的实时性和鲁棒性。

通过以上的前端和后端融合手段,3D高斯点云技术能够高效地处理和渲染复杂场景,为实时图形学和计算机视觉等领域的发展提供了强有力的支持。

2. 环境安装

2.1 软件要求

  1. 操作系统:Linux(推荐使用Ubuntu 22.04)
  2. CMake:版本3.24或更高
  3. CUDA:版本11.7或更高(可能支持低版本,但需手动设置并测试)
  4. Python:需要安装开发头文件
  5. libtorch:详细的安装说明见项目文档
  6. 其他依赖:通过CMake脚本自动处理

2.2 安装步骤

以下是安装环境的详细步骤:

# 更新系统
sudo apt-get update

# 安装基本依赖
sudo apt-get install build-essential cmake libtbb-dev python3-dev

# 安装CUDA(根据您的系统选择合适的CUDA版本)
# 请参考NVIDIA官网获取安装指导

# 安装Eigen
git clone https://github.com/eigenteam/eigen-git-mirror
cd eigen-git-mirror
git checkout 3.3.7
mkdir build && cd build
cmake .. && sudo make install
cd ../../

# 安装nlohmann-json
sudo apt-get install nlohmann-json3-dev

# 安装GLM
sudo apt-get install libglm-dev

# 安装OpenCV
mkdir -p opencv && cd opencv
git clone https://github.com/opencv/opencv.git
git clone https://github.com/opencv/opencv_contrib.git
cd opencv
git checkout 4.4.0
cd ../opencv_contrib
git checkout 4.4.0
cd ../../
mkdir -p opencv/opencv/build && cd opencv/opencv/build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules ..
make -j16
sudo make install
cd ../../

# 安装PCL
sudo apt-get install libboost-all-dev libgl1-mesa-dev libglu1-mesa-dev libflann-dev
git clone https://github.com/PointCloudLibrary/pcl.git
cd pcl
git checkout pcl-1.13.0
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=None -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_GPU=OFF -DBUILD_apps=OFF -DBUILD_examples=OFF ..
make -j16
sudo make install
cd ../../

到此为止我们所有需要的环境就装完了
在这里插入图片描述

3. 测试与性能评估

在环境安装完成后,您可以开始测试项目的性能。以下是一些测试步骤和性能评估结果。

3.1 测试数据集

项目不包含数据集,您可以从原始仓库下载数据集:

  • 下载链接:tanks & trains

将下载的文件解压到项目的 data 文件夹。

3.2 gaussian-splatting-cuda安装编译

安装cuda环境

git clone --recursive https://github.com/lovelyyoshino/gaussian-splatting-cuda
cd gaussian-splatting-cuda
wget https://download.pytorch.org/libtorch/cu118/libtorch-cxx11-abi-shared-with-deps-2.0.1%2Bcu118.zip  
unzip  libtorch-cxx11-abi-shared-with-deps-2.0.1+cu118.zip -d external/
rm libtorch-cxx11-abi-shared-with-deps-2.0.1+cu118.zip
cmake -B build -DCMAKE_BUILD_TYPE=Release -DTORCH_CXX_FLAGS=/gaussian-splatting-cuda/external/libtorch
cmake --build build -- -j

在这里插入图片描述

安装SIBR view

sudo apt install -y libglew-dev libassimp-dev libboost-all-dev libgtk-3-dev libopencv-dev libglfw3-dev libavdevice-dev libavcodec-dev libeigen3-dev libxxf86vm-dev libembree-dev
                        
git clone --recursive https://github.com/Lurvelly/SIBR_viewers.git SIBR_viewers
cd SIBR_viewers
cmake -B build . -DCMAKE_BUILD_TYPE=Release
cmake --build build -j24 --target install --config Release -- -j 8
cd ..

在这里插入图片描述

出现ASSIMP 找不到的问题时候,采纳这个解决方法:https://github.com/graphdeco-inria/gaussian-splatting/issues/840。需要将FIND_LIBRARY(ASSIMP_LIBRARY中的名字改成libassimp.so

CMake Error at /usr/share/cmake-3.28/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  ASSIMP wasn't found correctly.  Set ASSIMP_DIR to the root SDK installation
  directory.  (missing: ASSIMP_INCLUDE_DIR ASSIMP_LIBRARIES)

在这里插入图片描述
我们需要监控网络,因为一般的imgui安装会比较慢,如果没有网络上下行,那需要杀掉重新cmake编译,这里我们使用

sudo apt install ifstat
ifstat

在这里插入图片描述
或者使用ethstatus可以更直观查看网络

sudo apt-get install ethstatus
sudo ethstatus -i eth0

在这里插入图片描述

3.3 执行测试

在构建完成后,您可以使用以下命令行选项运行项目,我们下面可以看到只需要2分钟就可以完成整个全部处理:

./build/gaussian_splatting_cuda -d /gaussian-splatting-cuda/data/tandt/truck -o /gaussian-splatting-cuda/output/ -i 10000

在这里插入图片描述

我们在/start.d文件下添加了自启文件,可以非常方便的支持vnc的可视化操作,复制进去后执行bash。关于VNC安装的可以查看Compshare云服务平台Genesis世界模型的上手与测试这篇文章的配置内容。

#!/bin/bash

# 定义锁文件路径
LOCK_DIR="/tmp"
LOCK_PATTERN="*-lock"

# 查找并删除所有 .x-lock 文件
echo "Deleting all files matching $LOCK_PATTERN in $LOCK_DIR"
find "$LOCK_DIR" -name "$LOCK_PATTERN" -exec rm -f {} \;
rm -rf /tmp/.X11-unix/*
echo "Cleanup complete."

export USER=root
export DISPLAY=:99
export VTKI_OFF_SCREEN=True

/usr/bin/vncserver -geometry 1920x1080  > /dev/null 2>&1 &
echo "Started vncserver"
/usr/lib/noVNC/utils/novnc_proxy --vnc localhost:5901 >/dev/null 2>&1 &
echo "Started noVNC"

/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
echo "Started Xvfb"
/usr/bin/x11vnc  -listen 0.0.0.0 -rfbport 5902 -noipv6 -forever -noxrecord -repeat -nopw -display :99 > /dev/null 2>& 1 &
echo "Started x11vnc"

然后浏览器访问 http://${外网EIP地址}:6080/vnc.html即可打开Novnc

然后我们使用SIBR_viewer这个来看可视化

./SIBR_viewers/install/bin/SIBR_gaussianViewer_app -m /gaussian-splatting-cuda/output/

在这里插入图片描述

3.4 性能评估

截至2023年8月17日,我在不同的NVIDIA GPU上进行了性能测试,结果如下:

  • NVIDIA GeForce RTX 4090:7000次迭代约87秒
  • NVIDIA GeForce RTX 3090:7000次迭代约180秒
  • NVIDIA GeForce RTX 3050:7000次迭代约725秒

这些结果展示了不同硬件配置下的执行时间,尽管尚未完全优化,但性能提升还是相当可观的。接下来的目标是将7000次迭代的时间缩短至60秒。

4. colmap文件格式生成

在3D高斯点云(3DGS)技术中,与COLMAP格式的数据兼容性是非常重要的。COLMAP是一个广泛使用的多视图重建软件,其数据格式被许多点云和计算机视觉项目所采用。

4.1 colmap的文件格式

COLMAP是一个流行的多视图几何重建软件,广泛用于从图像中恢复三维场景。COLMAP能够生成高质量的点云,并提供了一种标准化的数据格式,方便用户在多个应用程序之间共享数据。COLMAP的输出通常包括以下几种文件:

  • images.bin:保存每张图像的相关信息,包括图像ID、相机参数、图像文件名、相机姿态等。
  • cameras.bin:保存相机的内参和模型信息。
  • points3D.bin:保存三维点云数据,包括每个点的坐标、颜色和其他属性。

通过将3DGS与COLMAP格式结合,用户可以轻松地处理和可视化点云数据,为后续的分析和应用提供便利。

  • images.bin

    注释中的内容说明了cameras的数量,然后每一行依次是:相机id、相机模型、宽、高、相机参数(不同的相机参数不同)

    宽、高是相机传感器的参数,决定了拍摄出的图片的尺寸,

# Camera list with one line of data per camera:
#   CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]
# Number of cameras: 3 1 
SIMPLE_PINHOLE 3072 2304 2559.81 1536 1152 2 
PINHOLE 3072 2304 2560.56 2560.56 1536 1152 3 
SIMPLE_RADIAL 3072 2304 2559.69 1536 1152 -0.0218531 
  • cameras.bin

    每条数据分为两行:

# 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: 2, mean observations per image: 2 
1 0.851773 0.0165051 0.503764 -0.142941 -0.737434 1.02973 3.74354 1 P1180141.JPG 2362.39 248.498 58396 1784.7 268.254 59027 1784.7 268.254 -1 
2 0.851773 0.0165051 0.503764 -0.142941 -0.737434 1.02973 3.74354 1 P1180142.JPG 1190.83 663.957 23056 1258.77 640.354 59070 

概念介绍:

quaternion(QW、QX、QY、QZ):四元数(Hamilton定义)。用于实现相机坐标系到世界坐标系的转换。四元数(QW、QX、QY、QZ)转换为->旋转矩阵R

\mathbf{R} = \begin{bmatrix} 1 - 2(QY^2 + QZ^2) & 2(QXQY - QWQZ) & 2(QXQZ + QWQY) \ 2(QXQY + QWQZ) & 1 - 2(QX^2 + QZ^2) & 2(QYQZ - QWQX) \ 2(QXQZ - QWQY) & 2(QYQZ + QWQX) & 1 - 2(QX^2 + QY^2) \end{bmatrix}
t为平移向量。

  • 第一行依次是IMAGE_ID、quaternion(QW、QX、QY、QZ)、变换矩阵(TX、TY、TZ)、Camera_id、图片名

  • 第二行依次是:

  • 世界坐标系中的相机中心坐标:C = -R^t \cdot t。

  • 相机坐标系P_c点 - > 世界坐标系P_w: P_w = R^t \cdot (P_c -C)

  • 世界坐标系P_w点 ->相机坐标系P_c:P_c = R \cdot(P_w - t)

  • points3D.bin

    ERROR是3D的重投影误差,单位为像素。就是3D点投影回2D的时候和实际检测到的角点的误差(感觉可以用来算loss啊)。

    TRACK[]是跟踪信息,记录该3D点在哪些图像中被观测到,包含两个值:

# 3D point list with one line of data per point:
#   POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX)

# Number of points: 3, mean track length: 3.3334 
63390 1.67241 0.292931 0.609726 115 121 122 1.33927 16 6542 15 7345 6 6714 14 7227 
63376 2.01848 0.108877 -0.0260841 102 209 250 1.73449 16 6519 15 7322 14 7212 8 3991 
63371 1.71102 0.28566 0.53475 245 251 249 0.612829 118 4140 117 4473 
  • IMAGE_ID和POINT2D_IDX

  • POINT2D_IDX是在image.txt中记录的对应的2D图像的角点的索引。

好的,让我们深入探讨3D高斯点云(3DGS)技术及其与COLMAP文件格式之间的关系,同时详细解释代码的实现过程。

4.2 代码实现详解

以下是一个CUDA源文件的实现示例,用于保存图像和点云数据,格式与COLMAP相兼容。

#include <iostream>
#include <fstream>
#include <vector>
#include <filesystem>
#include <point_cloud.cuh>
#include <image.cuh>
#include <camera_info.cuh>

#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>


using PointCloudXYZRGB = pcl::PointCloud<pcl::PointXYZRGB>;

std::unordered_map<int, std::pair<CAMERA_MODEL, uint32_t>> write_camera_model_ids = {
    {0, {CAMERA_MODEL::SIMPLE_PINHOLE, 3}},
    {1, {CAMERA_MODEL::PINHOLE, 4}},
    {2, {CAMERA_MODEL::SIMPLE_RADIAL, 4}},
    {3, {CAMERA_MODEL::RADIAL, 5}},
    {4, {CAMERA_MODEL::OPENCV, 8}},
    {5, {CAMERA_MODEL::OPENCV_FISHEYE, 8}},
    {6, {CAMERA_MODEL::FULL_OPENCV, 12}},
    {7, {CAMERA_MODEL::FOV, 5}},
    {8, {CAMERA_MODEL::SIMPLE_RADIAL_FISHEYE, 4}},
    {9, {CAMERA_MODEL::RADIAL_FISHEYE, 5}},
    {10, {CAMERA_MODEL::THIN_PRISM_FISHEYE, 12}},
    {11, {CAMERA_MODEL::UNDEFINED, -1}}};

struct Track {
    uint32_t _image_ID;
    uint32_t _max_num_2D_points;
};

// 保存图像数据的函数
void save_images(const std::filesystem::path& file_path, const std::vector<std::pair<cv::Mat,int>>& images, const std::vector<Eigen::Matrix4d>& poses) {
    if (images.size() != poses.size()) {
        throw std::invalid_argument("Images and poses must have the same size.");
    }

    std::ofstream output_stream(file_path / "images.bin", std::ios::binary);
    if (!output_stream) {
        throw std::runtime_error("Unable to open file for writing: " + (file_path / "images.bin").string());
    }

    uint64_t image_count = images.size();
    output_stream.write(reinterpret_cast<const char*>(&image_count), sizeof(image_count));
    struct ImagePoint { // we dont need this later
        double _x;
        double _y;
        uint64_t _point_id;
    };
    for (size_t i = 0; i < image_count; ++i) {
        std::pair<cv::Mat,int> img = images[i];
        const auto& pose = poses[i];

        // 从Matrix4d中提取旋转和平移
        Eigen::Quaterniond q(pose.block<3, 3>(0, 0)); // 提取旋转部分
        Eigen::Vector3d t = pose.block<3, 1>(0, 3); // 提取平移部分

        // 写入相机 ID(可以使用索引作为 ID)
        uint32_t image_id = static_cast<uint32_t>(i); // 使用索引作为相机 ID
        output_stream.write(reinterpret_cast<const char*>(&image_id), sizeof(image_id));

        // 写入四元数
        output_stream.write(reinterpret_cast<const char*>(&q.coeffs()), sizeof(double) * 4); // 四元数的四个系数
        // 写入平移向量
        output_stream.write(reinterpret_cast<const char*>(&t), sizeof(Eigen::Vector3d));

        // 写入相机 ID(可以使用索引作为 ID)
        uint32_t camera_id = static_cast<uint32_t>(img.second); // 使用索引作为相机 ID
        output_stream.write(reinterpret_cast<const char*>(&camera_id), sizeof(camera_id));

        // 写入图像名称(使用索引作为名称)
        std::string image_name = "image_" + std::to_string(i) + ".png"; // 假设为 PNG 文件
        output_stream.write(image_name.c_str(), image_name.size() + 1); // 包括空字符
# if 0
        // 写入图像数据大小
        uint64_t number_points = img.first.total(); // 获取图像数据的字节大小
        output_stream.write(reinterpret_cast<const char*>(&number_points), sizeof(number_points)); // 写入图像大小
        std::vector<ImagePoint> points(number_points);
        for(int i = 0; i < img.first.rows; i++) {
            for(int j = 0; j < img.first.cols; j++) {
                points[i*img.first.cols+j]._x = j;
                points[i*img.first.cols+j]._y = i;
                points[i*img.first.cols+j]._point_id = i*img.first.cols+j ;
            }
        }
        // 写入所有点数据
        output_stream.write(reinterpret_cast<char*>(points.data()), number_points * sizeof(ImagePoint));
#else
        uint64_t number_points = 0;
        output_stream.write(reinterpret_cast<const char*>(&number_points), sizeof(number_points)); // 写入图像大小
#endif

    }

    output_stream.close();
}

void save_cameras(const std::filesystem::path& file_path, 
                  const std::vector<std::tuple<uint32_t, int, uint64_t, uint64_t, std::vector<double>>>& cameras) {
    std::ofstream output_stream(file_path / "cameras.bin", std::ios::binary);
    if (!output_stream) {
        throw std::runtime_error("Unable to open file for writing: " + (file_path / "cameras.bin").string());
    }

    uint64_t camera_count = cameras.size();
    output_stream.write(reinterpret_cast<const char*>(&camera_count), sizeof(camera_count));

    for (const auto& params : cameras) {
        uint32_t camera_id = std::get<0>(params);
        // 解包参数
        int model_id = std::get<1>(params);
        uint64_t width = std::get<2>(params);
        uint64_t height = std::get<3>(params);
        const auto& camera_parameters = std::get<4>(params);

        // 写入相机 ID
        output_stream.write(reinterpret_cast<const char*>(&camera_id), sizeof(camera_id));
        
        // 写入相机模型
        output_stream.write(reinterpret_cast<const char*>(&model_id), sizeof(model_id));
        
        // 写入图像尺寸
        output_stream.write(reinterpret_cast<const char*>(&width), sizeof(width));
        output_stream.write(reinterpret_cast<const char*>(&height), sizeof(height));
        CAMERA_MODEL camera_model  = std::get<0>(write_camera_model_ids[model_id]);
        uint32_t camera_model_id = std::get<1>(write_camera_model_ids[model_id]);
        output_stream.write(reinterpret_cast<const char*>(&camera_model), sizeof(camera_model));
        output_stream.write(reinterpret_cast<const char*>(&camera_model_id), sizeof(camera_model_id));
        // 写入参数
        size_t params_size = camera_parameters.size();
        output_stream.write(reinterpret_cast<const char*>(camera_parameters.data()), params_size * sizeof(double));
    }
    output_stream.close();
}


void save_point_cloud(const std::filesystem::path& file_path, const PointCloudXYZRGB& cloud, const std::vector<std::vector<Track>>& tracks) {
    std::ofstream output_stream(file_path, std::ios::binary);
    if (!output_stream) {
        throw std::runtime_error("Unable to open file for writing: " + file_path.string());
    }

    // 写入点云的点数量
    uint64_t point3D_count = cloud.size();
    output_stream.write(reinterpret_cast<const char*>(&point3D_count), sizeof(point3D_count));

    for (size_t i = 0; i < point3D_count; ++i) {
        const auto& point = cloud.points[i];

        // 写入点的坐标 (x, y, z)
        double x = static_cast<double>(point.x);
        double y = static_cast<double>(point.y);
        double z = static_cast<double>(point.z);
        output_stream.write(reinterpret_cast<const char*>(&x), sizeof(x));
        output_stream.write(reinterpret_cast<const char*>(&y), sizeof(y));
        output_stream.write(reinterpret_cast<const char*>(&z), sizeof(z));

        // 写入颜色 (r, g, b)
        uint8_t r = point.r;
        uint8_t g = point.g;
        uint8_t b = point.b;
        output_stream.write(reinterpret_cast<const char*>(&r), sizeof(r));
        output_stream.write(reinterpret_cast<const char*>(&g), sizeof(g));
        output_stream.write(reinterpret_cast<const char*>(&b), sizeof(b));

        // 写入被忽略的值(如深度)
        double ignored_value = 0.0; // 这里可以根据实际需要设定
        output_stream.write(reinterpret_cast<const char*>(&ignored_value), sizeof(ignored_value));
# if 0
        uint64_t track_length = tracks[i].size();
        output_stream.write(reinterpret_cast<const char*>(&track_length), sizeof(track_length));
        output_stream.write(reinterpret_cast<const char*>(tracks[i].data()), track_length * sizeof(uint32_t));
#else
        uint64_t track_length = 0;
        output_stream.write(reinterpret_cast<const char*>(&track_length), sizeof(track_length));
#endif
    }

    output_stream.close();
}


// 创建假数据并调用保存函数
int main() {
    // 假设的图像数据和位姿
    std::vector<std::pair<cv::Mat, int>> images;
    std::vector<Eigen::Matrix4d> poses;
    
    for (int i = 0; i < 5; ++i) { // 创建 5 张假图像
        cv::Mat img = cv::Mat::zeros(480, 640, CV_8UC3); // 黑色图像
        images.emplace_back(img, 0); // 使用相机 ID 0
        Eigen::Matrix4d pose = Eigen::Matrix4d::Identity(); // 单位矩阵表示无旋转和平移
        poses.push_back(pose);
    }

    // 保存图像
    std::filesystem::path image_path = "./";
    save_images(image_path, images, poses);

    // 假设的相机参数
    std::vector<std::tuple<uint32_t, int, uint64_t, uint64_t, std::vector<double>>> cameras;
    for (int i = 0; i < 5; ++i) {
        uint32_t camera_id = static_cast<uint32_t>(i);
        int model_id = 0; // 使用简单针孔模型
        uint64_t width = 640;
        uint64_t height = 480;
        std::vector<double> camera_parameters = {320.0, 240.0, 600.0}; // fx, fy, cx
        cameras.emplace_back(camera_id, model_id, width, height, camera_parameters);
    }

    // 保存相机参数
    std::filesystem::path camera_path = "./";
    save_cameras(camera_path, cameras);

    // 创建假点云数据
    PointCloudXYZRGB cloud;
    for (int i = 0; i < 1000; ++i) {
        pcl::PointXYZRGB point;
        point.x = static_cast<float>(rand() % 100) / 10.0f; // 随机生成点坐标
        point.y = static_cast<float>(rand() % 100) / 10.0f;
        point.z = static_cast<float>(rand() % 100) / 10.0f;
        point.r = static_cast<uint8_t>(rand() % 256); // 随机颜色
        point.g = static_cast<uint8_t>(rand() % 256);
        point.b = static_cast<uint8_t>(rand() % 256);
        cloud.points.push_back(point);
    }
    cloud.width = cloud.size();
    cloud.height = 1; // 点云数据是未组织的

    // 保存点云数据
    std::filesystem::path point_cloud_path = "./point_cloud.bin";
    save_point_cloud(point_cloud_path, cloud, {});

    std::cout << "Data saved successfully!" << std::endl;

    return 0;
}

跑完过后,我们的测试bin文件将可以在下面看到。
在这里插入图片描述

5. 总结

通过本文的讲述,我们可以学习到cuda的高斯溅射使用以及用户如何轻松地将图像、相机参数和点云数据保存为COLMAP格式。这种实现不仅简化了数据处理流程,还为激光雷达加入3D高斯点云技术的应用提供了基础。用户可以在此基础上进行更复杂的操作,如点云重建、数据分析和可视化等。通过与COLMAP的兼容性,用户能够利用现有的工具和资源来进一步提升其项目的效率和效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

框架模块说明 #09 日志模块_01

背景 日志模块是系统的重要组成部分&#xff0c;主要负责记录系统运行状态和定位错误问题的功能。通常&#xff0c;日志分为系统日志、操作日志和安全日志三类。虽然分布式数据平台是当前微服务架构中的重要部分&#xff0c;但本文的重点并不在此&#xff0c;而是聚焦于自定义…

conda指定路径安装虚拟python环境

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

aws(学习笔记第二十二课) 复杂的lambda应用程序(python zip打包)

aws(学习笔记第二十二课) 开发复杂的lambda应用程序(python的zip包) 学习内容&#xff1a; 练习使用CloudShell开发复杂lambda应用程序(python) 1. 练习使用CloudShell CloudShell使用背景 复杂的python的lambda程序会有许多依赖的包&#xff0c;如果不提前准备好这些python的…

driftingblues6靶场攻略

首先 打开kali&#xff0c;扫描主机 地址是192.168.111.143 访问网站 主页源码看一看&#xff0c;没啥用 老套路&#xff0c; 用nmap扫描一下开放端口 用dirsearch扫描一下目录 如果说扫描不到&#xff0c;那就可能是字典不行&#xff0c;换工具就完了 nmap -sV 192.168.…

【顶刊TPAMI 2025】多头编码(MHE)之Part 6:极限分类无需预处理

目录 1 标签分解方法的消融研究2 标签分解对泛化的影响3 讨论4 结论 论文&#xff1a;Multi-Head Encoding for Extreme Label Classification 作者&#xff1a;Daojun Liang, Haixia Zhang, Dongfeng Yuan and Minggao Zhang 单位&#xff1a;山东大学 代码&#xff1a;https:…

vue视频录制 限制大小,限制时长

<template><div style"height: 100vh;background: #000;"><span style"color: #fff;font-size: 18px;">切换数量&#xff1a;{{ devices.length }}</span><video ref"video" autoplay muted playsinline></vid…

毕业项目推荐:基于yolov8/yolov5的行人摔倒检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

高等数学学习笔记 ☞ 无穷小比较与等价无穷小替换

1. 无穷小比较 1. 本质&#xff1a;就是函数的极限趋于0时的速度&#xff0c;谁快谁慢的问题。 2. 定义&#xff1a;若是在同一自变量的变化过程中的无穷小&#xff0c;且&#xff0c;则&#xff1a; ①&#xff1a;若&#xff0c;则称是比的高阶无穷小&#xff0c;记作&…

基于Spring Boot的智能笔记的开发与应用

一、项目背景与意义 智能笔记系统旨在为用户提供便捷、高效的笔记记录与管理服务。通过智能化的功能&#xff0c;如自动分类、标签管理、全文搜索等&#xff0c;用户可以更加轻松地管理和查找自己的笔记。同时&#xff0c;基于Spring Boot的开发使得系统具有高度的可扩展性和可…

word中插入zotero引用

1、参考文献末尾没有文献&#xff1f; 在文献条目要显示的地方点击“refresh” 2、参考文献条目没有悬挂缩进&#xff1f; 把“书目”添加到样式库中&#xff0c;修改样式为悬挂缩进1.5字符 3、交叉引用&#xff1f; 宏 新建一个宏 粘贴下面代码 Public Sub ZoteroLinkCita…

简历_专业技能_熟悉Redis常用数据结构及其操作命令

系列博客目录 文章目录 系列博客目录1.Redis通用命令2.String类型3.Hash类型4.List类型5.Set类型6.Sorted类型7.StringRedisTemplate 1.Redis通用命令 通用指令是部分数据类型的&#xff0c;都可以使用的指令&#xff0c;常见的有&#xff1a; KEYS&#xff1a;查看符合模板的…

快速将索尼手机联系人导出为 HTML 文件

我想将 Sony Xperia 手机上的联系人导出到计算机上进行备份&#xff0c;并在需要时进行编辑。这可以做到吗&#xff1f;如何做到&#xff1f;作为助手我需要下载什么工具吗&#xff1f; 当您的 Android 手机上存储了如此多的重要联系人&#xff0c;而您又不想丢失它们时&#…

爬虫案例-爬取某度文档

文章目录 1、第三方库的安装和pytesseract安装2、爬取某度文档的代码3、效果图 1、第三方库的安装和pytesseract安装 #以下是安装http请求的第三方库 pip install requests #以下是安装处理文档的第三方库 pip install python-docx #以下是安装处理图片的第三方库 pip install…

《塑战核心》V1.0.0.9952官方中文版

体验打击感满分的近距离战斗。击败蜂拥而至的敌人&#xff0c;每次击杀都会让你变得更强。 《塑战核心》官方中文版https://pan.xunlei.com/s/VODW7effpagQN1JU0UpBQQ5uA1?pwdmr8g#

电子邮件对网络安全的需求

&#xff08; 1&#xff09;机密性&#xff1a;传输过程中不被第三方阅读到邮件内容&#xff0c;只有真正的接收方才可以阅读邮件。&#xff08; 1.5 分&#xff09; &#xff08; 2&#xff09;完整性&#xff1a;支持在邮件传输过程中不被篡改&#xff0c;若发生篡改&#…

【嵌入式硬件】直流电机驱动相关

项目场景&#xff1a; 驱动履带车&#xff08;双直流电机&#xff09;前进、后退、转弯 问题描述 电机驱动MOS管烧毁 电机驱动采用IR2104STRH1R403NL的H桥方案&#xff08;这是修改之后的图&#xff09; 原因分析&#xff1a; 1.主要原因是4路PWM没有限幅&#xff0c;修改…

用户注册模块(芒果头条项目进度4)

1 创建⽤户模块⼦应⽤ 1.1 在项⽬包⽬录下 创建apps的python包。 1.2 在apps包下 创建应⽤userapp $ cd 项⽬包⽬录/apps $ python ../../manage.py startapp userapp 1.3 配置导包路径 默认情况下导包路径指向项⽬根⽬录 # 通过下⾯语句可以打印当前导包路径 print(sys.pa…

element输入框及表单元素自定义前缀

如图所示&#xff1a; <el-input class"custom-input" placeholder"请输入" prefix-icon"prefix" v-model"form.name" clearable></el-input> :deep(.custom-input) {.el-input__icon {display: inline-block;width: 40…

使用MPTCP+BBR进行数据传输,让网络又快又稳

1.前言 在前文《链路聚合技术——多路径传输Multipath TCP(MPTCP)快速实践》中我们使用mptcpize run命令实现了两个节点间通信使用MPTCP协议进行传输&#xff0c;并实现了传输速率的聚合。 实际应用中更推荐原生支持mptcp的应用&#xff0c;在MPTCP官网中可以看到如TCPDump、…

电商Google广告:2025年提升转化率的5种策略

展望 2025 年&#xff0c;Google 广告领域将迎来一系列显著变化&#xff0c;这些趋势对于提升广告转化率至关重要&#xff0c;值得我们提前关注与布局。 智能化程度持续加深&#xff0c;用户搜索习惯愈发精细&#xff0c;广告格式推陈出新&#xff0c;视频广告势头正猛...那么…