【CGAL】Region_Growing 检测平面并保存

目录

  • 说明
  • 一、算法原理
  • 二、代码展示
  • 三、结果展示

说明

本篇博客主要介绍CGAL库中使用Region_Growing算法检测平面的算法原理、代码以及最后展示结果。其中,代码部分在CGAL官方库中提供了例子。我在其中做了一些修改,使其可以读取PLY类型的点云文件,并在检测到平面后,为属于同一个平面的点赋予相同的颜色。最后再保存为PLY文件以方便我们查看检测结果。

在CGAL中,Region_Growing算法不仅可以用来检测平面,还可以检测圆、直线、圆锥等基本的几何。除此之外,用户也可以自定义模型并使用算法检测。

环境

  • Win10/Win11
  • VS2022
  • CGAL 5.6.1

上述环境仅为运行此代码时的电脑环境。

一、算法原理

Region_Growing算法应用“贪心”的思想,利用种子点与邻居点的曲率差异来筛选点。在平面检测时,除了利用曲率差异,还会使用当前拟合平面与当前点的距离作为评判标准。具体看算法流程。

算法流程

  1. 选取种子点,若不指定种子点,则按索引顺序选取。
  2. 搜索种子点的邻居,可以按球形范围搜索,也可以按邻居个数搜索。
  3. 包含满足条件的邻居点。
  4. 在包含点中重复1-3。
  5. 如果区域内点数不再增加,并且还有未分类的点,则在未分类点中重新选取种子点。

参数

  • search_sphere_radius - 球形邻域搜索半径
  • max_distance_to_plane - 当前点到拟合平面的最大距离
  • max_accepted_angle - 当前点与拟合平面的角度阈值
  • min_region_size - 形成一个平面的最小点数

