Jak usunąć co drugą klatkę z animowanego gifa?

22

Mam folder pełen filmów, które chcę przekonwertować na animowane gify. ffmpeg / avconv robi złą robotę, robiąc to bezpośrednio, więc zamiast tego przekonwertowałem wideo na gif, najpierw wysyłając każdą klatkę jako png, a następnie konwertując z powrotem na gif za pomocą imagemagick. Problem polega na tym, że powoduje to duży gif pod względem wielkości pliku. Aby rozwiązać ten problem, chcę „upuszczać” co sekundę lub n-tą klatkę z gif, albo pomijając każdy plik obrazu podczas konwersji do gif, albo usuwając klatki z gif. Jak mogę to zrobić w systemie Ubuntu (13.04) za pomocą imagemagick lub innego narzędzia wiersza polecenia?

hellocatfood
źródło

Odpowiedzi:

14

Za pomocą skryptu bash

Aby to zrobić z wiersza poleceń, możesz użyć narzędzia o nazwie Gifsicle . Nie ma wbudowanej metody usuwania każdej innej klatki, więc będziesz musiał ubrudzić sobie ręce przy skryptowaniu.

Oto krótki skrypt, który zrobiłem, aby zrobić tylko jeden 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

Przetestowałem to za pomocą prostego GIF-a odliczającego:

wprowadź opis zdjęcia tutaj

A oto wynik po uruchomieniu go przez skrypt:

wprowadź opis zdjęcia tutaj

Ten skrypt oczywiście nie jest kuloodporny, ale powinien poprowadzić cię we właściwym kierunku.

JohnB
źródło
Pamiętaj, że pętlę usuwania można uprościć dolet i=0; while [[ $i -lt $(($numframes / 2)) ]]; do gifsicle $2 --delete "#$i" -o $2; let i=i+1; done
Ilmari Karonen,
1
W rzeczywistości nie potrzebujesz w ogóle pętli: gifsicle "$1" --unoptimize $(seq -f "#%g" 0 2 $numframes) -O2 -o "$2"zrobisz to za jednym razem.
Ilmari Karonen,
Właściwie nie wiem, jak uruchomić ten skrypt z wiersza poleceń. Próbowałem zapisać go jako plik gifdrop.sh i uruchomić go zgodnie z instrukcją użycia (./gifdrop.sh in.gif out.gif) na gif o nazwie in.gif i napisano nieznane polecenie gifdrop
mheavers
@mheavers czy wykonałeś plik? chmod +x gifdrop.sh
JohnB
1
Działa lepiej na macOS: github.com/colindean/hejmo/blob/master/scripts/… - moja wersja, która używa perla zamiast grep, więc działa na macOS, na którym GNU grep nie jest domyślnie zainstalowany. Obsługuje również spacje w nazwach plików.
Colin Dean
27

Oto prostsze rozwiązanie z użyciem gifsicle niż skrypt JohnB:

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

To polecenie powinno działać w większości powłok uniksowych; Przetestowałem to w bashu. Wymień input.gifi output.gifz nazwami plików wejściowych i wyjście, a 99wraz z liczbą klatek animacji. (Można użyć większej liczby, ale gifsicle narzeka na to.)

Niektóre uwagi:

  • -UPrzełącznik połączą klatek animacji wejściowego z tych poprzednich, tak że każdy klatki stoi sam i nie zależy w żadnym innym. Naprawdę chcesz to zrobić, zanim zrobisz prawie wszystko z animacjami, w przeciwnym razie możesz uzyskać niechlujne wyniki. (Jeśli twoja animacja wejściowa jest już niezoptymalizowana, gifsicle może wydrukować ostrzeżenie o tym, ale jest to również całkowicie nieszkodliwe).

  • Natomiast -O2przełącznik ponownie optymalizuje animację wyjściową, aby zminimalizować rozmiar pliku. Dzięki przykładowej animacji JohnB zmniejsza rozmiar wyjściowy o 27%.

  • seqKomenda właśnie wysyła ciąg liczb od 0 do 99, licząc się z krokiem 2. -f "#%g"czyni go wydrukować #przed każdym numerem, co sprawia gifsicle zrozumieć go jako selekcji ramki zamiast nazwy pliku. Backticks ( `) wokół polecenia seq powodują, że jego dane wyjściowe są uwzględniane jako parametry w wierszu polecenia gifsicle.

