2012-07-13 12 views
10

Sto cercando di passare a un fotogramma specifico impostando la proprietà CV_CAP_PROP_POS_FRAMES e quindi la lettura del frame del tipo:Ottenere i singoli fotogrammi che utilizzano CV_CAP_PROP_POS_FRAMES in cvSetCaptureProperty

cvSetCaptureProperty(input_video, CV_CAP_PROP_POS_FRAMES, current_frame); 
frame = cvQueryFrame(input_video); 

Il problema che sto affrontando è che, OpenCV 2.1 rendimenti lo stesso frame per i 12 valori consecutivi di current_frame mentre voglio leggere ogni singolo frame, non solo i fotogrammi chiave. Qualcuno può dirmi cosa c'è che non va?


Ho fatto qualche ricerca e ho scoperto che il problema è causato dall'algoritmo di decompressione.

Gli algoritmi MPEG (inclusi HD, et tutti) non comprimono ciascun frame separatamente, ma salvano un fotogramma chiave di volta in volta, quindi solo le differenze tra l'ultimo fotogramma e i fotogrammi successivi.

Il problema segnalato è causato dal fatto che, quando si seleziona un frame, il decoder (ffmpeg, probabilmente) avanza automaticamente al fotogramma chiave successivo.

Quindi, c'è un modo per aggirare questo? Non voglio solo fotogrammi chiave ma ogni singolo fotogramma.

+0

Non sono stato in grado di trovare alcuna soluzione a questo. quello che sto facendo è evitare OpenCv e svolgere il compito in Matlab che fornisce ogni frame ma a scapito della bassa velocità. qualsiasi soluzione al problema in OpenCv per favore: -/ – Random

risposta

1

CV_CAP_PROP_POS_FRAMES passa a un fotogramma chiave. Ho avuto lo stesso problema e ho lavorato attorno usando questo codice (python). Probabilmente non è del tutto efficiente, ma ottenere è il lavoro fatto:

def seekTo(cap, position): 
    positiontoset = position 
    pos = -1 
    cap.set(cv.CV_CAP_PROP_POS_FRAMES, position) 
    while pos < position: 
    ret, image = cap.read() 
    pos = cap.get(cv.CV_CAP_PROP_POS_FRAMES) 
    if pos == position: 
     return image 
    elif pos > position: 
     positiontoset -= 1 
     cap.set(cv.CV_CAP_PROP_POS_FRAMES, positiontoset) 
     pos = -1 
4

Non so se questo sarebbe sufficiente precisione per il vostro scopo, ma ho avuto il successo di arrivare a un punto particolare in un MPEG video afferrando la frequenza dei fotogrammi, convertendo il numero di fotogramma in un tempo, quindi avanzando al tempo. In questo modo:

cv::VideoCapture sourceVideo("/some/file/name.mpg"); 
double frameRate = sourceVideo.get(CV_CAP_PROP_FPS); 
double frameTime = 1000.0 * frameNumber/frameRate; 
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime); 
+1

Ho lo stesso problema. Anche se questa risposta non ha risolto il problema, ho trovato una cosa mancante: frameTime è in secondi e dovrebbe essere in msec: 'double frameTime = 1000.0 * frameNumber/frameRate;' – cbuchart

+0

Good point, @cbuchart. Ho aggiornato la risposta. Puoi dirmi perché questa risposta non ha funzionato per te? –

+0

Sembra che manchi un fotogramma chiave. Anche se creo un sourceVideo.set (CV_CAP_PROP_POS_MSEC, 0.0) e comincio da lì, mi dà solo fotogrammi sbagliati (con effetto "fantasma") – cbuchart

3

A causa di questa limitazione in OpenCV, può essere saggio da utilizzare per FFMPEG invece. Moviepy è una bella libreria di wrapper.

# Get nth frame from a video 
from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader 
cap = FFMPEG_VideoReader("movie.mov",True) 
cap.initialize() 
cap.get_frame(n/FPS) 

Anche le prestazioni sono eccezionali. La ricerca sull'ennesimo fotogramma con get_frame è O (1), e viene utilizzato un aumento di velocità se vengono richiesti (quasi) fotogrammi consecutivi. Ho ottenuto risultati migliori del tempo reale caricando simultaneamente tre video da 720p.

0

ho usato con successo quanto segue su OpenCV 3/Python 3:

# Skip to 150 frame then read the 151th frame 
cap.set(cv2.CAP_PROP_POS_FRAMES, 150)) 
ret, frame = cap.read() 
0

Dopo alcuni anni assumendo questo come un bug insalvabile, credo di aver trovato un modo per utilizzare con un buon equilibrio tra velocità e correttezza.

Una soluzione precedente suggerito di utilizzare la proprietà CV_CAP_PROP_POS_MSEC prima di leggere la struttura:

cv::VideoCapture sourceVideo("/some/file/name.mpg"); 
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS); 

void readFrame(int frameNumber, cv::Mat& image) { 
    const double frameTime = 1000.0 * frameNumber/frameRate; 
    sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime); 
    sourceVideo.read(image); 
} 

Funzione RETURN telaio previsto, ma il problema è che l'utilizzo CV_CAP_PROP_POS_MSEC può essere molto lenta, ad esempio per un conversione video.

Nota: utilizzo di variabili globali per semplicità.


D'altra parte, se si desidera solo per leggere il video in sequenza, è sufficiente leggere telaio senza cercare a tutti.

for (int frameNumber = 0; frameNumber < nFrames; ++frameNumber) { 
    sourceVideo.read(image); 
} 

La soluzione nasce dalla combinazione sia: utilizzando una variabile per ricordare l'ultimo fotogramma interrogato, lastFrameNumber, e cercare solo quando telaio richiesto non è la prossima. In questo modo è possibile aumentare la velocità in una lettura sequenziale consentendo nel contempo la ricerca casuale se necessario.

cv::VideoCapture sourceVideo("/some/file/name.mpg"); 
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS); 
const int lastFrameNumber = -2; // guarantee seeking the first time 

void readFrame(int frameNumber, cv::Mat& image) { 
    if (lastFrameNumber + 1 != frameNumber) { // not the next frame? seek 
    const double frameTime = 1000.0 * frameNumber/frameRate; 
    sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime); 
    } 

    sourceVideo.read(image); 
    lastFrameNumber = frameNumber; 
} 
Problemi correlati