最近在手机一些手势的数据,所以需要使用的手机的录像功能来收集手势数据,我们采取的办法是,录一整段视频,其中三秒换一次动作,这样在后期处理的过程中,只需要对一整段视频每三秒截成一小段,这样的话就可以得到每一种手势作为训练的样本了,这些当然是后话了。
这里要分享的是在截视频的过程中遇到的一些问题,主要有两个问题:
1、关于压缩参数CV_FOURCC的问题;
2、关于手机前置摄像头的帧率变化问题。
我的代码是在win10+VS2013+OpenCV3.1下运行的。
对于视频的操作,主要是用到了OpenCV中的VideoCapture类和VideoWriter类,关于这两个类的操作(即视频的读写),可以参考下面的链接:
http://blog.jobbole.com/84231/
OpenCV中对视频的读写是以帧为单位的,我们定义一个VideoCapture类的对象,然后调用该对象的read()成员函数可以实现对视频每一帧的读入,同样的道理,定义一个VideoWriter类的对象, 就可以实现视频的写出。其中,有一点需要注意的地方是:
VideoWriter writer; //定义关于视频写出类VideoWriter的实例
writer.open(videoPathCutTotal, CV_FOURCC('D', 'I', 'V', 'X'), rate, videoSize);
在调用VideoWriter类的对象writer的open函数的时候,有一个
CV_FOURCC参数,这个参数表示的是,将视频写出的时候采用的压缩方式,不同的压缩方式会有不同的效果,当然由于编解码器的原因,可能某些压缩方式是不支持的,一开始的时候,我把写出的视频的格式设为.mp4,但是一直出错(在我这里,当读入视频是.mp4/.avi/.mov都是可以的),后来我把写出的格式改成.avi就可以了。所以,感觉使用OpenCV的话,还是对.avi格式的视频进行操作相对来说比较保险一点。
关于CV_FOURCC参数可以查看下面的链接:
http://www.07net01.com/program/144635.html
除了遇到上面的问题之外,还有一个比较有趣的现象就是,我发现我使用的华为荣耀6的前置摄像头录出来的视频帧率是变化的,比如说,我录了A、B、C、D、E五段视频,可能A、C的帧率是15帧/秒,而B、E的帧率是24帧/秒,D可能是另外的帧率。对这个现象比较感兴趣,我拿了几个朋友的手机做了简单的测试。我发现,我测试的苹果、魅族、三星手机的前置摄像头的帧率是不变的,而华为、小米手机的前置摄像头的帧率是变化的。我这里只是简单地做了一下测试,可能没有什么代表性,而且大家注意,这里讨论的是“前置”摄像头,有兴趣的朋友可以用自己的手机试试,检验一下。
说这个现象只是想着可能以后有朋友在截视频的时候,注意到这样的一个细节可能会有所帮助。
所以,最后,我选择了用前置摄像头录像帧率固定的苹果手机作为样本的采集手机(该手机帧率为30帧/秒),这样的话,我在程序里面只需要进行简单的计数,当读入和写出的图片数目为90张(即3秒长)的时候,就将输出的视频保存为一小段,具体的可以参考下面的代码:
#include
#include
#include
using namespace std;
using namespace cv;
string num2str(double); //自己定义的函数,用于将数字转换成string
int main()
{
int videoNum = 0;
string videoPath = "F:\\My_Desktop\\video\\IMG_0098.mov"; //待切割的视频
string videoPathCut = "F:\\My_Desktop\\video\\cut1\\"; //切割后的视频存放的文件夹
string videoPathCutTotal = "F:\\My_Desktop\\video\\cut1\\0.avi";
VideoCapture capture(videoPath); //定义关于视频处理类VideoCapture的实例
if (!capture.isOpened())
{
cout << "Loading error!\n";
abort();
}
int rate = 30; //帧率
Size videoSize(capture.get(CV_CAP_PROP_FRAME_WIDTH),capture.get(CV_CAP_PROP_FRAME_HEIGHT)); //获取读入的视频的宽和高规格
VideoWriter writer; //定义关于视频写出类VideoWriter的实例
writer.open(videoPathCutTotal, CV_FOURCC('D', 'I', 'V', 'X'), rate, videoSize);
Mat frame;
while (1)
{
if (capture.read(frame))
{
videoNum++;
writer << frame;
if (!(videoNum%(rate*3)))
{
videoPathCutTotal = videoPathCut + num2str(videoNum / (rate * 3)) + ".avi"; //当读入rate * 3张图片(这里rate=30,所以为90)的时候,修改视频写出的路径,起到对视频进行分段的效果
writer.open(videoPathCutTotal, CV_FOURCC('D', 'I', 'V', 'X'), rate, videoSize);
}
}
else
{
break;
}
}
}
string num2str(double i)
{
stringstream ss;
ss << i;
return ss.str();
}