当前位置: 首页 > news >正文

Open3D实现点云数据的序列化与网络传输

转载自个人博客:Open3D实现点云数据的序列化与网络传输

在处理点云数据的时候,有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输,但Open3D源码在实现RPC功能时就提供了一套序列化及传输的方法及思路,那我们就可以搬过来化为己用。

其中利用Open3D方法序列化点云最为重要,总的流程为根据需要进行有损压缩、序列化、根据需要进行无损压缩、网络传输,即:根据需要使用2.1、1.1、根据需要使用2.2、3.1。

本文全文使用C++实现,另外,本文参考的Open3D源码是V0.18.0版本,若想查看完整Open3D源码,请自行Down\Clone:Open3D Github

1. 序列化(重点)

1.1 实现

Open3D在实现其RPC功能时提供了标准类型open3d::io::rpc::messages::SetMeshData,至于为什么要标准类型,是方便msgpack进行序列化。

那么流程即为:先将点云数据open3d::geometry::PointCloud转化为这个类型,再使用msgpack进行序列化。

将PointCloud转化为标准数据类型SetMeshData是需要逐步转化的,我在这里用一个函数封装,具体代码见下:

#include "open3d/geometry/PointCloud.h"
#include "open3d/io/rpc/Messages.h"/// 将需要序列化的目标点云数据pcd设定为:
/// std::shared_ptr<open3d::geometry::PointCloud> pcd;/// 定义的结构体,用于使用msgpack对其进行序列化
/// 可以添加其他需要一起发送的标准数据,一起序列化
struct PACK{/// Point cloud dataopen3d::io::rpc::messages::SetMeshData pcdMesh;/// Text datastd::vector<std::string> text;/// 用于msgpack序列化,只支持标准数据(std)MSGPACK_DEFINE_ARRAY(pcdMesh, text);
};PACK serialization(std::shared_ptr<open3d::geometry::PointCloud> pcd)
{PACK pack = {{}, {}};if(pcd == nullptr) return pack;pack.pcdMesh.path = "";pack.pcdMesh.time = 0;pack.pcdMesh.layer = "";/// pcd->points_pack.pcdMesh.data.vertices = open3d::io::rpc::messages::Array::FromPtr((double*)pcd->points_.data(), {int64_t(pcd->points_.size()), 3});/// pcd->normals_if(pcd->HasNormals()){pack.pcdMesh.data.vertex_attributes["normals"] =open3d::io::rpc::messages::Array::FromPtr((double*)pcd->normals_.data(), {int64_t(pcd->normals_.size()), 3});}/// pcd->colors_if(pcd->HasColors()){pack.pcdMesh.data.vertex_attributes["colors"] =open3d::io::rpc::messages::Array::FromPtr((double*)pcd->colors_.data(), {int64_t(pcd->colors_.size()), 3});}return pack;
}void main(){// std::shared_ptr<open3d::geometry::PointCloud> pcd;.../// 1.将PointCloud转化为标准数据类型SetMeshDataPACK pack = serialization(pcd);/// 2.将SetMeshData所在结构体用msgpack序列化msgpack::sbuffer sbuf;msgpack::pack(sbuf, pack); /// 得到序列化后的数据sbuf...
}

1.2 源码参考(可跳过)

Open3D源码中对处理点云数据的声明定义:

