点云数据与多相机图像融合实现3D场景的彩色可视化

引言

在现代3D计算机视觉和机器人感知领域,点云数据和图像信息的融合正变得越来越重要。点云数据提供了精确的几何结构,而图像则包含了丰富的颜色和纹理细节。将这两种数据源结合起来,我们能够创建更加逼真和信息丰富的3D场景表示。本文将深入探讨一个结合点云数据和多相机图像的项目,旨在生成高质量的彩色3D点云,从而大幅提升场景的可视化效果。

我们的项目利用了多个开源库,包括Point Cloud Library (PCL)用于点云处理,OpenCV用于图像处理,以及Eigen用于高效的矩阵运算。项目的核心目标是将来自多个相机的2D图像信息精确地映射到3D点云上,最终生成一个色彩丰富、细节清晰的3D场景表示。

数据集的构成包括以下几个关键部分:

  1. 一个存储为PCD格式的点云文件(frame3438.pcd)
  2. 五张来自不同相机的JPEG格式图像(frame3438cam1.jpg 到 frame3438cam5.jpg)
  3. 每个相机的参数矩阵,包括:
    • 内参矩阵(K),描述相机的光学特性
    • 畸变系数(D),用于校正镜头畸变
    • 外参矩阵(t_word_to_cam),描述相机在世界坐标系中的位置和姿态

项目的核心算法实现主要包括以下几个步骤:

  • 点云加载:使用PCL库的io模块加载PCD文件。
  • 图像处理:利用OpenCV加载并预处理图像。
  • 点云投影:将3D点云中的每个点投影到2D图像平面上。
  • 颜色映射:从图像中提取对应的颜色信息,并将其赋给点云中的点。
  • 可视化:使用PCL的可视化模块展示最终的彩色点云。

CMakeList.txt

cmake_minimum_required(VERSION 3.28)
project(UGV_Pt2Pc)set(CMAKE_CXX_STANDARD 11)
set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS TRUE CACHE INTERNAL "")
# Find required packages
find_package(OpenCV REQUIRED)
find_package(PCL REQUIRED)
find_package(Eigen3 REQUIRED)# Include directories
include_directories(${OpenCV_INCLUDE_DIRS}${PCL_INCLUDE_DIRS}${EIGEN3_INCLUDE_DIRS}
)# Add definitions and link directories for PCL
add_definitions(${PCL_DEFINITIONS})
link_directories(${PCL_LIBRARY_DIRS})# Add executable
add_executable(UGV_Pt2Pc main.cpp)# Link libraries
target_link_libraries(UGV_Pt2Pc${OpenCV_LIBS}${PCL_LIBRARIES}
)

 main.cpp

