PCL 计算字符型ply文件的法向量

news2024/12/23 5:38:22

文章目录

  • ply格式
  • 计算法向量意义
  • 具体代码

ply格式

PLY(Polygon File Format)是一种用于存储三维模型数据的文件格式。在PLY文件中,法向量是指每个顶点或面片的法向量,用于描述表面的朝向和光照计算。

在PLY文件中,法向量的定义通常位于文件头部的元数据(header)中。具体而言,法向量可以通过以下两种方式定义:

1.顶点法向量(Vertex Normals):对于每个顶点,可以指定一个法向量来描述该顶点周围的表面朝向。通常以属性名称"nx", “ny”, "nz"表示,表示顶点法向量在x、y、z轴上的分量。

2.面片法向量(Face Normals):对于每个面片(三角形或四边形),可以指定一个法向量来描述该面片的朝向。通常以属性名称"nx", “ny”, "nz"表示,表示面片法向量在x、y、z轴上的分量。

计算法向量意义

1.表面重建和拟合通过计算点云中每个点的法向量,可以对表面进行重建和拟合操作。法向量可用于估计点云中点的法线方向,从而得到平滑的表面拟合结果,用于生成三维模型或重建真实世界物体的几何形状。
2.物体识别和分类点云法向量可以用于物体的识别和分类。通过分析点云中每个点的法向量,可以提取表面的几何特征,如曲率、角度变化等,用于区分不同物体或物体的不同部分。
3.特征提取和描述点云法向量可用于特征提取和描述。通过计算点云中每个点的法向量,可以获得点云的局部几何信息。这些法向量可以与其他特征(如表面法线直方图、曲率等)结合使用,用于点云的特征提取、配准、匹配和分类等任务。
4.表面分析和曲面平滑点云法向量可以提供有关点云表面的局部几何信息,如表面的曲率、凹凸性等。这些信息可用于表面分析、曲面平滑和去噪等操作,帮助改善点云数据的质量和几何细节。

具体代码

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <cmath>

//定义一个名为Vec3的结构体,用于表示三维向量,并提供向量操作,向量减法、叉积和归一化
struct Vec3 {
    float x, y, z;

    Vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}

    Vec3 operator-(const Vec3& other) const {
        return Vec3(x - other.x, y - other.y, z - other.z);
    }

    Vec3 cross(const Vec3& other) const {
        return Vec3(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);
    }

    void normalize() {
        float length = std::sqrt(x * x + y * y + z * z);
        x /= length;
        y /= length;
        z /= length;
    }
};

//定义一个Face的结构体,用于表示面,其中包含了一个保存顶点索引的整数向量
struct Face {
    std::vector<int> vertex_indices;
};


bool readPLYFile(const std::string& file_path, std::vector<Vec3>& vertices, std::vector<Face>& faces) {
    std::ifstream ply_file(file_path);
    if (!ply_file.is_open()) {
        std::cerr << "Error: Could not open the PLY file." << std::endl;
        return false;
    }

    //解析文件头:通过逐行读取文件内容,循环解析文件的头部信息。头部通常包括描述顶点数量和面数量的元数据以及其他元数据。这些信息以文本行的形式存储在文件中。
    std::string line;
    while (std::getline(ply_file, line)) {
        //如果在某一行中找到了包含"element vertex"的字符串,
        //就提取出顶点数量并分配空间给vertices向量,以便存储顶点数据。
        if (line.find("element vertex") != std::string::npos) {
            int num_vertices;
            std::istringstream iss(line.substr(line.find_last_of(" ") + 1));
            iss >> num_vertices;
            vertices.reserve(num_vertices);
        }
        else if (line.find("element face") != std::string::npos) {//如果在某一行中找到了包含"element face"的字符串,
                                                                  //就提取出面的数量并分配空间给faces向量,以便存储面的数据。
            int num_faces;
            std::istringstream iss(line.substr(line.find_last_of(" ") + 1));
            iss >> num_faces;
            faces.reserve(num_faces);
        }
        else if (line == "end_header") {//如果在某一行中找到了"end_header"字符串,表示头部信息结束,跳出循环,开始读取顶点和面数据。
            break; 
        }
    }

    //读取顶点数据:使用一个循环,按照顶点的数量逐个读取文件中的顶点数据。每个顶点通常由三个浮点数(x、y、z坐标)表示,所以从文件中读取这三个浮点数,并将它们作为Vec3对象添加到vertices向量中
    for (int i = 0; i < vertices.capacity(); ++i) {
        float x, y, z;
        ply_file >> x >> y >> z;
        vertices.emplace_back(x, y, z);
    }

    //读取面数据:使用另一个循环,按照面的数量逐个读取文件中的面数据。每个面通常包括一个整数(表示该面的顶点数)和一组顶点索引。在循环内,首先读取面的顶点数,然后读取相应数量的顶点索引,并将它们作为Face对象添加到faces向量中
    for (int i = 0; i < faces.capacity(); ++i) {
        int num_vertices;
        ply_file >> num_vertices;

        Face face;
        for (int j = 0; j < num_vertices; ++j) {
            int vertex_index;
            ply_file >> vertex_index;
            face.vertex_indices.push_back(vertex_index);
        }

        faces.push_back(face);
    }

    ply_file.close();
    return true;
}


