点云曲面重建

news2024/11/18 5:51:38

        曲面重建技术在逆向工程、数据可视化、机器视觉、虚拟现实、医疗技术等领域中得到了广泛的应用 。 例如,在汽车、航空等工业领域中,复杂外形产品的设计仍需要根据手工模型,采用逆向工程的手段建立产品的数字化模型,根据测量数据建立人体以及骨骼和器官的计算机模型,在医学、定制生产等方面都有重要意义 。

        除了上述传统的行业,随着新兴的廉价 RGBD 获取设备在数字娱乐行业的病毒式扩展,使得更多人开始使用点云来处理对象并进行工程应用 。 根据重建曲面和数据点云之间的关系,可将曲面重建分为两大类:插值法和逼近法。前者得到的重建曲面完全通过原始数据点,而后者则是用分片线性曲面或其他形式的曲面来逼近原始数据点,从而使得得到的重建曲面是原始点集的一个逼近曲面。

关联算法:Search、KdTree、Octree

基于多项式重构的平滑和法线估计

        在测量较小的数据时会产生一些误差,这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有漏洞,可以采用对数据重采样来解决这样问题,通过对周围的数据点进行高阶多项式插值来重建表面缺少的部分。

        上图左侧,在包含两个配准点云的数据集中看到了配准后的效果及表面法线估计。由于对齐错误,所产生的法线有噪声。在右侧,使用移动最小二乘法对表面法线估计进行平滑处理后,在同一数据集中看到了该效果。绘制每个点的曲率,作为重新采样前后特征值关系的度量,得到:

代码实现

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/kdtree.h>  //kd-tree搜索对象的类定义的头文件
#include <pcl/surface/mls.h>        //最小二乘法平滑处理类定义头文件
#include <pcl/visualization/cloud_viewer.h>
#include <boost/thread/thread.hpp>

int main(int argc, char** argv) 
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
    // 从文件读取点云图
    pcl::PCDReader reader;
    reader.read("G:/vsdata/PCLlearn/PCDdata/bun0.pcd", *cloud);

    // 创建 KD-Tree
    pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);

    // 输出具有PointNormal类型,以便存储MLS计算的法线
    pcl::PointCloud<pcl::PointNormal>::Ptr mls_points(new pcl::PointCloud<pcl::PointNormal>);

    // Init对象(第二种点类型用于法线,即使未使用)
    pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;

    mls.setComputeNormals(true);

    // 设置参数
    mls.setInputCloud(cloud);
    mls.setPolynomialOrder(2);
    mls.setSearchMethod(tree);
    mls.setSearchRadius(0.1);

    // 重建
    mls.process(*mls_points);

    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("before"));
    viewer1->addPointCloud<pcl::PointXYZ>(cloud, "before");
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("smoothed"));
    viewer->addPointCloud<pcl::PointNormal>(mls_points, "smoothed");
    while (!viewer->wasStopped()) {
        viewer->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(1000));
    }
    // 保存
    pcl::io::savePCDFile("bun0-mls.pcd", *mls_points);
}

实现效果

在平面模型上提取凸(凹)多边形

        该实例先从点云中提取平面模型,再通过该估计的平面模型系数从滤波后的点云投影一组点集形成点云,最后为投影后的点云计算其对应的二维凸多边形。

代码实现

#include <pcl/ModelCoefficients.h>           //采样一致性模型相关类头文件
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/filters/passthrough.h>
#include <pcl/filters/project_inliers.h>          //滤波相关类头文件
#include <pcl/segmentation/sac_segmentation.h>   //基于采样一致性分割类定义的头文件
#include <pcl/surface/concave_hull.h>                 //创建凹多边形类定义头文件
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>

