Miniature significative per un video usando FFmpeg


80

FFmpeg può catturare immagini da video che possono essere utilizzate come miniature per rappresentare il video. I modi più comuni per farlo sono catturati nel Wiki di FFmpeg .

Ma non voglio scegliere fotogrammi casuali ad alcuni intervalli. Ho trovato alcune opzioni usando i filtri su FFmpeg per catturare i cambiamenti di scena:

Il filtro thumbnailcerca di trovare i frame più rappresentativi nel video:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

e il seguente comando seleziona solo i fotogrammi che hanno più del 40% di cambiamenti rispetto ai precedenti (e quindi probabilmente sono cambiamenti di scena) e genera una sequenza di 5 PNG.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Credito informativo per i comandi di cui sopra a Fabio Sonnati . Il secondo mi è sembrato migliore in quanto ho potuto ottenere n immagini e scegliere il meglio. L'ho provato e ha generato la stessa immagine 5 volte.

Qualche ulteriore indagine mi ha portato a:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrti assicura di ottenere immagini diverse. Questo seleziona sempre il primo fotogramma del video, nella maggior parte dei casi il primo fotogramma è crediti / logo e non è significativo, quindi ho aggiunto un -ss3 per scartare i primi 3 secondi del video.

Il mio comando finale è simile al seguente:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

Questo è stato il massimo che ho potuto fare. Ho notato che dal momento che scelgo solo 5 video, sono tutti per lo più dall'inizio del video e potrebbero perdere importanti scene che si verificano più avanti nel video

Vorrei scegliere il tuo cervello per qualsiasi altra opzione migliore.


Bei esempi di comandi. FWIW, non ho riscontrato alcun problema con le immagini JPEG generate da FFmpeg su OS X (10.8, FFmpeg 1.1 e precedenti). Il tuo penultimo comando funziona bene per me, così come l'ultimo, e nessuno di questi risulta in file JPG vuoti. Ho compilato con libopenjpeg.. non sono sicuro se questo fa la differenza.
Slhck,

Grazie slhck. Modificata la domanda con i dettagli della configurazione / versione di ffmpeg. Non ho eseguito l'aggiornamento a 1.1 su questa macchina. Lo farò e vedrò se cambia qualche risultato.
d33pika,

1
Quindi sei su Ubuntu? Puoi provare l'ultima versione di Git Master da una build statica o compilare te stesso ed eseguire di nuovo? O l'ultima stalla. Ho appena controllato, usa anche l' mjpegencoder per me, e ho anche controllato jpegoptime exiv2, entrambi i quali funzionano bene per me con tutti i risultati JPG dai tuoi comandi di esempio.
Slhck,

1
Ho aggiornato e ora funziona! Immagino che la versione precedente avesse alcuni bug.
d33pika,

Puoi andare avanti e pubblicare la soluzione - nuova versione, preferibilmente con link al log delle modifiche che mostra il bug che hai riscontrato e successivamente risolto con la nuova versione?
Lizz

Risposte:


29

Che ne dici di cercare, idealmente, il primo> 40% -scambia fotogramma all'interno di ciascuno di 5 intervalli di tempo, in cui gli intervalli di tempo sono il 1 °, 2 °, 3 °, 4 ° e 5 ° 20% del video.

Puoi anche dividerlo in 6 intervalli di tempo e ignorare il primo per evitare crediti.

In pratica, ciò significherebbe impostare gli fps su un numero basso mentre si applica il controllo del cambio di scena e l'argomento per buttare via il primo bit del video.

...qualcosa di simile a:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg

1
Le immagini completamente in bianco e nero vengono raccolte. Come posso evitarle?
d33pika,