#include <iostream>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
#include <pcl/visualization/cloud_viewer.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/eigen.hpp>
#include <Eigen/Dense>using namespace cv;
using namespace std;// RGB colour visualisation example
boost::shared_ptr<pcl::visualization::PCLVisualizer> rgbVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));viewer->setBackgroundColor(0, 0, 0);viewer->addPointCloud<pcl::PointXYZRGB>(cloud, "sample cloud");viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");viewer->addCoordinateSystem(1.0);viewer->initCameraParameters();return (viewer);
}void projectPointsToImage(pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud, const Mat& img, const Mat& K, const Mat& D, const Mat& t_word_to_cam)
{Mat camera_par = K * Mat::eye(3, 4, CV_64F);Mat UndistortImage;cv::undistort(img, UndistortImage, K, D, K);int rows = UndistortImage.rows;int cols = UndistortImage.cols;Mat word_h = Mat(4, 1, CV_64FC1);Mat p_result = Mat(3, 1, CV_64FC1);for (int nIndex = 0; nIndex < point_cloud->points.size(); nIndex++){double c_x = point_cloud->points[nIndex].x;double c_y = point_cloud->points[nIndex].y;double c_z = point_cloud->points[nIndex].z;word_h = (Mat_<double>(4, 1) << c_x, c_y, c_z, 1);p_result = camera_par * t_word_to_cam * word_h;double p_w = p_result.at<double>(2, 0);int p_u = (int)((p_result.at<double>(0, 0)) / p_w);int p_v = (int)((p_result.at<double>(1, 0)) / p_w);if(p_u >= 0 && p_u < cols && p_v >= 0 && p_v < rows && p_w > 0){Vec3b color = UndistortImage.at<Vec3b>(p_v, p_u);point_cloud->points[nIndex].r = color[2];point_cloud->points[nIndex].g = color[1];point_cloud->points[nIndex].b = color[0];}}
}int main()
{pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);string path = "/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438.pcd";pcl::io::loadPCDFile(path, *point_cloud);vector<string> imgPaths = {"/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438cam1.jpg","/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438cam2.jpg","/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438cam3.jpg","/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438cam4.jpg","/home/fairlee/CLionProjects/UGVPoint2Picture/Data/frame3438cam5.jpg"};vector<Mat> K_matrices = {(Mat_<double>(3, 3) << 7.449480591354508e+02, 0.0, 6.239975192140465e+02, 0.0, 7.477630653570311e+02, 3.718130492325440e+02, 0.0, 0.0, 1.0),(Mat_<double>(3, 3) << 7.496397448476606e+02, 0.0, 6.406747754223233e+02, 0.0, 7.498672914667321e+02, 3.738034702122652e+02, 0.0, 0.0, 1.0),(Mat_<double>(3, 3) << 7.453318932115172e+02, 0.0, 6.353637094339318e+02, 0.0, 7.451280155876273e+02, 3.580752219734694e+02, 0.0, 0.0, 1.0),(Mat_<double>(3, 3) << 7.342274827447166e+02, 0.0, 6.544610847908701e+02, 0.0, 7.343520333235763e+02, 3.620389108671441e+02, 0.0, 0.0, 1.0),(Mat_<double>(3, 3) << 7.476554400662411e+02, 0.0, 6.788461062313610e+02, 0.0, 7.477630653570311e+02, 3.185225546149534e+02, 0.0, 0.0, 1.0)};vector<Mat> D_matrices = {(Mat_<double>(5, 1) << -0.012152688773890, 0.006312744290089, 0.0, 0.0, 0.0),(Mat_<double>(5, 1) << -0.004788988582043, 1.664519226194170e-04, 0.0, 0.0, 0.0),(Mat_<double>(5, 1) << -0.010742962922821, 0.004899856208959, 0.0, 0.0, 0.0),(Mat_<double>(5, 1) << -0.008567792387783, -0.003979577222784, 0.0, 0.0, 0.0),(Mat_<double>(5, 1) << -0.004199529851394, -0.006221124713178, 0.0, 0.0, 0.0)};vector<Mat> t_word_to_cam_matrices = {(Mat_<double>(4, 4) << -0.573083193216839, 0.819337857655761, 0.016159475995802, 0.004020654954081, -0.037263512116764, -0.006355334635993, -0.999285264769970, -5.940790067281352e-04, -0.818649549146101, -0.573275749298479, 0.034173541653635, -0.131669026218482, 0.0, 0.0, 0.0, 1.0),(Mat_<double>(4, 4) << 0.606118826106767, 0.795372432267145, -0.001631756232093, 0.015694798142177, 0.012875614383068, -0.011863195183812, -0.999846729831273, -0.045896562287801, -0.795269883242924, 0.606004916308009, -0.017431414667482, -0.110877525711608, 0.0, 0.0, 0.0, 1.0),(Mat_<double>(4, 4) << -0.945075320161107, -0.325701878485533, 0.027403021245412, 0.012344765860022, -0.014072099115867, -0.043215933241321, -0.998966645659681, -0.063750385501104, 0.326549560172502, -0.944484340508130, 0.036259002827830, -0.129189511868516, 0.0, 0.0, 0.0, 1.0),(Mat_<double>(4, 4) << 0.957103431035445, -0.289410872954947, -0.013941625286710, -0.010506759453144, -0.026517852059621, -0.039578790762524, -0.998864516760867, -0.028600625727241, 0.288530459089372, 0.956386358088211, -0.045555551148511, -0.153290341112109, 0.0, 0.0, 0.0, 1.0),(Mat_<double>(4, 4) << -0.058774247147796, -0.998268213079476, -0.002482463961589, -0.016783534106880, -0.004403582752182, 0.002746003285998, -0.999986533871781, -0.013253349162886, 0.998261587125917, -0.058762523950764, -0.004557350960968, -0.137347689399076, 0.0, 0.0, 0.0, 1.0)};for (int i = 0; i < 5; i++){Mat img = imread(imgPaths[i]);if (img.empty()) {cout << "Failed to load image: " << imgPaths[i] << endl;continue;}if (img.channels() != 3) {cout << "RGB pics needed for image: " << imgPaths[i] << endl;continue;}projectPointsToImage(point_cloud, img, K_matrices[i], D_matrices[i], t_word_to_cam_matrices[i]);}boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer;viewer = rgbVis(point_cloud);while (!viewer->wasStopped()){viewer->spinOnce(100);}return 0;
}

结果 

 注:里面的数据来自MATLAB工具箱标定的数据

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

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

相关文章

Django学习笔记之Django基础学习

Django笔记 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录…

剧场的客户端形式区别,APP,小程序,H5的不同优势以及推广方案

剧场的客户端形式区别与推广策略 在数字化时代&#xff0c;剧场的线上化成为大势所趋。不同的线上平台如APP、小程序和H5各有千秋&#xff0c;如何选择最适合自己的平台&#xff0c;并制定有效的推广方案&#xff0c;成为了剧场管理者需要考虑的重要问题。 APP&#xff1a;深度…

nn.functional.softmax(X, dim=-1)

dim-1表示在最后一个维度&#xff08;大概率是一行&#xff09;应用Softmax函数&#xff0c;将值标准化为概率分布。 实例 假设我们有一个张量X&#xff0c;形状为&#xff08;2&#xff0c;3&#xff09;&#xff0c;内容如下&#xff1a; import torch import torch.nn.…

vite 打包前请求接口和打包后的不一致

在使用 Vite 进行项目打包时&#xff0c;如果发现打包前请求接口和打包后的行为不一致&#xff0c;这可能是由于多种原因导致的。以下是一些可能的原因和相应的解决方案&#xff1a; 1. 代理配置问题 开发环境&#xff1a;在开发环境中&#xff0c;Vite 通常使用 vite.config…

【AIGC】OpenAI API在快速开发中的实践与应用:优化ChatGPT提示词Prompt加速工程

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;使用最新型号确保最佳实践利用最新模型进行高效任务处理为什么要选择最新模型&#xff1f;结论 &#x1f4af;指令与上下文的分隔最佳实践分隔指令和上下文的重要性使用符…

univer实现excel协同

快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src&q…

【分布式微服务云原生】 探索SOAP协议:简单对象访问协议的深度解析与实践

探索SOAP协议&#xff1a;简单对象访问协议的深度解析与实践 摘要&#xff1a; 在现代分布式系统中&#xff0c;SOAP&#xff08;简单对象访问协议&#xff09;扮演着至关重要的角色&#xff0c;提供了一种标准化的方式来实现不同系统间的通信。本文深入探讨了SOAP的工作原理、…

无线费控智能水表:智能生活的守护者

在当今智能化日益普及的时代&#xff0c;无线费控智能水表作为一项重要的技术创新&#xff0c;正在逐步改变我们的生活方式。它不仅能够实现远程抄表&#xff0c;自动计费&#xff0c;还能有效监控用水情况&#xff0c;促进水资源的合理利用&#xff0c;是现代城市智慧化管理不…

如何在 cPanel 中使用 PHP-FPM

PHP性能一直是影响网站托管的一个重要问题。PHP是当前网络上使用最广泛的服务器编程语言&#xff0c;远远领先于其他语言。最受欢迎的内容管理系统和电子商务应用程序&#xff0c;如WordPress、Joomla、Drupal、Magento等&#xff0c;都是用PHP编写的。 PHP-FPM加速了在繁忙服务…

【微信小程序_10_wxss模板中的内边距与外边距】

