Come rimuovere ogni secondo fotogramma da una gif animata?


22

Ho una cartella piena di video che voglio convertire in gif animate. ffmpeg / avconv fa un brutto lavoro nel farlo direttamente, quindi invece converto il video in una gif prima emettendo ciascun fotogramma come png e poi riconvertendolo in gif usando imagemagick. Il problema è che questo si traduce in una grande gif in termini di dimensioni del file. Per risolvere questo, voglio "eliminare" ogni secondo o ennesimo fotogramma dalla GIF, saltando ogni file di immagine durante la conversione in GIF o rimuovendo i frame da GIF. Come posso farlo su Ubuntu (13.04) usando imagemagick o qualche altra utility da riga di comando?

Risposte:


14

Usando uno script bash

Per fare ciò dalla riga di comando, è possibile utilizzare un'utilità chiamata Gifsicle . Non esiste un metodo integrato per eliminare ogni altro frame, quindi dovrai sporcarti le mani con alcuni script.

Ecco una breve sceneggiatura che ho creato per fare una sola GIF:

#!/bin/bash
# This script will take an animated GIF and delete every other frame
# Accepts two parameters: input file and output file
# Usage: ./<scriptfilename> input.gif output.gif

# Make a copy of the file
cp $1 $2

# Get the number of frames
numframes=`gifsicle $1 -I | grep -P "\d+ images" --only-matching | grep -P "\d+" --only-matching`

# Deletion
let i=0
while [[ $i -lt $numframes  ]]; do
    rem=$(( $i % 2 ))

    if [ $rem -eq 0 ]
    then
        gifsicle $2 --delete "#"$(($i/2)) -o $2 
    fi

    let i=i+1 
done

L'ho provato con un semplice conto alla rovescia GIF:

inserisci qui la descrizione dell'immagine

Ed ecco il risultato dopo averlo eseguito attraverso lo script:

inserisci qui la descrizione dell'immagine

Questo script ovviamente non è a prova di proiettile, ma dovrebbe condurti nella giusta direzione.


Nota che il tuo ciclo di eliminazione può essere semplificato a sololet i=0; while [[ $i -lt $(($numframes / 2)) ]]; do gifsicle $2 --delete "#$i" -o $2; let i=i+1; done
Ilmari Karonen,

1
In effetti, non hai bisogno di un loop: gifsicle "$1" --unoptimize $(seq -f "#%g" 0 2 $numframes) -O2 -o "$2"lo farà in una sola chiamata.
Ilmari Karonen,

In realtà non so come eseguire questo script dalla riga di comando. Ho provato a salvarlo come file gifdrop.sh ed eseguirlo come indicato nell'uso (./gifdrop.sh in.gif out.gif) su una gif chiamata in.gif e diceva comando sconosciuto gifdrop
mheavers

@mheavers hai reso eseguibile il file? chmod +x gifdrop.sh
John B

1
Funziona meglio su macOS: github.com/colindean/hejmo/blob/master/scripts/… - la mia versione che utilizza perl invece di grep in modo che funzioni su macOS che non ha GNU grep installato per impostazione predefinita. Inoltre, gestisce gli spazi nei nomi dei file.
Colin Dean,

27

Ecco una soluzione più semplice usando gifsicle rispetto allo script di JohnB:

gifsicle -U input.gif `seq -f "#%g" 0 2 99` -O2 -o output.gif

Questo comando dovrebbe funzionare nella maggior parte delle shell Unix; L'ho provato a Bash. Sostituisci input.gife output.gifcon i nomi dei file di input e output e 99con il numero di frame nell'animazione. (Va bene usare un numero maggiore, ma gifsicle se ne lamenterà.)

Alcune note:

  • L' -Uinterruttore unirà i frame nell'animazione di input con i precedenti, in modo che ciascun frame sia solo e non dipenda da nessun altro. Vuoi davvero farlo prima di fare praticamente qualsiasi cosa con le animazioni, altrimenti avrai probabilmente risultati disordinati. (Se l'animazione di input è già non ottimizzata, gifsicle potrebbe stampare un avviso a riguardo, ma anche questo è completamente innocuo.)

  • Al contrario, l' -O2interruttore ottimizza nuovamente l'animazione di output per ridurre al minimo le dimensioni del file. Con l'animazione di esempio di JohnB, riduce le dimensioni dell'output del 27%.

  • Il seqcomando emette semplicemente una sequenza di numeri da 0 a 99, contando in incrementi di 2. Lo -f "#%g"fa stampare un #prima di ogni numero, il che fa capire a gifsicle come una selezione di frame anziché un nome di file. I backticks ( `) attorno al comando seq fanno sì che il suo output sia incluso come parametri nella riga di comando gifsicle.

Se non si desidera accelerare il GIF, è possibile utilizzare gifsicle -I input.gifper ottenere il ritardo del fotogramma corrente, moltiplicarlo per 2 e utilizzare gifsicle -d ${delay} ....


Ben fatto! Se lo volessi davvero, potresti usare il grep dal mio script per fornire con precisione il numero di frame (e creare un comando di un comando). O forse c'è un modo più semplice per restituire il numero di fotogrammi in una GIF animata?
John B

3
Ho finito per fare un colosso di un comando, @JohnB -gifsicle input.gif `seq -f "#%g" 0 2 $(identify input.gif | tail -1 | cut -d "[" -f2 - | cut -d "]" -f1 -)` --unoptimize -O2 -o output.gif
Kasra Rahjerdi

In Windows 7 x64 con la versione 1.71, ho ricevuto: useless unoptimization-related input option. Quindi l'ho fatto in due passaggi (dall'interno di Git Bash): 1. gifsicle -U -o unoptimized.gif input.gif2.gifsicle unoptimized.gif `seq -f "#%g" 0 2 99` -O2 -o output.gif
feklee

