《点云处理》平面拟合

前言

在众多点云处理算法中,其中关于平面拟合的算法十分广泛。本篇内容主要是希望总结归纳各类点云平面拟合算法,并且将代码进行梳理保存。

环境:

VS2019 + PCL1.11.1

1.RANSAC

使用ransac对平面进行拟合是非常常见的用法,PCL库中就有RANSAC拟合平面的实现代码,而且还集成了 两种拟合平面的代码。
方法一:

/// <summary>
/// 使用PCL中集成的RANSAC方法进行平面拟合
/// </summary>
/// <param name="cloud_in">输入待拟合的点云</param>
/// <param name="inliers">RANSAC拟合得到的内点</param>
/// <param name="coefficients">得到的平面方程参数</param>
/// <param name="iterations">平面拟合最大迭代次数</param>
/// <param name="threshold">RANSAC拟合算法距离阈值</param>
void RANSAC(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_in, std::vector<int>& inliers, Eigen::VectorXf& coefficients,const int& iterations, const double& threshold)
{inliers.clear();                                          // 用于存放内点索引的vectorpcl::shared_ptr<pcl::SampleConsensusModelPlane<pcl::PointXYZ>> model(new pcl::SampleConsensusModelPlane<pcl::PointXYZ>(cloud_in)); //定义待拟合平面的model,并使用待拟合点云初始化pcl::RandomSampleConsensus<pcl::PointXYZ> ransac(model);  // 定义RANSAC算法模型ransac.setDistanceThreshold(threshold);                   // 设定阈值ransac.setMaxIterations(iterations);                      // 设置最大迭代次数ransac.setNumberOfThreads(10);                            // 设置线程数量ransac.computeModel();                                    // 拟合 ransac.getInliers(inliers);                               // 获取内点索引Eigen::VectorXf params;                                   // 第一次得到的平面方程ransac.getModelCoefficients(params);                      // 获取拟合平面参数,对于平面ax+by_cz_d=0,params分别按顺序保存a,b,c,dmodel->optimizeModelCoefficients(inliers, params, coefficients);      // 优化平面方程参数std::vector<double> vDistance;                            // 用于存储每个点到拟合平面的距离的vector容器model->getDistancesToModel(coefficients, vDistance);      // 得到每个点到平面的距离的集合std::cout << "params: " << params[0] << ", " << params[1] << ", " << params[2] << ", " << params[3] << std::endl;std::cout << "coefficients: " << coefficients[0] << ", " << coefficients[1] << ", " << coefficients[2] << ", " << coefficients[3] << std::endl;
}

上述代码需要注意的是,一定需要model->optimizeModelCoefficients(inliers, params, coefficients); 通过这一句代码去优化平面参数。优化前后差别很大,如下图所示:
在这里插入图片描述
方法二:

/// <summary>
/// 使用PCL中集成的用于点云分割的RANSAC方法进行平面拟合
/// </summary>
/// <param name="cloud_in">输入待拟合的点云</param>
/// <param name="inliers">RANSAC拟合得到的内点</param>
/// <param name="coefficients">得到的平面方程参数</param>
/// <param name="iterations">平面拟合最大迭代次数</param>
/// <param name="threshold">RANSAC拟合算法距离阈值</param>
void SEG_RANSAC(const pcl::PointCloud<pcl::PointXYZ>::Ptr& cloud_in, pcl::PointIndices::Ptr& inliers, Eigen::VectorXf& coefficients,const int& iterations, const double& threshold)
{if (inliers == nullptr) inliers.reset(new pcl::PointIndices);pcl::ModelCoefficients::Ptr coefficients_m(new pcl::ModelCoefficients);pcl::SACSegmentation<pcl::PointXYZ> seg;seg.setOptimizeCoefficients(true);seg.setModelType(pcl::SACMODEL_PLANE);seg.setMethodType(pcl::SAC_RANSAC);seg.setMaxIterations(iterations);                     // 设置最大迭代次数seg.setDistanceThreshold(threshold);                  // 设定阈值seg.setNumberOfThreads(10);                           // 设置线程数量seg.setInputCloud(cloud_in);seg.segment(*inliers, *coefficients_m);coefficients.resize(4);coefficients[0] = coefficients_m->values[0]; coefficients[1] = coefficients_m->values[1];coefficients[2] = coefficients_m->values[2]; coefficients[3] = coefficients_m->values[3];std::cout << "SEG coefficients: " << coefficients[0] << ", " << coefficients[1] << ", " << coefficients[2] << ", " << coefficients[3] << std::endl;
}

