以前在opencv2.0里面用到cvSetCaptureProperty函数的时候总是发生定位不准确的问题,明明是让其跳到100帧,结果却总不是100帧,定位一段连续的视频,总是出现跳跃的现象。同样的代码在opencv1.0里面完全没错。可是这是为什么?这个问题一直困扰了我半年,终于在今天知道原因了。
经过差不多一晚上的探究,得出粗略的结论。原因在于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库,就可以了。
P.S:测试的代码
参考:http://wsqhs.spaces.live.com/blog/cns!94F639580F58209C!697.entry
http://www.ffmpeg.com.cn/index.php/%E5%85%B3%E4%BA%8E_frame%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86
问题说明:
OpenCV 2.X 版本中,调用cvCaptureProperty()定位视频到指定帧,采用下面两种方法都会出现定位不准的问题。
HIGHGUI ERROR: AVI: could not seek to position 2.701
其中黄色数字就是OpenCV函数中对应的帧数,不知道因为什么原因,变成非整数,与之前程序中指定的帧数不一致,导致无法定位到准确的位置。
之前用OpenCV 2.2版本,一样出现相同的问题。而使用OpenCV 1.1版本,就可以正常定位。
更详细的问题说明:
很多人都遇到这个问题,更详细的实验可以参见下面文章:
《设定cvSetCaptureProperty后取帧不准的问题》 见 http://www.linuxidc.com/Linux/2011-10/46045.htm
作者实验中使用的测试代码如下:
原因与改进方法:
原因在于opencv2.0以后,采用ffmpeg采集视频,而在opencv1.0采用vfw采集视频(具体的概念暂时还不清楚,有时间继续补上)。而opencv在定位时候,调用的ffmpeg的av_seek_frame()函数,此函数原型为:
其中,最后一个参数有
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,
也就是按照默认的读取关键帧。因此,视频跳跃就出现了。
解决这个问题需要将0改为 AVSEEK_FLAG_ANY ,即:
我在OpenCV 2.3.1中的处理方法:
OpenCV 2.3.1中的与cvCaptureProperty()和FFMPEG相关的文件是:opencv2.3.1解压目录\modules\highgui\src\cap_ffmpeg_impl.hpp
在函数 bool CvCapture_FFMPEG::setProperty( int property_id, double value ) 中
相关的原始代码如下:
仅修改第一行,还是不能达到效果。
与OpenCV 2.0的代码进行比较,发现OpenCV 2.0的代码更少,在2.0版本基础上进行修改: