点云融合代码学习

点云融合代码学习

#include "slamBase.hpp"

int main( int argc, char** argv )
{
	// 相机内参包括焦距(fx,fy)、主点位置(cx, cy),以及深度比例因子scale
    CAMERA_INTRINSIC_PARAMETERS cameraParams{517.0,516.0,318.6,255.3,5000.0};
    int frameNum = 3;
    // 由于 Eigen 使用 SIMD(单指令多数据)优化,存储该类型时需要使用对齐分配器(Eigen::aligned_allocator)。
    vector<Eigen::Isometry3d, Eigen::aligned_allocator<Eigen::Isometry3d>> poses;
    PointCloud::Ptr fusedCloud(new PointCloud());	
    string path = "./data/";
    string cameraPosePath = path + "cameraTrajectory.txt";
    readCameraTrajectory(cameraPosePath, poses);
    for (int idx = 0; idx < frameNum; idx++)
    {
        string rgbPath = path + "rgb/rgb" + to_string(idx) + ".png";
        string depthPath = path + "depth/depth" + to_string(idx) + ".png";

        FRAME frm;
        frm.rgb = cv::imread(rgbPath);
        if (frm.rgb.empty()) {
            cerr << "Fail to load rgb image!" << endl;
        }
        frm.depth = cv::imread(depthPath, -1);
        if (frm.depth.empty()) {
            cerr << "Fail to load depth image!" << endl;
        }

        if (idx == 0)	// 如果是第一帧,把第一帧转为点云
        {
            fusedCloud = image2PointCloud(frm.rgb, frm.depth, cameraParams);
        }
        else	// 如果非首帧,则把当前帧加入点云,即点云融合
        {
            fusedCloud = pointCloudFusion( fusedCloud, frm, poses[idx], cameraParams );
        }
    }
    pcl::io::savePCDFile( "./fusedCloud.pcd", *fusedCloud );	// 保存点云
    return 0;
}



#include "slamBase.hpp"

PointCloud::Ptr image2PointCloud(Mat rgb, Mat depth, CAMERA_INTRINSIC_PARAMETERS camera)
{
    // cloud 是指向 PointCloud 对象的智能指针(Ptr 表示 shared_ptr 或 boost::shared_ptr 类型)。通过 -> 操作符,可以访问智能指针所指向的对象成员。
    PointCloud::Ptr cloud ( new PointCloud );
    for (int m = 0; m < depth.rows; m++)
        for (int n = 0; n < depth.cols; n++)
        {
            // 获取深度图中(m,n)处的值
            ushort d = depth.ptr<ushort>(m)[n];
            // d 可能没有值,若如此,跳过此点
            if (d == 0)
                continue;
            // d 存在值,则向点云增加一个点
            PointT p;
            // 计算这个点的空间坐标
            p.z = double(d) / camera.scale;
            p.x = (n - camera.cx) * p.z / camera.fx;
            p.y = (m - camera.cy) * p.z / camera.fy;

            // 从rgb图像中获取它的颜色
            p.b = rgb.ptr<uchar>(m)[n * 3];
            p.g = rgb.ptr<uchar>(m)[n * 3 + 1];
            p.r = rgb.ptr<uchar>(m)[n * 3 + 2];

            // 把p加入到点云中
            cloud->points.push_back(p);
        }
    // 设置并保存点云
    cloud->height = 1;
    cloud->width = cloud->points.size();
    cloud->is_dense = false;  // 是否为稠密(没有无效点)点云,设置为false表示可能包含无效点
    return cloud;
}


PointCloud::Ptr pointCloudFusion( PointCloud::Ptr &original, FRAME& newFrame, Eigen::Isometry3d T, CAMERA_INTRINSIC_PARAMETERS camera )
{
	// ---------- 开始你的代码  ------------- -//
	// 简单的点云叠加融合
    PointCloud::Ptr newCloud(new PointCloud()), transCloud(new PointCloud());
    newCloud = image2PointCloud(newFrame.rgb, newFrame.depth,camera);   // 此时的点云有像素信息,位置xyz为相机坐标系下的坐标

    pcl::transformPointCloud(*newCloud,*transCloud,T.matrix()); //  将新点云从相机坐标系转为世界坐标系
    *original += *transCloud;   // 更新点云
    return original;
    // ---------- 结束你的代码  ------------- -//
}

