Wie entferne ich jedes zweite Bild von einem animierten GIF?

22

Ich habe einen Ordner voller Videos, die ich in animierte Gifs konvertieren möchte. ffmpeg / avconv macht einen schlechten Job, wenn ich es direkt mache, also konvertiere ich das Video stattdessen in ein GIF, indem ich zuerst jedes Bild als PNG ausgebe und dann mit imagemagick zurück in GIF konvertiere. Das Problem ist, dass dies zu einem großen GIF in Bezug auf die Dateigröße führt. Um dies zu lösen, möchte ich jedes zweite oder n-te Bild aus dem GIF "entfernen", indem ich beim Konvertieren in ein GIF jede Bilddatei überspringe oder Bilder aus einem GIF entferne. Wie kann ich das unter Ubuntu (13.04) mit imagemagick oder einem anderen Befehlszeilenprogramm machen?

hellocatfood
quelle

Antworten:

14

Verwendung eines Bash-Skripts

Verwenden Sie dazu in der Befehlszeile das Dienstprogramm Gifsicle . Es gibt keine eingebaute Methode, um jeden anderen Frame zu löschen. Sie müssen sich also mit ein wenig Skripten die Hände schmutzig machen.

Hier ist ein kurzes Skript, mit dem ich nur ein einziges GIF erstellt habe:

#!/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

Ich habe es mit einem einfachen Countdown-GIF getestet:

Bildbeschreibung hier eingeben

Und hier ist das Ergebnis, nachdem es durch das Skript ausgeführt wurde:

Bildbeschreibung hier eingeben

Dieses Skript ist natürlich nicht kugelsicher, aber es sollte Sie in die richtige Richtung führen.

JohnB
quelle
Beachten Sie, dass Ihre Löschschleife auf "nur" vereinfacht werden kannlet i=0; while [[ $i -lt $(($numframes / 2)) ]]; do gifsicle $2 --delete "#$i" -o $2; let i=i+1; done
Ilmari Karonen,
1
Tatsächlich brauchen Sie überhaupt keine Schleife: Sie gifsicle "$1" --unoptimize $(seq -f "#%g" 0 2 $numframes) -O2 -o "$2"erledigen dies in einem Aufruf.
Ilmari Karonen
Ich kann dieses Skript nicht über die Befehlszeile ausführen. Ich habe versucht , es als gifdrop.sh - Datei zu speichern und ausführen , wie in der Gebrauchs angewiesen (./gifdrop.sh in.gif out.gif) auf einem gif genannt in.gif und der unbekannten Befehl gifdrop
mheavers
@mheavers hast du die Datei ausführbar gemacht? chmod +x gifdrop.sh
JohnB
1
Funktioniert unter macOS besser: github.com/colindean/hejmo/blob/master/scripts/… - meine Version verwendet Perl anstelle von grep, sodass sie unter macOS funktioniert, auf dem GNU grep nicht standardmäßig installiert ist. Behandelt auch Leerzeichen in den Dateinamen.
Colin Dean
27

Hier ist eine einfachere Lösung mit Gifsicle als JohnBs Skript:

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

Dieser Befehl sollte in den meisten Unix-Shells funktionieren. Ich habe es in bash getestet. Ersetzen Sie input.gifund output.gifdurch die Namen der Eingabe- und Ausgabedateien und 99durch die Anzahl der Bilder in Ihrer Animation. (Es ist in Ordnung, eine größere Zahl zu verwenden, aber das Gifsicle wird sich darüber beschweren.)

Einige Notizen:

  • Der -USchalter fügt Frames in der Eingabeanimation mit den vorherigen Frames zusammen, sodass jedes Frame für sich steht und von keinem anderen abhängig ist. Sie möchten dies wirklich tun, bevor Sie so ziemlich alles mit Animationen machen, sonst erhalten Sie wahrscheinlich unordentliche Ergebnisse. (Wenn Ihre Eingabeanimation bereits nicht optimiert ist, gibt Gifsicle möglicherweise eine Warnung darüber aus, aber dies ist auch völlig harmlos.)

  • Umgekehrt -O2optimiert der Schalter die Ausgabeanimation neu, um die Dateigröße zu minimieren. Mit der Beispielanimation von JohnB wird die Ausgabegröße um 27% verkleinert.

  • Der seqBefehl gibt nur eine Folge von Zahlen von 0 bis 99 aus, die in Schritten von 2 hochgezählt werden. Dadurch wird vor jeder Zahl -f "#%g"ein gedruckt #, sodass Gifsicle diese als Rahmenauswahl anstelle eines Dateinamens versteht. Die Backticks ( `) um den Befehl seq bewirken, dass seine Ausgabe als Parameter in die Gifsicle-Befehlszeile eingefügt wird.

