PCL_点云合并

一、理论部分

简单的点云合并步骤为:读点云 --> 点云合并 --> 写点云。

1. 读写点云

(1)读PCD文件

应用pcl::io::loadPCDFilepcl::PointT(pcd文件,点云对象)函数实现PCD文件的读取。

#include <iostream>			
#include <pcl/io/pcd_io.h>    //pcd流头文件
#include <pcl/point_types.h>  //点类型即现成的pointT

int main (int argc, char** argv)
{
 //创建点云指针
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
 //直接读取pcd文件到cloud中,并判断是否读取正确
  if (pcl::io::loadPCDFile<pcl::PointXYZ> ("test_pcd.pcd", *cloud) == -1) //* load the file
  {
    PCL_ERROR ("Couldn't read file test_pcd.pcd \n");
    return (-1);
  }
  //控制台输出点云宽高(无序点云为总数)
  std::cout << "Loaded "
            << cloud->width * cloud->height
            << " data points from test_pcd.pcd with the following fields: "
            << std::endl;
   //遍历点云输出坐标
  for (const auto& point: *cloud)
    std::cout << "    " << point.x
              << " "    << point.y
              << " "    << point.z << std::endl;

  return (0);
}

(2)写PCD文件

应用pcl::io::savePCDFileASCII (pcd文件, 点云对象)函数将点云写入PCD文件。

注意:写入的代码点云对象不是指针所以与读取代码略有不同,具体栈和堆创建区别自行查找C++基础。

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

int main (int argc, char** argv)
{
 //创建栈上的点云对象
  pcl::PointCloud<pcl::PointXYZ> cloud;

  //为点云基础属性赋值;
  cloud.width    = 5;//总数
  cloud.height   = 1;
  cloud.is_dense = false;//是否有限,即是否包含NAN或者INF
  cloud.points.resize (cloud.width * cloud.height);//更新点云内部存储结构大小
//随机为每个点赋值
  for (auto& point: cloud)
  {
    point.x = 1024 * rand () / (RAND_MAX + 1.0f);
    point.y = 1024 * rand () / (RAND_MAX + 1.0f);
    point.z = 1024 * rand () / (RAND_MAX + 1.0f);
  }
//将cloud存入pcd文件中
  pcl::io::savePCDFileASCII ("test_pcd.pcd", cloud);
  std::cerr << "Saved " << cloud.size () << " data points to test_pcd.pcd." << std::endl;
//控制台输出
  for (const auto& point: cloud)
    std::cerr << "    " << point.x << " " << point.y << " " << point.z << std::endl;

  return (0);
}

2. 点云合并

点云合并的方式有两种,分别为横向合并和纵向合并。

(1)官方文档

由于官方文档代码过长,大部分都是随机创建数据,只提取关键部分;
点云合并的官方文档

(提示:官方文档中合并的代码整个流程即对A和B两个点云的位置XYZ纵向合并 和对A点云的位置XYZ字段与B点云的法向量XYZ字段横向合并)

小结:
对于纵向合并,直接A+B即可
横向字段合并,采用pcl::concatenateFields (cloud_a, n_cloud_b, p_n_cloud_c);

(2)横向合并

横向合并(Horizontal Concatenation)则可能是将两个点云的属性合并,每个点的属性数量增加。例如,A点云有XYZ坐标,B点云有法向量Nx, Ny, Nz,横向合并后每个点将包含XYZ和Nx, Ny, Nz,前提是两者的点数相同,即一一对应。

适用场景:
①属性融合:将不同传感器数据整合到同一组点上(如坐标+颜色+法向量)。
②点级关联:需要为每个点附加额外属性(如将法向量与坐标绑定)。
③数据对齐:两个点云的点一一对应(如来自同一视角的不同属性提取结果)。

限制:
①点数一致:两个点云必须有相同的点数(N=M),否则无法逐点合并。
②属性互补:合并后的属性需有实际意义(如XYZ+法向量可用于表面重建)。

(3)纵向合并

纵向合并(Vertical Concatenation)通常指的是将两个点云的点数叠加,形成一个新的点云,点数等于两者之和,但属性维度不变。例如,A有100个点,每个点有XYZ坐标,B有50个点,每个点也有XYZ坐标,纵向合并后得到150个点,每个点仍保持XYZ坐标。

适用场景:
①场景拼接:合并两个不同位置或视角采集的点云(如两片不重叠的墙面点云),形成更完整的场景。
②数据增强:将多个独立采集的点云合并为一个更大的数据集。
③简单叠加:无需点间属性关联,只需将点数量累加。

限制:
①属性一致性:两个点云的属性必须完全一致(如都只有XYZ坐标)。
②无对应关系:合并后的点之间没有逻辑关联,仅简单叠加。

二、Demo

注意:横向合并需要确保两个点云的点数相同,否则会出错。代码中需要添加检查点数的步骤。而纵向合并则没有这个限制,但需要确保点的类型一致。

1. 总结步骤:

  1. 横向合并:创建两个点数相同的点云,合并属性到新的点云结构中。
  2. 纵向合并:合并两个点云的所有点到同一个点云中。
  3. 添加必要的检查和注释,确保代码健壮性。
  4. 提供保存和输出的示例,方便验证结果。

2. 纵向合并(Vertical Concatenation)

将两个点云的 点数量(行) 叠加,生成点数更多的点云。要求两个点云的属性一致(如均为XYZ坐标)。

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h>

int main() {
    // 创建两个点云(假设均为XYZ坐标)
    pcl::PointCloud<pcl::PointXYZ> cloud_A, cloud_B, cloud_merged;

    // 填充点云A(示例数据)
    for (int i = 0; i < 100; ++i) {
        cloud_A.push_back(pcl::PointXYZ(i, i, i));
    }

    // 填充点云B(示例数据)
    for (int i = 0; i < 50; ++i) {
        cloud_B.push_back(pcl::PointXYZ(i * 2, i * 2, i * 2));
    }

    // 纵向合并:将B的点追加到A的末尾
    cloud_merged = cloud_A;
    cloud_merged += cloud_B;  // 等同于 cloud_merged.insert(cloud_merged.end(), cloud_B.begin(), cloud_B.end());

    // 输出合并后的点数
    std::cout << "纵向合并后的点数: " << cloud_merged.size() << std::endl;

    // 保存结果
    pcl::io::savePCDFile("vertical_merged.pcd", cloud_merged);
    return 0;
}

3. 横向合并(Horizontal Concatenation)

将两个点云的 属性(列) 叠加,生成属性更丰富的点云。要求两个点云点数相同(如将XYZ坐标与法向量合并)。

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h>

// 自定义点类型:包含XYZ坐标和法向量
struct MyPoint {
    float x, y, z;
    float normal_x, normal_y, normal_z;
};
POINT_CLOUD_REGISTER_POINT_STRUCT(
    MyPoint,
    (float, x, x)
    (float, y, y)
    (float, z, z)
    (float, normal_x, normal_x)
    (float, normal_y, normal_y)
    (float, normal_z, normal_z)
);

int main() {
    // 创建两个点云:A为XYZ坐标,B为法向量
    pcl::PointCloud<pcl::PointXYZ> cloud_A;
    pcl::PointCloud<pcl::Normal> cloud_B;
    pcl::PointCloud<MyPoint> cloud_merged;

    // 填充点云A(XYZ坐标)
    for (int i = 0; i < 100; ++i) {
        cloud_A.push_back(pcl::PointXYZ(i, i, i));
    }

    // 填充点云B(法向量)
    for (int i = 0; i < 100; ++i) {
        pcl::Normal normal;
        normal.normal_x = 0.1f;
        normal.normal_y = 0.2f;
        normal.normal_z = 0.3f;
        cloud_B.push_back(normal);
    }

    // 检查点数是否一致
    if (cloud_A.size() != cloud_B.size()) {
        std::cerr << "错误:点数不一致,无法横向合并!" << std::endl;
        return -1;
    }

    // 横向合并:将XYZ坐标与法向量合并为自定义点类型
    cloud_merged.resize(cloud_A.size());
    for (size_t i = 0; i < cloud_A.size(); ++i) {
        MyPoint point;
        point.x = cloud_A[i].x;
        point.y = cloud_A[i].y;
        point.z = cloud_A[i].z;
        point.normal_x = cloud_B[i].normal_x;
        point.normal_y = cloud_B[i].normal_y;
        point.normal_z = cloud_B[i].normal_z;
        cloud_merged[i] = point;
    }

    // 输出合并后的属性维度
    std::cout << "横向合并后的点数: " << cloud_merged.size() << std::endl;

    // 保存结果
    pcl::io::savePCDFile("horizontal_merged.pcd", cloud_merged);
    return 0;
}

4.代码说明

(1)纵向合并:
使用 += 运算符或 insert 方法直接将两个点云的点叠加。
适用于简单拼接场景,如合并两个独立的扫描数据。

(2)横向合并:
自定义点类型:定义包含XYZ坐标和法向量的结构体 MyPoint。
数据对齐:确保两个点云的点数相同,否则合并失败。
逐点赋值:将XYZ坐标和法向量依次填充到新点云中。
适用于融合多属性数据,如坐标+法向量+颜色。

(3)输入数据:
示例中直接生成数据,实际应用可替换为从PCD文件加载:
pcl::io::loadPCDFile(“cloud_A.pcd”, cloud_A);

(4)输出验证:
使用CloudCompare打开生成的 vertical_merged.pcd 和 horizontal_merged.pcd 验证结果。

三、说明

文章理论部分,参考引用了一篇文章,文章链接如下,感兴趣的可以去看看,本文添加了合并的理论知识和两种合并方法的Demo。
链接:https://blog.youkuaiyun.com/qq_41795143/article/details/112245395

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值