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(重置定时器)并返…...

每日一面 day03
Q:介绍一下MySQL的三种日志(redo,undo,bin) Redo Log 和 Undo Log 是存储引擎 InnoDB 层面实现的,Bin Log 是 MySQL 层面实现的。 下面是三种日志的简要介绍: Redo Log:保证事务的…...

ssm基于SSM框架的餐馆点餐系统的设计+VUE
系统包含:源码论文 所用技术:SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习,获取源码请私聊我 需要定制请私聊 目 录 摘要 I Abstract II 1绪论 1 1.1研究背景与意义 1 1.1.1研究背景 1 1.1.2研究意义 1 1.2国内外研究…...

多人播报配音怎么弄?简单4招分享
想象一下,你手中的小说突然间活了起来,每个角色都有了自己的声音和情感。 这就是多人配音的魅力所在。它让文字跃然纸上,赋予了故事新的生命。 那么,如何制作一部引人入胜的小说呢?多人配音怎么制作的呢?…...

《Windows PE》4.1导入表
导入表顾名思义,就是记录外部导入函数信息的表。这些信息包括外部导入函数的序号、名称、地址和所属的DLL动态链接库的名称。Windows程序中使用的所有API接口函数都是从系统DLL中调用的。当然也可能是自定义的DLL动态链接库。对于调用方,我们称之为导入函…...

计算机专业大学生应该如何规划大学四年?
计算机专业的大学生在学习过程中应该注重以下几个方面,以确保他们在快速变化的技术领域中保持竞争力: 基础知识: 数学基础:离散数学、线性代数、概率论等数学课程对于理解算法和数据结构至关重要。编程基础:学习至少一…...

R知识图谱1—tidyverse玩转数据处理120题
以下是本人依据张老师提供的tidyverse题库自行刷题后的tidyverse Rmd文件,部分解法参考张老师提示,部分解法我本人灵感提供 数据下载来源https://github.com/zhjx19/tidyverse120/tree/main/data 参考https://github.com/MaybeBio/R_cheatsheet/tree/mai…...

【赵渝强老师】K8s中的有状态控制器StatefulSet
在K8s中,StatefulSets将Pod部署成有状态的应用程序。通过使用StatefulSets控制器,可以为Pod提供持久存储和持久的唯一性标识符。StatefulSets控制器与Deployment控制器不同的是,StatefulSets控制器为管理的Pod维护了一个有粘性的标识符。无论…...

机器学习笔记(持续更新)
使用matplotlib绘图: import matplotlib.pyplot as plt fig, axplt.subplots() #创建一个图形窗口 plt.show() #不绘制任何内容,直接显示空图 重复值处理: 重复值处理代码: import pandas as pd data pd.DataFrame({学号: [1…...

Nginx 配置之server块
在 Nginx 配置中使用两个 server 块是为了处理 HTTP 和 HTTPS 请求的不同需求。具体来说: 第一个 server 块: 监听 80 端口(HTTP)。将所有 HTTP 请求重定向到 HTTPS(443 端口)。 第二个 server 块ÿ…...

魅族Lucky 08惊艳亮相:极窄四等边设计引领美学新风尚
在这个智能手机设计趋于同质化的时代,魅族以其独特的设计理念和创新技术,再次为市场带来了一股清新之风。 近日,魅族全新力作——Lucky 08手机正式曝光,其独特的“极窄物理四等边”设计瞬间吸引了众多消费者的目光,而…...