Wenn Sie nicht möchten, dass das GIF schneller wird, können Sie gifsicle -I input.gifmit die aktuelle Frame-Verzögerung ermitteln, sie mit 2 multiplizieren und verwenden gifsicle -d ${delay} ....

Ilmari Karonen
quelle
Schön gemacht! Wenn Sie es wirklich wollten, könnten Sie das grep aus meinem Skript verwenden, um die Anzahl der Frames genau anzugeben (und ein Ungetüm aus einem Befehl zu machen). Oder gibt es eine einfachere Möglichkeit, die Anzahl der Frames in einem animierten GIF zurückzugeben?
JohnB
3
Am Ende habe ich ein Ungetüm aus einem Kommando gemacht, @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
Unter Windows 7 x64 mit Version 1.71, ich habe: useless unoptimization-related input option. Also habe ich es in zwei Schritten gemacht (aus Git Bash heraus): 1. gifsicle -U -o unoptimized.gif input.gif2.gifsicle unoptimized.gif `seq -f "#%g" 0 2 99` -O2 -o output.gif
feklee
11

ANMERKUNG : Diese Antwort wurde gepostet, bevor eine Befehlszeilen- / Open Source-Anforderung bestand, aber ich lasse sie offen, da sie möglicherweise in Zukunft jemand anderem helfen wird


Verwenden von Photoshop

Dies ist keine Open Source- oder Befehlszeilenlösung, aber Sie können dies mit Photoshop tun:

DateiImportierenVideorahmen in Ebenen ...

Video importieren

" Limit To Every __ Frames " erledigt den Trick für Sie

JohnB
quelle
0

Hier ist meine Lösung mit Batch-Skriptverarbeitung ...

Kopieren Sie zuerst das erfasste animierte GIF in die Datei input.gif, starten Sie dann den Befehlsinterpreter und geben Sie Folgendes ein:

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

Schauen Sie in die Datei input.txt und sehen Sie, wie lang die Animation ist - wie viele Frames sie enthält ...

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

Dann editiere-erstelle test.bat und ändere den Wert der Variablen len = specific_length_number_from_input.txt und speichere 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

Starten Sie dann schließlich process1.bat und process2.bat und die zugeschnittene Animation mit jedem ungeraden Frame wird in die Datei input.gif verarbeitet

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
quelle
0

Ich denke, ich brauche eine neue, raffiniertere Erklärung (Frame-Verzögerung ist auch eine wichtige Sache, die ebenfalls berücksichtigt werden sollte) ... Für diesen neuen Prozess müssen Sie JREPL.BAT von diesem Link herunterladen: http://www.dostips.com /forum/viewtopic.php?t=6044

Kopieren Sie zuerst das erfasste GIF in die Datei input.gif, starten Sie dann den Befehlsinterpreter und geben Sie Folgendes ein:

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

Schauen Sie in die Datei input.txt und sehen Sie, wie lang die Animation ist - wie viele Frames sie enthält ... Schauen Sie auch in die Verzögerung des Frames und wenn dies 0.07s sind, sollten Sie die Option -d14 (7msec * 2) in diese Zeile setzen in process.cmd: gifsicle -b -U -d14 input.gif, nach der Bearbeitung save process.cmd

Bearbeiten Sie dann test.bat und ändern Sie den Wert der Variablen 166 in dieser Zeile entsprechend der Anzahl der Animationsbilder: Geben Sie für / L %% i IN (1,2,166) nach "# %% i" >> input.bat ein editieren speichern test.bat ...

Starten Sie dann process.cmd und die zugeschnittene Animation mit jedem ungeraden Bild wird in die Datei input.gif verarbeitet

PS Der Vorteil dieser Methode ist auch, dass Sie die volle Kontrolle darüber haben, was Sie aus gif löschen möchten (jedes zweite Bild (1,2,166) oder jedes dritte (1,3,166)), und so weiter, ändern Sie einfach die mittlere Zahl in Batch-Zeile, aber beachten Sie, dass Sie bei Angabe von (1,3,166) -d14 (Verzögerungsoption) von process.cmd entsprechend ändern sollten, um die Änderung (7 ms * 3/2 = 10) widerzuspiegeln, anstatt (7 ms * 2) / 1 = 14), Option für Verzögerung sollte -d10 sein ...

Andere Dateien (zum Beispiel zum Entfernen jedes zweiten Frames): 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"

Und Helfer Readme-Datei 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
quelle