opencv中cvSetCaptureProperty定位不准的原因及解决

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:测试的代码

  1. #include "opencv/highgui.h" 
  2. #include <iostream> 
  3. using namespace std; 
  4. int main( int argc,char** argv ) 
  5. {  
  6.     cvNamedWindow( "Example2", CV_WINDOW_AUTOSIZE ); 
  7.     CvCapture* capture = cvCreateFileCapture( "d://11.avi" ); 
  8.     IplImage* frame; 
  9.     int pos=0; 
  10.     int pos1=0; 
  11.     while(1) 
  12.     { 
  13.         cvSetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES,pos); 
  14.         cout<<pos; 
  15.         frame = cvQueryFrame(capture); 
  16.         pos1=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES); 
  17.         cout<<"/t"<<pos1<<endl; 
  18.         if( !frame ) break
  19.         cvShowImage( "Example2", frame ); 
  20.         char c = cvWaitKey(33); 
  21.         if( c == 27 ) break
  22.         pos++; 
  23.     } 
  24.     cvReleaseCapture( &capture ); 
  25.     cvDestroyWindow( "Example2" ); 

=========================================================================================================================

转载自:http://www.opencv.org.cn/forum/viewtopic.php?t=11276#p41262

 

你可能感兴趣的:(Stream,video,float,byte,iostream,avi)