// camTransFile:存储相机轨迹的文件路径,文件的每一行包含一帧相机的位姿信息。
// poses:一个 vector,用来存储解析后的相机位姿。每个 Eigen::Isometry3d 对象包含相机的旋转和位移信息。
void readCameraTrajectory(string camTransFile, vector<Eigen::Isometry3d, Eigen::aligned_allocator<Eigen::Isometry3d>> &poses)	// 注意内存对齐
{   // ifstream 是 C++ 标准库中的类,属于 <fstream> 头文件。全称为 "input file stream"(输入文件流),fcamTrans 是一个 ifstream 对象的实例
    ifstream fcamTrans(camTransFile);
    if(!fcamTrans.is_open())
    {
        cerr << "trajectory is empty!" << endl;
        return;
    }

   	// ---------- 开始你的代码  ------------- -//
	// 参考作业8 绘制轨迹
    string lineData;
    while((getline(fcamTrans,lineData)) && !lineData.empty()){
    
        Eigen::Quaterniond quad;   // 表示旋转部分(四元数)。
        Eigen::Vector3d t;         // 表示平移部分(位置向量)。
        Eigen::Isometry3d T = Eigen::Isometry3d::Identity();  // 表示最终的位姿,它是一个 3D 同质变换矩阵(旋转+平移)。初始化为单位矩阵。

        if(lineData[0] == '#'){
            continue;
        }

// 创建一个 istringstream 对象来解析当前行的内容。从这一行中解析出相机的 位置向量 t 和 旋转四元数 quad,这两个值会被依次存储到相应的变量中。解析的顺序是:位置的三个分量 t[0], t[1], t[2] 和四元数的四个分量 quad.x(), quad.y(), quad.z(), quad.w()。
        istringstream strData(lineData);    // istringstream全称是 "input string stream"(输入字符串流)
        // 流操作符(>>)会从 strData 中依次读取数据,解析为数值类型,并存储到指定的变量中。
        strData>>t[0]>>t[1]>>t[2]>>quad.x()>>quad.y()>>quad.z()>>quad.w();

        T.rotate(quad);           //将四元数 quad 的旋转应用到变换矩阵 T 的旋转部分。
        T.pretranslate(t);        // 将平移向量 t 直接添加到变换矩阵 T 的平移部分(第四列)。
        //cout<<"test "<<endl;
        poses.push_back(T);
       
    }

	// ---------- 结束你的代码  ------------- -//
}

通过上述代码,我们先做一个简单的内容回顾:

相机内参

一般由焦距,主点坐标,缩放因子,与附加内参(径向畸变,切向畸变)等构成

内参与成像模型

相机内参用于从空间点(X, Y, Z)投影到图像平面上的像素坐标(u, v)

通过成像几何关系可以得到图像坐标系中的像素位置转换到 相机坐标系 下。
在这里插入图片描述

在这里插入图片描述
其中 归一化坐标系为:
在这里插入图片描述
这里设定了缩放参数scale为1。

scale

深度图是深度传感器或深度相机生成的二维图像,其中每个像素的值表示相机到场景中对应点的距离。
这些深度值通常以整数形式存储,例如 16 位无符号整数(uint16),以节省存储空间。

深度图中的像素值(如d)可能需要乘以一个比例因子(即camera.scale)才能得到真实的深度值。

例如,如果深度图的单位是毫米,而你的计算需要以米为单位表示深度值,那么 camera.scale 的值可能是 1000。

例子

上述代码中的image2PointCloud函数中有下面的代码,作用是由图像坐标映射到空间坐标

   // d 存在值,则向点云增加一个点
  PointT p;
  // 计算这个点的空间坐标
  p.z = double(d) / camera.scale;
  p.x = (n - camera.cx) * p.z / camera.fx;
  p.y = (m - camera.cy) * p.z / camera.fy;

内参获取

