使用OpenNI2读取oni格式的文件,并将其中的彩色视频,深度视频显示并保存

来源:互联网 发布:信捷plc编程软件xc,xd 编辑:程序博客网 时间:2024/03/29 06:05

前几天做人体行为识别的师姐在网上下载了一个以.oni格式保存的数据集,师姐让我帮忙将.oni格式的数据读出并保存为图片序列,以便能用OpenCV/matlab进行处理弄了两三天,终于能完整的将oni文件中的视频数据一帧不少的读出来了,现在将我的方法分享一下。


之前没有接触过OpenNI,这次用到OpenNI是从零开始学的,到今天我也只学了不到4天,有错误的地方还请指教。


首先说一下我对OpenNI2的认识,开始也用过OpenNI1.5但是发现太难,结构不如OpenNI2这种C++风格的清晰,因此,在最初接触了OpenNI几个小时后我就果断的选择了使用OpenNI2,而且OpenNI2的配置也相对比较简单,我用的版本是OpenNI-Windows-x86-2.2,系统是Win7+VS2010。(我的配置是按照这个方法进行的)


由于我仅仅用OpenNI2读取oni格式的数据并保存,下面就只谈与之相关的。(其他的我也不懂)


目前能查到的将oni格式文件中的视频流中每一帧视频都读出来的方法有两种:

一、设置循环。首先读取视频流中的总帧数,然后用这个总帧数设置循环次数每一次读取一帧并保存。

二、设置事件。为视频流添加一个事件,每当视频流更新则读取当前帧并保存。

这两种方法我都用过但是都会出现同样的问题,那就是丢帧,即不能按顺序的一帧不落的将所有帧都读出,究其原因可能是我的电脑太慢了,在视频流下一帧更新之前不能将当前帧读取并保存。


几经挣扎我终于找到的能一帧不落的读完所有帧,那就是要在设置循环的方法中设置视频流的速度,将视频流的速度设置为-1,即只有程序在进行读取视频流的时候视频流才更新,其他时间视频流是不动作的。


另外,程序中的图片显示以及保存都是用的OpenCV,关于OpenCV,网上的资料很多我就不做过多的介绍了,如果有需要还可以用OpenCV将oni文件保存为.avi的视频。


下面在程序以及注释中进行详细的解释


#include <iostream>#include <OpenNI.h>#include <opencv2\core\core.hpp>#include <opencv2\imgproc\imgproc.hpp>#include <opencv2\highgui\highgui.hpp>using namespace std;int main(){//定义oni文件中视频的总帧数以及得到的图片的保存目录int total = 0;char* imagefile = "D:\\data\\data1";//初始化OpenNI环境openni::OpenNI::initialize();//声明设备并打开oni文件openni::Device fromonifile;fromonifile.open("D:\\USER28_CAL11\\qw\\S13_C11_U26_D3.oni");//声明控制对象,这对视频流的控制起到了关键作用openni::PlaybackControl* pController = fromonifile.getPlaybackControl();//声明视频流对象以及帧对象openni::VideoStream streamColor;openni::VideoFrameRef frameColor;//验证是否有彩色传感器(是否有彩色视频)和建立与设备想关联的视频流if(fromonifile.hasSensor(openni::SENSOR_COLOR)){if(streamColor.create( fromonifile, openni::SENSOR_COLOR ) == openni::STATUS_OK ){cout<<"建立视频流成功"<<endl;}else{cerr<<"ERROR: 建立视频流没有成功"<<endl;system("pause");return -1;}}else{cerr << "ERROR: 该设备没有彩色传感器" << endl;system("pause");return -1;}//建立显示窗口cv::namedWindow("Image");//获取总的视频帧数并将该设备的速度设为-1以便能留出足够的时间对每一帧进行处理、显示和保存total = pController->getNumberOfFrames(streamColor);pController->setSpeed(-1);//开启视频流streamColor.start();for (int i = 1;i <= total; ++ i){//读取视频流的当前帧streamColor.readFrame(&frameColor);cout<<"当前正在读的帧数是:"<<frameColor.getFrameIndex()<<endl;cout<<"当前的循环次数是:  "<<i<<endl;//将帧保存到Mat中并且将其转换到BGR模式,因为在OpenCV中图片的模式是BGRcv::Mat rgbImg(frameColor.getHeight(), frameColor.getWidth(), CV_8UC3, (void*)frameColor.getData());cv::Mat bgrImg;cvtColor(rgbImg, bgrImg, CV_RGB2BGR);//将每一帧按顺序帧保存到图片目录下char imagefullname[255];char imagenum[50];sprintf(imagenum,"\\%03d.png",i);strcpy(imagefullname,imagefile);strcat(imagefullname,imagenum);cv::imwrite(imagefullname,bgrImg);//显示当前帧cv::imshow("Image",bgrImg);if (cv::waitKey(30) == 27){break;}}//销毁显示窗口cv::destroyWindow("Image");//关闭视频流streamColor.destroy();//关闭设备fromonifile.close();//关闭OpenNIopenni::OpenNI::shutdown();return 0;}

程序运行的截图(数据集是从网上下载的,不要在意视频上那个人,我也不认识)



如果想要读深度数据那么将30和32行的SENSOR_COLOR改成SENSOR_DEPTH

当然由于在OpenNI中的深度数据到OpenCV中要用CV_16UC1来保存并且要想较好的显示还需要下面的转换

const cv::Mat mImageDepth( frame.getHeight(), frame.getWidth(), CV_16UC1, (void*)frame.getData());int iMaxDepth = stream.getMaxPixelValue();cv::Mat mScaledDepth;mImageDepth.convertTo( mScaledDepth, CV_8U, 255.0 / iMaxDepth );


以上,就是我的一些经验,不免有疏漏,请多多包含。

参考:

http://www.cnblogs.com/yemeishu/archive/2013/01/11/2856859.html

http://blog.csdn.net/chenxin_130/article/details/8580636

http://www.dotblogs.com.tw/v6610688/archive/2014/03/02/openni_oni_format_file_to_avi_opencv.aspx#feedback

0 0