二、代码展示

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/IO/read_points.h>
#include <CGAL/property_map.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Shape_detection/Region_growing/Region_growing.h>
#include <CGAL/Shape_detection/Region_growing/Point_set.h>
#include <CGAL/Polygonal_surface_reconstruction.h>
#include <CGAL/IO/write_ply_points.h>#include <fstream>
#include <CGAL/Timer.h>
#include <boost/range/irange.hpp>
typedef CGAL::Exact_predicates_inexact_constructions_kernel        Kernel;
typedef Kernel::FT       FT;
typedef Kernel::Point_3         Point;
typedef Kernel::Vector_3 Vector;
// Point with normal, and plane index.
typedef boost::tuple<Point, Vector, int> PNI;
typedef std::vector<PNI> Point_vector;
typedef CGAL::Nth_of_tuple_property_map<0, PNI>        Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNI>        Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNI>        Plane_index_map;
using Point_map_region_growing = CGAL::Compose_property_map<CGAL::Random_access_property_map<Point_vector>, Point_map >;
using Normal_map_region_growing = CGAL::Compose_property_map<CGAL::Random_access_property_map<Point_vector>, Normal_map >;
using Region_type = CGAL::Shape_detection::Point_set::Least_squares_plane_fit_region<Kernel, std::size_t, Point_map_region_growing, Normal_map_region_growing>;
using Neighbor_query = CGAL::Shape_detection::Point_set::Sphere_neighbor_query<Kernel, std::size_t, Point_map_region_growing>;
using Region_growing = CGAL::Shape_detection::Region_growing<Neighbor_query, Region_type>;//----------------------------------save_points_with_color_about-------------------------------------------
typedef std::array<unsigned char, 4> Color;
typedef std::tuple<Point, Vector, Color> PNC;
typedef CGAL::Nth_of_tuple_property_map<0, PNC> Save_Point_map;
typedef CGAL::Nth_of_tuple_property_map<1, PNC> Save_Normal_map;
typedef CGAL::Nth_of_tuple_property_map<2, PNC> Save_Color_map;
//----------------------------------save_points_with_color_about-------------------------------------------// Define how a color should be stored
namespace CGAL {template< class F >struct Output_rep< ::Color, F > {const ::Color& c;static const bool is_specialized = true;Output_rep(const ::Color& c) : c(c){ }std::ostream& operator() (std::ostream& out) const{if (IO::is_ascii(out))out << int(c[0]) << " " << int(c[1]) << " " << int(c[2]) << " " << int(c[3]);elseout.write(reinterpret_cast<const char*>(&c), sizeof(c));return out;}};
} // namespace CGALint main(int argc, char* argv[])
{Point_vector points;// Load point set from a file.const std::string input_file = "cube_point_cloud.ply";std::ifstream input_stream(input_file.c_str());if (input_stream.fail()) {std::cerr << "Failed open file \'" << input_file << "\'" << std::endl;return EXIT_FAILURE;}input_stream.close();std::cout << "Loading point cloud: " << input_file << "...";CGAL::Timer t;t.start();if (!CGAL::IO::read_points(input_file.c_str(), std::back_inserter(points),CGAL::parameters::point_map(Point_map()).normal_map(Normal_map()))) {std::cerr << "Error: cannot read file " << input_file << std::endl;return EXIT_FAILURE;}elsestd::cout << " Done. " << points.size() << " points. Time: "<< t.time() << " sec." << std::endl;// Shape detection.// Default parameter values for the data file cube.pwn.const FT          search_sphere_radius = FT(2) / FT(100);const std::size_t k = 12;const FT          max_distance_to_plane = FT(0.5) / FT(100);const FT          max_accepted_angle = FT(30);const std::size_t min_region_size = 500;Point_map_region_growing point_map_rg(CGAL::make_random_access_property_map(points));Normal_map_region_growing normal_map_rg(CGAL::make_random_access_property_map(points));// Create instances of the classes Neighbor_query and Region_type.Neighbor_query neighbor_query(boost::irange<std::size_t>(0, points.size()), CGAL::parameters::sphere_radius(search_sphere_radius).point_map(point_map_rg));//Neighbor_query neighbor_query(//    boost::irange<std::size_t>(0, points.size()), CGAL::parameters::k_neighbors(k).point_map(point_map_rg));Region_type region_type(CGAL::parameters::maximum_distance(max_distance_to_plane).maximum_angle(max_accepted_angle).minimum_region_size(min_region_size).point_map(point_map_rg).normal_map(normal_map_rg));// Create an instance of the region growing class.Region_growing region_growing(boost::irange<std::size_t>(0, points.size()), neighbor_query, region_type);std::cout << "Extracting planes...";std::vector<typename Region_growing::Primitive_and_region> regions;t.reset();region_growing.detect(std::back_inserter(regions));std::cout << " Done. " << regions.size() << " planes extracted. Time: "<< t.time() << " sec." << std::endl;// Stores the plane index of each point as the third element of the tuple.for (std::size_t i = 0; i < points.size(); ++i)// Uses the get function from the property map that accesses the 3rd element of the tuple.points[i].get<2>() = static_cast<int>(get(region_growing.region_map(), i));// 随机生成颜色(要保证颜色种类大于提取的平面个数)std::vector<Color> rand_colors;for (size_t i = 0; i < regions.size() + 1; i++){Color p_color = {static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256),static_cast<unsigned char>(rand() % 256), 255 };rand_colors.push_back(p_color);}std::vector<PNC> points_with_color;// 为所属平面相同的点赋予相同的颜色for (std::size_t i = 0; i < points.size(); i++){// 获取单个点坐标Point point = points[i].get<0>();// 获得单个点法线Vector normal = points[i].get<1>();// 获取点对应的颜色,所属平面相同的点颜色相同int plane_index = points[i].get<2>();Color p_color;if (plane_index == -1)    // 未分配平面的点为白色p_color = { 255, 255, 255, 255 };elsep_color = rand_colors[plane_index];points_with_color.push_back(std::make_tuple(point, normal, p_color));}std::ofstream f("result.ply", std::ios::binary);CGAL::IO::set_binary_mode(f); // The PLY file will be written in the binary formatCGAL::IO::write_PLY_with_properties(f, points_with_color,CGAL::make_ply_point_writer(Save_Point_map()),CGAL::make_ply_normal_writer(Save_Normal_map()),std::make_tuple(Save_Color_map(),CGAL::IO::PLY_property<unsigned char>("red"),CGAL::IO::PLY_property<unsigned char>("green"),CGAL::IO::PLY_property<unsigned char>("blue"),CGAL::IO::PLY_property<unsigned char>("alpha")));return EXIT_SUCCESS;
}

