【译】PCL官网教程翻译(10):使用矩阵转换点云 - Using a matrix to transform a point cloud

英文原网页查看。
修订:20201115

使用矩阵转换点云

在本教程中,我们将学习如何使用4x4矩阵转换点云。我们将对加载的点云应用旋转和平移,然后显示结果。
该程序能够加载一个PCD或PLY文件;对其应用矩阵变换,并显示原始和变换后的点云。

代码

首先,在您最喜欢的编辑器中创建一个文件,例如matrix_transform.cpp,并在其中放置以下代码:

#include <iostream>

#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_cloud.h>
#include <pcl/console/parse.h>
#include <pcl/common/transforms.h>
#include <pcl/visualization/pcl_visualizer.h>

// 此函数显示帮助功能
void
showHelp(char * program_name)
{
  std::cout << std::endl;
  std::cout << "Usage: " << program_name << " cloud_filename.[pcd|ply]" << std::endl;
  std::cout << "-h:  Show this help." << std::endl;
}

// 这是main主函数
int
main (int argc, char** argv)
{
  // 显示帮助
  if (pcl::console::find_switch (argc, argv, "-h") || pcl::console::find_switch (argc, argv, "--help")) {
    showHelp (argv[0]);
    return 0;
  }

  // 获取参数中的点云文件名 | 处理PCD和PLY文件
  std::vector<int> filenames;
  bool file_is_pcd = false;

  filenames = pcl::console::parse_file_extension_argument (argc, argv, ".ply");

  if (filenames.size () != 1)  {
    filenames = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");

    if (filenames.size () != 1) {
      showHelp (argv[0]);
      return -1;
    } else {
      file_is_pcd = true;
    }
  }

  // 加载文件 | 处理PCD和PLY文件
  pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud (new pcl::PointCloud<pcl::PointXYZ> ());

  if (file_is_pcd) {
    if (pcl::io::loadPCDFile (argv[filenames[0]], *source_cloud) < 0)  {
      std::cout << "Error loading point cloud " << argv[filenames[0]] << std::endl << std::endl;
      showHelp (argv[0]);
      return -1;
    }
  } else {
    if (pcl::io::loadPLYFile (argv[filenames[0]], *source_cloud) < 0)  {
      std::cout << "Error loading point cloud " << argv[filenames[0]] << std::endl << std::endl;
      showHelp (argv[0]);
      return -1;
    }
  }

  /*提醒:变换矩阵是如何工作的:
           |-------> 这一列是平移变换
    | 1 0 0 x |  \
    | 0 1 0 y |   }-> 左边是3x3单位矩阵(没有旋转)
    | 0 0 1 z |  /
    | 0 0 0 1 |    -> 我们不用这行(它必须保持0,0,0,1)
    方法 #1: 使用Matrix4f
    这是“人工”方法,完美的理解,但容易出错!
  */
  Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity();

  // 定义一个旋转矩阵 (参考 https://en.wikipedia.org/wiki/Rotation_matrix)
  float theta = M_PI/4; // 以弧度为单位的旋转角度
  transform_1 (0,0) = std::cos (theta);
  transform_1 (0,1) = -sin(theta);
  transform_1 (1,0) = sin (theta);
  transform_1 (1,1) = std::cos (theta);
  //    (行, 列)
  // 在x轴上定义2.5米的平移量.
  transform_1 (0,3) = 2.5;

  // 打印变换矩阵
  printf ("Method #1: using a Matrix4f\n");
  std::cout << transform_1 << std::endl;

  /*  方法 #2:使用Affine3f
    这种方法更简单,更不容易出错
  */
  Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();

  // 在x轴上定义2.5米的平移量.
  transform_2.translation() << 2.5, 0.0, 0.0;

  // 与之前相同的旋转矩阵以弧度为单位的绕Z轴转角
  transform_2.rotate (Eigen::AngleAxisf (theta, Eigen::Vector3f::UnitZ()));

  // 打印变换矩阵
  printf ("\nMethod #2: using an Affine3f\n");
  std::cout << transform_2.matrix() << std::endl;

  // 执行转换
  pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud (new pcl::PointCloud<pcl::PointXYZ> ());
  // 您可以应用transform_1或transform_2;它们是一样的
  pcl::transformPointCloud (*source_cloud, *transformed_cloud, transform_2);

  // 可视化
  printf(  "\nPoint cloud colors :  white  = original point cloud\n"
      "                        red  = transformed point cloud\n");
  pcl::visualization::PCLVisualizer viewer ("Matrix transformation example");

   // 为点云定义R、G、B颜色
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_cloud_color_handler (source_cloud, 255, 255, 255);
  // 我们将点云添加到查看器并传递颜色处理句柄
  viewer.addPointCloud (source_cloud, source_cloud_color_handler, "original_cloud");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transformed_cloud_color_handler (transformed_cloud, 230, 20, 20); // 红色
  viewer.addPointCloud (transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");

  viewer.addCoordinateSystem (1.0, "cloud", 0);
  viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
  //viewer.setPosition(800, 400); // 设置visualiser窗口位置

  while (!viewer.wasStopped ()) { // 显示可视化器,直到按下“q”键
    viewer.spinOnce ();
  }

  return 0;
}

解释

现在,让我们逐步分解代码。

#include <iostream>

#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/point_cloud.h>
#include <pcl/console/parse.h>
#include <pcl/common/transforms.h>
#include <pcl/visualization/pcl_visualizer.h>

包括我们将使用的头文件。#include <pcl/common/transforms.h>允许我们使用pcl::transformPointCloud函数。

// 此函数将显示帮助功能
void
showHelp(char * program_name)
{
  std::cout << std::endl;
  std::cout << "Usage: " << program_name << " cloud_filename.[pcd|ply]" << std::endl;
  std::cout << "-h:  Show this help." << std::endl;
}

如果用户没有提供足够的参数,此函数用于显示帮助。

  // 显示帮助
  if (pcl::console::find_switch (argc, argv, "-h") || pcl::console::find_switch (argc, argv, "--help")) {
    showHelp (argv[0]);
    return 0;
  }

我们解析命令行上的参数,使用**-h-help**将显示帮助,这将终止程序

  //从参数中得到点云文件名 | 适用于PCD和PLY文件
  std::vector<int> filenames;
  bool file_is_pcd = false;

  filenames = pcl::console::parse_file_extension_argument (argc, argv, ".ply");

  if (filenames.size () != 1)  {
    filenames = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");

    if (filenames.size () != 1) {
      showHelp (argv[0]);
      return -1;
    } else {
      file_is_pcd = true;
    }
  }

我们在参数中查找.ply或.pcd文件名,如果没有找到则终止程序。布尔参数 file_is_pcd将帮助我们加载PCD或PLY文件。

  //加载电云文件 | 适用于PCD和PLY文件
  pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud (new pcl::PointCloud<pcl::PointXYZ> ());

  if (file_is_pcd) {
    if (pcl::io::loadPCDFile (argv[filenames[0]], *source_cloud) < 0)  {
      std::cout << "Error loading point cloud " << argv[filenames[0]] << std::endl << std::endl;
      showHelp (argv[0]);
      return -1;
    }
  } else {
    if (pcl::io::loadPLYFile (argv[filenames[0]], *source_cloud) < 0)  {
      std::cout << "Error loading point cloud " << argv[filenames[0]] << std::endl << std::endl;
      showHelp (argv[0]);
      return -1;
    }
  }

现在,我们加载PCD/PLY文件,并检查文件是否已成功加载。否则,终止程序。

  /*提醒:变换矩阵是如何工作的:
           |-------> 这一列是平移变换
    | 1 0 0 x |  \
    | 0 1 0 y |   }-> 左边是3x3单位矩阵(没有旋转)
    | 0 0 1 z |  /
    | 0 0 0 1 |    -> 我们不用这行(它必须保持0,0,0,1)
    方法 #1: 使用Matrix4f
    这是“人工”方法,完美的理解,但容易出错!
  */
  Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity();
  }