内参参数通常通过相机标定(Camera Calibration)获得,过程如下:
拍摄带有已知几何结构的标定板(如棋盘格)的多张照片。
使用标定算法计算内参矩阵 K 和畸变参数,标定方法有很多,这里暂时不做介绍。

PCL 中 PointCloud 类的常见成员变量及其功能

成员变量类型描述
pointsstd::vector存储点云中的所有点, PointT 可以是各种点类型(如 pcl::PointXYZ、pcl::PointXYZRGB 等)
widthuint32_t点云的宽度(列数或点总数)
heightuint32_t点云的高度(行数)
is_densebool是否为稠密点云
sensor_origin_Eigen::Vector4f点云的传感器原点
sensor_orientation_Eigen::Quaternionf点云的传感器姿态, 以四元数形式记录传感器的旋转信息
fieldsstd::vector < pcl::PCLPointField >点的字段描述(如 x、y、z)
datastd::vector< uint8_t >点云的原始字节数据
headerpcl::PCLHeader点云头部信息
pcl::transformPointCloud(*newCloud, *transCloud, T.matrix())

template <typename PointT> void pcl::transformPointCloud(const pcl::PointCloud<PointT>& cloud_in, pcl::PointCloud<PointT>& cloud_out, const Eigen::Matrix4f& transform);
这个函数是 Point Cloud Library (PCL) 中的一个重要函数,用于对点云数据进行变换。变换操作通常是旋转和平移,常用于点云的配准、对齐等操作。

  • cloud_in:输入的点云数据。
  • cloud_out:输出的点云数据,变换后的点云结果。
  • transform:一个 4x4 齐次变换矩阵(通常是一个 Eigen::Matrix4f 类型),包含了点云变换的旋转和平移信息。

点云融合处理过程

  1. 点云预处理
    在融合前对点云数据进行清理和优化:
  • 降噪:去除孤立点、噪声点。例如,使用统计滤波或半径滤波方法。
  • 下采样:对点云进行体素化降采样,减少点的数量,同时保留结构信息,提高计算效率。
  • 裁剪:根据感兴趣的区域裁剪点云,移除不需要的部分。
  1. 点云配准
    将不同视角或帧的点云对齐到一个统一的坐标系下。
  • 粗配准:
    使用全局特征提取(如 FPFH)计算点云的初始配准。
    方法:RANSAC 或其他全局搜索算法。
  • 精配准:
    使用迭代优化算法(如 ICP:Iterative Closest Point(迭代最近点算法))对点云进行精细对齐。
    优化目标是最小化点云之间的最近点距离。
  1. 坐标变换
  • 通过刚体变换矩阵(旋转和平移)将配准后的点云转换到全局坐标系。
  • 使用从点云配准计算得到的旋转和平移,将局部点云数据映射到统一的全局坐标系中。
  1. 点云融合
    将多个点云合并为一个统一的点云模型,常见的方法包括:
  • 直接合并:将点云数据点直接合并,形成新的点云集合。
  • 体素融合:将点云划分为体素网格,对每个体素进行点统计(例如取平均值或加权平均)。
  • 概率地图:在动态环境中,采用概率方法更新体素的占据状态(如 OctoMap)。
  1. 冲突处理
    处理点云融合中可能出现的冲突或冗余点:
  • 重复点去除:通过距离阈值或体素化方法移除重叠的点。
  • 密度平衡:调整融合后的点云密度,避免某些区域点过于稀疏或密集。
  1. 表面重建(可选)
    对融合后的点云进行表面重建,使其从离散点集变成连续的三维表面模型:
  • 三角网格重建:如使用 Delaunay 三角剖分或 Poisson Surface Reconstruction。
  • 法线估计和纹理映射:为点云添加法线和纹理,提升模型的质量和真实性。
  1. 融合后优化
    对最终融合的点云进行后处理,提升质量:
  • 边界修整:去除非平滑或断裂的区域。
  • 孔洞填补:使用插值方法填补点云模型中的孔洞。
  • 重新采样:根据需求调整点的密度,使点云均匀分布。
  1. 应用领域的特定处理
    根据具体应用(如 SLAM、3D建模、AR/VR 等)进行额外处理:
  • 语义分割:将点云中的不同部分标注为特定类别(如建筑物、道路等)。
  • 颜色融合:将不同视角的颜色信息结合到点云中。
  • 动态点云处理:处理动态场景中的点云变化,例如背景建模和动态物体移除。