void calculateAndPrintNormals(const std::vector<Vec3>& vertices, const std::vector<Face>& faces) {
    for (const Face& face : faces) {//循环遍历faces向量:通过for循环,迭代遍历存储了面数据的faces向量,其中每个元素都是一个Face结构体,表示一个面
        if (face.vertex_indices.size() >= 3) {//检查面的顶点数量:对于每个面,首先检查其顶点索引数量是否大于等于3。这是因为计算法向矢量通常需要至少3个顶点来定义一个平面。
            //从vertices向量中提取出这些顶点的坐标信息。这些坐标信息存储在Vec3结构体中。
            const Vec3& v1 = vertices[face.vertex_indices[0]];
            const Vec3& v2 = vertices[face.vertex_indices[1]];
            const Vec3& v3 = vertices[face.vertex_indices[2]];
            //计算两个边向量:使用提取的顶点信息计算两个边向量,edge1和edge2。这些边向量是面的两条边的矢量差。
            Vec3 edge1 = v2 - v1;
            Vec3 edge2 = v3 - v1;
            //计算法向矢量:通过对边向量使用叉积运算计算法向矢量normal。法向矢量表示了面的朝向和方向。
            Vec3 normal = edge1.cross(edge2);
            normal.normalize();//归一化法向矢量:通过调用normalize方法,将法向矢量归一化,确保其长度为1

            std::cout << "Face Normal: (" << normal.x << ", " << normal.y << ", " << normal.z << ")\n";
        }
    }
}

int main() {

    std::string inputFile = "E:\\****\\coordoutput.ply";
    std::vector<Vec3> vertices;
    std::vector<Face> faces;

     读取PLY文件并解析顶点和面数据
    readPLYFile(inputFile, vertices, faces);
     计算法向并打印结果
    calculateAndPrintNormals(vertices, faces);
    

    return 0;
}

法向量信息展示
在这里插入图片描述

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

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

相关文章

springboot导出(POI)

POI官方文档 引入依赖 <!--POI--><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId&…

Spring Cloud Alibaba:Nacos服务治理平台

文章目录 什么是Nacos&#xff1f;使用Nacos进行服务注册与发现服务注册服务发现 负载均衡分析与拓展安全性性能监控日志记录 &#x1f389;欢迎来到架构设计专栏~Spring Cloud Alibaba&#xff1a;Nacos服务治理平台 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&…

学习笔记|矩阵按键控制原理|数值转化为键码|密码锁|STC32G单片机视频开发教程(冲哥)|第十四集:矩阵按键原理及实践

文章目录 1.矩阵按键是什么2.矩阵按键的控制原理3.矩阵按键程序的编写将数值转化为键码完整代码&#xff1a;demo.c&#xff1a;key.c:key.h: 密码锁&#xff08;简易版&#xff09;需求分析&#xff1a; 总结课后练习&#xff1a; 1.矩阵按键是什么 这个矩阵按键也是我们这个…

网上管理系统的分析及设计---应用UML建模

目 录 第1章 系统需求 第2章 需求分析 2.1 识别参与者 2.2 识别用例 2.3 用例的事件流描述 第3章 静态结构模型 3.1 定义系统对象 3.2 定义用户界面类 3.3 建立类图 第4章 动态行为模型 4.1 创建系统顺序图&#xff08;协作图&#xff09; 4.2 创建系统…

网络爬虫-----初识爬虫

目录 1. 什么是爬虫&#xff1f; 1.1 初识网络爬虫 1.1.1 百度新闻案例说明 1.1.2 网站排名&#xff08;访问权重pv&#xff09; 2. 爬虫的领域&#xff08;为什么学习爬虫 ?&#xff09; 2.1 数据的来源 2.2 爬虫等于黑客吗&#xff1f; 2.3 大数据和爬虫又有啥关系&…

Java低代码:jvs-list (子列表)表单回显及触发逻辑引擎配置说明

一、子列表【新增】表单默认回显主列表关联字段 子列表新增表单可使用表单回显配置&#xff0c;在新增表单中默认回显&#xff0c;如图效果 1、子列表中进入新增页面配置 2、切换到表单设置&#xff0c;选择回显设置&#xff0c;进入回显逻辑引擎。 3、在画布中拖入【对象变量…

记录一次对登录接口的性能测试

测试环境 客户端: win10 这里可以用linux,但没用,因为想直观查看结果。 被测环境:linux X86 4核CPU16G内存 被测接口:登录接口,没有做数据驱动。 场景设计 设置线程数19,持续时间5分钟,并用后端监听器监听结果,使用grafana+prometheus监控服务器资源。 测试执行 …

fabic如何将绘图原点移到画布中心

