Halcon点云转unsigned char*序列化及MongoDB反序列化实现
问题描述
我用C++开发,从2D/3D传感器(比如Photoneo XL)采集数据,要通过MongoDB的GridFS存储图像和点云(因为数据量超16MB,得序列化成unsigned char*缓冲区)。为了避免存文件的耗时,直接把传感器输出变量转成字节处理。
软件采用微服务架构,一个服务负责采集并上传到MongoDB,其他服务通过ID引用访问数据。
图像的序列化/反序列化已经实现,代码如下:
图像序列化代码
QImage img = QImage("./smallimage.png"); mongocxx::gridfs::bucket bucket = db.gridfs_bucket(); auto uploader = bucket.open_upload_stream("sample_gridfs_file"); uploader.write((unsigned char*)img.bits(), img.sizeInBytes());
图像反序列化代码
while (auto length_read = downloader.read(buffer.get(), static_cast<std::size_t>(buffer_size))) { bytes_counter += static_cast<std::int32_t>(length_read); tmp_char = (unsigned char*)buffer.get(); memcpy(array_char+cum_size,tmp_char, buffer_size); cum_size += buffer_size; } QImage out = QImage(array_char, img.width(), img.height(), img.format());
但处理点云时遇到困难:点云用Halcon采集,存在HTuple里,已经提取出X、Y、Z坐标的三个double*数组,需要把这些数据序列化成unsigned char*上传到MongoDB,之后再读取解码成新的double*数组重建点云。初始代码如下:
HalconCpp::ReadObjectModel3d("./point_cloud.ply", "m", HalconCpp::HTuple(), HalconCpp::HTuple(),&hv_ObjectModel3D, &hv_Status); HalconCpp::GetObjectModel3dParams(hv_ObjectModel3D, "num_points", &numPoints); HalconCpp::GetObjectModel3dParams(hv_ObjectModel3D, "point_coord_x", &pointsX); HalconCpp::GetObjectModel3dParams(hv_ObjectModel3D, "point_coord_y", &pointsY); HalconCpp::GetObjectModel3dParams(hv_ObjectModel3D, "point_coord_z", &pointsZ); double* xArray = pointsX.DArr(); double* yArray = pointsY.DArr(); double* zArray = pointsZ.DArr();
点云序列化与反序列化解决方案
1. 点云序列化(上传到GridFS)
核心思路是先存储点的数量(方便反序列化时确定数据长度),再把三个double*数组按顺序拼接成连续字节流,直接写入GridFS。
实现代码
// 获取点的数量 int num_points = numPoints.I(); // 计算总字节数:点数量(int) + 3*点数量*double字节数 size_t total_bytes = sizeof(int) + 3 * num_points * sizeof(double); // 分配缓冲区 std::unique_ptr<unsigned char[]> buffer(new unsigned char[total_bytes]); // 第一步:写入点数量 memcpy(buffer.get(), &num_points, sizeof(int)); // 第二步:依次写入X、Y、Z数组 unsigned char* ptr = buffer.get() + sizeof(int); memcpy(ptr, xArray, num_points * sizeof(double)); ptr += num_points * sizeof(double); memcpy(ptr, yArray, num_points * sizeof(double)); ptr += num_points * sizeof(double); memcpy(ptr, zArray, num_points * sizeof(double)); // 上传到GridFS mongocxx::gridfs::bucket bucket = db.gridfs_bucket(); auto uploader = bucket.open_upload_stream("point_cloud_data"); uploader.write(buffer.get(), total_bytes); uploader.close(); // 必须关闭流确保数据写入完成
2. 点云反序列化(从GridFS读取重建)
读取时先解析点的数量,再依次提取X、Y、Z的字节数据转成double*数组,最后用Halcon重建点云。
实现代码
// 假设已从GridFS下载完整数据到data缓冲区,data_size为总字节长度 int num_points; memcpy(&num_points, data, sizeof(int)); // 分配X、Y、Z数组内存 std::unique_ptr<double[]> xArr(new double[num_points]); std::unique_ptr<double[]> yArr(new double[num_points]); std::unique_ptr<double[]> zArr(new double[num_points]); // 解析数据 const unsigned char* ptr = data + sizeof(int); memcpy(xArr.get(), ptr, num_points * sizeof(double)); ptr += num_points * sizeof(double); memcpy(yArr.get(), ptr, num_points * sizeof(double)); ptr += num_points * sizeof(double); memcpy(zArr.get(), ptr, num_points * sizeof(double)); // 用Halcon重建点云 HalconCpp::HTuple hv_X, hv_Y, hv_Z; hv_X = HalconCpp::HTuple(num_points, xArr.get()); hv_Y = HalconCpp::HTuple(num_points, yArr.get()); hv_Z = HalconCpp::HTuple(num_points, zArr.get()); HalconCpp::HTuple hv_ObjectModel3D; HalconCpp::CreateObjectModel3d("point_cloud", hv_X, hv_Y, hv_Z, HalconCpp::HTuple(), HalconCpp::HTuple(), &hv_ObjectModel3D);
关键注意事项
- 字节序兼容:如果微服务运行在不同架构机器(如x86/ARM),需统一字节序(比如转大端),避免解析错误。可使用
htobe32处理int,htobe64处理double,或自定义转换函数。 - 内存安全:用智能指针(如
std::unique_ptr)管理缓冲区,避免大数据量点云导致的内存泄漏。 - 流操作规范:上传/下载GridFS流时必须关闭,否则会出现数据不完整问题。
- HTuple转换校验:将
double*转HTuple时,确保数组长度与点数量一致,防止越界。
内容的提问来源于stack exchange,提问作者Stefano Grossi
相关产品推荐
相关产品推荐

