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

news2024/11/23 15:44:37

引言

在现代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.coloradmin.cn/o/2209424.html

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

相关文章

【C++】拆分详解 - vector

文章目录 一、vector的介绍二、vector的使用1. 构造2. 迭代器3. vector 空间增长问题4. 增删查改5. vector 迭代器失效问题5.1 底层空间改变&#xff08;扩容、缩容&#xff09;5.2 指定位置元素的删除操作5.3 Linux与VS平台差异 三、vector 模拟实现0. 整体框架1. 构造 / 析构…

4个方法教你快速取消Word文档底色

在使用Word编辑文档时&#xff0c;我们有时会遇到文字或段落带有不必要的底色&#xff0c;这不仅影响文档的美观&#xff0c;还可能干扰阅读。那么&#xff0c;如何轻松去除这些底色呢&#xff1f;以下是几种实用的方法&#xff1a; 方法1&#xff1a;使用底纹功能键 首先&…

点可云ERP进销存V8版本——其他收入单使用说明进

其他收入单用于记录除销售内容外其他收入资金&#xff0c;如&#xff1a;废品出售、安装维修服务等。新增保存之后&#xff0c;对应资金账户将增加金额额度&#xff0c;并做存储记录&#xff0c;可在现金银行报表中体现。 新增操作 接下来我们讲解新增单据步骤。如上图所示&am…

怎样设置Windows系统不会自动同步时间

一、背景 我们在进行测试一些软件的时候需要调整Windows系统的时间到指定的日期,并且希望这个手动调整的日期可以固定住不变,不希望电脑重启后恢复到当前的最新日期。 二、操作方法 注意:如下的操作方法是以Windows7系统为例进行演示说明: 1、选中右下角的日期然后点击鼠…

C++笔记---红黑树的插入删除

1. 红黑树的概念 红黑树是一棵二叉搜索树&#xff0c;他的每个结点增加一个存储位来表示结点的颜色&#xff0c;可以是红色或者黑色。 通过对任何一条从根到叶子的路径上各个结点的颜色进行约束&#xff0c;红黑树确保对于任意一个结点&#xff0c;没有一条到NULL结点的路径会…

【C++算法】9.滑动窗口_长度最小的子数组

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 209. 长度最小的子数组 题目描述&#xff1a; 解法 解法一&#xff1a;暴力求解&#xff08;会超时&#xff09; 暴力枚举出所有子数组的和。 查找子数组n2&#xff0…

【hot100-java】K 个一组翻转链表

链表篇 参考题解 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ …

文件传输遗漏

查看失败的 Failed to transfer file ‘E:\m3dmpre\datasets\mvtec3d\foam\train\good\xyz\184.tiff’. Could not close the output stream for file “sftp://172.29.6.20/home/cszx/zgp/datasets/mvtec3d/foam/train/good/xyz/184.tiff”. 将faild的全部重传一遍

知识二: 马尔科夫决策过程

强化学习从入门到精通&#xff08;马尔科夫决策过程&#xff09;&#xff08;7天入门强化学习&#xff09; 知识二&#xff1a;马尔科夫决策过程 先介绍马尔可夫过程&#xff08;Markov process&#xff09;以及马尔可夫奖励过程&#xff08;Markov reward process&#xff0…

matlab不小心删除怎么撤回

预设项——>删除文件——>移动至临时文件夹 tem临时文件夹下

中级软考-软件设计师重要性与含金量以及就业方向(文末分享考试真题与笔记)

中级软考软件设计师在中国IT行业中具有重要的地位和高含金量&#xff0c;特别是在以下几个方面&#xff1a; 重要性与含金量 职业认可&#xff1a;该证书是国家认可的&#xff0c;证明持证人具备一定的软件设计和开发能力&#xff0c;对求职者在招聘市场上具有较强的竞争力。 …

如何在 iPad 上恢复已删除的历史记录?

iPad 配备了一个名为 Safari 的内置网络浏览器。这是一种在旅途中保持联系和浏览网页的强大且便捷的方式。但如果您不小心删除了浏览历史记录&#xff0c;则尝试恢复它可能会很令人沮丧。 幸运的是&#xff0c;您可以通过多种方法在 iPad 上恢复已删除的 Safari 历史记录。您应…

匹配全国地址的正则表达式工具类

正则表达式&#xff0c;匹配全国五级地址工具类&#xff0c;可以直接放在项目中使用~ 1级&#xff1a;国 &#xff08;可忽略不填&#xff09; 2级&#xff1a;**省、**自治区、**直辖市、**特别行政区、&#xff08;四个直辖市可忽略不填&#xff09; 3级&#xff1a;**市、**…

低代码和零代码开发方式如何改变软件开发行业?

低代码和零代码开发方式如何改变软件开发行业&#xff1f; 随着技术的进步和数字化转型的加速&#xff0c;软件开发行业正在经历一场革命。在这场革命中&#xff0c;低代码和零代码平台正逐渐成为企业开发应用的重要工具。它们以其简单易用的特性&#xff0c;极大地降低了软件…

面试笔记-js基础篇

1、因为在 JS 的最初版本中&#xff0c;使用的是 32 位系统&#xff0c;为了性能考虑使用低位存储了变量的类型信息&#xff0c;000 开头代表是对象&#xff0c;然而 null 表示为全零&#xff0c;所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了&#xff…

计算机网络第1章(概述)万字笔记详细版

1.1、计算机网络在信息时代的作用 计算机网络已由一种通信基础设施发展成为一种重要的信息服务基础设施计算机网络已经像水&#xff0c;电&#xff0c;煤气这些基础设施一样&#xff0c;成为我们生活中不可或缺的一部分 我国互联网发展状况 中国互联网络信息中心CNNIC 1.2、…

万字详解AI实践,零手写编码用AI完成开发 + 数据清洗 + 数据处理 的每日新闻推荐,带你快速成为AI大神

用AIdify完成前后端开发数据处理和数据清洗。 引言数据获取和数据处理dify构建workflow进行数据清洗前端页面构建和前后端交互总结 引言 AI时代对开发人员的加强是非常明显的&#xff0c;一个开发人员可以依靠AI横跨数个自己不熟悉的领域包括前后端、算法等。让我们来做个实践…

模板和静态文件

模板和静态文件 1、templates模板2、静态文件2.1、static目录2.2、引用静态文件 1、templates模板 "templates"目录用于存放模板文件&#xff0c;通常是用于动态生成页面的文件。 在app01目录下创建templates文件夹&#xff0c;html文件均保存在templates中 在urls.p…

Linux的hadoop集群部署

1.hadoop是一个分布式系统基础架构,主要解决海量数据额度存储与海量数据的分析计算问题 hdfs提供存储能力,yarn提供资源管理能力,MapReduce提供计算能力 2.安装 一:调整虚拟机内存,4G即可 二:下载安装包 网址:https://mirrors.aliyun.com/apache/hadoop/common/hadoop-3.4.0/…

【文心智能体 AI大师工坊】『​​​​​​​人间夸夸机』情感类智能体开发调优全过程详解

&#x1f680;『人间夸夸机』点击前往体验&#xff1a;https://snhoio.smartapps.baidu.com/?_swebScene3611000000000000 最近参加了百度文心智能体平台AI大师工坊&#x1f389;活动&#xff0c;在这个活动中&#xff0c;我利用文心平台提供的各种插件、大模型等工具&#xf…