摘要:本文介绍了微信小程序开发中内边距(padding)和外边距(margin)的概念及作用。内边距是元素内容与边框间的距离,可调整文字与边框的间隔;外边距是元素边框与相邻元素间的距离,用于控制元素间隔及实现水平居中。合理运用这两个属性可实现美观、合理的页面设计。 微信…

计算机毕设选题推荐【基础功能+创新点】【Python方向】

以下是50条精选Python方向的毕业设计选题&#xff0c;每个选题包含基础功能和创新功能&#xff0c;涵盖多样化的应用场景&#xff0c;以帮助计算机专业学生完成毕业设计。 1. 基于Python的个人理财管理系统 基础功能&#xff1a;用户注册与登录、支出记录管理、收入记录管理、…

Debezium系列之:实时从TDengine数据库采集数据到Kafka Topic

Debezium系列之:实时从TDengine数据库采集数据到Kafka Topic 一、认识TDengine二、TDengine Kafka Connector三、什么是 Kafka Connect?四、前置条件五、安装 TDengine Connector 插件六、启动 Kafka七、验证 kafka Connect 是否启动成功八、TDengine Source Connector 的使用…

24/10/12 算法笔记 NiN

LeNet、AlexNet和VGG都有一个共同的设计模式&#xff1a;通过一系列的卷积层与汇聚层来提取空间结构特征&#xff1b;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 或者&#xff0c;可以想象在这个过程的早期使用全连…

用java来编写web界面

一、ssm框架整体目录架构 二、编写后端代码 1、编写实体层代码 实体层代码就是你的对象 entity package com.cv.entity;public class Apple {private Integer id;private String name;private Integer quantity;private Integer price;private Integer categoryId;public…

C++:STL:vector类常用函数介绍(附加部分重要函数模拟实现)

cplusplus.com/reference/vector/vector/https://cplusplus.com/reference/vector/vector/ vector在实际中非常的重要&#xff0c;在实际中我们熟悉常见的接口就可以&#xff0c;有了string的基础&#xff0c;vector其实大体使用方法上二者是类似的&#xff1a; 这里我们先给…

ScriptableObject基本使用

使用方法 自定义类继承ScriptableObject 可以在类内部增加数据或者数据类&#xff0c;一般用于配置 注意事项 给继承ScriptableObject的类增加CreateAssetMenu特性。 CreateAssetMenu一般默认三个参数 第一个参数是父目录 第二个参数是父目录的子选项 第三个参数是可以…

k8s yaml编写规范

yaml简介 yaml 是专门用来写配置文件的语言 yaml文件也是一种配置文件类型&#xff0c;后缀名是.yaml或.yml都可以 yaml语法规则 大小写敏感使用缩进表示层级关系&#xff08;不能用Tab&#xff0c;只能用空格&#xff09;相同层级的元素左对齐#号表示单行注释字符串可以不用…

多态(二)

1.多态的原理 虚函数表 class Base { public:virtual void Func1(){cout << "Func1()" << endl;} private:int _b 1; };b对象是8bytes&#xff0c;除了_b成员&#xff0c;还多一个__vfptr放在对象的前面(注意有些 平台可能会放到对象的最后面&#xf…

微信小程序启动不起来,报错凡是以~/包名/*.js路径的文件,都找不到,试过网上一切方法,最终居然这么解决的,【避坑】命运的齿轮开始转动

app.json "resolveAlias": {"~/*": "/*"},文件代码也没有问题&#xff0c;网上的方法试过来了&#xff0c;大模型AI也问过遍&#xff0c;熬夜到凌晨2点半&#xff0c;最不可思议的是居然是因为微信开发者工具版本的问题&#xff0c;我真的是笑死…

量化之一:均值回归策略

文章目录 均值回归策略理论基础数学公式 关键指标简单移动平均线&#xff08;SMA&#xff09;标准差Z-Score 交易信号实际应用优缺点分析优点缺点 结论 实践backtrader参数&#xff1a;正常情况&#xff1a;异常情况&#xff1a; 均值回归策略 均值回归&#xff08;Mean Rever…