2015-07-06 87 views
2

我想從使用OpenCV 2.4.11的視頻文件獲取特定幀。 我試圖按照正確的文檔和在線教程,現在已經測試了兩種方法:OpenCV VideoCapture:如何正確獲取特定幀?

1)第一種方法是使用video.grab()讀取每幀的蠻力,直到達到我想要的特定幀(時間戳)。如果視頻序列中的特定幀晚了,此方法會很慢!

string videoFile(videoFilename); 
VideoCapture video(videoFile); 
double videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC); 
int videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES)); 
while (videoTimestamp < targetTimestamp) 
{ 
    videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC); 
    videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES)); 

    // Grabe frame (but don't decode the frame as we are only "Fast forwarding") 
    video.grab(); 
} 
// Get and save frame 
if (video.retrieve(frame)) 
{ 
    char txtBuffer[100]; 
    sprintf(txtBuffer, "Video1Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber); 
    string imgName = txtBuffer; 
    imwrite(imgName, frame); 
} 

2)第二種方法我使用video.set(...)。如果特定幀在視頻序列中較晚,則此方法速度更快並且似乎不會更慢。

string videoFile(videoFilename); 
VideoCapture video2(videoFile); 
videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC); 
videoFrameNumber = static_cast<int>(video2.get(CV_CAP_PROP_POS_FRAMES)); 
video2.set(CV_CAP_PROP_POS_MSEC, targetTimestamp); 
while (videoTimestamp < targetTimestamp) 
{ 
    videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC); 
    videoFrameNumber = (int)video2.get(CV_CAP_PROP_POS_FRAMES); 

    // Grabe frame (but don't decode the frame as we are only "Fast forwarding") 
    video2.grab(); 
} 
// Get and save frame 
if (video2.retrieve(frame)) 
{ 
    char txtBuffer[100]; 
    sprintf(txtBuffer, "Video2Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber); 
    string imgName = txtBuffer; 
    imwrite(imgName, frame); 
} 

問題)現在的問題是,使用這兩種方法並最終與目標圖像幀的內容相同的幀數量不等於?!?

我很想知道方法1是正確的,並且OpenCV video.set(...)方法有問題。但是如果我使用VLC播放器找到近似的目標幀位置,實際上是方法2最接近「正確」的結果?

作爲一些額外的信息:我測試了相同的視頻序列,但在兩個不同的視頻文件分別編碼'avc1'MPG4和'WMV3'WMV編解碼器。

使用WMV文件找到的兩個框架是關閉的?

使用MPG4文件兩個找到的幀只是稍微關閉?

有沒有人有這方面的經驗,可以解釋我的發現並告訴我從視頻文件中獲取特定幀的正確方法?

+0

我不是這方面的專家,但我懷疑問題根本不是問題。檢查您的視頻文件是否包含索引。如果不是,那麼直接倒回到任何位置(不處理它之前的所有幀)只是比特率的平均估計。所以例程會估計文件從幀開始會有多少距離,然後圍繞關鍵幀塊的位置進行掃描。這可以是很多... – Spektre

回答

0

我認爲OpenCV使用FFmpeg進行視頻解碼。

我們曾經有類似的問題,但直接使用FFmpeg。默認情況下,不保證隨機(但確切)幀訪問。 WMV解碼器特別模糊。 較新版本的FFmpeg允許您訪問可用於爲框架構建檢索功能的較低級別的例程。這個解決方案有點牽扯,我現在還記得我的頭腦。我稍後嘗試找到更多細節。

作爲一個快速的解決方法,我建議解碼您的視頻離線然後工作序列關閉圖像。但是,這增加了所需的存儲量,它保證了精確的隨機幀訪問。您可以使用的FFmpeg來convert your video file in to a sequence of images這樣的:

ffmpeg -i "input.mov" -an -f image2 "output_%05d.png" 
+0

它仍然是不可能使用OpenCV提取特定幀的情況? – mobcdi

+0

我這麼認爲,是的。 – nils

0

顯然還是有OpenCV中/ ffmpeg的一個bug。 ffmpeg不提供所需的幀和/或opencv不處理這個。請參閱herehere

[編輯: ]在修正錯誤之前(無論是在ffmpeg還是(作爲opencv中的解決方法)),按照數字來確定幀的唯一方法就是像你一樣快速前進。 (關於VLC播放器:我猜想它使用了那個有問題的set()接口。對於一個播放器來說,尋找frame-exact通常不是太重要,但是對於編輯器來說)。]

+0

您錯過了觀點:評論不允許發佈爲答案。非現場鏈接也只有答案...你最後的編輯使事情變得更好,刪除過程結束,贊成不刪除你的帖子。 – Spektre

相關問題