¿Cómo eliminar cada segundo cuadro de un gif animado?

22

Tengo una carpeta llena de videos que quiero convertir a un gif animado. ffmpeg / avconv hace un mal trabajo al hacerlo directamente, por lo que, en cambio, convierto el video en un gif al generar primero cada fotograma como png y luego convertirlo nuevamente a gif usando imagemagick. El problema es que esto resulta en un gran gif en términos de tamaño de archivo. Para resolver esto, quiero "soltar" cada segundo o enésimo fotograma del gif, ya sea omitiendo cada archivo de imagen al convertirlo en un gif o quitando fotogramas de un gif. ¿Cómo puedo hacer esto en Ubuntu (13.04) usando imagemagick o alguna otra utilidad de línea de comandos?

holacatfood
fuente

Respuestas:

14

Usando un script bash

Para hacer esto desde la línea de comandos, puede usar una utilidad llamada Gifsicle . No hay un método integrado para eliminar cada dos cuadros, por lo que deberá ensuciarse las manos con algunas secuencias de comandos.

Aquí hay un script rápido que hice para hacer solo un 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

Lo probé con un simple GIF de cuenta regresiva:

ingrese la descripción de la imagen aquí

Y aquí está el resultado después de ejecutarlo a través del script:

ingrese la descripción de la imagen aquí

Por supuesto, este script no es a prueba de balas, pero debería guiarlo en la dirección correcta.

JohnB
fuente
Tenga en cuenta que su ciclo de eliminación se puede simplificar a sololet i=0; while [[ $i -lt $(($numframes / 2)) ]]; do gifsicle $2 --delete "#$i" -o $2; let i=i+1; done
Ilmari Karonen
1
De hecho, no necesita un bucle en absoluto: gifsicle "$1" --unoptimize $(seq -f "#%g" 0 2 $numframes) -O2 -o "$2"lo hará en una sola llamada.
Ilmari Karonen
En realidad no sé cómo ejecutar este script desde la línea de comandos. Traté de guardarlo como archivo gifdrop.sh y ejecutarlo como se indica en el uso (./gifdrop.sh in.gif out.gif) en un gif llamado in.gif y decía comando desconocido gifdrop
mheavers
@mheavers ¿Hiciste el archivo ejecutable? chmod +x gifdrop.sh
JohnB
1
Funciona mejor en macOS: github.com/colindean/hejmo/blob/master/scripts/… - mi versión que usa perl en lugar de grep para que funcione en macOS que no tiene GNU grep instalado por defecto. Además, maneja espacios en los nombres de archivo.
Colin Dean
27

Aquí hay una solución más simple usando gifsicle que el script de JohnB:

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

Este comando debería funcionar en la mayoría de los shells de Unix; Lo he probado en bash. Reemplace input.gify output.gifcon los nombres de los archivos de entrada y salida, y 99con el número de cuadros en su animación. (Está bien usar un número mayor, pero el gifsicle se quejará).

Algunas notas:

  • El -Uinterruptor combinará fotogramas en la animación de entrada con los anteriores, de modo que cada fotograma se mantenga solo y no dependa de ningún otro. Realmente desea hacer esto antes de hacer casi cualquier cosa con animaciones, de lo contrario es probable que obtenga resultados desordenados. (Si su animación de entrada ya no está optimizada, el gifsicle puede imprimir una advertencia al respecto, pero esto también es completamente inofensivo).

  • Por el contrario, el -O2conmutador vuelve a optimizar la animación de salida para minimizar el tamaño del archivo. Con la animación de muestra de JohnB, reduce el tamaño de salida en un 27%.

  • El seqcomando solo genera una secuencia de números del 0 al 99, contando en pasos de 2. Lo -f "#%g"hace imprimir un #antes de cada número, lo que hace que el gifsicle lo entienda como una selección de marco en lugar de un nombre de archivo. Los backticks ( `) alrededor del comando seq hacen que su salida se incluya como parámetros en la línea de comando gifsicle.

Si no desea que el gif se acelere, puede usarlo gifsicle -I input.gifpara obtener el retraso de fotograma actual, multiplíquelo por 2 y úselo gifsicle -d ${delay} ....

Ilmari Karonen
fuente
¡Bien hecho! Si realmente quisieras, puedes usar el grep de mi script para dar con precisión el número de fotogramas (y hacer que un comando sea enorme). ¿O tal vez hay una manera más simple de devolver el número de fotogramas en un GIF animado?
JohnB
3
Terminé haciendo un comando gigante, @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
En Windows 7 x64 con la versión 1.71, que tengo: useless unoptimization-related input option. Así que lo hice en dos pasos (desde dentro de 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 : Esta respuesta se publicó antes de que hubiera un requisito de línea de comando / código abierto, pero lo dejo ya que puede ayudar a alguien más en el futuro


Usando Photoshop

Esta no es una solución de código abierto o de línea de comando, pero puede hacerlo con Photoshop:

ArchivoImportarCuadros de video a capas ...

Importar Video

" Limit to Every __ Frames " hará el truco por ti

JohnB
fuente
0

Aquí está mi solución usando el procesamiento de script por lotes ...

Primero copie el gif animado original capturado en el archivo input.gif, luego inicie el intérprete de comandos y escriba:

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

mire input.txt y vea cuál es la longitud de la animación: cuántos fotogramas 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

luego edite-cree test.bat y cambie el valor de la variable len = especifique_length_number_from_input.txt y guarde 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

luego finalmente inicie process1.bat y process2.bat y la animación recortada con cada cuadro impar se procesará en la entrada de archivo.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"
Marko Ribar
fuente
0

Creo que necesito una nueva explicación más sofisticada (el retraso de fotogramas también es algo importante que debe considerarse también) ... Para este nuevo proceso, deberá descargar JREPL.BAT desde este enlace: http://www.dostips.com /forum/viewtopic.php?t=6044

Primero copie el gif capturado en el archivo input.gif, luego inicie el intérprete de comandos y escriba:

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

mire input.txt y vea cuál es la longitud de la animación: cuántos cuadros contiene ... también mire el retraso del cuadro y si es 0.07s, eso significa que debe poner la opción -d14 (7msec * 2) en esta línea en process.cmd: gifsicle -b -U -d14 input.gif, después de editar guardar el proceso.cmd

luego edite test.bat y cambie el valor de la variable 166 de esta línea para que coincida con el número de cuadros de animación: para / L %% i IN (1,2,166) DO echo "# %% i" >> input.bat, después editar guardar test.bat ...

luego inicie process.cmd y la animación recortada con cada cuadro impar se procesará en la entrada de archivo.gif

PD La ventaja de este método también es que tiene control total de lo que desea eliminar del gif (cada segundo cuadro (1,2,166) o cada tercero (1,3,166)), y así sucesivamente, simplemente cambie el número del medio en línea de lote, pero tenga en cuenta que si especifica (1,3,166), debe cambiar -d14 (opción de retraso) de process.cmd para reflejar el cambio (7msec * 3/2 = 10), por lo que en lugar de (7msec * 2 / 1 = 14), la opción de retraso debería ser -d10 ...

Otros archivos (por ejemplo, eliminar cada segundo cuadro): 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 -

proceso.cmd:

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

Y el archivo léame auxiliar 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
Marko Ribar
fuente