2015-05-04 183 views
14

我通過網絡攝像頭捕獲視頻,該視頻給出了mjpeg流。 我在工作線程中做了視頻捕捉。 我開始捕獲這樣的:由於捕獲緩衝區導致OpenCV VideoCapture滯後

const std::string videoStreamAddress = "http://192.168.1.173:80/live/0/mjpeg.jpg?x.mjpeg"; 
qDebug() << "start"; 
cap.open(videoStreamAddress); 
qDebug() << "really started"; 
cap.set(CV_CAP_PROP_FRAME_WIDTH, 720); 
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 576); 

相機是餵養在20fps的流。 但是,如果我不喜歡這20fps的閱讀:

if (!cap.isOpened()) return; 

     Mat frame; 
     cap >> frame; // get a new frame from camera 
     mutex.lock(); 

     m_imageFrame = frame; 
     mutex.unlock(); 

然後有一個3+秒落後。 原因是捕獲的視頻首先存儲在緩衝區中。當我第一次啓動攝像機時,緩衝區會累積,但我沒有讀出幀。所以如果我從緩衝區讀取它總是給我舊的幀。 我現在唯一的解決方案是以30fps的速度讀取緩衝區,這樣可以快速清除緩衝區,並且沒有更嚴重的延遲。

是否有其他可能的解決方案,以便每次啓動相機時都可以手動清理/沖洗緩衝區?

+1

爲什麼要限制到20fps?你在工作者線程中等待嗎? – mirosval

+1

是緩衝你自己的一個或cv :: VideoCapture內的東西? – Micka

+0

@ mirosval,是的,我這樣做,因爲我不想要太多的CPU ... – Nyaruko

回答

16

根據this來源,可以設置cv::VideoCapture對象的緩衝區大小。

cv::VideoCapture cap; 
cap.set(CV_CAP_PROP_BUFFERSIZE, 3); // internal buffer will now store only 3 frames 

// rest of your code... 

然而有一個重要的限制:存儲在內部緩衝存儲器中幀的

CV_CAP_PROP_BUFFERSIZE金額(注:僅由DC1394 v 2.x的後端當前支持)


如果解決方案不起作用,請參閱this post,其中解釋瞭如何解決回答這個問題。

簡而言之:測量查詢幀所需的時間;如果它太低,則意味着幀被從緩衝區中讀取並可以被丟棄。繼續查詢幀,直到測量時間超過一定限度。發生這種情況時,緩衝區是空的,並且返回的幀是最新的。

(上鍊接後顯示了答案:從緩衝區中返回一個框架需要大約1/8返回一個最新的幀的時間您的里程可能會有所不同,當然!)


一個不同的解決方案,受this的啓發,是創建第三個線程,以高速連續抓取幀以保持緩衝區爲空。此線程應使用cv::VideoCapture.grab()以避免開銷。

您可以使用簡單的自旋鎖來同步實際工作線程和第三個線程之間的讀取幀。

+1

我真的不知道有什麼方法可以告訴我緩衝區是空的而不是測量時間。這是相當不方便的... – Nyaruko

+2

'cv :: VideoCapture'界面不允許你獲取這些信息。另一種解決方案是創建一個不斷抓取幀的不同線程(['cv :: VideoCapture.grab()'](http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_highgui/doc/reading_and_writing_images_high_guid_video.html#videocapture-grab )功能)。這將確保真正的工作線程讀取下一幀時緩衝區爲空(當然,不要忘記在讀取幀時同步這些線程)。 –

+0

謝謝,這就是我現在正在做的。 – Nyaruko

2

您可以確保抓取框架需要一點時間。編碼非常簡單,雖然有點不可靠;潛在的,這個代碼可能會導致死鎖。

#include <chrono> 
using clock = std::chrono::high_resolution_clock; 
using duration_float = std::chrono::duration_cast<std::chrono::duration<float>>; 
// ... 
while (1) { 
    TimePoint time_start = clock::now(); 
    camera.grab(); 
    if (duration_float(clock::now() - time_start).count() * camera.get(cv::CAP_PROP_FPS) > 0.5) { 
     break; 
    } 
} 
camera.retrieve(dst_image); 

該代碼使用C++ 11。

+0

根據[docs](http:// docs。 opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html)該功能的主要用途是在多相機環境中,特別是當相機沒有硬件同步時。也就是說,您爲每臺攝像機調用VideoCapture :: grab(),然後調用較慢的方法VideoCapture :: retrieve()來解碼並從每個攝像機獲取幀。這樣消除了去馬賽克或運動JPEG壓縮等的開銷,並且來自不同相機的檢索幀將會更接近時間。 **這不是修復**。但我高舉了它。 – AlexanderVX