您的位置:首页 > 运维架构

opencv学习笔记(二)——播放AVI视频(下)

2015-04-01 19:12 253 查看
从上一篇可以看出opencv播放视频很是简单,我的环境是VS2010+opencv2.49.这篇文章中我们就一起学习了如何在OpenCV中用createTrackbar函数创建和使用轨迹条,以视频进度的调整。废话少说,先上代码和结果截图再说。

#include<iostream>
#include<highgui.h>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

//进度条控制
void onTrackbarSlide(int pos,void* capture){
(*((VideoCapture*)capture)).set(CV_CAP_PROP_POS_FRAMES,pos);
}
int main(void){

//打开视频文件
VideoCapture capture("海贼王.avi");

//isOpen判断视频是否打开成功
if(!capture.isOpened())
{
cout<<"Movie open Error"<<endl;
return -1;
}
//获取视频帧频
double rate=capture.get(CV_CAP_PROP_FPS);
cout<<"帧率为:"<<" "<<rate<<endl;
//获取总帧数
int total_rate=capture.get(CV_CAP_PROP_FRAME_COUNT);
cout<<"总帧数为:"<<" "<<total_rate<<endl;//输出帧总数

Mat frame;
namedWindow("Movie Player");

int slider_position=0;
//创建进度条
if(total_rate!=0){
createTrackbar("position","Movie Player",&slider_position,total_rate,onTrackbarSlide,&capture);
}

while(1)
{
//读取视频帧
if(!capture.read(frame))
break;
imshow("Movie Player",frame);
//获取按键值
char c=waitKey(33);
if(c==27)
break;
}
capture.release();
destroyWindow("Movie Player");
return 0;
}


运行结果如下:



这个结果界面比原来增加一个进度条。通过进度条我们可以调节播放的的进度。和上一篇的代码相比,这次在代码里面我们就添加了几行代码。来吧,我们先来看看进度条的创建与使用。

我是焦点 || 创建轨迹条——createTrackbar

我们先来看看这个函数的原型:

C++: int createTrackbar(conststring& trackbarname, conststring& winname,
int* value, int count, TrackbarCallback onChange=0,void* userdata=0);


第一个参数,const string&类型的trackbarname,表示进度条的名字,用来代表我们创建的轨迹条

第二个参数,const string&类型的winname,填窗口的名字,表示这个进度条会依附到哪个窗口上,即对应namedWindow()创建窗口时填的某一个窗口名,滑动条不会遮挡图像。

第三个参数,int* 类型的value,一个指向整型的指针,表示滑块的位置。并且在创建时,滑块的初始位置就是该变量当前的值,当滑动条被拖到时,OpenCV会自动将进度条当前位置所代表的值传给指针指向的整数。

第四个参数,int类型的count,表示滑块可以达到的最大位置的值。注意:滑块最小的位置的值始终为0

第五个参数,TrackbarCallback类型的onChange,首先注意他有默认值0。这是一个指向回调函数的指针,每次滑块位置改变时,这个函数都会进行回调。并且这个函数的原型必须为void XXXX(int,void*);其中第一个参数是轨迹条的位置,第二个参数是用户数据(看下面的第六个参数)。如果回调是NULL指针,表示没有回调函数的调用,仅第三个参数value有变化。

第六个参数,void*类型的userdata,他也有默认值0。这个参数是用户传给回调函数的数据,用来处理进度条事件。在上述代码我就是使用该参数传递一个VideoCapture变量,以至于进度条能控制视频帧。

这个createTrackbar函数,为我们创建一个具有特定名称和范围的进度条,并且指定一个和进度条位置同步的变量。而且要指定回调函数onChange(第五个参数),在轨迹条位置改变的时候来调用这个回调函数。可能有些朋友看完上述解释还是有点模糊,还是搞不懂进度条怎么和视频联系到一块呢?首先是进度条通过窗口的名字(第二个参数)绑定到了指定的窗口上,再通过一个整形变量(第三个参数)记录进度条所滑动到达的位置。当进度条滑动时候,会触发回调掉函数(第五个参数),而第六个参数是正是回调函数的一个参数。

下面我们看到自己写的一个回调函数onTrackbarSlide(int pos,void* capture),他的作用就是当进度条被触发时候,设置读取视频帧的位置。里面的set函数上篇博客已经有所介绍,这里就不多说啦。

注意

但是当我们在播放时候,我们移动进度条,该进度条总是定位不准,比如说我们想滑动进度条到500帧,结果是很难滑到该帧处的,会出现跳跃的现象。因为我的是opencv2.0版本,同样的代码在opencv1.0里面可能完全没错。可是这是为什么?在网上搜了一些资料,把他贴到这里供大家参考(/article/2415180.html)。

原因在于opencv2.0以后,采用ffmpeg采集视频,而在opencv1.0采用vfw采集视频。而opencv在定位时候,调用的ffmpeg的av_seek_frame()函数,此函数原型为:

int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);

其中,最后一个参数有

AVSEEK_FLAG_BACKWARD = 1; ///< seek backward

AVSEEK_FLAG_BYTE = 2; ///< seeking based on position >in bytes

AVSEEK_FLAG_ANY = 4; ///< seek to any frame, even non >key-frames

ffmpeg默认的是选取关键帧(这个概念需要具体定义)。opencv里面这个函数的参数flag是0,

int ret = av_seek_frame(ic, video_stream, timestamp, 0);

也就是按照默认的读取关键帧。因此,视频跳跃就出现了。

解决这个问题需要将0改为 AVSEEK_FLAG_ANY ,即:

int ret = av_seek_frame(ic, video_stream, timestamp, AVSEEK_FLAG_ANY );

之后重新编译opencv库,就可以了。

视频播放的例程就讲解到这里,在关于进度条的只是还有几个函数也是经常用到的如获取当前进度条的位置——getTrackbarPos函数,以及设置当前进度条的位置函数setTrackbarPos()等等,这些函数都定义在opencv2/highgui/highgui.hpp路径的highgui.hpp里面,有需要的可以去里面查看。这些函数也是很容易理解的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: