web-dev-qa-db-fra.com

Lecture de chaque nième image à partir de VideoCapture dans OpenCV

Est-il possible de lire les images d'une vidéo par étapes (par exemple, je veux lire chaque cinquième image d'un flux vidéo). Actuellement, je fais ceci comme solution de contournement mais ce n'est pas très efficace.

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

Avez-vous des suggestions pour ne pas avoir à parcourir les cinq cadres pour obtenir le cadre souhaité?

12
user3079474

J'ai bien peur que vous ne puissiez pas faire grand chose et que ce ne soit pas simplement une lacune d'OpenCV. Vous voyez, les codecs vidéo modernes sont généralement des bêtes complexes. Pour obtenir un taux de compression plus élevé, l'encodage d'une image dépend souvent d'images précédentes et parfois même successives.

Donc, la plupart du temps, vous devez décoder les images avant celle désirée, même si vous n'en avez pas besoin.

Il existe des astuces non triviales pour encoder spécifiquement un fichier vidéo, de sorte qu'il serait peu coûteux d'obtenir chaque Nième image, mais ce n'est pas faisable dans le cas général.

Cela dit, vous pouvez essayer la fonctionnalité de recherche fournie par OpenCV (voir OpenCV Seek Function/Rewind ). Cela peut (ou pas) fonctionner plus rapidement en fonction des circonstances. Cependant, personnellement, je ne parierais pas là-dessus.

12
Sergei Nosov

Voici ce que je suggère:

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

Maintenant que vous avez sauvegardé tous les cadres localement, vous pouvez rapidement lire le nième cadre souhaité.
CATUION: Un exemple de vidéo de 12 secondes que j'ai eu était composé de> 200 images. Cela va prendre beaucoup d'espace.

Une optimisation simple mais efficace consistera à lire le nième cadre en utilisant l’approche que vous utilisez ou celle suggérée par @sergie. Après cela, vous pouvez enregistrer l'image avec son index afin qu'une requête ultérieure portant le même index renvoie l'image enregistrée au lieu de devoir ignorer les cadres tels que vous êtes. De cette façon, vous économiserez de l’espace que vous auriez perdu en sauvegardant des images que vous n’auriez pas demandées et où il a fallu du temps pour lire et enregistrer ces images non désirées.

1
Deepthought

Lorsque j'avais le même objectif avec OpenCV, je me suis concentré sur le nombre "d'images clés" que je voulais par seconde de vidéo, quel que soit le taux de trame ou le nombre total de trames. Donc, cela me donne la N-ème clé étant donné mon KPS cible.

# python3.6 code using OpenCV 3.4.2

import cv2
KPS = 5 # Target Keyframes Per Second
VIDEO_PATH = "path/to/video/folder" # Change this
IMAGE_PATH = "path/to/image/folder" # ...and this 
EXTENSION = ".png"

cap = cv2.VideoCapture(VIDEO_PATH)
    # Set frames-per-second for capture
    fps = round(cap.get(cv2.CAP_PROP_FPS))
    hop = round(fps / KPS)
    curr_frame = 0
    while(True):
        ret, frame = cap.read()
        if not ret: break
        if curr_frame % hop == 0:
            print('Creating... {0}'.format(name,))
            name = IMAGE_PATH + "_" + str(curr_frame) + EXTENSION
            cv2.imwrite(name, frame)
        curr_frame += 1
    cap.release()

Notez que je roule dans toutes les images, mais que je n’écris que la N-ième image en utilisant hop comme N.

0
azatar

Il n’est pas possible d’extraire des trames aléatoires car le schéma de codage est généralement extrêmement complexe. Par exemple, dans MPEG-4, seules les informations contenant la différence entre deux images sont stockées. Par conséquent, les images précédentes sont clairement requises.

0
Dheeraj M Pai

J'ai réussi à utiliser Python 3 avec un simple compteur et en définissant la capture sur l'image de ce compteur, comme suit:

import cv2

cap = cv2.VideoCapture('XYZ.avi')
count = 0

while cap.isOpened():
    ret, frame = cap.read()

    if ret:
        cv2.imwrite('frame{:d}.jpg'.format(count), frame)
        count += 30 # i.e. at 30 fps, this advances one second
        cap.set(1, count)
    else:
        cap.release()
        break

J'ai essayé de trouver un moyen de rendre cela un peu plus pythonique en utilisant une instruction with mais je ne crois pas que la bibliothèque CV2 a été mise à jour pour elle.

0
benJephunneh