这是创建变换矩阵的第一种方法。这将帮助您理解转换矩阵是如何工作的。用单位矩阵初始化4x4矩阵;

    |  1  0  0  0  |
i = |  0  1  0  0  |
    |  0  0  1  0  |
    |  0  0  0  1  |

注意
当数相乘时,单位矩阵等价于“1”;它改变不了什么。它是一个方阵,主对角线上是1,其它地方是0。

这意味着没有转换(没有旋转和平移),我们不用矩阵的最后一行。
前3行和列(左上角)是旋转矩阵,最后一列的前3行是平移。

  // 定义一个旋转矩阵 (参考 https://en.wikipedia.org/wiki/Rotation_matrix)
  float theta = M_PI/4; // 以弧度为单位的旋转角度
  transform_1 (0,0) = std::cos (theta);
  transform_1 (0,1) = -sin(theta);
  transform_1 (1,0) = sin (theta);
  transform_1 (1,1) = std::cos (theta);
  //    (行, 列)
  // 在x轴上定义2.5米的平移量.
  transform_1 (0,3) = 2.5;

  // 打印变换矩阵
  printf ("Method #1: using a Matrix4f\n");
  std::cout << transform_1 << std::endl;

这里我们定义了绕Z轴45°(PI/4)旋转和沿X轴的平移。这就是我们刚刚定义的变换

    |  cos(θ) -sin(θ)  0.0 |