1
Esistono filtri per rilevare cornici nere e loro sequenze ( ffmpeg.org/ffmpeg-filters.html#blackframe o ffmpeg.org/ffmpeg-filters.html#blackdetect ). Non riesci a lavorare con nessuno dei due nel tuo one-liner in crescita, ma dovresti sicuramente essere in grado di rimuovere le cornici nere (passaggio separato) ed estrarre le miniature dal video risultante.
AM

5
Per quanto riguarda le cornici bianche, ora sta diventando complicato, ma sembra ancora che ci sia un modo: 1. rimuovi le cornici nere 2. nega (il bianco diventa nero) 3. togli le cornici bianche 4. nega di nuovo 5. estrai le miniature (Se riesci a rimuovere le cornici bianche o nere, in particolare su una riga, puoi postarle di nuovo qui? Posso aggiungerlo alla risposta per i futuri lettori. ... o in realtà potresti creare una nuova domanda e risposta Sì. Lo voterei sicuramente.)
AM

3
Per evitare immagini noiose come il semplice bianco e nero: genera più miniature del necessario, comprimile con jpeg e scegli le immagini con file di dimensioni maggiori. Questo funziona sorprendentemente bene da solo per ottenere miniature decenti.
Sam Watkins,

2
Questo comando non ha funzionato, ho ricevuto un errore Unable to find a suitable output format for 'fps=fps=1/600'. La soluzione è aggiungere -vfprima di tale argomento (vedi stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman

7

Definire un significato è difficile, ma se si desidera creare N miniature in modo efficiente che coprono l'intero file video, questo è quello che uso per generare miniature in produzione con contenuti caricati dall'utente.

Pseudo-codice

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Dove:

  • D - durata del video letto da ffmpeg -i <movie>solo o ffprobeche ha un buon scrittore di output JSON tra l'altro
  • N: numero totale di anteprime desiderate
  • X - numero miniatura, da 1 a N
  • T - punto temporale per il bicchiere

Semplicemente quanto sopra scrive il fotogramma chiave centrale di ogni partizione del film. Ad esempio, se il film è lungo 300 e si desidera 3 miniature, è necessario un fotogramma chiave dopo 50, 150 e 250 secondi. Per 5 miniature sarebbero 30, 90, 150, 210, 270. È possibile regolare N in base alla durata del film D, ad esempio se un film di 5 minuti avrà 3 anteprime ma oltre 1 ora avrà 20 anteprime.

Prestazione

Ogni invocazione del ffmpegcomando sopra richiede una frazione di secondo (!) Per ~ 1 GB H.264. Questo perché salta all'istante in <time>posizione (mente -ssprima -i) e prende il primo fotogramma chiave che è praticamente completo JPEG. Non c'è tempo perso per il rendering del film in modo che corrisponda alla posizione esatta del tempo.

Post produzione

Puoi mescolare sopra con scaleo qualsiasi altro metodo di ridimensionamento. Puoi anche rimuovere cornici a tinta unita o provare a mescolarlo con altri filtri come thumbnail.


2
wow passare -ss Na prima -iè un suggerimento incredibile. grazie!
apinstein,

2

Una volta ho fatto qualcosa di simile, ma ho esportato tutti i fotogrammi del video (in 1 fps) e li ho confrontati con un'utilità perl che ho trovato che calcola la differenza tra le immagini. Ho confrontato ogni fotogramma con le anteprime precedenti e, se era diverso da tutte le anteprime, l'ho aggiunto alla raccolta di anteprime. Il vantaggio qui è che se il tuo video si sposta dalla scena A alla B e ritorna ad A, ffmpeg esporterà 2 fotogrammi di A.


Quali fattori hai usato per confrontare 2 immagini?
d33pika,

Purtroppo non ricordo, è stato molto tempo fa. Devi prima decidere quale metodo di confronto utilizzare, quindi fare alcuni test per trovare il fattore corretto. Questo non dovrebbe richiedere molto tempo.
Eran Ben-Natan,

Sto solo pensando ad alta voce qui, ma non è questa opzione peggiore? Quando confronti 2 immagini esportate da video, hai già perso alcune informazioni da esso. Voglio dire, è più facile calcolare la somiglianza dalle informazioni del codec che dalle 2 immagini, no? Recentemente ho cercato alcuni algoritmi / librerie per confrontare le immagini e non funzionano come si potrebbe desiderare.
Samuel,

Bene, con ffmpeg puoi estrarre i frame senza perdita di qualità usando il flag same_quality. Se si utilizzano le informazioni sul codec, è necessario verificare che non si ottengano solo gli Iframe. Comunque quell'utility perl ha funzionato bene per me ed è molto configurabile. Guarda qui: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan,

2

Prova questo

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

Utilizzando questo comando sono in grado di generare il numero richiesto di miniature che sono rappresentative dell'intero video.


Non sarebbe la formula corretta per fpsessere (no_of_frames_req * fps_of_vid) / total_video_frames?
flolilo,

Sto cercando una soluzione opposta: selezionare più fotogrammi dai periodi in cui la fotocamera è più stabile e non si muove? (dove la differenza tra i fotogrammi successivi è minore, non superiore). C'è un modo per farlo?
Tina J,

1

Ecco cosa faccio per generare una miniatura periodica per i flussi live m3u8 da utilizzare come poster. Ho scoperto che eseguire un'attività ffmpeg continua solo per generare anteprime consuma tutta la mia CPU, quindi eseguo un cronjob ogni 60 secondi che genera tutte le anteprime per i miei stream.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Se hai bisogno di fare più frequentemente di 60 secondi (limitazione di cronjob), puoi fare un whileciclo nel tuo script e verrà eseguito per sempre. Aggiungi a sleep 30per cambiare la frequenza in 30 secondi. Non consiglio di farlo con un gran numero di video, poiché la corsa precedente potrebbe non essere completata prima dell'inizio di quella successiva.

Idealmente con cronjob, lo eseguo ogni 5 minuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.