11

NOTA : questa risposta è stata pubblicata prima che esistesse un requisito da riga di comando / open source, ma la lascio perdere perché potrebbe aiutare qualcun altro in futuro


Utilizzando Photoshop

Questa non è una soluzione open source o da riga di comando, ma puoi farlo con Photoshop:

FileImportaFotogrammi video su livelli ...

Importa video

" Limit To Every __ Frames " farà il trucco per te


0

Ecco la mia soluzione che utilizza l'elaborazione di script batch ...

Per prima cosa copia la gif animata originale catturata nel file input.gif, quindi avvia l'interprete dei comandi e digita:

gifsicle input.gif -I "# -1"> input.txt

guarda input.txt e vedi qual è la lunghezza dell'animazione: quanti fotogrammi contiene ...

input.txt:

* input.gif 166 images
  logical screen 1366x768
  global color table [256]
  background 15
  loop forever
  + image #165 1x1 at 1365,767 transparent 15
    disposal asis delay 0.07s

quindi modifica-crea test.bat e cambia il valore della variabile len = specific_length_number_from_input.txt e salva test.bat ...

test.bat:

@echo off
set /A len=166
set /A i=1
set /A ii=0
:loop
if %i%==%len% goto :eof
if %ii%==0 (set /A ii=1) else (set /A ii=0)
set /A iii=%ii%*%i%
if %i%==%iii% echo gifsicle -b input.gif --delete "#1" --done
set /A i=%i%+1
goto :loop

quindi infine avvia process1.bat e process2.bat e l'animazione ritagliata con ogni fotogramma dispari verrà elaborata nel file input.gif

process1.bat:

gifsicle -b -U input.gif
test.bat > input.bat

process2.bat:

call "input.bat"
gifsicle -b -O2 input.gif
erase "input.bat"

0

Penso di aver bisogno di nuove spiegazioni più sofisticate (anche il ritardo dei frame è una cosa importante che dovrebbe essere considerata) ... Per questo nuovo processo dovrai scaricare JREPL.BAT da questo link: http://www.dostips.com /forum/viewtopic.php?t=6044

Per prima cosa copia il file GIF catturato nel file input.gif, quindi avvia l'interprete dei comandi e digita:

gifsicle input.gif -I "# -1"> input.txt

guarda input.txt e vedi qual è la lunghezza dell'animazione - quanti frame contiene ... guarda anche nel ritardo del frame e se i suoi 0.07s significa che dovresti inserire l'opzione -d14 (7msec * 2) in questa riga in process.cmd: gifsicle -b -U -d14 input.gif, dopo la modifica salva process.cmd

quindi modifica test.bat e modifica il valore della variabile 166 da questa riga in modo che corrisponda al numero di fotogrammi dell'animazione: per / L %% i IN (1,2,166) DO echo "# %% i" >> input.bat, after modifica salva test.bat ...

quindi avvia process.cmd e l'animazione ritagliata con ogni fotogramma dispari verrà elaborata nel file input.gif

PS Il vantaggio di questo metodo è anche che hai il pieno controllo di cosa vuoi eliminare da gif (ogni secondo fotogramma (1.2.166) o ogni terzo (1.32666)), e così via basta cambiare il numero medio in riga batch, ma tieni presente che se specifichi (1.366), dovresti cambiare di conseguenza -d14 (opzione di ritardo) da process.cmd per riflettere il cambiamento (7msec * 3/2 = 10), quindi invece di (7msec * 2 / 1 = 14), l'opzione per il ritardo dovrebbe essere -d10 ...

Altri file (ad esempio per rimuovere ogni secondo frame): test.bat:

@echo off
echo gifsicle -b input.gif --delete>> input.bat
for /L %%i IN (1,2,166) DO echo  "#%%i">> input.bat
echo  --done>> input.bat
call jrepl.bat "\n" "" /x /m /f input.bat /o -

process.cmd:

gifsicle -b -U -d14 input.gif
call "test.bat"
call "input.bat"
gifsicle -b -O3 input.gif
erase "input.bat"

E file readme helper input.txt:

* input.gif 166 images
  logical screen 1366x768
  global color table [256]
  background 15
  loop forever
  + image #165 1x1 at 1365,767 transparent 15
    disposal asis delay 0.07s
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.