Une vidéo d’un paysage permet de refléter de la dynamique de la scène qu’une photo n’aurait pas pu illustrer. Cependant, un plan fixe d’une scène peut provoquer de la lassitude pour le spectateur. L’objectif de cet article sera de donner une dynamique de mouvement à une vidéo prise par un appareil fixe. Nous allons générer des effets de travelling et de zoom sur des vidéos en plan fixe, permettant ainsi de donner une impression de mouvement. En d’autres termes, nous allons générer une caméra virtuelle à partir d’une vidéo prise par une caméra fixe.

Dans cet article, nous allons utiliser une vue de Saigon disponible ici.

Génération de travelling et de zoom: vue de Saigon
Vue de Saigon (source)

Préparation du programme

La première partie de notre programme consistera à récupérer la vidéo d’origine, définir les paramètres de transformation de la vidéo (taille de la vidéo de sortie, effet désiré) et à préparer la création de la vidéo de sortie.

import cv2
import argparse
import os

ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True, help="Chemin de la video")
ap.add_argument("-t", "--travelling",default=False, help="Active la fonction de generation d'un travelling",action='store_true')
ap.add_argument("-z", "--zooming",default=False,help="Active la fonction de generation d'un zoom",action='store_true')
ap.add_argument("-r","--ratio",type=float,default='0.9',help="ratio de réduction de l'image [0...1]")
args = vars(ap.parse_args())

path=os.path.dirname(os.path.abspath(args["video"]))

cap=cv2.VideoCapture(args["video"])
fourcc = cv2.VideoWriter_fourcc(*'XVID')
fps=cap.get(cv2.CAP_PROP_FPS)

ret,frame=cap.read()
h,l,c=frame.shape
h_out=int(args["ratio"]*h)
l_out=int(args["ratio"]*l)

Nous importons la bibliothèque OpenCV (ligne 1) pour toutes les opérations sur les images, argparse (ligne 2) pour gérer les arguments lors de l’appel de notre programme et os (ligne 3) afin de communiquer avec le système.

Il est alors temps de définir les arguments de notre programme (ligne 5-10).

Nous aurons besoin :

  • De la vidéo d’origine (ligne 6)
  • De connaître l’opération à réaliser : un travelling (ligne 7) et/ou un zoom (ligne 8)
  • Et enfin de spécifier un ratio de rétrécissement de l’image (ligne 9)

Pourquoi devons-nous réduire l’image ? Car afin de réaliser notre mouvement, nous aurons besoin de prendre une partie plus petite de l’image comme illustrée dans l’image suivante. Il est donc nécessaire de spécifier la taille de la vidéo de sortie comme étant une proportion de la vidéo d’origine (compris entre 0 et 1). Plus le ratio est proche de 0, plus la vidéo de sortie sera de petite taille, plus le mouvement de la fenêtre de sortie sera rapide.

Fenêtre de sortie d'un travelling (ratio=0.9)
Fenêtre de sortie d’un travelling (ratio=0.8)

Nous récupérons ensuite le chemin de la vidéo d’origine pour s’en servir pour l’écriture de la vidéo retravaillée (l. 12). Puis, nous pouvons passer à l’ouverture du flux vidéo (l. 14). Enfin, nous récupérons les informations pour l’enregistrement de la vidéo : le codec (l. 15) et le FPS (l. 16).

Pour finir, nous lisons une trame pour obtenir la taille de la vidéo d’origine (l. 19) afin de calculer la taille de la vidéo de sortie (l. 20-21).

Nous avons maintenant toutes les informations nécessaires pour passer aux deux transformations que nous voulons réaliser: le travelling et le zoom.

Réalisation d’un travelling

Si l’argument ‘-t’ est spécifié lors de l’appel de notre programme, nous réalisons un travelling. Le principe du travelling est de déplacer une fenêtre (de taille plus petite que l’image d’origine) tout au long de l’image donnant ainsi l’impression d’un mouvement de la caméra.

if args["travelling"]:
    print("Generation du travelling")
    cap=cv2.VideoCapture(args["video"])
    out = cv2.VideoWriter(path+'/output_travelling.avi',fourcc, fps, (l_out,h_out))
    maximum=l-l_out
    deplacement=maximum/cap.get(cv2.CAP_PROP_FRAME_COUNT)
    while cap.get(cv2.CAP_PROP_POS_FRAMES) < cap.get(cv2.CAP_PROP_FRAME_COUNT):
        ret,frame=cap.read()
        start=int(deplacement*cap.get(cv2.CAP_PROP_POS_FRAMES))
        fenetre=frame[int((h/2)-(h_out/2)):int((h/2)+(h_out/2)), start:start+l_out]
        out.write(fenetre)

    out.release()
    cap.release()
    print("Travelling done")

