如何从原始深度数据创建.oni文件?科研场景格式转换技术问询
解决方案:将TCP接收的原始深度数据转换为ONI格式
我来帮你梳理下实现这个需求的具体步骤——毕竟我之前也处理过类似的深度数据格式转换问题,踩过不少坑,应该能帮到你。
一、先搞懂ONI格式的核心逻辑
ONI是OpenNI的专属格式,本质是封装了深度流的元数据(分辨率、帧率、像素格式)和每帧的深度数据。刚好你的原始数据是无符号16位(uint16)的毫米级距离值,完美匹配OpenNI的DEPTH_1MM像素格式,这一步省了不少格式转换的麻烦。
二、整体流程拆解
1. 先搞定TCP数据的接收
首先得保证你能稳定收到完整的一帧深度数据:
- 先和发送端约定好TCP包结构:比如前4字节存帧宽,接下来4字节存帧高,后面跟着
宽×高×2字节的uint16数据(每个像素占2字节) - 接收时先解析宽高,再读取对应长度的字节流,转成uint16数组就行
2. 用OpenNI API生成ONI文件(最靠谱的方式)
OpenNI本身提供了录制ONI的API,完全不需要自己手动解析ONI的二进制结构。下面是核心代码逻辑(以C++为例,Python可以用pyopenni绑定):
#include <OpenNI.h> using namespace openni; int main() { // 初始化OpenNI环境 Status rc = OpenNI::initialize(); if (rc != STATUS_OK) { printf("OpenNI初始化失败: %s\n", OpenNI::getExtendedError()); return 1; } // 创建ONI录制器 Recorder recorder; rc = recorder.create("output.oni"); // 输出的ONI文件名 if (rc != STATUS_OK) { printf("创建录制器失败: %s\n", OpenNI::getExtendedError()); OpenNI::shutdown(); return 1; } // 创建虚拟深度流(因为没有实际的Kinect设备,得模拟一个流载体) VideoStream depthStream; rc = depthStream.create(Device(), SENSOR_DEPTH); if (rc != STATUS_OK) { printf("创建深度流失败: %s\n", OpenNI::getExtendedError()); recorder.destroy(); OpenNI::shutdown(); return 1; } // 配置流参数——必须和你的原始数据完全匹配! VideoMode vm = depthStream.getVideoMode(); vm.setResolution(640, 480); // 替换成你的实际分辨率(比如640×480) vm.setFps(30); // 替换成你的实际帧率(比如30fps) vm.setPixelFormat(PIXEL_FORMAT_DEPTH_1_MM); // 对应你的uint16毫米数据 rc = depthStream.setVideoMode(vm); if (rc != STATUS_OK) { printf("设置视频模式失败: %s\n", OpenNI::getExtendedError()); depthStream.destroy(); recorder.destroy(); OpenNI::shutdown(); return 1; } // 把深度流绑定到录制器 rc = recorder.attach(depthStream); if (rc != STATUS_OK) { printf("绑定流到录制器失败: %s\n", OpenNI::getExtendedError()); depthStream.destroy(); recorder.destroy(); OpenNI::shutdown(); return 1; } // 开始录制 rc = recorder.start(); if (rc != STATUS_OK) { printf("开始录制失败: %s\n", OpenNI::getExtendedError()); depthStream.destroy(); recorder.destroy(); OpenNI::shutdown(); return 1; } // 循环接收TCP数据并写入ONI while (true) { // 这里替换成你的TCP接收逻辑:获取一帧的uint16数组data,以及宽width、高height uint16_t* data = receiveDepthFrameFromTCP(width, height); // 获取OpenNI的空帧,把我们的数据拷进去 VideoFrameRef frame; depthStream.readFrame(&frame); memcpy(frame.getData(), data, width * height * sizeof(uint16_t)); // 写入录制器 recorder.writeFrame(frame); // 释放资源 delete[] data; frame.release(); // 可以加个退出条件,比如检测到用户输入退出指令 } // 收尾工作 recorder.stop(); recorder.destroy(); depthStream.destroy(); OpenNI::shutdown(); return 0; }
3. 几个必须注意的坑
- 元数据必须完全匹配:分辨率、帧率如果和原始数据对不上,播放ONI时画面会拉伸或错乱,这个一定要仔细核对
- TCP粘包处理:深度帧数据量不小(比如640×480的话是614400字节),必须用固定长度的帧头(比如宽高+帧总长度)来拆分数据包,避免接收不完整
- 内存管理:每次接收的帧数据要及时释放,不然跑久了会内存泄漏
- Python替代方案:如果不想写C++,可以用
pyopenni库,逻辑和上面完全一致,只是API调用更简洁
三、验证转换结果
转换完成后,用OpenNI自带的NiViewer工具打开生成的.oni文件,看看深度画面是否正常显示。如果没问题,就可以直接用NiTE的人体跟踪、手势识别等算法处理了。
内容的提问来源于stack exchange,提问作者user9251386
相关产品推荐
相关产品推荐

