点云教程

介绍

本教程介绍了如何使用Zivid SDK进行 点云 数据的相关操作。

小技巧

如果您更喜欢观看视频教程,我们的网络研讨会 Getting your point cloud ready for your application (为您的应用准备好点云)涵盖了点云教程。

先决条件

帧(Frame)

Zivid::Frame 包含了点云和彩色图像(存储在计算设备内存中)以及捕获设置和相机的信息。

捕获(Capture)

当您使用Zivid捕获图像时,您会得到一个帧(frame)作为返回值。

const auto frame = camera.capture(settings);

查看 捕获教程 了解有关如何捕获图像的详细说明。

加载(Load)

可以通过ZDF文件加载图像帧。

const auto dataFile = std::string(ZIVID_SAMPLE_DATA_DIR) + "/Zivid3D.zdf";
std::cout << "Reading ZDF frame from file: " << dataFile << std::endl;
const auto frame = Zivid::Frame(dataFile);

点云

从Frame获取句柄

您现在可以从GPU上的点云数据获取句柄。

const auto pointCloud = frame.pointCloud();

点云包含了XYZ、RGB和SNR数据,分布在2D网格上。

如需了解更多信息,请查看 点云结构

方法 Zivid::Frame::pointCloud() 不从GPU内存执行任何复制。

备注

Zivid::Camera::capture() 方法在相机完成捕获原始图像后的某个时刻返回。来自 Zivid::Frame::pointCloud() 的句柄可以立即可用。但是,实际的点云数据只有在GPU上的处理完成后才可用。任何对数据复制功能的调用(下面的部分)都将在阻塞并等待处理完成,然后再继续请求的复制操作。

请查看 点云捕获过程 了解更多细节。

将数据从GPU复制到CPU内存

您现在可以根据需要有选择地复制数据。下面的列表列出了输出数据格式以及如何从GPU复制它们。

返回类型

复制功能

单像素数据量

总数据量

Zivid::Array2D<Zivid::PointXYZ>

PointCloud::copyPointsXYZ()PointCloud::copyData<Zivid::PointXYZ>()

12 bytes

28 MB

Zivid::Array2D<Zivid::PointXYZW>

PointCloud::copyPointsXYZW()PointCloud::copyData<Zivid::PointXYZW>()

16 bytes

37 MB

Zivid::Array2D<Zivid::PointZ>

PointCloud::copyPointsZ()PointCloud::copyData<Zivid::PointZ>()

4 bytes

9 MB

Zivid::Array2D<Zivid::ColorRGBA>

PointCloud::copyColorsRGBA()PointCloud::copyData<Zivid::ColorRGBA>()

4 bytes

9 MB

Zivid::Array2D<Zivid::SNR>

PointCloud::copySNRs()PointCloud::copyData<Zivid::SNR>()

4 bytes

9 MB

Zivid::Array2D<Zivid::PointXYZColorRGBA>

PointCloud::copyData<PointXYZColorRGBA>()

16 bytes

37 MB

Zivid::Array2D<Zivid::PointXYZColorBGRA>

PointCloud::copyPointsXYZColorsBGRA()PointCloud::copyData<PointXYZColorBGRA>()

16 bytes

37 MB

Zivid::Image<Zivid::ColorRGBA>

PointCloud::copyImageRGBA()

4 bytes

9 MB

以下是如何复制数据的示例。

const auto data = pointCloud.copyData<Zivid::PointXYZColorRGBA>();

内存分配选项

在内存分配方面,复制数据有两种方式:

  • Zivid SDK可以分配内存缓冲区并将数据复制到其中。

  • 用户可以将指针传递给预分配的内存缓冲区,Zivid SDK会将数据复制到预分配的内存缓冲区。

我们将展示两个使用了OpenCV的内存分配的示例。

将选定数据从GPU复制到 CPU内存(Zivid 分配)