Nous réinitialisons notre flux vidéo d’entrée afin de repartir au début de la vidéo (Nous avons déjà lu une trame auparavant.) et nous ouvrons l’enregistreur du fichier de sortie grâce aux informations spécifiées précédemment.

Nous calculons ensuite la position maximale (l. 28) que pourra atteindre la fenêtre afin de pouvoir garantir que la dernière image de la fenêtre sera contenu dans l’image d’origine. Ce maximum est ensuite utilisé afin de calculer la vitesse du déplacement de la fenêtre (l.29). Cette vitesse étant définie par la distance maximale à parcourir divisée pas la longueur de la vidéo (nombre de trames). À chaque image, nous déplacerons donc la fenêtre du nombre de pixels définit produisant ainsi un mouvement régulier.

Pour chaque trame que nous lisons, nous calculons la nouvelle position de la fenêtre (l. 32), en se basant sur le numéro de la trame courante. Nous utilisons cette position pour extraire de l’image d’origine la fenêtre voulue, centrée au milieu de l’image en hauteur (l. 33). Enfin, nous enregistrons cette fenêtre dans notre fichier de sortie.

Génération de travelling et de zoom: déplacement de la fenêtre

Réalisation d’un zoom.

L’autre dynamique que nous souhaitons mettre en place est la génération d’un zoom, c’est-à-dire un agrandissement au fur et à mesure que la vidéo avance.

Génération de travelling et de zoom: déplacement de la fenêtre

Pour réaliser ce mouvement, nous allons diminuer la taille de la fenêtre en partant de la taille initiale de l’image pour aller vers la taille de notre vidéo de sortie (l_out, h_out).

if args["zooming"]:
    print("Generation du zoom")
    cap=cv2.VideoCapture(args["video"])
    out = cv2.VideoWriter(path+'/output_zooming.avi',fourcc, fps, (l_out,h_out))
    facteur=(1-args["ratio"])/cap.get(cv2.CAP_PROP_FRAME_COUNT)
    while cap.get(cv2.CAP_PROP_POS_FRAMES) < cap.get(cv2.CAP_PROP_FRAME_COUNT):
        ret,frame=cap.read()
        ratio_n=1-(cap.get(cv2.CAP_PROP_POS_FRAMES)*facteur)
        l_n=int(l*ratio_n)
        h_n=int(h*ratio_n)
        fenetre=frame[int((h/2)-(h_n/2)):int((h/2)+(h_n/2)), int((l/2)-(l_n/2)):int((l/2)+(l_n/2))]
        resized = cv2.resize(fenetre, (l_out,h_out), interpolation = cv2.INTER_LINEAR)
        out.write(resized)
    out.release()
    cap.release()
    print("Zoom done")

Pour ce faire, nous allons calculer un facteur de diminution du ratio.

Rien ne vaut mieux qu’un exemple pour expliciter la manipulation. Dans le cas où le ratio défini serait de 0.8, nous allons faire varier ce ratio de 1 (taille d’origine) jusqu’à 0.8 (taille finale). Notre vidéo étant composée de 150 trames, nous allons faire diminuer le ratio de 0.00133 à chaque nouvelle image ((1-0.8)/150)

Pour chaque image, nous allons donc calculer une fenêtre dont la taille dépendra de la position dans la vidéo. Nous calculons un nouveau ratio à chaque image (l. 48) (dans notre exemple : [1, 0.99866,0.99733, …, 0.8]) afin de calculer la nouvelle taille de la fenêtre (l_n, h_n) (l. 49-50).

Nous extrayons ensuite la fenêtre de l’image en la centrant sur le centre de l’image d’origine (l. 51). Comme la taille de la fenêtre change à chaque nouvelle image, nous utilisons la fonction cv2.resize  afin d’obtenir une image de  sortie de taille (l_out, h_out) (l. 52).

Résultats

Voici les résultats obtenus avec un ratio de 0.8. En haut à droite, vous pouvez observer le mouvement de la fenêtre en même temps que la vidéo de sortie.

Conclusions

Nous avons réussi à générer un effet de travelling et un effet de zoom sur une vidéo. Mais de nombreuses améliorations peuvent être prises en compte.

Une première serait d’étendre cette technique à des images fixes, permettant ainsi de les rendre dynamiques. Il suffirait juste de spécifier le temps de la vidéo de sortie et de déplacer la fenêtre dans l’image pour le nombre total de frame désiré.

Une autre modification possible serait de générer un déplacement. Pour l’instant, nous déplaçons la fenêtre sur un axe horizontal situé au milieu de l’image d’origine. Il serait imaginable d’utiliser la souris afin de déterminer des points de départ et d’arrivée de la fenêtre. Il faudrait bien sûr prendre en compte les limites de l’image d’origine afin de ne pas pouvoir « sortir » de l’image.

Enfin, nous pourrions générer d’autres effets, comme par exemple une rotation pour ajouter une dynamique lors d’un plan pris par un drone.


0 commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.