R = |  sin(θ)  cos(θ)  0.0 |
    |  0.0     0.0     1.0 |

t = < 2.5, 0.0, 0.0 >
  /*  方法 #2:使用Affine3f
    这种方法更简单,更不容易出错
  */
  Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();

  // 在x轴上定义2.5米的平移量.
  transform_2.translation() << 2.5, 0.0, 0.0;

  // 与之前相同的旋转矩阵以弧度为单位的绕Z轴转角
  transform_2.rotate (Eigen::AngleAxisf (theta, Eigen::Vector3f::UnitZ()));

  // 打印变换矩阵
  printf ("\nMethod #2: using an Affine3f\n");
  std::cout << transform_2.matrix() << std::endl;

第二种方法更容易理解,也更不容易出错。如果你想做几个旋转,一定要小心;旋转是不可交换的!这意味着大多数情况下:rotA * rotB != rotB * rotA。

 // 执行转换
  pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud (new pcl::PointCloud<pcl::PointXYZ> ());
  // 您可以应用transform_1或transform_2;它们是一样的
  pcl::transformPointCloud (*source_cloud, *transformed_cloud, transform_2);

现在我们将这个矩阵应用到点云source_cloud上,并将结果保存到新创建的transformed_cloud中。

 // 可视化
  printf(  "\nPoint cloud colors :  white  = original point cloud\n"
      "                        red  = transformed point cloud\n");
  pcl::visualization::PCLVisualizer viewer ("Matrix transformation example");

   // 为点云定义R、G、B颜色
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_cloud_color_handler (source_cloud, 255, 255, 255);
  // 我们将点云添加到查看器并传递颜色处理句柄
  viewer.addPointCloud (source_cloud, source_cloud_color_handler, "original_cloud");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transformed_cloud_color_handler (transformed_cloud, 230, 20, 20); // 红色
  viewer.addPointCloud (transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");

  viewer.addCoordinateSystem (1.0, "cloud", 0);
  viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
  //viewer.setPosition(800, 400); // 设置visualiser窗口位置

  while (!viewer.wasStopped ()) { // 显示可视化器,直到按下“q”键
    viewer.spinOnce ();
  }

  return 0;

然后我们使用PCLVisualizer将结果可视化。原始点云将显示为白色,转换后的点云将显示为红色。将显示coordoniates轴。我们还设置了可视化程序的背景颜色和点显示大小。

编译和运行程序

在CMakeLists.txt文件中添加以下行:

cmake_minimum_required(VERSION 2.6 FATAL_ERROR)

project(pcl-matrix_transform)

find_package(PCL 1.7 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable (matrix_transform matrix_transform.cpp)
target_link_libraries (matrix_transform ${PCL_LIBRARIES})

完成可执行文件后,运行它,将路径传递给PCD或PLY文件。要重现如下所示的结果,可以下载cube.ply文件:

$ ./matrix_transform cube.ply

你会看到类似的结果:

./matrix_transform cube.ply
[pcl::PLYReader] /home/victor/cube.ply:12: property 'list uint8 uint32 vertex_indices' of element 'face' is not handled
Method #1: using a Matrix4f
 0.707107 -0.707107         0       2.5
 0.707107  0.707107         0         0
        0         0         1         0
        0         0         0         1

Method #2: using an Affine3f
 0.707107 -0.707107         0       2.5
 0.707107  0.707107         0         0
        0         0         1         0
        0         0         0         1

Point cloud colors :  white   = original point cloud
                       red    = transformed point cloud

在这里插入图片描述

更多关于转换

现在,您已经成功地使用转换矩阵转换了一个点云。
如果你想变换一个点呢?一个向量?

在三维空间中定义一个点,该点有三个坐标;x y z(在笛卡尔坐标系中)
如何用4x4矩阵乘以一个向量(三个坐标)?你就是不能!如果你不知道为什么,请参考维基百科上的矩阵乘法

我们需要一个有四个分量的向量。最后一个分量是什么?这取决于你想做什么:

  • 如果你想转换一个点:把1放在向量的末尾,这样平移就被考虑进去了。
  • 如果你想变换矢量的方向:在矢量的末尾放0,忽略平移。
    这里有一个例子,我们想转换以下向量:
[10, 5, 0, 3, 0, -1]

其中前三个分量定义原点坐标,后三个分量定义方向。
这个向量从点(10,5,0)开始到点(13,5,1)结束。
这是你需要做的转换向量:

[10, 5, 0,  1] * 4x4转换矩阵
[3,  0, -1, 0] * 4x4转换矩阵
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
使用 VTK 显示 PCL 的点云,需要将 PCL 的点云数据转换为 VTK 的数据类型。可以使用 `pcl::visualization::PCLVisualizer` 类来实现这个功能。以下是一个简单的示例程序,展示如何使用 VTK 来显示 PCL 点云数据: ```cpp #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> #include <vtkRenderWindow.h> int main () { // 创建一个 PCL 的点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); cloud->width = 5; cloud->height = 1; cloud->is_dense = false; cloud->points.resize(cloud->width * cloud->height); for (size_t i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f); cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f); cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f); } // 创建一个可视化窗口 pcl::visualization::PCLVisualizer viewer("Cloud Viewer"); // 将 PCL 的点云数据转换为 VTK 的数据类型 vtkSmartPointer<vtkPoints> vtk_points = vtkSmartPointer<vtkPoints>::New(); for (size_t i = 0; i < cloud->points.size(); ++i) { vtk_points->InsertNextPoint(cloud->points[i].x, cloud->points[i].y, cloud->points[i].z); } vtkSmartPointer<vtkPolyData> vtk_polydata = vtkSmartPointer<vtkPolyData>::New(); vtk_polydata->SetPoints(vtk_points); // 将 VTK 的数据类型添加到可视化窗口中 viewer.addModelFromPolyData(vtk_polydata, "cloud"); // 显示可视化窗口 while (!viewer.wasStopped()) { viewer.spinOnce(); } return 0; } ``` 在上面的示例程序中,我们首先创建了一个 PCL 的点云数据,并使用 `pcl::visualization::PCLVisualizer` 类创建了一个 VTK 的可视化窗口。然后,我们将 PCL 的点云数据转换为 VTK 的数据类型,并将其添加到可视化窗口中。最后,我们进入了一个循环,显示可视化窗口,直到用户关闭窗口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值