opencv中cvSetCaptureProperty定位不准的原因及解决
以前在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 );
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
源代码包涵在OpenCV2.1\src\highgui\cvcap_ffmpeg.cpp文件中:
- bool CvCapture_FFMPEG::setProperty( int property_id, double value )
{
if( !video_st ) return false;
switch( property_id )
{
case CV_CAP_PROP_POS_MSEC:
case CV_CAP_PROP_POS_FRAMES:
case CV_CAP_PROP_POS_AVI_RATIO:
{
int64_t timestamp = 0;
AVRational time_base;
switch( property_id )
{
case CV_CAP_PROP_POS_FRAMES:
timestamp=(int64_t)value;
if(ic->start_time != AV_NOPTS_VALUE_)
timestamp += ic->start_time;
break;
case CV_CAP_PROP_POS_MSEC:
time_base=ic->streams[video_stream]->time_base;
timestamp=(int64_t)(value*(float(time_base.den)/float(time_base.num))/1000);
if(ic->start_time != AV_NOPTS_VALUE_)
timestamp += ic->start_time;
break;
case CV_CAP_PROP_POS_AVI_RATIO:
timestamp=(int64_t)(value*ic->duration);
if(ic->start_time != AV_NOPTS_VALUE_ && ic->duration != AV_NOPTS_VALUE_)
timestamp += ic->start_time;
break;
}
if ( filename )
{
// ffmpeg's seek doesn't work...
if (!slowSeek((int)timestamp))
{
fprintf(stderr, "HIGHGUI ERROR: AVI: could not (slow) seek to position %0.3f\n",
(double)timestamp / AV_TIME_BASE);
return false;
}
}
else
{
int ret = av_seek_frame(ic, video_stream, timestamp, AVSEEK_FLAG_ANY);
if (ret < 0)
{
fprintf(stderr, "HIGHGUI ERROR: AVI: could not seek to position %0.3f\n",
(double)timestamp / AV_TIME_BASE);
return false;
}
}
picture_pts=(int64_t)value;
}
break;
default:
return false;
}
return true;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
之后重新编译opencv库,就可以了。
P.S:测试的代码
- #include "opencv/highgui.h"
- #include <iostream>
- using namespace std;
- int main( int argc,char** argv )
- {
- cvNamedWindow( "Example2", CV_WINDOW_AUTOSIZE );
- CvCapture* capture = cvCreateFileCapture( "d://11.avi" );
- IplImage* frame;
- int pos=0;
- int pos1=0;
- while(1)
- {
- cvSetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES,pos);
- cout<<pos;
- frame = cvQueryFrame(capture);
- pos1=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES);
- cout<<"/t"<<pos1<<endl;
- if( !frame ) break;
- cvShowImage( "Example2", frame );
- char c = cvWaitKey(33);
- if( c == 27 ) break;
- pos++;
- }
- cvReleaseCapture( &capture );
- cvDestroyWindow( "Example2" );
- }
=========================================================================================================================
转载自:http://www.opencv.org.cn/forum/viewtopic.php?t=11276#p41262