0. 简介
我们在上一节主要讲了基础的点云变换,对应了pcl::transformPointCloud这种方法。而我们这一章将会讲解 pcl::CropBox这种点云裁剪步骤。点云裁剪是在点云中选择出一部分区域的过程,以便于后续的处理和分析。在点云处理领域,点云裁剪是一个非常常见的任务,因为在现实中,往往只需要关注某些区域的点云数据,而不需要处理整个点云数据集。点云裁剪通常是通过一个定义良好的几何形状来实现,比如长方体、球体等等。
1. CUDA与Thrust
在点云裁剪中,CUDA和Thrust都提供了不同的方法来实现。使用CUDA时,一般会借助于CUDA中的点云裁剪算法来实现,CUDA中的点云裁剪算法需要使用CUDA提供的数据结构和API,以及CUDA的线程和内存模型等相关知识。在GPU上运行,加快点云处理的速度。而Thrust是一个基于C++ STL的高性能并行编程库,它可以在CPU和GPU上运行,并提供了许多通用的算法和数据结构,可以轻松地实现点云裁剪。CUDA 和 Thrust 提供了一种高效的方式来处理大规模的点云数据。CUDA 提供了低级别的 GPU 计算能力,需要程序员手动管理内存和线程。而 Thrust 为开发者提供了一组高级别的算法和数据结构,可以更方便地编写 CUDA 代码,而无需手动管理内存和线程。
2. CUDA代码完成加速
下面我们来看一下这个点云裁剪部分的操作。该代码实现了使用CUDA并行计算来移除在球体内部的点云数据。该代码包含了一个名为 kernel_cudaRemovePointsInsideSphere 的函数,该函数使用了 CUDA 的并行计算功能。该函数中首先通过线程索引计算每个线程要处理的点云数据的位置,然后获取该点云数据的坐标,接着计算该点云数据到原点的距离,并将其与球体半径的平方进行比较。如果该点云数据距离原点的距离小于球体半径的平方,则将其标记为 false,表示该点云数据在球体内部。如果该点云数据距离原点的距离大于等于球体半径的平方,则将其标记为 true,表示该点云数据不在球体内部。最后该函数使用了 CUDA 的并行计算功能来同时处理多个点云数据,并通过同步函数 cudaDeviceSynchronize 来保证计算结果的正确性。此外还包含了一个名为 cudaRemovePointsInsideSphere 的函数,该函数则负责调用 kernel_cudaRemovePointsInsideSphere 函数,设置线程块和线程数,并调用 kernel 函数来完成点云数据的移除。
__global__ void kernel_cudaRemovePointsInsideSphere(pcl::PointXYZ *d_point_cloud, bool *d_markers, int number_of_points, float sphere_radius)
{
int ind = blockIdx.x * blockDim.x + threadIdx.x; // 线程索引
if (ind < number_of_points) // 线程索引小于点云数
{
float x = d_point_cloud[ind].x; // 获取点云数据
float y = d_point_cloud[ind].y;
float z = d_point_cloud[ind].z;
float distance = (x * x + y * y + z * z); // 计算点云数据到原点的距离
if (distance < sphere_radius * sphere_radius) // 判断点云数据是否在球体内
{
d_markers[ind] = false; // 将点云数据标记为false
}
else
{
d_markers[ind] = true;
}
}
}
cudaError_t cudaRemovePointsInsideSphere(int threads, pcl::PointXYZ *d_point_cloud,
bool *d_markers, int number_of_points, float sphere_radius)
{
kernel_cudaRemovePointsInsideSphere<<<number_of_points / threads + 1, threads>>>(d_point_cloud, d_markers, number_of_points, sphere_radius); // 设置线程块和线程数,并调用kernel来完成remove移除
cudaDeviceSynchronize(); // 同步
return cudaGetLastError();
}
然后下面的代码为调用上述代码的函数,该函数首先设置线程数和球半径等参数,并为点云、标记在设备上分配内存空间。将主机上的点云数据复制到设备上。调用函数cudaRemovePointsInsideSphere,该函数使用kernel_cudaRemovePointsInsideSphere内核函数来移除点云中的在球体内的点。将标记数据从设备复制到主机上,并将标记为true的点添加到新的点云new_point_cloud中。
bool removePointsInsideSphere(pcl::PointCloud<pcl::PointXYZ> &point_cloud)
{
int threads; // 线程数
float sphere_radius = 1.0f; // 球半径
pcl::PointXYZ *d_point_cloud; // 点云,设备DEVICE
bool *d_markers; // 标记,设备DEVICE
bool *h_markers; // 标记,主机HOST
cudaError_t err = ::cudaSuccess; // 设置错误类型
err = cudaSetDevice(0); // 设置设备
if (err != ::cudaSuccess) // 如果类型不为cudaSuccess
return false; // 返回
threads = getNumberOfAvailableThreads(); // 获取线程数
err = cudaMalloc((void **)&d_point_cloud, point_cloud.points.size() * sizeof(pcl::PointXYZ)); // 为点云分配内存
if (err != ::cudaSuccess) // 如果类型不为cudaSuccess
return false;
err = cudaMemcpy(d_point_cloud, point_cloud.points.data(), point_cloud.points.size() * sizeof(pcl::PointXYZ), cudaMemcpyHostToDevice); // 将点云数据从主机复制到设备
if (err != ::cudaSuccess)
return false;
err = cudaMalloc((void **)&d_markers, point_cloud.points.size() * sizeof(bool)); // 为标记分配内存
if (err != ::cudaSuccess)
return false;
err = cudaRemovePointsInsideSphere(threads, d_point_cloud, d_markers, point_cloud.points.size(), sphere_radius); // 移除点云中的点,这里面的算法在另一个文件中
if (err != ::cudaSuccess)
return false;
h_markers = (bool *)malloc(point_cloud.points.size() * sizeof(bool)); // 为标记分配内存
err = cudaMemcpy(h_markers, d_markers, point_cloud.points.size() * sizeof(bool), cudaMemcpyDeviceToHost); // 将标记数据从设备复制到主机
if (err != ::cudaSuccess)
return false;
pcl::PointCloud<pcl::PointXYZ> new_point_cloud;
for (size_t i = 0; i < point_cloud.points.size(); i++)
{
if (h_markers[i]) // 如果标记为true
new_point_cloud.push_back(point_cloud[i]); // 将点云中的点添加到新的点云中
}
std::cout << "Number of points before removing points: " << point_cloud.size() << std::endl; // 输出移除前的点云数
point_cloud = new_point_cloud;
std::cout << "Number of points after removing points: " << point_cloud.size() << std::endl; // 输出移除后的点云数
free(h_markers); // 释放内存
err = cudaFree(d_markers); // 释放内存
d_markers = NULL; // 将指针置空
if (err != ::cudaSuccess)
return false;
err = cudaFree(d_point_cloud); // 释放内存
d_point_cloud = NULL;
if (err != ::cudaSuccess)
return false;
return true;
}
3. Thrust代码完成加速
这段代码定义了一个is_inside_sphere函数对象,它用于检查一个点是否在给定的球体内部。该函数对象接受一个float4类型的参数,该参数包含球体的中心坐标和半径。函数对象的operator()方法返回一个布尔值,表示输入点是否在球体内部。removePointsInsideSphere函数使用thrust::remove_if算法,将输入点云中位于给定球体内部的点删除。删除后,函数返回一个新的设备向量,包含了所有在球体外部的点。
#include <thrust/device_vector.h>
#include <pcl/point_types.h>
#include <cmath>
// 定义点云结构体
struct PointXYZ
{
float x, y, z;
};
struct is_inside_sphere
{
float4 sphere; // x,y,z, and r
is_inside_sphere(float4 sphere) : sphere(sphere) {}
__host__ __device__ bool operator()(const pcl::PointXYZ &pt) const
{
float dx = pt.x - sphere.x;
float dy = pt.y - sphere.y;
float dz = pt.z - sphere.z;
float distance = sqrt(dx * dx + dy * dy + dz * dz);
return distance > sphere.w;
}
};
thrust::device_vector<PointXYZ> removePointsInsideSphere(const thrust::device_vector<PointXYZ> &point_cloud)
{
// Define the sphere parameters
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float r = 1.0f;
float4 sphere = make_float4(x, y, z, r);
// Create a functor that checks if a point is inside the sphere
is_inside_sphere predicate(sphere);
// Remove the points inside the sphere using thrust::remove_if
thrust::device_vector<PointXYZ> output;
output.resize(point_cloud.size());
auto new_end = thrust::remove_if(point_cloud.begin(), point_cloud.end(), predicate);
point_cloud.erase(new_end, point_cloud.end());
output.resize(point_cloud.size());
thrust::copy(point_cloud.begin(), point_cloud.end(), output.begin());
return output;
}
评论(0)
您还未登录,请登录后发表或查看评论