歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

OpenCV 2.3.1 中關於cvCaptureProperty()定位不准的問題

問題說明:

OpenCV 2.X 版本中,調用cvCaptureProperty()定位視頻到指定幀,采用下面兩種方法都會出現定位不准的問題。

  1. cvSetCaptureProperty( capture, CV_CAP_PROP_POS_AVI_RATIO, t)  
  1. cvSetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES, t);  
都會顯示諸如此類的錯誤警告信息:

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

作者實驗中使用的測試代碼如下:

  1. #include "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.   
  10.    int pos=0;  
  11.    int pos1=0;  
  12.    while(1)  
  13.    {  
  14.       cvSetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES,pos);  
  15.       cout<<pos;  
  16.       frame = cvQueryFrame(capture);  
  17.   
  18.       pos1=cvGetCaptureProperty(capture,CV_CAP_PROP_POS_FRAMES);  
  19.       cout<<"\t"<<pos1<<endl;  
  20.   
  21.       if( !frame ) break;  
  22.       cvShowImage( "Example2", frame );  
  23.       char c = cvWaitKey(33);  
  24.       if( c == 27 ) break;  
  25.   
  26.       pos++;  
  27.    }  
  28.    cvReleaseCapture( &capture );  
  29.    cvDestroyWindow( "Example2" );  
  30. }  
作者發現,在OpenCV 2.X版本中,隨著pos值遞增,pos1值並不與pos1相等,而是有不規則的跳動,造成無法准確定位視頻幀。


原因與改進方法:

原因在於opencv2.0以後,采用ffmpeg采集視頻,而在opencv1.0采用vfw采集視頻(具體的概念暫時還不清楚,有時間繼續補上)。而opencv在定位時候,調用的ffmpeg的av_seek_frame()函數,此函數原型為:

  1. 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,

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

也就是按照默認的讀取關鍵幀。因此,視頻跳躍就出現了。

解決這個問題需要將0改為 AVSEEK_FLAG_ANY ,即:

  1. int ret = av_seek_frame(ic, video_stream, timestamp, AVSEEK_FLAG_ANY );  
之後重新編譯opencv庫,就可以了。

我在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 ) 中

相關的原始代碼如下:

  1. int flags = AVSEEK_FLAG_FRAME;  
  2. if (timestamp < ic->streams[video_stream]->cur_dts)  
  3. flags |= AVSEEK_FLAG_BACKWARD;  
  4. int ret = av_seek_frame(ic, video_stream, timestamp, flags);  
  5. if (ret < 0)  
  6. {  
  7.    fprintf(stderr, "HIGHGUI ERROR: AVI: could not seek to position %0.3f\n",  
  8.           (double)timestamp / AV_TIME_BASE);  
  9.    return false;  
  10. }  
問題就在於flags的值為 AVSEEK_FLAG_FRAME,而不是AVSEEK_FLAG_ANY

僅修改第一行,還是不能達到效果。

與OpenCV 2.0的代碼進行比較,發現OpenCV 2.0的代碼更少,在2.0版本基礎上進行修改:

  1. int ret = av_seek_frame(ic, video_stream, timestamp, AVSEEK_FLAG_ANY);  
  2. if (ret < 0)  
  3. {  
  4.     fprintf(stderr, "HIGHGUI ERROR: AVI: could not seek to position %0.3f\n",  
  5.     (double)timestamp / AV_TIME_BASE);  
  6.     return false;  
  7. }  
這樣就可以正確定位了,但還不清楚這樣修改對2.3整體代碼有什麼影響,有待更進一步研究。
Copyright © Linux教程網 All Rights Reserved