情况说明&#xff1a; fabic默认绘图原点为left&#xff1a;0&#xff0c;top&#xff1a;0 后端给我的内容是按照x&#xff0c;y返回的&#xff0c;需要将坐标系移到fabic画布的中心位置&#xff0c;找了下网上合适的砖&#xff0c;想一句命令直接设置&#xff0c;结果没有。…

二叉排序树(BST)的算法分析以及基本操作(结点的查询,插入,删除)

1.二叉排序树的定义 二叉排序树&#xff0c;又称二叉查找树&#xff08;BST&#xff0c;Binary Search Tree) 默认不允许两个结点的关键字相同。 1.二叉排序树的性质: 任意一棵二叉排序树的子树的结点大小都满足“左小右大”。 左子树上所有结点的关键字均小于根结点的关键…

廉价的全闪存雷电 NAS 折腾笔记:NUC9 操作系统踩坑

上一篇文章中&#xff0c;分享了关于低成本全闪存 NAS 的个人方案选择。 本篇文章&#xff0c;来聊聊硬件相关部分&#xff0c;以及软件的基础配置部分&#xff0c;也聊聊雷电组网的踩坑之旅。 写在前面 我使用的设备是 NUC9i5QNX&#xff0c;这台设备的硬件基础规格&#x…

【软件测试】selenium3

自动化测试的概念 自动化测试指软件测试的自动化&#xff0c;在预设状态下运行应用程序或者系统&#xff0c;预设条件包括正常和异常&#xff0c;最 后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。 自动化测试就相当于将人工测试手段进行转换&#xff0c;让代码…

国产视觉检测设备崛起,以AI机器视觉及自研算法破解智造难题

机器视觉作为人工智能的前沿分支之一&#xff0c;被称为智能制造的“智慧之眼”&#xff0c;在工业领域中&#xff0c;能够代替人工完成识别、测量、定位、检测等工作&#xff0c;以实现对设备精密控制及产线智能化、自动化升级。 同时&#xff0c;深度学习和3D视觉的技术升级…

Java本地开发环境搭建

概述 Java语言是企业级应用软件开发语言&#xff0c;本文主要描述Java开发环境的搭建。 如上所示&#xff0c;TIOBE提供2023年9月份全球开发语言的排行榜&#xff0c;其中&#xff0c;Java排名第四&#xff0c;而Python已经跃升到第一位&#xff0c;因为&#xff0c;Python是人…

芯科蓝牙BG27开发笔记6-精简第一个程序

1. 这些IO的控制代码在哪里&#xff1f; 还是蓝牙点灯程序&#xff1a; 首先需要对pinout做一些精简&#xff1a; 为了简化工程&#xff0c;去掉了不必要的IO。 至于PTI接口是什么&#xff0c;怎么用&#xff0c;不知道&#xff0c;现在不考虑&#xff1a; 但是提出以下问题…

第6章_freeRTOS入门与工程实践之创建FreeRTOS工程

本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id724601559592 配套资料获取&#xff1a;https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103 freeRTOS系列教程之freeRTOS入…

应该下那个 ActiveMQ

最近在搞 ActiveMQ 的时候&#xff0c;发现有 2 个 ActiveMQ 可以下载。 应该下那个呢&#xff1f; JMS 即Java Message Service&#xff0c;是JavaEE的消息服务接口。 JMS主要有两个版本&#xff1a;1.1和2.0。 2.0和1.1相比&#xff0c;主要是简化了收发消息的代码。 所谓…

Redis——其他数据类型介绍

概要介绍 Redis中有10种不同的数据类型。之前的blog中介绍了Redis中常见的五大数据类型&#xff1a;String&#xff0c;List&#xff0c;Hash&#xff0c;Set&#xff0c;ZSet。而Redis中还有许多其他的数据类型&#xff0c;一般在特定的场景中使用 Stream 首先介绍一下什么…

笔记本多拓展出一个屏幕

一、首先要知道&#xff0c;自己的电脑有没有Type-c接口&#xff0c;支持不支持VGA 推荐&#xff1a; 自己不清楚&#xff0c;问客服&#xff0c;勤问。 二、显示屏与笔记本相连&#xff0c;通过VGA 三、连接好了&#xff0c;需要去配置 网址&#xff1a;凑合着看&#xff…

代码随想录算法训练营Day42 | 动态规划(4/17) 0-1背包问题理论基础 LeetCode 416.分割等和子集

开始背包问题的练习&#xff01; 1. 背包问题的理论基础 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff0c;就够用了&#xff0c;最多可以再来一个多重背包。这里附上代码随想录的图&#xff0c;可以对背包问题进行一个分类。 1.1 十分重要的基础&a…

Java class 文件安全加密工具对比与ClassFinal实战

文章目录 前言常见加密方案对比XJarProGuardClassFinal ClassFinal实战纯命令方式maven插件方式 写在最后 前言 相信不少的同学开发的软件都是用户商业化&#xff0c;对于这些商业运营的项目很多都会直接部署在客户方&#xff0c;这样就可能会导致项目源码泄露。当然&#xff…