如果您只关心例如点云的RGB颜色数据,您可以仅将该数据复制到CPU内存。

        std::cout << "Capturing frame" << std::endl;
        frame = camera.capture(settings);
        pointCloud = frame.pointCloud();

        std::cout << "Copying colors with Zivid API from GPU to CPU" << std::endl;
        auto colors = pointCloud.copyColorsRGBA();

        std::cout << "Casting the data pointer as a void*, since this is what the OpenCV matrix constructor requires."
                  << std::endl;
        auto *dataPtrZividAllocated = const_cast<void *>(static_cast<const void *>(colors.data()));

        std::cout << "Wrapping this block of data in an OpenCV matrix. This is possible since the layout of \n"
                  << "Zivid::ColorRGBA exactly matches the layout of CV_8UC4. No copying occurs in this step."
                  << std::endl;
        const cv::Mat rgbaZividAllocated(colors.height(), colors.width(), CV_8UC4, dataPtrZividAllocated);

将选定数据从GPU复制到CPU内存(用户分配)

在上面的示例中,数据的所有权由返回的 Zivid::Array2D<> 对象拥有。或者您可以提供预先分配的内存缓冲区到 Zivid::PointCloud::copyData(dataPtr)dataPtr 的类型定义了将被复制的数据(PointXYZ, ColorRGBA 等。)。

现在让我们看一下与上面完全相同的用例。但是,这一次我们允许OpenCV来分配必要的存储空间。然后我们让Zivid API将数据直接从GPU复制到这个内存位置。

        std::cout << "Allocating the necessary storage with OpenCV API based on resolution info before any capturing"
                  << std::endl;
        auto rgbaUserAllocated = cv::Mat(resolution.height(), resolution.width(), CV_8UC4);

        std::cout << "Capturing frame" << std::endl;
        auto frame = camera.capture(settings);
        auto pointCloud = frame.pointCloud();

        std::cout << "Copying data with Zivid API from the GPU into the memory location allocated by OpenCV"
                  << std::endl;
        pointCloud.copyData(reinterpret_cast<Zivid::ColorRGBA *>(rgbaUserAllocated.data));

转换

你可能想要 转换 点云,将其原点从相机坐标系转换到机器人基坐标系,或者比如, 通过将点云从mm转换为m来缩放点云

pointCloud.transform(transformBaseToCamera);

降采样

有时您可能不需要相机输出的高空间分辨率(高空间分辨率意味着更多的细节和更短的点之间的距离)点云。那么您对点云进行 降采样

可以就地进行降采样,从而修改当前的点云。

pointCloud.downsample(Zivid::PointCloud::Downsampling::by2x2);

也可以将降采样后的点云作为一个新的点云实例,它不会改变现有的点云。

auto downsampledPointCloud = pointCloud.downsampled(Zivid::PointCloud::Downsampling::by2x2);

Zivid SDK支持以下降采样率: by2x2, by3x3, 和 by4x4, 可以进行多次降采样。

法线

一些应用需要计算点云的 法线 数据。

std::cout << "Computing normals and copying them to CPU memory" << std::endl;
const auto normals = pointCloud.copyData<Zivid::NormalXYZ>();

法线API将计算点云中每个点的法线,并将法线从GPU内存复制到CPU内存。其结果是一个法向量矩阵,每条法线对应输入点云的每个点。法线的大小和输入点云的大小相等。

可视化

您可以通过帧(frame)可视化点云。

std::cout << "Setting up visualization" << std::endl;
Zivid::Visualization::Visualizer visualizer;

std::cout << "Visualizing point cloud" << std::endl;
visualizer.showMaximized();
visualizer.show(frame);
visualizer.resetToFit();

std::cout << "Running visualizer. Blocking until window closes" << std::endl;
visualizer.run();

您也可以从点云对象来可视化点云。

std::cout << "Getting point cloud from frame" << std::endl;
auto pointCloud = frame.pointCloud();

std::cout << "Setting up visualization" << std::endl;
Zivid::Visualization::Visualizer visualizer;

std::cout << "Visualizing point cloud" << std::endl;
visualizer.showMaximized();
visualizer.show(pointCloud);
visualizer.resetToFit();

std::cout << "Running visualizer. Blocking until window closes" << std::endl;
visualizer.run();

如需了解更多信息,请查看 可视化教程,里面包含了如何使用第三方库实现点云、彩色图像、深度图和法线的可视化。

结论

本教程展示了如何使用Zivid SDK来提取、操作、转换和可视化点云。