La tâche, et pourquoi elle est plus compliquée qu'elle n'y paraît

"Donnez-moi une image par seconde de ce clip" est l'une des tâches vidéo les plus courantes. Un testeur QA a besoin de captures d’écran régulièrement espacées pour les joindre à un rapport de bug. Un chercheur a besoin d’une séquence d’images échantillonnées pour alimenter un outil d’annotation. Un éditeur social a besoin de vingt photos de candidats provenant d'un montage de deux minutes sans parcourir la chronologie à la main.

La tâche en elle-même est simple. Ce qui rend les choses compliquées, c'est que « une image par seconde » est ambigu au moment où vous regardez de près. Voulez-vous dire une image pour chaque seconde de lecture, ou pour chaque Nème image du flux encodé ? Ce sont des choses différentes : un clip à 24 ips et un clip à 60 ips produisent le même décompte sous la première lecture et des décomptes très différents sous la seconde. Choisissez le mauvais et vous obtenez soit un dossier de quasi-doublons, soit une séquence trop clairsemée pour être utile.

Les deux approches ci-dessous produisent des pixels identiques. Ils diffèrent dans la façon dont vous exprimez ce que vous voulez et dans la rapidité avec laquelle vous découvrez que vous l’avez mal exprimé.

Méthode 1 : FFmpeg, paramètre par paramètre

La commande canonique est courte :

ffmpeg -i input.mp4 -vf fps=1 -q:v 2 frame_%04d.jpg

Chaque élément compte :

  • -i input.mp4 — le fichier source. FFmpeg le lit une fois, du début à la fin ; il ne recherche pas chaque horodatage individuellement.
  • -vf fps=1 — le filtre vidéo qui effectue le travail réel. fps=1 signifie une image de sortie par seconde de temps de lecture, quelle que soit la fréquence d’images source. Utiliser fps=1/5 pour une image toutes les cinq secondes, ou fps=2 pour deux par seconde. Il s'agit de la lecture du temps de lecture.
  • -q:v 2 — Qualité JPEG, sur une échelle inversée où 2 est presque sans perte et 31 est fortement compressé. Omettez-le et vous obtenez la valeur par défaut de FFmpeg, qui est nettement plus douce que ce à quoi la plupart des gens s'attendent. Si vous souhaitez une sortie sans perte, écrivez frame_%04d.png à la place et supprimez entièrement cet indicateur.
  • frame_%04d.jpg — le modèle de sortie. %04d met à zéro le compteur à quatre chiffres : frame_0001.jpg, frame_0002.jpg. Le rembourrage n’est pas cosmétique ; voir le problème de numérotation ci-dessous.

Si vous voulez l'autre lecture - toutes les Nème image encodée plutôt que chaque N secondes - le filtre change :

ffmpeg -i input.mp4 -vf "select=not(mod(n\,10))" -vsync vfr frame_%04d.png

Cela conserve les images là où l'index des images n est divisible par 10 et -vsync vfr empêche FFmpeg de dupliquer les images pour compléter un taux de sortie constant. Laissez cet indicateur désactivé et vous obtiendrez beaucoup plus de fichiers que ce que vous avez demandé, ce qui est un premier résultat courant et déroutant.

Pour limiter le travail à un segment, mettez -ss avant -i donc FFmpeg recherche avant le décodage plutôt que de décoder et de supprimer :

ffmpeg -ss 00:01:30 -to 00:02:15 -i input.mp4 -vf fps=1 frame_%04d.png

Méthode 2 : le même travail dans un navigateur

La extracteur d'image sur ce site se mappe aux deux mêmes lectures, avec l'ambiguïté rendue explicite dans l'interface au lieu d'être cachée dans une chaîne de filtre.

  1. Déposez le fichier. Il est lu directement du disque dans la mémoire du navigateur — il n'y a pas de téléchargement, donc une source de 3 Go commence le traitement immédiatement plutôt qu'après un transfert.
  2. Choisissez la méthode d'extraction. « Intervalle de temps » est l'équivalent de fps=1/N  : vous saisissez l'intervalle en secondes. "Frame Rate (FPS)" est l'équivalent du select/mod filtre — vous entrez N à conserver chaque Nème image.
  3. Lisez l'indice de nombre d'images avant d'extraire. C'est la partie qui fait gagner le plus de temps. L'interface montre combien d'images vos paramètres actuels produiront avant tout fonctionne, donc l'erreur de 4 000 fichiers devient un numéro que vous voyez plutôt qu'un dossier que vous découvrez.
  4. Définir le format et la qualité. PNG pour des images sans perte, JPG ou WebP avec le curseur 10-100 % lorsque la taille compte. La position du curseur correspond à ce que -q:v des contrôles, exprimés comme la plupart des gens y pensent.
  5. Découpez la plage si nécessaire. Le curseur à double poignée est le -ss/-to équivalent, avec les horodatages affichés lorsque vous faites glisser.
  6. Exporter. Les cadres apparaissent dans une grille d'aperçu ; cliquez sur un pour l'enregistrer seul, ou prenez l'ensemble comme un seul ZIP.

Règle pouce pour choisir un intervalle : pour une tête parlante ou un enregistrement d'écran, 1 image toutes les 2 à 5 secondes capture chaque changement d’état significatif. Pour le sport ou tout ce qui implique des mouvements rapides, 2 à 5 images par seconde. Réservez « chaque image » pour une véritable analyse précise à l'image : une minute de 60 ips équivaut à 3 600 images et plusieurs gigaoctets de PNG.

Trois problèmes qui coûtent du temps aux gens

1. Le résultat est constitué de milliers d’images presque identiques

Presque toujours une confusion d'unités : vous vouliez dire "toutes les 10 images" et vous avez écrit fps=10, ce qui signifie dix images par seconde — sur une source à 30 ips qui contient 3 fois plus d'images que ce que l'on attendait naïvement, et non 3 fois moins.

Avant d'exécuter quoi que ce soit, faites le calcul. Mode de durée de lecture : duration ÷ interval. Mode d'index de trame : duration × source_fps ÷ N. Un clip de 10 minutes à 30 ips « toutes les 10 images » correspond à 600 × 30 ÷ 10 = 1 800 images. Si ce chiffre vous surprend, ajustez-le avant de commencer plutôt qu’après. Dans l'outil de navigation, le même chiffre apparaît automatiquement sous le champ d'intervalle.

2. L’extraction s’arrête au milieu d’une longue vidéo

Différentes causes de chaque côté, donc la solution diffère également.

Avec FFmpeg, un blocage est généralement lié au disque : des milliers de PNG atterrissent dans un seul répertoire. et le système de fichiers, et non le décodeur, devient le goulot d'étranglement. Écrivez dans un nouveau répertoire vide, préférez JPG lorsque vous n'avez pas besoin du sans perte et divisez les sources très longues en segments avec -ss/-to.

Dans le navigateur, la limite est la RAM : chaque image extraite est conservée en mémoire jusqu'à ce que vous téléchargiez le ZIP. Quelques centaines de 4K PNG représentent plusieurs gigaoctets et un onglet peut être supprimé pour cela. Le modèle fiable consiste à travailler par morceaux : utilisez le curseur de plage horaire pour prendre 0 h 00 à 2 h 00, téléchargez ce ZIP, puis déplacez la plage entre 2 h 00 et 4 h 00. Fermer d'autres onglets avant un gros travail aide vraiment, et JPG avec une qualité de 85 à 90 % réduit la pression de la mémoire de plusieurs fois sans différence visible pour la plupart des utilisations.

3. La numérotation des images rompt l'ordre d'importation

Celui-ci mord après l'exportation semble correct. Outils qui trient les noms de fichiers sous forme de texte - et la plupart le font - dans l'ordre frame_10.png avant frame_2.png, car "1" trie avant "2" caractère par caractère. Votre séquence est lue en mode crypté et la raison n'est pas évidente.

Le correctif est un remplissage nul, suffisamment large pour les plus grandes. comptez que vous produirez un jour. C'est ce que %04d est pour ; use %05d si vous risquez de dépasser 9 999 images. L'outil du navigateur se remplit automatiquement, ce qui est la principale raison pour laquelle sa sortie a tendance à tomber directement dans un importateur de séquence d'images sans renommer.

Quand FFmpeg est toujours le bon choix

La plupart du temps. Ce n'est pas un cas où le navigateur gagne sur tous les fronts :

  • Tout ce qui est scripté ou programmé. Une tâche cron, une étape CI, un pipeline côté serveur – un navigateur ne peut pas du tout y participer.
  • Lots répartis sur de nombreux fichiers. Une boucle shell sur 200 clips est triviale dans FFmpeg et fastidieuse partout ailleurs.
  • Codecs que le navigateur ne décodera pas. ProRes, DNxHD, formats de caméra bruts, conteneurs inhabituels. Si le navigateur ne peut pas le lire, il ne peut pas en extraire. FFmpeg gère presque tout.
  • Filtrage pendant l'extraction. Désentrelacement, mise à l'échelle, conversion de l'espace colorimétrique, recadrage, détection de changement de scène : tous disponibles sous forme de filtres en un seul passage.
  • Reproductibilité. Une commande dans un README est exacte. "Réglez le curseur à peu près ici" ne l'est pas.

L'outil de navigation est le meilleur choix pour les tâches ponctuelles, pour quiconque ne souhaite pas installer et maintenir une chaîne d'outils pour une tâche occasionnelle, lorsque la source est suffisamment confidentielle pour que le traitement uniquement local soit important, et lorsque vous souhaitez examiner les images avant de vous engager dans l'exportation. Ce sont des situations véritablement différentes, pas un classement.

Référence rapide

Ce que vous voulez FFmpeg Outil de navigation
Une image par seconde -vf fps=1 Intervalle de temps = 1
Une image toutes les 5 secondes -vf fps=1/5 Temps Intervalle = 5
Chaque dixième image codée -vf "select=not(mod(n\,10))" -vsync vfr Mode fréquence d'images, N = 10
Seulement 1h30 à 2h15 -ss 00:01:30 -to 00:02:15 Curseur de plage de temps
Sortie sans perte .png modèle de sortie Format = PNG
Fichiers plus petits -q:v 5 avec .jpg JPG + curseur de qualité
Voir le décompte avant d'exécuter Calculer manuellement Affiché sous le champ d'intervalle
Aperçu avant d'enregistrer Ouvrez le dossier de sortie Grille de prévisualisation intégrée
Automatisation / batch Boucle Shell, cron, CI Pas possible
ProRes, DNxHD, brut Supporté Uniquement ce que le navigateur décode

Quel que soit l'itinéraire que vous empruntez, la décision d'intervalle vient en premier et l'outil en second. Déterminez le nombre d'images dont vous avez réellement besoin, confirmez le décompte avant de commencer, et le reste est mécanique.