方法二其实也提到了优化平面参数,seg.setOptimizeCoefficients(true);这句代码就是比较关键的,需要加上。
上述两种方法的运行结果如下,从结果和运行时间来看,两种方法几乎一致。
在这里插入图片描述
两种方法中都有一个设置线程的功能setNumberOfThreads(10); 但是这句话根本没有起到作用,下图中有提示[pcl::RandomSampleConsensus::computeModel] Parallelization is requested, but OpenMP 3.1 is not available! Continuing without parallelization.其实在VS中已经开启了openmp,也包含了头文件#include <omp.h>,只不过版本不对应,该问题也查阅了很久,没查到解决办法。

VS中开启openmp加速的配置方法:
在这里插入图片描述

2.最小二乘法拟合平面

除了RANSAC之外,另一种十分常见的拟合方程的方法是最小二乘法,这里就不过多介绍相关的理论知识,这里提到了相关的代码和原理

当然还是要贴上封装的C++代码实现:

/// <summary>
/// 使用最小二乘法拟合平面:Ax + By + Cz + D = 0 */   //拉格朗日乘子法 https://zhuanlan.zhihu.com/p/390294059
/// </summary>
/// <param name="xjData">输入待拟合平面的点云</param>
/// <param name="coefficients">输出拟合的平面方程</param>
/// <returns>输入点云点数符合要求,返回true,否则返回false</returns>
bool LeastSquare(const pcl::shared_ptr<pcl::PointCloud<pcl::PointXYZ>>& xjData, Eigen::VectorXf& coefficients)
{int count = xjData->points.size();if (count < 3) return false;double meanX = 0, meanY = 0, meanZ = 0;double meanXX = 0, meanYY = 0, meanZZ = 0;double meanXY = 0, meanXZ = 0, meanYZ = 0;for (int i = 0; i < count; i++){meanX += xjData->points[i].x;meanY += xjData->points[i].y;meanZ += xjData->points[i].z,meanXX += xjData->points[i].x * xjData->points[i].x;meanYY += xjData->points[i].y * xjData->points[i].y;meanZZ += xjData->points[i].z * xjData->points[i].z;meanXY += xjData->points[i].x * xjData->points[i].y;meanXZ += xjData->points[i].x * xjData->points[i].z;meanYZ += xjData->points[i].y * xjData->points[i].z;}meanX /= count; meanY /= count; meanZ /= count;meanXX /= count; meanYY /= count; meanZZ /= count;meanXY /= count; meanXZ /= count; meanYZ /= count;/* eigenvector */Eigen::Matrix3d eMat;eMat(0, 0) = meanXX - meanX * meanX; eMat(0, 1) = meanXY - meanX * meanY; eMat(0, 2) = meanXZ - meanX * meanZ;eMat(1, 0) = meanXY - meanX * meanY; eMat(1, 1) = meanYY - meanY * meanY; eMat(1, 2) = meanYZ - meanY * meanZ;eMat(2, 0) = meanXZ - meanX * meanZ; eMat(2, 1) = meanYZ - meanY * meanZ; eMat(2, 2) = meanZZ - meanZ * meanZ;Eigen::EigenSolver<Eigen::Matrix3d> xjMat(eMat);           // 求取矩阵特征值和特征向量的函数EigenSolverEigen::Matrix3d eValue = xjMat.pseudoEigenvalueMatrix();   // 获取矩阵伪特征值 3*3Eigen::Matrix3d eVector = xjMat.pseudoEigenvectors();      // 获取矩阵伪特征向量 3*1/* the eigenvector corresponding to the minimum eigenvalue */double v1 = eValue(0, 0); double v2 = eValue(1, 1); double v3 = eValue(2, 2);int minNumber = 0;if ((abs(v2) <= abs(v1)) && (abs(v2) <= abs(v3))){minNumber = 1;}if ((abs(v3) <= abs(v1)) && (abs(v3) <= abs(v2))){minNumber = 2;}double A = eVector(0, minNumber); double B = eVector(1, minNumber); double C = eVector(2, minNumber);double length = sqrt(A * A + B * B + C * C);A /= length; B /= length; C /= length;double D = -(A * meanX + B * meanY + C * meanZ);/* result */if (C < 0){A *= -1.0; B *= -1.0; C *= -1.0; D *= -1.0;}coefficients.resize(4);coefficients[0] = A; coefficients[1] = B; coefficients[2] = C; coefficients[3] = D;std::cout << "LS coefficients: " << coefficients[0] << ", " << coefficients[1] << ", " << coefficients[2] << ", " << coefficients[3] << std::endl;
}