int main(int argc, char** argv)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>), cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>), cloud_projected(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PCDReader reader;
    reader.read("G:/vsdata/PCLlearn/PCDdata/table_scene_mug_stereo_textured.pcd", *cloud);
    // 创建过滤器消除杂散的NaN
    pcl::PassThrough<pcl::PointXYZ> pass;
    pass.setInputCloud(cloud);                  //设置输入点云
    pass.setFilterFieldName("z");             //设置分割字段为z坐标
    pass.setFilterLimits(0, 1.1);             //设置分割阀值为(0, 1.1)
    pass.filter(*cloud_filtered);
    std::cerr << "PointCloud after filtering has: "
        << cloud_filtered->points.size() << " data points." << std::endl;

    pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
    pcl::PointIndices::Ptr inliers(new pcl::PointIndices);   //inliers存储分割后的点云
    // 创建分割对象
    pcl::SACSegmentation<pcl::PointXYZ> seg;
    // 设置优化系数,该参数为可选参数
    seg.setOptimizeCoefficients(true);
    // 强制性的
    seg.setModelType(pcl::SACMODEL_PLANE);
    seg.setMethodType(pcl::SAC_RANSAC);
    seg.setDistanceThreshold(0.01);

    seg.setInputCloud(cloud_filtered);
    seg.segment(*inliers, *coefficients);
    std::cerr << "PointCloud after segmentation has: "
        << inliers->indices.size() << " inliers." << std::endl;

    // 投影模型inliers
    pcl::ProjectInliers<pcl::PointXYZ> proj;//点云投影滤波模型
    proj.setModelType(pcl::SACMODEL_PLANE); //设置投影模型
    proj.setIndices(inliers);
    proj.setInputCloud(cloud_filtered);
    proj.setModelCoefficients(coefficients);      //将估计得到的平面coefficients参数设置为投影平面模型系数
    proj.filter(*cloud_projected);            //得到投影后的点云
    std::cerr << "PointCloud after projection has: "
        << cloud_projected->points.size() << " data points." << std::endl;

    // 存储提取多边形上的点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_hull(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::ConcaveHull<pcl::PointXYZ> chull;        //创建多边形提取对象
    chull.setInputCloud(cloud_projected);       //设置输入点云为提取后点云
    chull.setAlpha(0.1);
    chull.reconstruct(*cloud_hull);   //创建提取创建凹多边形

    std::cerr << "Concave hull has: " << cloud_hull->points.size()
        << " data points." << std::endl;

    pcl::PCDWriter writer;
    writer.write("table_scene_mug_stereo_textured_hull.pcd", *cloud_hull, false);
    // 可视化
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("before"));
    viewer->addPointCloud<pcl::PointXYZ>(cloud, "before");
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("filtered"));
    viewer1->addPointCloud<pcl::PointXYZ>(cloud_filtered, "filtered");
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer2(new pcl::visualization::PCLVisualizer("projected"));
    viewer2->addPointCloud<pcl::PointXYZ>(cloud_projected, "projected");
    boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer3(new pcl::visualization::PCLVisualizer("hull"));
    viewer3->addPointCloud<pcl::PointXYZ>(cloud_hull, "hull");

    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(1000));
    }
    return (0);
}

 结果:

 原始点云

滤波后点云(直通滤波)

 分割平面

多边形 

无序点云的快速三角化

        使用贪婪投影三角化算法对有向点云进行三角化,具体方法是:

        (1)先将有向点云投影到某一局部二维坐标平面内

        (2)在坐标平面内进行平面内的三角化

        (3)根据平面内三位点的拓扑连接关系获得一个三角网格曲面模型

        贪婪投影三角化算法原理:是处理一系列可以使网格“生长扩大”的点(边缘点)延伸这些点直到所有符合几何正确性和拓扑正确性的点都被连上,该算法可以用来处理来自一个或者多个扫描仪扫描到得到并且有多个连接处的散乱点云但是算法也是有很大的局限性,它更适用于采样点云来自表面连续光滑的曲面且点云的密度变化比较均匀的情况。

代码实现

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/features/normal_3d.h>
#include <pcl/surface/gp3.h>      //贪婪投影三角化算法
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>

int main(int argc, char** argv)
{
	// 将一个XYZ点类型的PCD文件打开并存储到对象中
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PCLPointCloud2 cloud_blob;
	pcl::io::loadPCDFile("G:/vsdata/PCLlearn/PCDdata/bun0.pcd", cloud_blob);
	pcl::fromPCLPointCloud2(cloud_blob, *cloud);

	// 正态估计
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n;   //法线估计对象
	pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);   //存储估计的法线
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);  //定义kd树指针
	tree->setInputCloud(cloud);   //用cloud构建tree对象
	n.setInputCloud(cloud);
	n.setSearchMethod(tree);
	n.setKSearch(20);
	n.compute(*normals);   //估计法线存储到其中
	//* 法线不应包含点法线+曲面曲率

	// 连接XYZ字段和法线字段*
	pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
	pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);    //连接字段
	//* cloud_with_normals = cloud + normals

	// 定义搜索树对象
	pcl::search::KdTree<pcl::PointNormal>::Ptr tree2(new pcl::search::KdTree<pcl::PointNormal>);
	tree2->setInputCloud(cloud_with_normals);   //点云构建搜索树

	// 初始化对象
	pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;   //定义三角化对象
	pcl::PolygonMesh triangles;                //存储最终三角化的网络模型

	// 设置连接点之间的最大距离(最大边长)
	gp3.setSearchRadius(0.025);  //设置连接点之间的最大距离,(即是三角形最大边长)

	// 设置各参数值
	gp3.setMu(2.5);  //设置被样本点搜索其近邻点的最远距离为2.5,为了使用点云密度的变化
	gp3.setMaximumNearestNeighbors(100);    //设置样本点可搜索的邻域个数
	gp3.setMaximumSurfaceAngle(M_PI / 4); // 设置某点法线方向偏离样本点法线的最大角度45
	gp3.setMinimumAngle(M_PI / 18); // 设置三角化后得到的三角形内角的最小的角度为10
	gp3.setMaximumAngle(2 * M_PI / 3); // 设置三角化后得到的三角形内角的最大角度为120
	gp3.setNormalConsistency(false);  //设置该参数保证法线朝向一致

	// 获取结果
	gp3.setInputCloud(cloud_with_normals);     //设置输入点云为有向点云
	gp3.setSearchMethod(tree2);   //设置搜索方式
	gp3.reconstruct(triangles);  //重建提取三角化

	// 附加顶点信息
	std::vector<int> parts = gp3.getPartIDs();
	std::vector<int> states = gp3.getPointStates();

	// 可视化
	// 创建可视化对象
	pcl::visualization::PCLVisualizer viewer("PolygonMesh Viewer");

	// 添加网格到可视化窗口
	viewer.addPolygonMesh(triangles, "mesh");

	// 设置观察点和方向
	viewer.setCameraPosition(0, 0, -2, 0, -1, 0);

	while (!viewer.wasStopped())
	{
		viewer.spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(1000));
	}
	return (0);
}

