2015-05-04 21 views
14

Sto catturando video attraverso una webcam che fornisce un flusso mjpeg. Ho eseguito l'acquisizione video in un thread di lavoro. comincio la cattura in questo modo:OpenCV VideoCapture lag a causa del buffer di acquisizione

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); 

la fotocamera sta alimentando il flusso a 20fps. Ma se ho fatto la lettura di 20fps in questo modo:

if (!cap.isOpened()) return; 

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

     m_imageFrame = frame; 
     mutex.unlock(); 

Poi c'è un 3+ secondi ritardo. Il motivo è che il video catturato viene prima memorizzato in un buffer. Quando accendo la videocamera per la prima volta, il buffer si accumula ma non ho letto i fotogrammi. Quindi se leggo dal buffer mi dà sempre le vecchie cornici. L'unica soluzione che ho ora è quella di leggere il buffer a 30fps in modo che pulisca rapidamente il buffer e non ci sia lag più serio.

Esiste un'altra soluzione possibile in modo che sia possibile pulire/svuotare il buffer manualmente ogni volta che si avvia la fotocamera?

+1

Perché si desidera limitare a 20 fps? Stai aspettando nel thread di lavoro? – mirosval

+1

è quel buffer proprio o qualcosa all'interno di cv :: VideoCapture? – Micka

+0

@ mirosval, sì, l'ho fatto perché non voglio troppo CPU ... – Nyaruko

risposta

16

In base all'origine this, è possibile impostare il buffer di un oggetto cv::VideoCapture.

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

// rest of your code... 

C'è una limitazione importante tuttavia:

CV_CAP_PROP_BUFFERSIZE Numero di frame memorizzati nella memoria buffer interna (nota: supportato solo da DC1394 v 2.x backend entra)


Se la soluzione non funziona, dare un'occhiata a this post che spiega come ack intorno al problema.

In breve: viene misurato il tempo necessario per interrogare un frame; se è troppo basso, significa che il frame è stato letto dal buffer e può essere scartato. Continua a interrogare i frame finché il tempo misurato supera un certo limite. Quando ciò accade, il buffer è vuoto e il frame restituito è aggiornato.

(La risposta sulle spettacoli pubblicare collegate: la restituzione di un fotogramma dal buffer è di circa 1/8th momento della restituzione di un fino a telaio data di situazione potrebbe essere diversa, naturalmente.!)


Una soluzione diversa, ispirata al post this, consiste nel creare un terzo thread che recuperi i fotogrammi in modo continuo ad alta velocità per mantenere il buffer vuoto. Questo thread dovrebbe utilizzare cv::VideoCapture.grab() per evitare l'overhead.

È possibile utilizzare un semplice spin-lock per sincronizzare i frame di lettura tra il thread di lavoro reale e il terzo thread.

+1

Mi chiedo davvero se c'è un modo per dirmi se il buffer è vuoto invece di misurare il tempo. È piuttosto scomodo ... – Nyaruko

+2

L'interfaccia 'cv :: VideoCapture' non ti consente di acquisire tali informazioni. Un'altra soluzione è la creazione di un thread diverso che afferra continuamente i frame (con ['cv :: VideoCapture.grab()'] (http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#videocapture-grab)) ad alta velocità. Ciò assicurerà che il buffer sia vuoto quando il thread worker reale legge il frame successivo (e non dimenticare di sincronizzare quei thread durante la lettura dei frame, ovviamente). –

+0

Grazie, è quello che sto facendo ora. – Nyaruko

2

È possibile assicurarsi che l'acquisizione del fotogramma abbia richiesto un po 'di tempo. È abbastanza semplice da codificare, anche se un po 'inaffidabile; potenzialmente, questo codice potrebbe portare a un deadlock.

#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); 

Il codice utilizza C++ 11.

+0

Secondo [documenti] (http: // docs. opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html) L'uso principale della funzione è negli ambienti multi-camera, specialmente quando le telecamere non hanno sincronizzazione hardware. Cioè, chiami VideoCapture :: grab() per ogni telecamera e successivamente chiami il metodo più lento VideoCapture :: retrieve() per decodificare e ottenere frame da ogni telecamera. In questo modo viene eliminato il sovraccarico sulla demosaffazione o la decompressione jpeg del movimento, ecc. E i fotogrammi recuperati da telecamere differenti saranno più vicini nel tempo. ** Questa non è la soluzione **. Ma l'ho svalutato. – AlexanderVX

Problemi correlati