同一份点云,使用最小二乘法拟合结果如下:
在这里插入图片描述
最小二乘法拟合平面方程和上述RANSAC拟合结果还是有些差别的,但是RANSAC运行时间约为0.02s,而最小二乘法运行时间需要0.0013s,最小二乘法运行时间要比RANSAC快很多。如果针对一份没有太多噪点,很平滑干净的点云,可以使用最小二乘法去拟合,但是如果是有噪点的点云,追求准确率的情况下,则需要使用RANSAC进行拟合。

3.SVD分解的方法求平面方程

可以通过Eigen实现使用SVD分解的方法进行平面方程求解

/// <summary>
/// 通过SVD分解的方法求拟合平面
/// </summary>
/// <param name="pcl_cloud">输入待拟合平面点云</param>
/// <param name="coefficients">输出拟合平面的参数</param>
void PlaneEigenSVD(const pcl::PointCloud<pcl::PointXYZ>& pcl_cloud, Eigen::VectorXf& coefficients)
{if (pcl_cloud.points.size() < 3) return;// Convert PCL point cloud to Eigen matrixEigen::Matrix<float, 3, Eigen::Dynamic> coord(3, pcl_cloud.size());for (size_t i = 0; i < pcl_cloud.size(); ++i){coord.col(i) << pcl_cloud[i].x, pcl_cloud[i].y, pcl_cloud[i].z;}// Calculate centroidEigen::Vector4f centroid;pcl::compute3DCentroid(pcl_cloud, centroid);// Subtract centroidcoord.row(0).array() -= centroid(0);coord.row(1).array() -= centroid(1);coord.row(2).array() -= centroid(2);// Perform singular value decomposition (SVD)Eigen::JacobiSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>> svd(coord, Eigen::ComputeThinU | Eigen::ComputeThinV);Eigen::Vector3f plane_normal = svd.matrixU().rightCols<1>();// Create PCL ModelCoefficientscoefficients.resize(4);coefficients[0] = plane_normal(0);coefficients[1] = plane_normal(1);coefficients[2] = plane_normal(2);coefficients[3] = -(plane_normal(0) * centroid(0) + plane_normal(1) * centroid(1) + plane_normal(2) * centroid(2));if (coefficients[2] < 0) coefficients = -coefficients;std::cout << "SVD coefficients: " << coefficients[0] << ", " << coefficients[1] << ", " << coefficients[2] << ", " << coefficients[3] << std::endl;return;
}

运行结果如下:
在这里插入图片描述
得到的结果其实和最小二乘法拟合得到的结果一样,但是耗时更长一些。

4.霍夫变换进行平面拟合

其实使用霍夫变换去拟合几何模型也是一件非常常见的方法,但是目前没有相关代码进行测试验证,如果后续有找到实现方法也会更新在这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/219500.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云部署k8s with kubesphere

阿里云ESC 创建实例 填入密码即可 云上的防火墙相关设置就是安全组 vpc 专有网络 划分私有ip 子网 vpc 隔离环境域 不同的vpc下 即使相同的子网也不互通 使用交换机继续划分子网 停止 释放 不收钱 k8s 服务器 4核8G*1 8核16G *2 git 创建凭证 pipeline 发邮箱 (p124)…

Word写大论文常见问题(持续更新)

脚注横线未定格 解决方案&#xff1a;“视图”-“草图”&#xff0c;“引用”-“显示备注”-选择“脚注分隔符”&#xff0c;把横线前的空格删掉。 2.PPT做的图插入word中清晰度太低 解决方案&#xff1a;PPT-图形-“另存为图片”-“可缩放矢量图格式”-粘贴到word中。

机器学习算法---异常检测

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…

【ArkTS】如何修改应用的首页

之前看到一种说法&#xff0c;说是应用首页是 entry > src > main > resources > base > profile > main_pages.json 中src配置中数组第一个路径元素。这种说法是不对的&#xff01;&#xff01;&#xff01; 如果需要修改应用加载时的首页&#xff0c;需要…

【Spring】09 BeanClassLoaderAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点&#xff0c;其中之一就是 Bean 生命周期中的回调接口。本文将聚焦于其中的一个接口 BeanClassLoaderAware&#xff0c;介…

动态规划优化技巧

一、斐波那契系列 1、滚动数组优化空间复杂度 2、dp数组初始化/处理边界优化 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台

设计模式 五种不同的单例模式 懒汉式 饿汉式 枚举单例 容器化单例(Spring单例源码分析) 线程单例

单例模式 第一种 饿汉式 优点&#xff1a;执行效率高&#xff0c;性能高&#xff0c;没有任何的锁 缺点&#xff1a;某些情况下&#xff0c;可能会造成内存浪费 /*** author LionLi*/ public class HungrySingleton {private static final HungrySingleton hungrySingleton n…

AIGC - 环境搭建

