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

news2024/10/4 7:58:08

转载自个人博客: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 data
	open3d::io::rpc::messages::SetMeshData pcdMesh;

	/// Text data
	std::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转化为标准数据类型SetMeshData
    PACK 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 space
    uLongf compressedMaxSize = compressBound(input.size());
    output.resize(compressedMaxSize);

    /// Compress and get the real size
    int result = compress2((Bytef *)output.data(), &compressedMaxSize,
                           (const Bytef *)input.data(), input.size(),
                           level);
    if (result != Z_OK) return result;

    /// Reallocate the real space
    output.resize(compressedMaxSize);
    return Z_OK;
}

void main(){
    // std::shared_ptr<open3d::geometry::PointCloud> pcd;
    ...
    /// 1.对原点云数据进行下采样(有损压缩)
	pcd = pcd->VoxelDownSample(0.002);
    
    /// 2.将PointCloud转化为标准数据类型SetMeshData
    PACK 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 space
    std::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转化为标准数据类型SetMeshData
    PACK 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
/// 得到序列化后的数据sbuf

zmq::message_t send_msg(sbuf.data(), sbuf.size());
auto reply = connection->Send(send_msg);

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

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

相关文章

用langchain+streamlit应用RAG实现个人知识库助手搭建

RAG原理概述 RAG&#xff08;Retrieval-Augmented Generation&#xff09; 是一种结合了信息检索和生成式人工智能技术的模型架构&#xff0c;旨在让模型生成更有根据和更准确的回答。通俗来讲&#xff0c;它让模型不只是凭借自己的“记忆”&#xff08;预训练数据&#xff09…

Java中的依赖注入(Dependency Injection, DI)详解

Java中的依赖注入&#xff08;Dependency Injection, DI&#xff09;是软件工程中的一种重要设计模式。它有助于提高系统的可测试性、可维护性和灵活性。通过依赖注入&#xff0c;组件不再负责创建它们所需的对象&#xff0c;而是通过外部的设置来提供这些对象。这种方式也与控…

无人机+无人车+机器狗:综合管控系统技术详解

无人机、无人车、机器狗的综合管控系统技术是一个集成了多种先进技术和设备的复杂系统&#xff0c;旨在实现高效、精准、协同的作业与管理。以下是对该系统技术的详细解析&#xff1a; 一、系统概述 综合管控系统通过集成无人机、无人车和机器狗等智能设备&#xff0c;结合物…

OSDU轻量化单机部署

首先更新系统 sudo apt update sudo apt upgrade -y安装docker sudo apt install -y docker.io sudo systemctl start docker sudo systemctl enable docker安装minikube curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 …

vmware Workstation16设置批量虚拟机开机自启 vmAutoStart

文章目录 前言解压压缩包一、使用步骤1.获取虚拟机所在目录2.获取vmware所在目录3.测试启动4.开机自启 二、gitee总结 前言 vmware workstation16不支持虚拟机开机自启&#xff0c;通常的办法是写脚本&#xff0c;但是有个问题就是不能启动多台虚拟机&#xff0c;因为有时候会…

Python | Leetcode Python题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()m, n len(g), len(s)i j count 0while i < m and j < n:while j < n and g[i] > s[j]:j 1if j < n:count 1i …

Spring框架使用Api接口实现AOP的切面编程、两种方式的程序示例以及Java各数据类型及基本数据类型的默认值/最大值/最小值列表

一、Spring框架使用Api接口-继承类实现AOP的切面编程示例 要使用Spring框架AOP&#xff0c;除了要导入spring框架包外&#xff0c;还需要导入一个织入的包org.aspectj&#xff0c;具体maven依赖如下&#xff1a; <dependency><groupId>org.springframework</gr…

JAVA-异常(通俗易懂)

目录 一、异常的概念 1.算术异常 2.数组越界异常 3.空指针异常 二、异常体系结构 三、异常的分类 1. 编译时异常 2. 运行时异常 四、异常处理 1.防御式编程 2.异常的抛出 3.异常的捕获 4.try-catch捕获并处理 5.finally 五、异常的处理流程 六. 自定义异常类…

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集 超过25000张图像的语义分割数据集

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集介绍 是一个包含超过25000张图像的语义分割数据集&#xff0c;这些图像被密集注释&#xff0c;覆盖室内和室外场景。 它由MIT发布&#xff0c;包含100个事物类别和50个物质类别&#xff0c; 用于训练和验证的图像数量分别…

(16)MATLAB仿真Nakagami-m分布1

文章目录 前言一、Nakagami分布二、MATLAB建模代码三、仿真结果画图四、总结 前言 Nakagami衰落模型最初是由于该模型与短波电离层传播的经验结果相匹配而提出的。它还用于仿真来自多个干扰源的情况&#xff0c;因为多个独立且同分布&#xff08;i.i.d&#xff09;的瑞利分布随…

线程池的实现和讲解:解决多线程并发服务器创建销毁线程消耗过大的问题

1.前言 多进程/线程并发服务器、多路I/O转接服务器的简单实现-CSDN博客 原先的多线程并发服务器&#xff0c;有多少个客户端连接服务器就有多少个线程&#xff0c;CPU需要在多个线程之间来回切换处理客户端的请求&#xff0c;系统消耗比较大(每次创建和消耗线程在操作系统内部…

linux学习--第七天(多路复用IO)

多路复用IO -阻塞IO与非阻塞IO -IO模型 IO的本质时基于操作系统接口来控制底层的硬件之间数据传输&#xff0c;并且在操作系统中实现了多种不同的IO方式&#xff08;模型&#xff09;比较常见的有下列三种&#xff1a; 1.阻塞型IO模型 2.非阻塞型IO模型 3.多路复用IO模型 -阻…

开源2+1链动模式AI智能名片O2O商城小程序源码:线下店立体连接的超强助力器

摘要&#xff1a;本文将为您揭示线下店立体连接的重大意义&#xff0c;您知道吗&#xff1f;线上越火&#xff0c;线下就得越深入经营。现代门店可不再只是卖东西的地儿&#xff0c;还得连接KOC呢&#xff01;咱们来看看门店要做的那些超重要的事儿&#xff0c;还有开源21链动模…

Authentication Lab | CVE-2019-7644 - JWT Signature Disclosure

关注这个靶场的其他相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;JWT Signature Disclosure 前情提要 本关的考点是 JWT&#xff08;Json Web Token&#xff09;漏洞&#xff0c;JWT 是一个用于跨域认证的技术。如果你不了解 JWT&…

计算机视觉——图像修复综述篇

目录 1. Deterministic Image Inpainting 判别器图像修复 1.1. sigle-shot framework (1) Generators (2) training objects / Loss Functions 1.2. two-stage framework 2. Stochastic Image Inpainting 随机图像修复 2.1. VAE-based methods 2.2. GAN-based methods …

攻防世界----->easyre-153

做题笔记。 下载 查壳。 UPX&#xff0c;---脱壳。 32ida打开。 先运行一下&#xff1a; 查找字符校位。 管道父子&#xff1f;有点像此前做的那个进程互斥。。。 分析&#xff1a; 跟进lol &#xff1f; 查看汇编窗口看看。(因为一个函数只存在一个打印函数&#xff0c;就很…

集合框架01:集合的概念、Collection体系、Collection接口

1.集合的概念 集合是对象的容器&#xff0c;定义了多个对象进行操作的常用方法。可实现数组的功能。 集合和数组的区别&#xff1a; 1.数组长度固定&#xff0c;集合长度不固定&#xff1b; 2.数组可以存储基本类型和引用类型&#xff0c;集合只能存储引用类型&#xff1b; …

读数据湖仓06数据集成

1. 数据湖仓中的数据集成 1.1. 数据湖仓的总体目标是为每一个人提供支持&#xff0c;包括从普通职员到CEO 1.2. 有了作为基础设施的基础数据&#xff0c;企业等组织才能实现真正的数据驱动 1.3. 提供组织所需的数据&#xff0c;最关键的一环在于提供集成的数据基础 1.3.1. 只…

信息安全工程师(32)认证技术方法

前言 认证技术方法是用于验证用户、设备或系统身份的各种技术手段和方法&#xff0c;旨在确保只有经过验证的实体才能访问系统资源&#xff0c;从而保护数据和系统的安全。 一、常见认证技术方法 密码认证 描述&#xff1a;用户通过输入预先设置的密码进行身份验证。优点&#…

The 14th Jilin Provincial Collegiate Programming Contest

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 #define ll long long #define pii pair<int, int> #define ld lo…