wordpress国内主机推荐,网络优化工程师是干什么的,辽宁工程招标网信息平台,中国域名网转载自个人博客#xff1a;Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候#xff0c;有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输#xff0c;但Open3D源码在实现RPC功能时就提供了一套序列化及传输的… 转载自个人博客Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输但Open3D源码在实现RPC功能时就提供了一套序列化及传输的方法及思路那我们就可以搬过来化为己用。
其中利用Open3D方法序列化点云最为重要总的流程为根据需要进行有损压缩、序列化、根据需要进行无损压缩、网络传输即根据需要使用2.1、1.1、根据需要使用2.2、3.1。 本文全文使用C实现另外本文参考的Open3D源码是V0.18.0版本若想查看完整Open3D源码请自行Down\CloneOpen3D 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_ptropen3d::geometry::PointCloud pcd;/// 定义的结构体用于使用msgpack对其进行序列化
/// 可以添加其他需要一起发送的标准数据一起序列化
struct PACK{/// Point cloud dataopen3d::io::rpc::messages::SetMeshData pcdMesh;/// Text datastd::vectorstd::string text;/// 用于msgpack序列化只支持标准数据(std)MSGPACK_DEFINE_ARRAY(pcdMesh, text);
};PACK serialization(std::shared_ptropen3d::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_ptropen3d::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_ptrConnectionBase 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_ptrConnection(new Connection());}auto reply connection-Send(send_msg);return ReplyIsOKStatus(*reply);
}}那么使用即为
/// 网络传输Connection的申明定义见3
auto connection std::make_sharedConnection(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_ptropen3d::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_castint(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.hint 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_ptropen3d::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_castint(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_ptrzmq::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_ptrzmq::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_ptrzmq::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_sharedConnection(tcp://127.0.0.1:51454, 500, 500);/// 序列化内容见1
/// 得到序列化后的数据sbufzmq::message_t send_msg(sbuf.data(), sbuf.size());
auto reply connection-Send(send_msg);