一. 硬件环境 1. 超微7048主板&#xff0c;最多可搭载4块GPU 2. 2个Intel的 Xen至强 14核 CPU 3. 目前安装了一块Nvidia 的P40 GPU&#xff0c;后续根据需要还最多可以扩展3块GPU 4. 4T机械 2T Nvme固态&#xff0c; 5. 4条64G DDR4内存条&#xff0c;共 196G内存…

docker版zerotier-planet服务端搭建

1&#xff1a;ZeroTier 介绍2&#xff1a;为什么要自建PLANET 服务器3&#xff1a;开始安装 3.1&#xff1a;准备条件 3.1.1 安装git3.1.2 安装docker3.1.3 启动docker 3.2&#xff1a;下载项目源码3.3&#xff1a;执行安装脚本3.4 下载 planet 文件3.5 新建网络 3.5.1 创建网络…

亚马逊云科技Amazon Bedrock,现推出更多模型选择和全新强大功能

亚马逊云科技在re:Invent 2023上宣布推出Amazon Bedrock更多模型选择和强大功能&#xff0c;帮助客户更轻松地构建和规模化针对其业务定制的生成式AI应用程序。 Amazon Bedrock是一项全面托管的服务&#xff0c;用户可轻松访问来自AI21 Labs、Anthropic、Cohere、Meta、Stabili…

【给C盘扩容】

用下面这个软件&#xff0c;给C盘分了20G的空间。 第一步&#xff1a; 此电脑-管理-从D盘分出20G的空余空间。 2、打开软件diskgenius 选择最上面的C盘&#xff0c;扩容分区&#xff0c;选择待分配的区域&#xff0c;点击确定&#xff0c;后面都点确定&#xff0c;等待分区完…

相机倾斜棋盘格标定全记录 vs200+opencv安装

论文参考是这个 Geiger A, Moosmann F, Car , et al. Automatic camera and range sensor calibration using a single shot[C]//Robotics and Automation (ICRA), 2012 IEEE International Conference on. IEEE, 2012: 3936-3943. 代码是这个github 花了一上午配好了c环境。。…

【数据结构—队列的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…

【vtkWidgetRepresentation】第十三期 VTKCaptionRepresentation

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享三维建模中的文字标注,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 目录 前言 1. vtkCaptionRepresentation

单机架构到分布式架构的演变

目录 1.单机架构 2.应用数据分离架构 3.应用服务集群架构 4.读写分离 / 主从分离架构 5.引入缓存 —— 冷热分离架构 6.垂直分库 7.业务拆分 —— 微服务 8.容器化引入——容器编排架构 总结 1.单机架构 初期&#xff0c;我们需要利用我们精干的技术团队&#xff0c;快…

C语言——预处理详解(#define用法+注意事项)

#define 语法规定 #define定义标识符 语法: #define name stuff #define例子 #include<stdio.h> #define A 100 #define STR "abc" #define FOR for(;;)int main() {printf("%d\n", A);printf("%s\n", STR);FOR;return 0; } 运行结果…

JRT实现在线打印预览

在JRT打印元素绘制协议一篇已经介绍过打印把绘图和打印逻辑进行了分离&#xff0c;这是和老设计最大的不同。因为老的设计时候没想着做在线预览功能&#xff0c;是后面硬性扩出来的。这次从最初设计就考虑绘图逻辑各处共用&#xff0c;包括打印预览&#xff0c;在线打印预览等、…

【案例】注册表简介,新建一个右键菜单打开方式选项

这里写目录标题 来源注册表的介绍注册表编辑器VScode的打开方式菜单![image-20231217201730121](https://img-blog.csdnimg.cn/img_convert/56c02643df9e8ec3afb4f3ac5cc0cdd5.png)如何自定义一个右键菜单备份注册表新建一个菜单选项”右键用记事本打开“ DWORDQWORD可扩充字符…

K8S(四)—pod详解

目录 pod介绍Pod的概念&#xff1a;Pod的特性&#xff1a;Pod的配置&#xff1a;Pod的控制&#xff1a;示例 YAML 文件&#xff1a; pod启动流程问题 两种方式启动镜像的升级和回滚更新 Deployment&#xff1a;回滚检查 Deployment 历史版本回滚到之前的修订版本缩放 Deploymen…

jmeter简单压测kafka

前言 这也是一个笔记&#xff0c;就是计划用jmeter做性能测试&#xff0c;但是这里是只要将数据放到kafka的topic里&#xff0c;后面查看下游业务处理能力。 一、方案 因为只要实现数据放到kafka&#xff0c;参考了下博友的方案&#xff0c;可行。 二、方案验证 详细过程就不…