Jeśli nie chcesz, aby gif przyspieszył, możesz użyć, gifsicle -I input.gifaby uzyskać bieżące opóźnienie ramki, pomnożyć je przez 2 i użyć gifsicle -d ${delay} ....

Ilmari Karonen
źródło
Ładnie wykonane! Jeśli naprawdę chciałeś, możesz użyć grep z mojego skryptu, aby dokładnie podać liczbę ramek (i zrobić jeden behemot polecenia). A może istnieje prostszy sposób na zwrócenie liczby klatek w animowanym pliku GIF?
JohnB
3
Skończyło się na behemocie polecenia, @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
W systemie Windows 7 x64 z wersji 1.71, mam: useless unoptimization-related input option. Zrobiłem to w dwóch krokach (z poziomu 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

UWAGA : Ta odpowiedź została opublikowana, zanim pojawiło się wymaganie dotyczące wiersza poleceń / open source, ale pozostawiam to, ponieważ może pomóc komuś innemu w przyszłości


Korzystanie z Photoshopa

Nie jest to rozwiązanie typu open source lub wiersza poleceń, ale można to zrobić za pomocą programu Photoshop:

PlikImportujRamki wideo do warstw ...

Importuj wideo

Limit do każdego __ Frames ” załatwi dla Ciebie

JohnB
źródło
0

Oto moje rozwiązanie wykorzystujące przetwarzanie skryptów wsadowych ...

Najpierw skopiuj przechwycony-oryginalny animowany gif do pliku input.gif, a następnie uruchom interpreter poleceń i wpisz:

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

zajrzyj do input.txt i zobacz, jaka jest długość animacji - ile klatek zawiera ...

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

następnie edytuj-utwórz test.bat i zmień wartość zmiennej len = określ_lość_numer_od_wejścia.txt i zapisz 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

następnie w końcu uruchom proces1.bat i proces2.bat i przycięta animacja z każdą nieparzystą klatką zostanie przetworzona do pliku input.gif

proces1.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
źródło
0

Myślę, że potrzebuję nowego, bardziej złożonego wyjaśnienia (opóźnienie ramki jest również ważną rzeczą, którą również należy wziąć pod uwagę) ... W tym nowym procesie musisz pobrać JREPL.BAT z tego linku: http://www.dostips.com /forum/viewtopic.php?t=6044

Najpierw skopiuj przechwycony gif do pliku input.gif, a następnie uruchom interpreter poleceń i wpisz:

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

spójrz na input.txt i zobacz, jaka jest długość animacji - ile klatek zawiera ... również sprawdź opóźnienie klatki i jeśli jej 0.07s oznacza, że ​​powinieneś wstawić opcję -d14 (7 ms * 2) w tym wierszu w process.cmd: gifsicle -b -U -d14 input.gif, po edycji zapisz proces.cmd

następnie edytuj test.bat i zmień wartość zmiennej 166 z tego wiersza, aby dopasować ją do liczby klatek animacji: for / L %% i IN (1,2,166) DO echa "# %% i" >> input.bat, po edytuj zapisz test.bat ...

następnie uruchom process.cmd i przycięte animacje z każdą nieparzystą klatką zostaną przetworzone do pliku input.gif

PS Zaletą tej metody jest również to, że masz pełną kontrolę nad tym, co chcesz usunąć z gif (co druga klatka (1,2,166) lub co trzecia (1,3,166)) i tak dalej, po prostu zmień środkową liczbę w linia wsadowa, ale pamiętaj, że jeśli podasz (1,3,166), odpowiednio zmień -d14 (opcja opóźnienia) z process.cmd, aby odzwierciedlić zmianę (7 ms * 3/2 = 10), więc zamiast (7 ms * 2 / 1 = 14), opcja opóźnienia powinna wynosić -d10 ...

Inne pliki (na przykład usuwanie co drugą klatkę): 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"

I plik pomocniczy readme 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
źródło