结果

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时代风口中的Web3.0基建平台,重新定义Web3.0!

近年来&#xff0c;Web3.0概念的广泛兴起&#xff0c;给加密行业带来了崭新的叙事方式&#xff0c;同时也为加密行业提供了更加具有想象力的应用场景与商业空间&#xff0c;并让越来越多的行业从业者们意识到只有更大众化的市场共性需求才能推动加密市场的持续繁荣。当前围绕这…

软件测试/测试开发丨App自动化—高级控件交互方法

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27687 一、Actions Actions执行一系列或多个键盘和指针&#xff08;触摸、鼠标、触控笔&#xff09;操作链w3c 二、用法 定义 ActionChains 实例定义输…

种草文案怎么写吸引人?纯干货

种草文案的最终目的是激发用户的购买欲望&#xff0c;使他们产生购买行为。为了实现这一目标&#xff0c;种草文案应当抓住用户的痛点&#xff0c;突出产品的优势&#xff0c;让用户感受到产品的实用价值。此外&#xff0c;种草文案还应注重情感诉求&#xff0c;通过生动的故事…

Bean的作用域和生命周期(Bean是线程安全的吗?Spring如何在并发情况下获取不完整的Bean...)

Bean 注解是 Spring 框架中的一个注解&#xff0c;用于告诉 Spring 容器需要将被注解修饰的方法的返回值注册为一个 Bean。通常情况下&#xff0c;Spring 容器会自动扫描并创建带有 Component 或其他类似注解的类&#xff0c;并将这些类的实例注册为 Bean。但对于某些特殊情况&…

iPhone手机上使用的定时提醒APP是哪个

在日常喧闹的生活和工作中&#xff0c;琐碎的任务会像喷泉一样突涌而至&#xff0c;如不及时规划&#xff0c;我们将陷入手足无措的境地。而想要让各项工作任务按时完成&#xff0c;我们可以借助一些比较好用的时间提醒软件来督促各项任务。 就拿常用的iPhone手机来讲&#xf…

Tomcat隔离web原理和热加载热部署

Tomcat 如何打破双亲委派机制 Tomcat 的自定义类加载器 WebAppClassLoader 打破了双亲委派机制&#xff0c;它首先自己尝试去加载某个类&#xff0c;如果找不到再代理给父类加载器&#xff0c;其目的是优先加载 Web 应用自己定义的类。具体实现就是重写 ClassLoader 的两个方法…

九小场所“一店一码”消防安全监管如何制作

将九小场所信息在凡尔码平台生成消防安全码落地粘贴场所&#xff0c;微信扫码了解场所以往消防安全检查情况、整改情况&#xff1b;同时也可以了解学习消防安全知识&#xff1b;“一店一码”实现场所消防安全动态管理。 监管部门检查微信扫码即可填写检查记录&#xff0c;有隐…

【智能家居项目】裸机版本——网卡设备接入输入子系统 | 业务子系统 | 整体效果展示

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f95e;网卡设备接入输入子系统&#x1f354;测试 &#x1f95e;业务子系统&#…

Astronomaly:利用 CNN 和主动学习识别 400 万张星系图像中的异常

星系中的异常现象是我们了解宇宙的关键。然而&#xff0c;随着天文观测技术的发展&#xff0c;天文数据正以指数级别增长&#xff0c;超出了天文工作者的分析能力。 尽管志愿者可以在线上参与对天文数据的处理&#xff0c;但他们只能进行一些简单的分类&#xff0c;还可能会遗漏…

