前几天做人体行为识别的师姐在网上下载了一个以.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中图片的模式是BGR
cv::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();
//关闭OpenNI
openni::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