PCL 基础计算【角度转换 + 质心 + 协方差矩阵】(Common_Angles + Common_Centroid)

PCL专栏目录及须知-优快云博客

1.角度转换<angles.h>

1)弧度转角度

#include <pcl/common/angles.h>
double angle = pcl::rad2deg(M_PI / 2);					// 弧度转角度

2)角度转弧度

#include <pcl/common/angles.h>
double radians = pcl::deg2rad(angle);					// 角度转弧度

3)弧度归一化到(-PI, PI]

#include <pcl/common/angles.h>
double normAngle = pcl::normAngle(M_PI * 3.87);			// 弧度归一化到(-PI, PI]

2.质心计算<centroid.h>

#include <pcl/common/centroid.h>
Eigen::Vector4f centroid;
pcl::compute3DCentroid(*cloud_in, centroid);

3.协方差矩阵计算<centroid.h>

#include <pcl/common/centroid.h>
Eigen::
<think>我们参考了Open3D中的hidden_point_removal方法,该方法来自论文[Katz2007]。在PCL中,虽然没有直接对应的函数,但我们可以通过以下步骤实现类似功能:1.计算点云的边界框,确定直径(diameter)2.设定相机位置(通常位于点云边界框外,例如在边界框中心的正上方或正下方,距离为直径的倍数)3.使用PCL的深度图生成功能(例如,通过创建RangeImage)来模拟从相机视角看到的点云深度,并据此判断点的可见性具体步骤:a)创建RangeImage:将点云投影到虚拟相机的成像平面上,生成深度图(距离图像)。b)对于点云中的每个点,根据其在深度图中的位置,判断其是否被遮挡(即是否有更近的点遮挡了它)。注意:PCL中有一个RangeImage类,可以用于生成深度图。然后,我们可以遍历点云中的每个点,将其投影到深度图上,检查该点是否在深度图中是可见的(即该点的深度值小于或等于深度图中对应像素的深度值,考虑一定的容差)。以下是一个大致的代码框架:```cpp#include<pcl/point_types.h>#include<pcl/range_image/range_image.h>#include<pcl/visualization/range_image_visualizer.h>#include<pcl/visualization/pcl_visualizer.h>#include<pcl/features/range_image_border_extractor.h>#include<pcl/keypoints/narf_keypoint.h>#include<pcl/filters/extract_indices.h>//定义点云类型typedefpcl::PointXYZPointType;//实现隐藏点移除pcl::PointCloud<PointType>::Ptrhidden_point_removal(constpcl::PointCloud<PointType>::Ptr&cloud,constEigen::Vector3f&camera_position,floatangular_resolution=0.5f){//计算点云直径(用于设置深度图的范围)Eigen::Vector4fmin_pt,max_pt;pcl::getMinMax3D(*cloud,min_pt,max_pt);floatdiameter=(max_pt-min_pt).norm();//设置深度图参数floatnoise_level=0.0;//噪声水平floatmin_range=0.0f;//最小距离floatmax_range=diameter*2;//最大距离(确保相机在点云外,所以最大范围设为直径的两倍)intsize_x,size_y;//深度图尺寸Eigen::Affine3fsensor_pose=Eigen::Affine3f::Identity();//设置相机位置(传感器位置)sensor_pose.translation()=camera_position;//注意:相机方向,这里默认相机朝向原点(点云中心),可以根据需要调整sensor_pose=sensor_pose*Eigen::AngleAxisf(-M_PI/2,Eigen::Vector3f::UnitY());//调整坐标系,使相机朝前(这里假设点云在相机下方,需要调整)//创建深度图pcl::RangeImagerange_image;range_image.createFromPointCloud(*cloud,angular_resolution,pcl::deg2rad(360.0f),pcl::deg2rad(180.0f),sensor_pose,pcl::RangeImage::CAMERA_FRAME,noise_level,min_range,max_range,size_x,size_y);//创建一个容器来存储可见点的索引pcl::Indicesvisible_indices;visible_indices.reserve(cloud->size());//遍历点云中的每个点,检查其在深度图中的可见性for(inti=0;i<cloud->size();++i){constPointType&point=cloud->points[i];//将点投影到深度图上的像素坐标intimage_x,image_y;floatrange;if(range_image.getImagePoint(point.getVector3fMap(),image_x,image_y,range)){//获取深度图中该像素的深度值floatrange_image_value=range_image.getRange(image_x,image_y);//如果点距离小于等于深度图记录的距离(考虑一定的容差),则该点可见if(range<=range_image_value+1e-4){visible_indices.push_back(i);}}}//提取可见点pcl::PointCloud<PointType>::Ptrvisible_cloud(newpcl::PointCloud<PointType>());pcl::ExtractIndices<PointType>extract;extract.setInputCloud(cloud);pcl::IndicesPtrindices_ptr(newstd::vector<int>(visible_indices));extract.setIndices(indices_ptr);extract.setNegative(false);extract.filter(*visible_cloud);returnvisible_cloud;}//使用示例intmain(){//读取点云pcl::PointCloud<PointType>::Ptrcloud(newpcl::PointCloud<PointType>);//...读取点云数据...//计算点云中心(用于设置相机位置)Eigen::Vector4fcentroid;pcl::compute3DCentroid(*cloud,centroid);Eigen::Vector3fcamera_position(centroid[0],centroid[1],centroid[2]);//初始在中心//将相机移动到点云外(例如,在点云上方,距离为直径的两倍)floatdiameter=...;//如前计算直径camera_position[2]+=diameter*2;//假设z轴向上,将相机放在点云上方//调用隐藏点移除pcl::PointCloud<PointType>::Ptrvisible_cloud=hidden_point_removal(cloud,camera_position);//可视化结果pcl::visualization::PCLVisualizerviewer("VisiblePoints");viewer.addPointCloud(visible_cloud,"visible_cloud");while(!viewer.wasStopped()){viewer.spinOnce();}return0;}```注意:上述代码是一个基本框架,实际使用时可能需要调整参数(如深度图的角分辨率、相机姿态等)以及处理边界情况(例如深度图中没有对应像素的点)。另外,深度图生成时坐标系的转换需要根据实际情况调整,确保相机的朝向正确。引用[^1]中提到的方法基于从给定视点生成一个虚拟球面投影,然后使用球面深度测试来确定点的可见性。在PCL中,我们使用平面深度图(RangeImage)来模拟类似的过程。此外,引用[^4]也展示了在Open3D中使用`hidden_point_removal`的示例,其参数设置中相机位置在点云边界框外,且半径(radius)设置得很大(比如直径的1000倍),这是因为Open3D的实现中,半径用于构造一个球面,而PCL的RangeImage是平面投影。因此,我们的实现是基于平面投影,可能不适用于360度全景,但可以通过调整深度图的视角范围(如360x180)来模拟全景。但是,请注意,Open3D的`hidden_point_removal`使用的是球面投影,而上述PCL实现使用的是平面投影。对于大视角(如360度)的情况,平面投影可能不适用,因此我们使用RangeImage的360x180度全景投影。在PCL中,我们通过设置`createFromPointCloud`的参数为360度和180度来创建全景深度图。这样,深度图就覆盖了整个球面,从而可以模拟从相机位置出发的所有方向。另外,上述代码中,我们假设相机坐标系为CAMERA_FRAME(Z轴向前,X轴向右,Y轴向下)。在设置相机姿态时,我们通过旋转调整使相机朝向点云中心。由于PCL的RangeImage生成函数在创建全景深度图时可能会消耗较多内存,因此对于大规模点云,可能需要调整角分辨率(angular_resolution)以平衡精度和性能。最后,我们通过比较点云中每个点与深度图中对应像素的深度值来判断可见性:如果该点的深度小于等于深度图中记录的深度(考虑浮点误差),则认为该点可见。这个实现与Open3D的方法在原理上类似,但具体实现细节可能有所不同。用户可以根据需要调整参数以获得最佳效果。§§相关问题§§1.隐藏点移除算法在点云处理中的主要应用场景是什么?2.除了隐藏点移除,还有哪些点云滤波或后处理技术可以改善点云的可视化效果?3.在PCL中,如何评估隐藏点移除算法的效果?4.隐藏点移除算法在处理大规模点云时可能会遇到性能问题,有哪些优化方法?</think>### 在PCL中实现隐藏点移除功能 在PCL中实现类似Open3D的隐藏点移除功能,核心思路是**通过深度图进行可见性分析**。以下是完整的C++实现方法: #### 实现步骤 1. **计算点云边界和相机位置** 2. **创建深度图模拟视角** 3. **生成可见点索引** 4. **提取可见点云** ```cpp #include <pcl/point_types.h> #include <pcl/range_image/range_image.h> #include <pcl/visualization/range_image_visualizer.h> #include <pcl/filters/extract_indices.h> pcl::PointCloud<pcl::PointXYZ>::Ptr hiddenPointRemoval( const pcl::PointCloud<pcl::PointXYZ>::Ptr& input_cloud, const Eigen::Vector3f& camera_position, float angular_resolution = 0.5f) { // 1. 计算点云直径 Eigen::Vector4f min_pt, max_pt; pcl::getMinMax3D(*input_cloud, min_pt, max_pt); const float diameter = (max_pt - min_pt).norm(); // 2. 创建深度图(范围图像) pcl::RangeImage range_image; const float noise_level = 0.0; const float min_range = 0.0f; const float max_range = diameter * 10; // 确保覆盖整个点云 // 设置相机姿态(朝向点云中心) Eigen::Affine3f sensor_pose = Eigen::Affine3f::Identity(); sensor_pose.translation() = camera_position; sensor_pose = sensor_pose * Eigen::AngleAxisf(-M_PI/2, Eigen::Vector3f::UnitY()); // 生成360度全景深度图 range_image.createFromPointCloud( *input_cloud, angular_resolution, pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), sensor_pose, pcl::RangeImage::CAMERA_FRAME, noise_level, min_range, max_range ); // 3. 检测可见点 std::vector<int> visible_indices; for (int i=0; i<input_cloud->size(); ++i) { const auto& point = input_cloud->points[i]; int image_x, image_y; float range; if (range_image.getImagePoint(point.getVector3fMap(), image_x, image_y, range)) { // 比较实际深度与深度图记录 if (range <= range_image.getRange(image_x, image_y) + 1e-4) { visible_indices.push_back(i); } } } // 4. 提取可见点 pcl::PointIndices::Ptr inliers(new pcl::PointIndices); inliers->indices = visible_indices; pcl::ExtractIndices<pcl::PointXYZ> extract; extract.setInputCloud(input_cloud); extract.setIndices(inliers); extract.setNegative(false); pcl::PointCloud<pcl::PointXYZ>::Ptr visible_cloud(new pcl::PointCloud<pcl::PointXYZ>); extract.filter(*visible_cloud); return visible_cloud; } // 使用示例 int main() { // 读取点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile("input.pcd", *cloud); // 计算相机位置(置于点云上方) Eigen::Vector4f centroid; pcl::compute3DCentroid(*cloud, centroid); Eigen::Vector3f camera_position( centroid[0], centroid[1], centroid[2] + 5.0 // 偏移高度 ); // 执行隐藏点移除 auto visible_cloud = hiddenPointRemoval(cloud, camera_position); // 保存结果 pcl::io::savePCDFile("visible_points.pcd", *visible_cloud); } ``` #### 关键参数说明 1. **相机位置**:需设置在点云边界外,通常沿Z轴偏移 2. **深度图参数**: - `angular_resolution`:角度分辨率(度/像素),值越小精度越高 - `max_range`:应大于点云直径,确保覆盖所有点 3. **可见性判断**:比较点在深度图中的投影距离与实际距离 #### 与Open3D实现的对比 | 特性 | Open3D实现[^1] | PCL实现 | |--------------|--------------------------|-----------------------| | 核心算法 | 球面投影法[Katz2007] | 深度图可见性分析 | | 输入要求 | 无需法线/重建 | 无需预处理 | | 计算效率 | 较高 | 依赖深度图分辨率 | | 视角范围 | 360°全景 | 可调(支持全景) | | 输出类型 | 点云索引 | 过滤后的点云 | #### 应用场景 此方法适用于: 1. 点云预处理:移除背景噪声点 2. 三维重建:保留可见表面点 3. 机器人导航:生成可通行区域 4. 虚拟视角渲染:特定角度的场景重建[^4] --- ### 相关问题 1. **如何优化PCL隐藏点移除算法的性能?** - 降低深度图分辨率(增大`angular_resolution`) - 使用八叉树加速空间搜索 - 并行化深度图生成过程 2. **点云处理中还有哪些类似的后处理技术?** - 统计滤波:移除离群点 - 半径滤波:基于密度的点云精简 - 凸包计算:提取外部轮廓[^5] - 基于法线的分割:分离不同表面 3. **如何评估隐藏点移除算法的效果?** - 可见性比率:`可见点数/总点数` - 边界保留度:检查特征边缘完整性 - 计算时间:处理百万级点云耗时 - 重建误差:与原始模型对比 4. **在动态场景中如何处理隐藏点移除?** - 结合SLAM获取相机位姿 - 多帧点云融合 - 实时深度图生成(需GPU加速) - 运动物体分割预处理[^2]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值