数字孪生与GIS数据为何高度互补?二者融合后能达到什么样的效果?

山海鲸可视化作为一款数字孪生软件&#xff0c;在GIS的融合方面处于业内领先水平&#xff0c;那么为什么一款数字孪生软件要花费巨大的精力&#xff0c;去实现GIS的融合&#xff0c;实现后又能达到什么样的效果呢&#xff1f;下面就让我们来一探究竟。 一、为什么数字孪生需要…

OpenCV级联分类器识别车辆实践笔记

1. OpenCV 级联分类器的基本原理 基于Haar特征的级联分类器的目标检测是Paul Viola和Michael Jones在2001年的论文中提出的一种有效的目标检测方法。这是一种基于机器学习的方法&#xff0c;从大量的正面和负面图像中训练级联函数。然后用它来检测其他图像中的物体。 Haar特征…

终于找到了!多种类型的电子期刊模板在这里!

经过我不懈的努力和搜寻&#xff0c;终于找到了一个提供多种类型电子期刊模板的网站。这个网站拥有丰富多样的模板&#xff0c;可以满足各种不同的需求&#xff0c;无论是学术研究、商业报告还是个人兴趣爱好&#xff0c;都能在这里找到心仪的模板。 一、网站介绍 这个网站叫做…

弧形进度条,弧形百分比

要帮助同事写一个弧度的进度条&#xff0c;进度条顶部有一个小圆&#xff0c;具体如下 需要指出的是&#xff0c;我们canvas的绘制是需要弧度&#xff0c;所以我们代码中使用角度&#xff0c;等待绘制的时候再砖话为弧度值 <!DOCTYPE html> <html lang"en"…

【MATLAB源码-第45期】基于matlab的16APSK调制解调仿真,使用卷积编码软判决。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 16APSK调制解调 16APSK (16-ary Amplitude Phase Shift Keying) 是一种相位调制技术&#xff0c;其基本思想是在恒定幅度的条件下&#xff0c;改变信号的相位&#xff0c;从而传送信息。 - 调制&#xff1a;在16APSK中&am…

【angular】TodoList小项目(已开源)

参考&#xff1a;https://segmentfault.com/a/1190000013519099 文章目录 准备工作headerTodo、Doing、Done样式&#xff08;HTMLCSS&#xff09;功能&#xff08;TS&#xff09;将输入框内容加入todoList&#xff08;addTodo&#xff09;将todo事件改到doing 服务 参考 效果&a…

C语言基础 C++

C语言基础 C 嘿&#xff0c;你想起点C基础知识&#xff0c;这是一个很好的开始&#xff01;C是一种通用的编程语言&#xff0c;被广泛用于开发各种应用程序&#xff0c;从简单的控制台程序到复杂的桌面应用和游戏开发。现在让我带你进入C的奇妙世界&#xff0c;看看它有什么特…

OBIA:900+ 患者、193w+ 影像,中科院基因组所发布我国首个生物影像共享数据库

看病就医&#xff0c;拍片已是常例。CT、核磁、X 光等影像资料可以用非侵入式手段透过人体&#xff0c;使内部器官、组织状况清晰可见&#xff0c;为临床诊断和疾病治疗提供可靠依据。 随着医学影像技术广泛发展&#xff0c;影像资料已占据国内医疗数据的 80% 以上&#xff0c…

Android平台GB28181设备接入侧如何实现SIP校时

规范解读 GB/T28181-2016规范里面&#xff0c;9.10.1章节&#xff0c;关于校时基本要求&#xff1a; 联网内设备支持基于SIP方式或 NTP方式的网络校时功能&#xff0c;标准时间为北京时间。 SIP方式校时见本节具体描述&#xff1b;NTP(见IETFRFC2030)协议的网络统一校时服务…

【通信系列 2 -- 射频电路介绍】

文章目录 1.1 射频电路介绍1.1.1 射频电路的原理1.1.2 射频电路组成和特点 1.1 射频电路介绍 射频&#xff08;RF&#xff09;是Radio Frequency的缩写&#xff0c;表示可以辐射到空间的电磁波频率&#xff0c;频率范围从300kHz&#xff5e;300GHz之间。射频就是射频电流&…

美瞳小程序经营配送商城的作用是什么

美瞳是不少小姑娘喜爱的产品&#xff0c;线上线下需求都比较旺盛&#xff0c;尤其是新款或极其漂亮的产品往往会成为疯抢的对象&#xff0c;当然市场高需求的同时商家也面临着一些难题。 通过【雨科】平台搭建美瞳商城小程序&#xff0c;将所有产品线上售卖&#xff0c;摆脱第三…