三、结果展示

源文件:由点云组成的立方体
由点云组成的立方体
平面检测结果:
在这里插入图片描述

该立方体点云文件可以使用open3D生成。也可以在这里下载。

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

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

相关文章

【搭建大语言模型】使用LocalGPT搭建本地大语言模型服务并实现远程访问进行交互

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

设计模式15——享元模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 享元模式&#xff08;Flyweigh…

Linux信号:信号的概念及意义

目录 一、什么是信号 kill-l查看信号 二、信号的产生 2.1系统调用 kill raise abort 2.2软件条件 13)SIGPIPE pipe信号 14&#xff09;SIGAKARM alarm信号 2.2硬件中断 2.3异常 8)SIGFPE 除0异常 11)SIGSEGV 野指针 2.4信号处理的常见方式 三、Core Dump和term…

一文详解SaaS增长模式:PLG、MLG、SLG哪种更适合你?

在SaaS&#xff08;软件即服务&#xff09;的领域中&#xff0c;增长策略的选择对于企业的成功至关重要。其中&#xff0c;PLG&#xff08;产品驱动增长&#xff09;、MLG&#xff08;市场驱动增长&#xff09;和SLG&#xff08;销售驱动增长&#xff09;是三种常见的策略&…

Centos 7 安装刻录至服务器

前言 在日常测试中&#xff0c;会遇到很多安装的场景&#xff0c;今天给大家讲一下centos 7 的安装&#xff0c;希望对大家有所帮助。 一.下载镜像 地址如下&#xff1a; centos官方镜像下载地址https://www.centos.org/download/ 按照需求依次点击下载 二.镜像刻录 镜像刻…

强悍!轻量级 Viedo Download 项目!!【送源码】

今天给大家分享一个非常轻量实用的命令行视频下载项目&#xff1a;Lux。 项目简介 Lux是一款基于Golang编写的快速、简单的视频下载库和命令行工具&#xff0c;支持众多个流行的视频网站&#xff0c;包括 YouTube、Bilibili、优酷、爱奇艺、腾讯视频、抖音快手等。 界面简洁易…

重生之 SpringBoot3 入门保姆级学习(07、整合 Redis 案例)

重生之 SpringBoot3 入门保姆级学习&#xff08;07、整合 Redis 案例&#xff09; 导入 Maven 依赖并刷新 Maven <dependencies><!--springboot3 Web 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring…

高效至臻,Media Encoder 2024 for Mac——您的专业媒体处理首选

Media Encoder 2024 for Mac是一款专为Mac用户打造的专业视频和音频编码工具&#xff0c;凭借其卓越的编码能力和智能编辑功能&#xff0c;为用户提供了前所未有的媒体处理体验。无论是高清、4K还是8K的视频文件&#xff0c;Media Encoder 2024都能轻松驾驭&#xff0c;确保在压…

学习javascript的函数

1.什么是函数&#xff1f; 可以重复被使用的代码块 作用&#xff1a;函数可以把具有相同或者相似逻辑的代码“包裹起来”&#xff0c;有利于代码的复用。 2.函数的基本使用 1.定义函数 利用关键字Function 定义函数&#xff08;声明函数&#xff09; function 函数名(){函…

4、xss-labs之level4、lecel5

一、level4 1、测试分析 level4跟之前的3一样的思路&#xff0c;闭合value的值&#xff0c;但是通过双引号闭合&#xff0c;然后使用onclick的属性弹窗绕过 输入3的payload&#xff1a; 2、所以构造payload payload&#xff1a;"οnclickalert(1)// 二、level5 1、测…

uniapp高校二手书交易商城回收系统 微信小程序python+java+node.js+php