using namespace open3d::utility;namespace open3d {
namespace io {
namespace rpc {bool SetPointCloud(const geometry::PointCloud& pcd,const std::string& path,int time,const std::string& layer,std::shared_ptr<ConnectionBase> connection) {// TODO use SetMeshData here after switching to the new PointCloud class.if (!pcd.HasPoints()) {LogInfo("SetMeshData: point cloud is empty");return false;}messages::SetMeshData msg;msg.path = path;msg.time = time;msg.layer = layer;msg.data.vertices = messages::Array::FromPtr((double*)pcd.points_.data(), {int64_t(pcd.points_.size()), 3});if (pcd.HasNormals()) {msg.data.vertex_attributes["normals"] =messages::Array::FromPtr((double*)pcd.normals_.data(),{int64_t(pcd.normals_.size()), 3});}if (pcd.HasColors()) {msg.data.vertex_attributes["colors"] = messages::Array::FromPtr((double*)pcd.colors_.data(), {int64_t(pcd.colors_.size()), 3});}msgpack::sbuffer sbuf;messages::Request request{msg.MsgId()};msgpack::pack(sbuf, request);msgpack::pack(sbuf, msg);/// 得到序列化后的数据sbuf/// 之后是网络传输的部分zmq::message_t send_msg(sbuf.data(), sbuf.size());if (!connection) {connection = std::shared_ptr<Connection>(new Connection());}auto reply = connection->Send(send_msg);return ReplyIsOKStatus(*reply);
}}

那么使用即为:

/// 网络传输Connection的申明定义见3
auto connection = std::make_shared<Connection>("tcp://127.0.0.1:51454", 500, 500);
ASSERT_TRUE(SetPointCloud(pcd, "", 0, "", connection));

2. 压缩

一般,需要显示完全场景的点云数据都占用较大内存,如果像我这样需要实时传输点云数据以实时更新场景的情况,那对点云数据进行压缩处理就有利于应对更多的网络场景。

Open3D自身实现的下采样(有损压缩)和第三方实现的无损压缩可以同时使用。

2.1 下采样(有损压缩)

Open3D本身就提供了多种下采样方法,通过对原生点云数据再采样,减少点的个数,即减小点云数据的大小。

详细的各种下采样方法见我另一篇文章:点云下采样有损压缩

这里以体素下采样为例:

int voxelSize = 0.002; // 设置体素的尺寸大小
pcd = pcd->VoxelDownSample(voxelSize);

2.2 无损压缩

既然前面已经实现数据的序列化了,那就可以用其他常用的压缩库进行压缩,比如zlib。

要注意的是,这种压缩会消耗一定的CPU资源,占用一定时间。

zlib官方文档:zlib 1.3.1 Manual

其提供了几种压缩等级(level):

#define Z_NO_COMPRESSION         0
#define Z_BEST_SPEED             1
#define Z_BEST_COMPRESSION       9
#define Z_DEFAULT_COMPRESSION  (-1)

完整代码如下:

#include "zlib.h"/// 序列化内容见上一节,这里略
/int compress(const std::string &input, std::string &output, int level)
{/// Estimate the maximum value of the compressed size and allocate spaceuLongf compressedMaxSize = compressBound(input.size());output.resize(compressedMaxSize);/// Compress and get the real sizeint result = compress2((Bytef *)output.data(), &compressedMaxSize,(const Bytef *)input.data(), input.size(),level);if (result != Z_OK) return result;/// Reallocate the real spaceoutput.resize(compressedMaxSize);return Z_OK;
}void main(){// std::shared_ptr<open3d::geometry::PointCloud> pcd;.../// 1.对原点云数据进行下采样(有损压缩)pcd = pcd->VoxelDownSample(0.002);/// 2.将PointCloud转化为标准数据类型SetMeshDataPACK pack = serialization(pcd);/// 3.将SetMeshData所在结构体用msgpack序列化msgpack::sbuffer sbuf;msgpack::pack(sbuf, pack); /// 得到序列化后的数据sbuf/// 4.对数据进行压缩QByteArray byteArray(sbuf.data(), static_cast<int>(sbuf.size()));std::string originalData(byteArray.data(), byteArray.size());std::string compressedData;if(compress(originalData, compressedData, 1) != Z_OK){std::cerr << "Compression failed." << std::endl;}/// 得到压缩后的数据compressedData
}

解压类似:

#include "zlib.h"int decompress(const std::string &input, std::string &output)
{/// Allocate an estimated spacestd::string data(input.size() * 10, '\0');uLongf size = data.size();int result = uncompress((Bytef *)data.data(), &size, (const Bytef *)input.data(), input.size());if (result != Z_OK) return result;output.assign(data.data(), size);return Z_OK;
}

3. 网络传输

我使用的是nanomsg,比源码中使用的ZeroMQ好用,当然,对数据流的传输可以使用其他的各种方法。

3.1 本人使用的传输方法

我使用的是nanomsg进行网络传输,nanomsg的使用参考我其他的文章。

这里的代码如下:

#include <nanomsg/nn.h>
#include <nanomsg/bus.h>/// 序列化内容和压缩内容见上两节,这里略
/
void main(){// std::shared_ptr<open3d::geometry::PointCloud> pcd;.../// 1.对原点云数据进行下采样(有损压缩)pcd = pcd->VoxelDownSample(0.002);/// 2.将PointCloud转化为标准数据类型SetMeshDataPACK pack = serialization(pcd);/// 3.将SetMeshData所在结构体用msgpack序列化msgpack::sbuffer sbuf;msgpack::pack(sbuf, pack); /// 得到序列化后的数据sbuf/// 4.对数据进行压缩QByteArray byteArray(sbuf.data(), static_cast<int>(sbuf.size()));std::string originalData(byteArray.data(), byteArray.size());std::string compressedData;if(compress(originalData, compressedData, 1) != Z_OK){std::cerr << "Compression failed." << std::endl;}/// 得到压缩后的数据compressedData/// 5.将压缩后的数据发送int socket = nn_socket(AF_SP, NN_BUS);nn_bind(socket, "ws://127.0.0.1:55555");if(nn_send(socket, compressedData.data(), compressedData.size(),0) < 0){std::cerr << "Send error" << std::endl;}/// 5.如果不需要压缩,直接发送序列化的数据if(nn_send(socket, sbuf.data(), sbuf.size(),0) < 0){std::cerr << "Send error" << std::endl;}
}

3.2 源码中的方法

Open3D使用ZeroMQ库进行网络传输

Open3D源码中对网络传输的声明定义:

using namespace open3d::utility;namespace open3d {
namespace io {
namespace rpc {Connection::Connection(): Connection(defaults.address, defaults.connect_timeout, defaults.timeout) {
}Connection::Connection(const std::string& address,int connect_timeout,int timeout): context_(GetZMQContext()), // GetZMQContext()在别处定义、实现socket_(new zmq::socket_t(*GetZMQContext(), ZMQ_REQ)),address_(address),connect_timeout_(connect_timeout),timeout_(timeout) {socket_->set(zmq::sockopt::linger, timeout_);socket_->set(zmq::sockopt::connect_timeout, connect_timeout_);socket_->set(zmq::sockopt::rcvtimeo, timeout_);socket_->set(zmq::sockopt::sndtimeo, timeout_);socket_->connect(address_.c_str());
}Connection::~Connection() { socket_->close(); }std::shared_ptr<zmq::message_t> Connection::Send(zmq::message_t& send_msg) {if (!socket_->send(send_msg, zmq::send_flags::none)) {zmq::error_t err;if (err.num()) {LogInfo("Connection::send() send failed with: {}", err.what());}}std::shared_ptr<zmq::message_t> msg(new zmq::message_t());if (socket_->recv(*msg)) {LogDebug("Connection::send() received answer with {} bytes",msg->size());} else {zmq::error_t err;if (err.num()) {LogInfo("Connection::send() recv failed with: {}", err.what());}}return msg;
}std::shared_ptr<zmq::message_t> Connection::Send(const void* data,size_t size) {zmq::message_t send_msg(data, size);return Send(send_msg);
}
}

那么使用即为:

auto connection = std::make_shared<Connection>("tcp://127.0.0.1:51454", 500, 500);/// 序列化内容见1
/// 得到序列化后的数据sbufzmq::message_t send_msg(sbuf.data(), sbuf.size());
auto reply = connection->Send(send_msg);

相关文章:

Open3D实现点云数据的序列化与网络传输

转载自个人博客&#xff1a;Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候&#xff0c;有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输&#xff0c;但Open3D源码在实现RPC功能时就提供了一套序列化及传输的…...

【C++11】右值引用

前言&#xff1a; 在C11中引入的右值引用&#xff08;rvalue references&#xff09;是现代C的一个重要特性&#xff0c;它允许开发者以更高效的方式处理临时对象&#xff08;右值&#xff09;&#xff0c;避免不必要的拷贝&#xff0c;提升性能。右值引用通常与C11的**移动语义…...

CSS元素显示类型

display 属性是 CSS 中最重要的属性之一&#xff0c;主要用来控制元素的布局&#xff0c;通过 display 属性您可以设置元素是否显示以及如何显示。 根据元素类型的不同&#xff0c;每个元素都有一个默认的 display 属性值&#xff0c;例如<div>默认的 display 属性值为 …...

Flink 介绍(特性、概念、故障容错、运维部署、应用场景)

概述 特性 概念 数据流 状态 时间 savepoint 故障容错 运维部署 部署应用到任意地方 Flink能够更方便地升级、迁移、暂停、恢复应用服务 监控和控制应用服务 运行任意规模应用 应用场景 事件驱动型应用 什么是事件驱动型应用? 事件驱动型应用的优势 Flink如何…...

Python+Flask接口判断身份证省份、生日、性别、有效性验证+docker部署+Nginx代理运行

这里写目录标题 一、接口样式二、部署流程2.1 镜像打包2.1.1 准备工作2.1.2 build打包2.1.3 dokcer部署运行2.1.4 Nginx代理 三、代码及文件3.1 index.py3.2 areaCodes.json3.3 Dockerfile 一、接口样式 https://blog.henryplus.cn/idcardApi/idCard/query?idcard{idcard} 二、…...

门店收银营销活动打折特价-收银系统源码

1.功能描述 功能描述&#xff1a;连锁店总部/门店可以将商品设置第二件打折&#xff0c;如保温杯第一件10元&#xff0c;第二件5折&#xff1b; 2.适用场景 ☑新店开业、门店周年庆、节假日等特定时间促销&#xff1b; ☑会员拉新&#xff0c;设置会员专享套餐&#xff1b; …...

QTabWidget的每个tab居中显示图标和文本

使用QTabWidget&#xff0c;给每个tab添加了图标之后&#xff0c;文字和图标之间有间距&#xff0c;没有完美居中显示。 遇到此问题&#xff0c;尝试了多种办法&#xff0c;均不理想&#xff0c;最终自定义QTabBar&#xff0c;重绘tab&#xff0c;完美解决。 #include <QT…...

Ubuntu20.04如何安装Microsoft Edge浏览器?

Microsoft Edge是由微软开发的一款网页浏览器,首次发布于2015年,作为Windows 10操作系统的默认浏览器,取代了之前的Internet Explorer。 基于Chromium内核:自2019年起,Microsoft Edge转向了使用开源的Chromium内核,这使得它与Google Chrome在性能和兼容性方面有很多相似之…...

美团Java一面

美团Java一面 9.24一面&#xff0c;已经寄了 收到的第一个面试&#xff0c;表现很不好 spring bean生命周期 作用域&#xff08;忘完了&#xff09; 为什么用redis缓存 redis和数据库的缓存一致性问题 redis集群下缓存更新不一致问题 aop说一下 arraylist和linkedlist 数据库的…...

C#中ref关键字和out关键字

值传递和引用传递 值传递和引用传递是编程中涉及数据传递的两种方式。它们的主要区别在于数据是如何在函数或方法之间传递的。 值传递 值传递意味着当你把一个变量传递给一个函数时&#xff0c;实际上传递的是这个变量的值的一个拷贝。也就是说&#xff0c;函数内部对这个参数…...

贴吧软件怎么切换ip

在网络使用中&#xff0c;有时我们需要切换IP地址来满足特定的需求&#xff0c;比如需要切换贴吧软件IP以进行不同的操作。本文将介绍几种贴吧切换IP地址的方法&#xff0c;帮助用户更好地管理自己的网络身份和访问权限。 1、更换网络环境‌ 通过连接到不同的Wi-Fi网络或使用移…...

图像分割恢复方法

传统的图像分割方法主要依赖于图像的灰度值、纹理、颜色等特征&#xff0c;通过不同的算法将图像分割成多个区域。这些方法通常可以分为以下几类&#xff1a; 1.基于阈值的方法 2.基于边缘的方法 3.基于区域的方法 4.基于聚类的方法 下面详细介绍这些方法及其示例代码。 1. 基…...

Ultralytics:YOLO11使用教程

Ultralytics&#xff1a;YOLO11使用教程 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows YOLO11使用教程进行目标检测进行实例分割进行姿势估计进行旋转框检测进行图像分类 参考文献 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多…...

前缀和算法——优选算法

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、算法 一、什么是前缀和&#xff1f; 前缀和是指从数组的起始位置到某一位置&#xff08;或矩阵的某个区域&#xff09;的所有元素的和。这种算法通过预处理数组或矩阵&#xff0c;…...

YOLO11改进|注意力机制篇|引入HAT超分辨率重建模块

目录 一、HAttention注意力机制1.1HAttention注意力介绍1.2HAT核心代码 二、添加HAT注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、HAttention注意力机制 1.1HAttention注意力介绍 HAT模型 通过结合卷积特征提取与多尺度注意…...

老牛也想吃嫩草,思科为何巨资投入云初创CoreWeave?

【科技明说 &#xff5c; 科技热点关注】 当我看到前些天思科(Cisco)的新闻时笑了。业内朋友对我说&#xff0c;老牛也想吃嫩草&#xff0c;人之常情尔&#xff0c;都是为了好好活着。 作为全球著名的网络产品巨头&#xff0c;思科Cisco论是遭遇到何种市场与行业巨变&#xff…...

Spring Boot 事务管理入门

在 Spring Boot 应用中&#xff0c;事务管理是一个至关重要的方面&#xff0c;它确保了数据的一致性和完整性。本文将深入探讨 Spring Boot 中事务管理的机制、使用方法以及注意事项&#xff0c;并提供丰富的示例代码。 其它教程&#xff1a; mysql事务详解 一、事务基础概念…...

20年408数据结构

第一题&#xff1a; 解析&#xff1a;这种题可以先画个草图分析一下&#xff0c;一下就看出来了。 这里的m(7,2)对应的是这图里的m(2,7),第一列存1个元素&#xff0c;第二列存2个元素&#xff0c;第三列存3个元素&#xff0c;第四列存4个元素&#xff0c;第五列存5个元素&#…...

4反馈、LC、石英、RC振荡器

1什么是振荡器&#xff1f; 我们看看振荡器在无线通信中扮演什么角色&#xff1f; 1&#xff09;无线通信的波是指电磁波‌。 2‌&#xff09;电磁波的频率高于100KHz才能在空气中传播。‌ 3&#xff09;空气中的高频电磁波的相位和振幅可以排列组合包含信息。 4&#xff09;无…...

go 的 timer reset

在 Go 语言 1.23 版本之前&#xff0c;与Timer&#xff08;定时器&#xff09;关联的通道是异步的&#xff08;有缓冲&#xff0c;容量为 1&#xff09;。这意味着即使在调用Timer.Stop&#xff08;停止定时器&#xff09;或Timer.Reset&#xff08;重置定时器&#xff09;并返…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...