• 【点云处理】点云法向量估计及其加速(5)


            在上一篇文章【点云处理】点云法向量估计及其加速(4)中我们尝试对pcl自带的KDTree的k近邻搜索过程使用OpenMP加速,效果比较明显,有将近1倍的提速。在这篇文章中我们暂时放弃pcl自带的KDTree,转而使用另一大杀器nanflann库提供的KDTree。nanoflann是一个c++11标准库,用于构建具有不同拓扑(R2,R3(点云),SO(2)和SO(3)(2D和3D旋转组))的KD树。nanoflann 算法对fastann进行了改进,效率以及内存使用等方面都进行了优化,而且代码十分轻量级且开源。nanoflann不需要编译或安装,你只需要在你的代码中加入#include 即可方便快捷地使用它。好了,下面使用nanoflann对我们的代码进行改写。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include "KDTreeTableAdaptor.h"
    13. int main(int argc, char** argv) {
    14. ros::init(argc, argv, "n_lidar_gpu_normal");
    15. ros::NodeHandle node;
    16. pcl::PointCloud::Ptr cloud(new pcl::PointCloud);
    17. const boost::function<void (const boost::shared_ptrconst>&)> callback =[&](sensor_msgs::PointCloud2::ConstPtr msg_pc_ptr) {
    18. pcl::fromROSMsg(*msg_pc_ptr, *cloud);
    19. size_t cloud_size = cloud->size();
    20. int dim=3,k=10;
    21. float* points = new float[cloud_size*dim];
    22. for (int i=0; i
    23. float* p = points + i*dim;
    24. p[0] = cloud->points[i].x;
    25. p[1] = cloud->points[i].y;
    26. p[2] = cloud->points[i].z;
    27. }
    28. auto t1 = std::chrono::steady_clock::now();
    29. KDTreeTableAdaptor<float,float> kdtree(cloud_size, dim, points, 64);
    30. kdtree.index->buildIndex();
    31. std::vectorint>> neighbors_all(cloud_size,std::vector<int>(k));
    32. std::vector<int> sizes(cloud_size,k);
    33. for (int i=0; i
    34. std::vector<size_t> out_ids(k);
    35. std::vector<float> out_dists_sqr(k);
    36. nanoflann::KNNResultSet<float> result_set(k);
    37. result_set.init(&out_ids[0], &out_dists_sqr[0]);
    38. kdtree.index->findNeighbors(result_set, &points[i*dim], nanoflann::SearchParams(k));
    39. for (int j=0; j
    40. neighbors_all[i][j] = out_ids[j];
    41. }
    42. }
    43. auto t2 = std::chrono::steady_clock::now();
    44. delete []points;
    45. std::vector<int> flatten_neighbors_all(k * cloud_size);
    46. pcl::gpu::PtrStep<int> ps(&flatten_neighbors_all[0], k * pcl::gpu::PtrStep<int>::elem_size);
    47. for (size_t i=0; i
    48. std::copy(neighbors_all[i].begin(), neighbors_all[i].end(), ps.ptr(i));
    49. }
    50. pcl::gpu::NeighborIndices gpu_neighbor_indices;
    51. pcl::gpu::NormalEstimation::PointCloud gpu_cloud;
    52. gpu_cloud.upload(cloud->points);
    53. gpu_neighbor_indices.upload(flatten_neighbors_all, sizes, k);
    54. pcl::gpu::NormalEstimation::Normals gpu_normals;
    55. pcl::gpu::NormalEstimation::computeNormals(gpu_cloud, gpu_neighbor_indices, gpu_normals);
    56. pcl::gpu::NormalEstimation::flipNormalTowardsViewpoint(gpu_cloud, 0.f, 0.f, 0.f, gpu_normals);
    57. auto t3 = std::chrono::steady_clock::now();
    58. auto compute_normal_time = std::chrono::duration<double,std::milli>(t3 - t2);
    59. std::vector normals;
    60. gpu_normals.download(normals);
    61. auto t4 = std::chrono::steady_clock::now();
    62. auto knn_time = std::chrono::duration<double,std::milli>(t2-t1);
    63. auto total_time = std::chrono::duration<double,std::milli>(t4-t1);
    64. spdlog::info("cloud size:{:d}, knn_time:{:.3f} ms,compute_normal_time:{:.3f} ms, total_time:{:.3f} ms", cloud->size(), knn_time.count(), compute_normal_time.count(), total_time.count());
    65. };
    66. ros::Subscriber pc_sub = node.subscribe("/BackLidar/lslidar_point_cloud", 1, callback);
    67. ros::spin();
    68. return 0;
    69. }

    对于for循环遍历查找k近邻索引部分我们先不加"# pragma omp parallel for",编译运行。

      哇,加速效果明显,8w点云knn时间从400ms降到150ms左右。比pcl自带KDTree使用上OpenMP并行加速还要快。

    要是能利用OpenMP做并行加速,岂不是要起飞??!!加上OpenMP加速试一试。

    1. # pragma omp parallel for
    2. for (int i=0; i
    3. std::vector<size_t> out_ids(k);
    4. std::vector<float> out_dists_sqr(k);
    5. nanoflann::KNNResultSet<float> result_set(k);
    6. result_set.init(&out_ids[0], &out_dists_sqr[0]);
    7. kdtree.index->findNeighbors(result_set, &points[i*dim], nanoflann::SearchParams(k));
    8. for (int j=0; j
    9. neighbors_all[i][j] = out_ids[j];
    10. }
    11. }

     编译运行,测试结果如下:

    哇,虽然有波动,但常能在50ms左右徘徊,相比曾几何时的400ms提速了8倍。 多核算力分配也很均衡,完美!

     所以,knn加速哪家强,nanoflann+OpenMP当称王!!!

    【参考文献】

    在C++中使用openmp进行多线程编程_线上幽灵的博客-CSDN博客_c++ omp

    PCL GPU实现计算法线和曲率 - 知乎

  • 相关阅读:
    Docker 从构建开始导出一个镜像
    [开源]传统实体产业提供进销存管理解决方案,助传统企业降本增效
    了解什么是架构基本概念和架构本质
    配置文件和yaml语法
    ESP8266智能家居(4)——开发APP基础篇
    (224)Verilog HDL:设计摩尔状态机
    统计学习:逻辑回归与交叉熵损失(Pytorch实现)
    Linux网络编程基础
    Linux-线程同步(条件变量、POSIX信号量)
    Java 修饰符2
  • 原文地址:https://blog.csdn.net/ChuiGeDaQiQiu/article/details/128053094