2014-03-28 30 views
8

是否可以逐步從視頻讀取幀(例如,我想讀取視頻流的每第五幀)。目前我正在做這個解決方法,但效率不高。從OpenCV中的VideoCapture讀取每第n幀

bool bSuccess 
int FramesSkipped = 5; 
for (int a = 0; < FramesSkipped; a++) 
     bSuccess = cap.read(NextFrameBGR); 

任何建議,所以我不必循環五幀得到所需的幀?

回答

8

恐怕沒有什麼可以做的,這不僅僅是OpenCV的一個缺點。你看,現代視頻編解碼器通常是複雜的動物。爲了獲得更高的壓縮率,幀的編碼通常取決於之前的幀,有時甚至是連續的幀。

因此,大多數情況下,即使您不需要它們,也必須在所需幀之前解碼幀。

對於特定的視頻文件進行編碼有相當不平凡的技巧,因此獲得每一個N幀都很便宜,但在一般情況下不可行。

也就是說,您可以嘗試OpenCV提供的查找功能(請參閱OpenCV Seek Function/Rewind)。它可能(也可能不會)根據具體情況加快工作速度。但是,我個人並不打賭。

+0

我不是專家,所以可能是非常錯誤的,但有可能跳過中間P和B幀高達的最後一個I幀的幀需要之前? – saurabheights

+1

也許,它提供的搜索功能就是這樣做的。但我不認爲OpenCV中有一個用於編解碼器級別的細粒度操作的API。 –

+0

謝謝。我最終選擇使用ffmpeg來提取關鍵幀。發現從opencv提取的幀沒有與ffmpeg一樣好的質量。 – saurabheights

1

這裏是我的建議:

 CvCapture* capture = cvCaptureFromFile("input_video_path"); 
    int loop = 0; 
    IplImage* frame = NULL; 
    Mat matframe;                 
    char fname[20];                 
    do { 
     frame = cvQueryFrame(capture); 
     matframe = cv::cvarrToMat(frame); 
     cvNamedWindow("video_frame", CV_WINDOW_AUTOSIZE);         
     cvShowImage("video_frame", frame); 
     sprintf(fname, "frame%d.jpg", loop); 
     cv::imwrite(fname, matframe);//save each frame locally 
     loop++; 
     cvWaitKey(100); 
    } while(frame != NULL); 

現在你已經保存的所有幀局部可以快速讀取所需的第n個幀。
CATUION:我拍攝的12秒樣本視頻由200多幅圖像組成。這會吃掉很多空間。

簡單而有效的優化將使用您正在使用的方法或@sergie建議的方法來讀取第n幀。之後,您可以使用索引保存圖像,以便稍後在相同索引處查詢將返回保存的圖像,而不必像您一樣跳過幀。通過這種方式,您可以節省您在保存不需要查詢的幀以及浪費時間讀取&以及保存這些不需要的幀的空間。

+1

我正在運行130個1分鐘的4K視頻樣本,我向你保證這是一個非常糟糕的主意! –

4

我得到它在Python中工作...請參閱下面的兩個示例用例和一些注意事項。

首先,導入一些包

import cv2 
import math 
import numpy as np 

捕捉每n秒(這裏,n = 5)

#################### Setting up the file ################ 
videoFile = "Jumanji.mp4" 
vidcap = cv2.VideoCapture(videoFile) 
success,image = vidcap.read() 

#################### Setting up parameters ################ 

seconds = 5 
fps = vidcap.get(cv2.CAP_PROP_FPS) # Gets the frames per second 
multiplier = fps * seconds 

#################### Initiate Process ################ 

while success: 
    frameId = int(round(vidcap.get(1))) #current frame number, rounded b/c sometimes you get frame intervals which aren't integers...this adds a little imprecision but is likely good enough 
    success, image = vidcap.read() 

    if frameId % multiplier == 0: 
     cv2.imwrite("FolderSeconds/frame%d.jpg" % frameId, image) 

vidcap.release() 
print "Complete" 

或者,捕捉每n個幀(這裏,n = 10)

#################### Setting up the file ################ 
videoFile = "Jumanji.mp4" 
vidcap = cv2.VideoCapture(videoFile) 
success,image = vidcap.read() 

#################### Setting up parameters ################ 

#OpenCV is notorious for not being able to good to 
# predict how many frames are in a video. The point here is just to 
# populate the "desired_frames" list for all the individual frames 
# you'd like to capture. 

fps = vidcap.get(cv2.CAP_PROP_FPS) 
est_video_length_minutes = 3   # Round up if not sure. 
est_tot_frames = est_video_length_minutes * 60 * fps # Sets an upper bound # of frames in video clip 

n = 5        # Desired interval of frames to include 
desired_frames = n * np.arange(est_tot_frames) 


#################### Initiate Process ################ 

for i in desired_frames: 
    vidcap.set(1,i-1)      
    success,image = vidcap.read(1)   # image is an array of array of [R,G,B] values 
    frameId = vidcap.get(1)    # The 0th frame is often a throw-away 
    cv2.imwrite("FolderFrames/frame%d.jpg" % frameId, image) 

vidcap.release() 
print "Complete" 

這就是它。


一些不幸的警告......根據您的opencv版本(這是爲opencv V3構建的),您可能需要以不同的方式設置fps變量。詳情請參閱 here。要了解您的版本,你可以做到以下幾點:

(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.') 
major_ver 
+0

在你的第二個例子中,你可以使用'cv2.CAP_PROP_FRAME_COUNT'來獲得確切的幀數並且避免對其進行計算。 另外,你能解釋'n * np.arange(est_tot_frames)'的用途嗎?它似乎沒有做它應該做的。 – StephaneMombuleau

相關問題