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

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-devgit 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 installationdirectory.  (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 laterdouble _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); // 使用索引作为相机 IDoutput_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); // 使用索引作为相机 IDoutput_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));
#elseuint64_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);// 写入相机 IDoutput_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 0uint64_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));
#elseuint64_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 0Eigen::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, cxcameras.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.mzph.cn/diannao/66094.shtml

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

相关文章

【游戏设计原理】46 - 魔杖

幻想&#xff0c;人们可以通过多种形式来引发&#xff0c;比如文字&#xff0c;图片&#xff0c;绘画&#xff0c;语言等&#xff0c;但游戏与以上这些形式的区别&#xff0c;正如游戏与其他艺术形式的区别一样&#xff0c;游戏作为一种艺术和娱乐形式&#xff0c;其独特之处在…

基于Java的敬老院管理系统的设计和实现【源码+文档+部署讲解】

基于Java的敬老院管理系统设计和实现 摘 要 新世纪以来,互联网与计算机技术的快速发展,我国也迈进网络化、集成化的信息大数据时代。对于大众而言,单机应用早已成为过去&#xff0c;传统模式早已满足不了当下办公生活等多种领域的需求,在一台电脑上不联网的软件少之又少&#x…

Git快速入门(一)·Git软件的安装以及GitHubDesktop客户端的安装

目录 1. 概述 1.1 版本控制介绍 1.1.1 集中式版本控制 1.1.2 分布式版本控制 1.1.3 多人协作开发 2. 安装Git 3. 安装GitHubDesktop客户端 1. 概述 Git 是一个免费的、开源的分布式版本控制系统。它能够快速高效地处理从小型到大型的各种项目。Git 具有易于学习…

数据挖掘——神经网络分类

神经网络分类 神经网络分类人工神经网络多层人工神经网络 误差反向传播&#xff08;BP&#xff09;网络后向传播算法 神经网络分类 人工神经网络 人工神经网络主要由大量的神经元以及它们之间的有向连接构成。包含三个方面&#xff1a; 神经元的激活规则 主要是指神经元输入…

PDF文件提示-文档无法打印-的解决办法

背景信息 下载了几个签名的PDF文件&#xff0c;想要打印纸质版&#xff0c;结果打印时 Adobe Acrobat Reader 提示【文档无法打印】: 解决办法 网上的方案是使用老版本的PDF阅读器&#xff0c; 因为无法打印只是一个标识而已。 PDF文件不能打印的五种解决方案-zhihu 这些方…

docker容器间基于bridge双向通信

前面介绍了通过link实现容器间的单向通信&#xff1a; docker容器间基于Link单向通信-CSDN博客 情景概述 通过前面已经知道了设置link来达到容器间通过容器名称双向通信&#xff0c;那是不是可以通过每个容器都设置link来达到双向通信&#xff0c;这种方式实现起来太麻烦&…

前端如何判断多个请求完毕

在前端开发中&#xff0c;经常会遇到需要同时发起多个异步请求&#xff0c;并在所有请求都完成后再进行下一步操作的情况。 这里有几个常用的方法来实现这一需求&#xff1a; 使用 Promise.all() Promise.all() 方法接收一个 Promise 对象的数组作为参数&#xff0c;当所有的…

云备份项目--服务端编写

文章目录 7. 数据管理模块7.1 如何设计7.2 完整的类 8. 热点管理8.1 如何设计8.2 完整的类 9. 业务处理模块9.1 如何设计9.2 完整的类9.3 测试9.3.1 测试展示功能 完整的代码–gitee链接 7. 数据管理模块 TODO: 读写锁&#xff1f;普通锁&#xff1f; 7.1 如何设计 需要管理…

exam0-试卷整理

exam0-试卷整理 2010&#xff0c;2013是梦开始的地方&#xff0c;大概率会出原题的 2010 2013 2015 大题 manchester RIP更新 说出ISO与TCP/IP模型的相同点和不同点&#xff08;8分&#xff09; 相似&#xff1a; 两者都有层次&#xff0c;网络专业人员都需要知道二者&a…

