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实现点云数据的序列化与网络传输
转载自个人博客:Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候,有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输,但Open3D源码在实现RPC功能时就提供了一套序列化及传输的…...
【C++11】右值引用
前言: 在C11中引入的右值引用(rvalue references)是现代C的一个重要特性,它允许开发者以更高效的方式处理临时对象(右值),避免不必要的拷贝,提升性能。右值引用通常与C11的**移动语义…...
CSS元素显示类型
display 属性是 CSS 中最重要的属性之一,主要用来控制元素的布局,通过 display 属性您可以设置元素是否显示以及如何显示。 根据元素类型的不同,每个元素都有一个默认的 display 属性值,例如<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.功能描述 功能描述:连锁店总部/门店可以将商品设置第二件打折,如保温杯第一件10元,第二件5折; 2.适用场景 ☑新店开业、门店周年庆、节假日等特定时间促销; ☑会员拉新,设置会员专享套餐; …...
QTabWidget的每个tab居中显示图标和文本
使用QTabWidget,给每个tab添加了图标之后,文字和图标之间有间距,没有完美居中显示。 遇到此问题,尝试了多种办法,均不理想,最终自定义QTabBar,重绘tab,完美解决。 #include <QT…...
Ubuntu20.04如何安装Microsoft Edge浏览器?
Microsoft Edge是由微软开发的一款网页浏览器,首次发布于2015年,作为Windows 10操作系统的默认浏览器,取代了之前的Internet Explorer。 基于Chromium内核:自2019年起,Microsoft Edge转向了使用开源的Chromium内核,这使得它与Google Chrome在性能和兼容性方面有很多相似之…...
美团Java一面
美团Java一面 9.24一面,已经寄了 收到的第一个面试,表现很不好 spring bean生命周期 作用域(忘完了) 为什么用redis缓存 redis和数据库的缓存一致性问题 redis集群下缓存更新不一致问题 aop说一下 arraylist和linkedlist 数据库的…...
C#中ref关键字和out关键字
值传递和引用传递 值传递和引用传递是编程中涉及数据传递的两种方式。它们的主要区别在于数据是如何在函数或方法之间传递的。 值传递 值传递意味着当你把一个变量传递给一个函数时,实际上传递的是这个变量的值的一个拷贝。也就是说,函数内部对这个参数…...
贴吧软件怎么切换ip
在网络使用中,有时我们需要切换IP地址来满足特定的需求,比如需要切换贴吧软件IP以进行不同的操作。本文将介绍几种贴吧切换IP地址的方法,帮助用户更好地管理自己的网络身份和访问权限。 1、更换网络环境 通过连接到不同的Wi-Fi网络或使用移…...
图像分割恢复方法
传统的图像分割方法主要依赖于图像的灰度值、纹理、颜色等特征,通过不同的算法将图像分割成多个区域。这些方法通常可以分为以下几类: 1.基于阈值的方法 2.基于边缘的方法 3.基于区域的方法 4.基于聚类的方法 下面详细介绍这些方法及其示例代码。 1. 基…...
Ultralytics:YOLO11使用教程
Ultralytics:YOLO11使用教程 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows YOLO11使用教程进行目标检测进行实例分割进行姿势估计进行旋转框检测进行图像分类 参考文献 前言 由于本人水平有限,难免出现错漏,敬请批评改正。更多…...
前缀和算法——优选算法
个人主页:敲上瘾-CSDN博客 个人专栏:游戏、数据结构、c语言基础、c学习、算法 一、什么是前缀和? 前缀和是指从数组的起始位置到某一位置(或矩阵的某个区域)的所有元素的和。这种算法通过预处理数组或矩阵,…...
YOLO11改进|注意力机制篇|引入HAT超分辨率重建模块
目录 一、HAttention注意力机制1.1HAttention注意力介绍1.2HAT核心代码 二、添加HAT注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、HAttention注意力机制 1.1HAttention注意力介绍 HAT模型 通过结合卷积特征提取与多尺度注意…...
老牛也想吃嫩草,思科为何巨资投入云初创CoreWeave?
【科技明说 | 科技热点关注】 当我看到前些天思科(Cisco)的新闻时笑了。业内朋友对我说,老牛也想吃嫩草,人之常情尔,都是为了好好活着。 作为全球著名的网络产品巨头,思科Cisco论是遭遇到何种市场与行业巨变ÿ…...
Spring Boot 事务管理入门
在 Spring Boot 应用中,事务管理是一个至关重要的方面,它确保了数据的一致性和完整性。本文将深入探讨 Spring Boot 中事务管理的机制、使用方法以及注意事项,并提供丰富的示例代码。 其它教程: mysql事务详解 一、事务基础概念…...
20年408数据结构
第一题: 解析:这种题可以先画个草图分析一下,一下就看出来了。 这里的m(7,2)对应的是这图里的m(2,7),第一列存1个元素,第二列存2个元素,第三列存3个元素,第四列存4个元素,第五列存5个元素&#…...
4反馈、LC、石英、RC振荡器
1什么是振荡器? 我们看看振荡器在无线通信中扮演什么角色? 1)无线通信的波是指电磁波。 2)电磁波的频率高于100KHz才能在空气中传播。 3)空气中的高频电磁波的相位和振幅可以排列组合包含信息。 4)无…...
go 的 timer reset
在 Go 语言 1.23 版本之前,与Timer(定时器)关联的通道是异步的(有缓冲,容量为 1)。这意味着即使在调用Timer.Stop(停止定时器)或Timer.Reset(重置定时器)并返…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