<think>嗯,用户想了解如何进行ZED相机的点云融合,我得仔细看看他们提供的参考引用。首先,用户提到了引用[2],里面详细描述了雨雾环境下多传感器融合SLAM方法,特别是点云配准和图像点云融合的步骤。这部分内容应该对回答有帮助。然后,引用[3]提到了ZED相机通过ROS wrapper启动节点,可能涉及到数据获取的部分。 我需要先确定ZED相机生成点云的基本流程。ZED相机通过立体视觉生成深度图,进而转化为点云。接下来,点云融合可能需要与其他传感器数据,比如图像特征点结合。根据引用[2],点云配准是关键步骤,通过将图像特征点和点云投影到球面,使用KD-tree搜索最近点来获取深度信息,并处理遮挡问题。 用户的问题集中在ZED相机的点云融合方法与实现,可能需要分步骤解释。首先,数据获取部分,使用ZED SDK或ROS wrapper启动相机节点,获取深度图和RGB图像。然后,点云生成,将深度数据转换为三维点云。接下来是特征提取,比如从图像中提取关键点。融合部分需要将图像特征点与点云匹配,这里可以引用球面投影和KD-tree搜索的方法,如引用[2]所述。此外,遮挡判断也是重要的一步,确保匹配的准确性。 另外,用户可能想知道具体的实现工具或库,比如使用PCL库进行点云处理,或者ROS中的相关包。引用[3]提到了ZED的ROS wrapper,所以可以建议用户通过ROS来启动节点,获取数据流,再结合PCL进行处理和融合。 需要注意的是,ZED相机本身可能已经提供了一些点云输出的功能,但融合其他传感器数据或图像特征可能需要额外处理。要确保步骤清晰,从数据获取到配准再到融合,每一步都解释清楚,并引用相关的文献支持。同时,提供示例代码或命令,如启动ROS节点和点云处理的代码片段,会更有帮助。 最后,用户可能对实际应用中的问题感兴趣,比如如何处理动态物体或优化配准效率,所以相关问题部分可以包括这些方面,引导进一步的学习。</think>### ZED相机点云融合方法与实现步骤 #### 1. **数据获取与预处理** - **ZED相机输出**:通过立体视觉生成深度图(Depth Map)和RGB图像,使用ROS节点`zed_wrapper`获取数据流[^3]。 - **点云生成**:将深度图转换为三维点云,公式为: $$ P(x,y) = \left( \frac{(u - c_x) \cdot d}{f_x}, \frac{(v - c_y) \cdot d}{f_y}, d \right) $$ 其中$(u,v)$为像素坐标,$d$为深度值,$f_x,f_y$为焦距,$(c_x,c_y)$为光心坐标[^1]。 #### 2. **多帧点云叠加** - 通过时间或空间配准,将多帧激光雷达或相机点云叠加,形成更稠密的点云数据[^2]。 #### 3. **特征点与点云配准** - **图像特征提取**:使用ORB、SIFT等算法提取RGB图像中的特征点。 - **球面投影**:将特征点和点云投影到以相机为中心的单位球面,消除视角差异。 - **深度关联**:采用**球面二维KD-tree**快速搜索特征点附近的最近点云点,获取精确深度值。 #### 4. **遮挡判断与融合** - 判断特征点与对应点云点是否存在遮挡(如深度值差异过大)。 - 通过阈值过滤无效匹配,完成图像特征与点云融合[^2]。 #### 5. **实现代码示例(ROS+PCL)** ```python # 启动ZED相机节点 roslaunch zed_wrapper zed2.launch # 点云处理示例(PCL库) import pcl cloud = pcl.load("zed_pointcloud.pcd") tree = cloud.make_kdtree_flann() # 特征点匹配与融合... ``` #### 6. **优化方向** - **动态物体处理**:结合语义分割过滤动态物体干扰。 - **计算效率**:使用GPU加速KD-tree搜索和点云配准。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值