ACL---访问控制列表---策略

在路由器流量流入或者流出的接口上匹配流量&#xff0c;之后执行设定好的动作---permit&#xff08;允许&#xff09;deny&#xff08;拒绝&#xff09; 1.访问控制&#xff1a; 在路由器流量流入或者流出的接口上匹配流量&#xff0c;之后执行设定好的动作---permit&#xf…

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…

C#调用Lua

目录 xLua导入 打包工具导入 单例基类导入与AB包管理器导入 Lua解析器 文件加载与重定向 Lua解析器管理器 全局变量获取 全局函数获取 对于无参数无返回值 对于有参数有返回值 对于多返回值 对于变长参数 完整代码 List与Dictionary映射Table 类映射Table 接口映射…

渗透测试-非寻常漏洞案例

声明 本文章所分享内容仅用于网络安全技术讨论&#xff0c;切勿用于违法途径&#xff0c;所有渗透都需获取授权&#xff0c;违者后果自行承担&#xff0c;与本号及作者无关&#xff0c;请谨记守法. 此文章不允许未经授权转发至除先知社区以外的其它平台&#xff01;&#xff0…

node.js之---事件循环机制

事件循环机制 Node.js 事件循环机制&#xff08;Event Loop&#xff09;是其核心特性之一&#xff0c;它使得 Node.js 能够高效地处理大量并发的 I/O 操作。Node.js 基于 非阻塞 I/O&#xff0c;使用事件驱动的模型来实现异步编程。事件循环是 Node.js 实现异步编程的基础&…

基于深度学习的视觉检测小项目(二) 环境和框架搭建

一、环境和框架要求 SAM的环境要求&#xff1a; Python>3.7 PyTorch>1.7 torchvision>0.8 YOLO V8的环境要求&#xff1a;YOLO集成在ultralytics库中&#xff0c;ultralytics库的环境要求&#xff1a; Python>3.7 PyTorch>1.10.0 1、确定pytorch版本…

MySQL 06 章——多表查询

多表查询&#xff0c;也称为关联查询&#xff0c;是指两个表或多个表一起完成查询操作 前提条件&#xff0c;这些一起查询的表之间是有关系的&#xff08;一对一、一对多&#xff09;&#xff0c;它们之间一定是有关联字段的。这个关联字段可能建立了外键&#xff0c;也可能没…

Web前端基础知识(五)

盒子模型 盒子模型是CSS中一种常用于布局的基本概念。描述了 文档中的每个元素都可以看成是一个矩形的盒子&#xff0c;包含了内容、内边距、文本边距、外边距。 ---------------------------------------------------------------------------------------------------------…

使用Clion在ubuntu上进行交叉编译,并在Linux上远程编译五子棋

目录 1.工具以及概念介绍 &#xff08;1&#xff09;Clion软件简介 &#xff08;2&#xff09;交叉编译 &#xff08;3&#xff09;远程编译 2.操作原理 3.详细操作步骤 &#xff08;1&#xff09;配置Clion与虚拟机ubuntu的ssh连接 CLion远程开发Ubuntu&#xff0c;并显…

我的博客年度之旅:感恩、成长与展望

目录 感恩有你 技能满点 新年新征程 嘿&#xff0c;各位技术大佬、数码潮咖还有屏幕前超爱学习的小伙伴们&#xff01;当新年的钟声即将敲响&#xff0c;我们站在时光的交汇点上&#xff0c;回首过往&#xff0c;满心感慨&#xff1b;展望未来&#xff0c;豪情满怀。过去的这…

STM32-笔记22-sg90舵机

一、接线 二、实验实现 动手让 SG90 每秒转动一下&#xff0c;0 -> 20 -> 40 -> 100 -> 180 如此循环。 舵机接A6 复制18-呼吸灯&#xff0c;重命名24-sg90舵机 把PWM重命名sg90 打开项目文件 在魔术棒和品上把PWM都去掉&#xff0c;加载sg90文件夹 加载之后…