每年因为有大量的学生在接受教育&#xff0c;每到大学毕业季的时候&#xff0c;所使用的大量书籍对他们自己来说&#xff0c;很多是没有用&#xff0c;同时由于书籍多和不方便携带&#xff0c;导致很多大学生在毕业时将教材直接丢弃是在校大学生处理已用教材的一种主要方式。然…

数据结构-二叉树系统性学习(四万字精讲拿捏)

前言 这里我会从二叉树的概念开始讲解&#xff0c;其次涉及到概念结构&#xff0c;以及堆的实现和堆排序。 目的是&#xff0c;堆比二叉树简单&#xff0c;同时堆本质上是二叉树的其中一种情况&#xff0c;堆属于二叉树顺序结构的实现 最后完善二叉树的讲解&#xff0c;也就是…

Java与Gradle 的版本兼容性矩阵验证

1.下面这个表格显示了java和gradle的版本兼容性情况 2.根据上面这份表格理解&#xff0c;是不是java17就需要gradle 7.3之后来支持。用android studio 来试验一下: jdk选择: build成功: 说明JDK17并不是一定需要Gradle 7.3之后版本 3.使用JDK1.8、JDK11验证一下Grade 7.2是否可…

四款开源电子表格组件,轻松集成到你的项目

hello&#xff0c;大家好&#xff0c;我是徐小夕。之前和大家分享了很多可视化&#xff0c;零代码和前端工程化的最佳实践&#xff0c;最近在研究在线电子表格的技术实现&#xff0c;发现了几个优质的开源电子表格项目&#xff0c;这里和大家一起分享一下。 同时我也把其中一款…

太速科技-基于FPGA Spartan6 的双路光纤PCIe采集卡(2路光纤卡)

基于FPGA Spartan6 的双路光纤PCIe采集卡(2路光纤卡) 1、板卡概述   板卡采用xilinx Spartan6系列芯片&#xff0c;支持 PCI Express Base Specification 1.1 x1。内含丰富的逻辑资源和存储单元&#xff0c;板卡FPGA外接双片32M*16bit DDR2缓存器&#xff0c;支持乒乓操作。…

超好用的AI插件—Fitten code【IDEA 和 VScode 安装】

Fitten code安装 Fitten code是什么IDEA从磁盘安装Fitten codeVScode安装Fitten code Fitten code是什么 这是Fitten code官网&#xff0c;功能还是很多的&#xff0c;包括代码自动补全、自动添加注释、智能bug查找等等&#xff1a; Fitten Code是由非十大模型驱动的AI编程助手…

视频号小店应该怎么操作呢?不了解的小伙伴快来看!

大家好&#xff0c;我是电商小V 普通人想利用视频号小店赚钱&#xff0c;奈何自己不会直播&#xff0c;不会拍视频&#xff0c;那么普通人应该怎么在视频号中赚钱呢&#xff1f;今天咱们就来详细的说一下&#xff0c; 其实不直播&#xff0c;不拍视频也是可以在视频号上面卖货赚…

Microsoft Remote Desktop for Mac:你的跨平台远程连接利器

Microsoft Remote Desktop for Mac是一款专为Mac用户设计的远程桌面连接工具&#xff0c;它凭借卓越的性能和多项实用功能&#xff0c;成为了跨平台远程协作的首选工具。 多平台支持&#xff1a;Microsoft Remote Desktop for Mac不仅支持与其他Mac设备进行连接&#xff0c;还…

Linux入侵应急响应与排查

入侵者在入侵成功后&#xff0c;往往会留下后门以便再次访问被入侵的系统&#xff0c; 而创建系统账号是一种比较常见的后门方式。 查询特权用户特权用户(uid 为0) awk -F: $30{print $1} /etc/passwd 查找远程可以登录的账户 awk /\$1|\$5|\$6/{print $1} /etc/shadow $1&a…

Less语言

Less是一门预编译语言&#xff0c;它扩展了CSS语言&#xff0c;增加了变量、Mixin、函数等特性&#xff0c;使CSS更易维护和扩展 Less也扩充了CSS语言&#xff0c;增加了诸如变量、混合运算、函数等功能。Less既可以运行在服务端(Node.js和Rhino平台)也可以运行在客户端(浏览器…