動画GIF変換コードの19種比較検証
何のために行ったか
動画からGIF変換の「速い・サイズが小さい・綺麗」を比較するために検証を行いました。
代表的な例を自分なりにチューンナップして全19種類のデータをとりましたので共有します。
なお汎用的なおすすめコードの紹介は主旨から外れますので触れません。
TOC
Performance table
処理時間とファイルサイズの関係
Time(mSec) | Size(MB) | |
---|---|---|
case 1 | 1310 | 3.3 |
case 2 | 4310 | 7.2 |
case 3-1 | 13650 | 6.9 |
case 3-2 | 13940 | 6.2 |
case 3-3 | 14430 | 6.9 |
case 4-1 | 5580 | 3.5 |
case 4-2 | 6020 | 4.6 |
case 4-3 | 6260 | 5.3 |
case 4-4 | 7340 | 6.6 |
case 5 | 10850 | 7.8 |
case 6 | 5330 | 8.0 |
GIFSICLE1-1 | 33560 | 5.6 |
GIFSICLE1-2 | 31980 | 5.7 |
GIFSICLE1-3 | 23070 | 10.6 |
GIFSICLE1-4 | 31060 | 7.1 |
GIFSICLE1-5 | 40040 | 7.3 |
GIFSICLE1-6 | 31570 | 7.1 |
GIFSICLE2-1 | 2280 | 3.3 |
GIFSICLE2-2 | 5860 | 5.2 |
数値が低いほうがbetter
時間 x サイズ = コスト
Cost (Time x Size) | |
---|---|
case 1 | 4323 |
case 2 | 31032 |
case 3-1 | 94185 |
case 3-2 | 86428 |
case 3-3 | 99567 |
case 4-1 | 19530 |
case 4-2 | 27692 |
case 4-3 | 33178 |
case 4-4 | 48444 |
case 5 | 84630 |
case 6 | 42640 |
GIFSICLE1-1 | 187936 |
GIFSICLE1-2 | 182286 |
GIFSICLE1-3 | 244542 |
GIFSICLE1-4 | 220526 |
GIFSICLE1-5 | 292292 |
GIFSICLE1-6 | 224147 |
GIFSICLE2-1 | 7524 |
GIFSICLE2-2 | 30472 |
数値が低いほうがbetter。コストの低さと出来上がる画質を比べる必要がある。
Gifsicleを除外
Ubuntu18.04では1.91、Ubuntu20.04では1.92がUniverseリポジトリに登録されています。今回1.91を用いました。1.92ではオプション--lossy
が使えるようになります。
ピンクの網掛け部分がGifsicleによる処理結果部分です。
Gifsicle2-1とGifsicle2-2の画質
Gifsicle以外と比べて非常に成績が悪かったため[1]以降Gifsicleの結果は外します。
--lossy
オプションがない状態で作成された各GIFファイルに適用した結果、サイズ変化は誤差の範囲でした。
gifsicle --colors 256 --optimize=3 --batch -i *.gif
- gifsicle適用前
- gifsicle適用後
計測方法
10ミリ秒単位で計測。こちらを参考にさせて頂きました。
bash で処理時間を 10 ミリ秒単位で計測する方法
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
処理
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
高速化
共通。改善点を教えて頂けると嬉しいです。
- ffmpeg
-
-threads 0
(デフォルトはslice+frame
。-threads 0 -thread_type frame
も可)
-
- pngquant
- GNU parallel
- ほか
結果1: コードと拡大画像の見た目比較
-
${FILE}
- インプットされる動画ファイル
-
${FPS}
- 設定したいFPS
-
${WIDTH}
- 設定したい横幅
CASE1
基本形。画像の粗さがかえってノスタルジーな気分にさせる。
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" -loop 0 -y output.gif
1秒310, 3.3 MB, 一部領域を拡大
CASE2
基本形の発展型。画質が綺麗。サイズ大きい。時間ははやい。尺がある場合使用をためらうタイプ。
- ffmpeg
- palettegen
-
stats_mode=full
(Default)
-
- paletteuse
-
dither=sierra2_4a
(Default) -
diff_mode=none
(Default)
-
- palettegen
ffmpeg -threads 0 -i "${FILE}" -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 -y output.gif
4秒310, 7.2 MB, 一部領域を拡大
CASE3-1
stats_mode=diff
は前景の動きが激しい場合に用います。bayer_scale=*1*
だと画質は荒くコストは高い。実験用。
- ffmpeg
- palettegen
stats_mode=diff
- paletteuse
dither=bayer
bayer_scale=1
diff_mode=rectangle
- palettegen
ffmpeg -threads 0 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=bayer:bayer_scale=1:diff_mode=rectangle"\
-y -loop 0 output.gif
13秒650, 6.9 MB, 一部領域を拡大
CASE3-2
3-1との違いはbayer_scale
値。case3-1に比べ僅かにサイズが小さくなる。時間は同じ様に長い。
- ffmpeg
- palettegen
stats_mode=diff
- paletteuse
dither=bayer
bayer_scale=5
diff_mode=rectangle
- palettegen
ffmpeg -threads 0 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"\
-y -loop 0 output.gif
13秒940, 6.2 MB, 一部領域を拡大
CASE3-3
画質は良い方。処理時間は長い。サイズ大きい。コスト高い。
- ffmpeg
- palettegen
stats_mode=diff
- paletteuse
dither=floyd_steinberg
- palettegen
ffmpeg -threads 0 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=floyd_steinberg"\
-y -loop 0 output.gif
14秒430, 6.9 MB, 一部領域を拡大
CASE4-1
サイズとても小さい。時間短い。コスト低い。画質が荒い。一時ファイルを大量に作るタイプ。
- pngquant
quality=0-5
mkdir '.tmp'
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-5 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm -r '.tmp'
5秒580, 3.5 MB, 一部領域を拡大
CASE4-2
速いしサイズも小さくてコストが低い。
- pngquant
quality=0-20
mkdir '.tmp'
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-20 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm -r '.tmp'
6秒020, 4.6 MB, 一部領域を拡大
CASE4-3
画質・サイズ・速度・コスト全てにおいて良いバランスです。
- pngquant
quality=0-40
mkdir '.tmp'
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-40 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm -r '.tmp'
6秒260, 5.3 MB, 一部領域を拡大
CASE4-4
処理時間は全検証中真ん中くらい。画質は綺麗。サイズ大きめ。
- pngquant
quality=0-60
mkdir '.tmp'
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-60 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm -r '.tmp'
7秒340, 6.6 MB, 一部領域を拡大
CASE5
convertコマンドにlayer optimize
をつけました。処理時間長い感じでサイズは大きくなります。コスト大きい。
- pngquant
quality=0-60
- convert
layers optimize
mkdir '.tmp'
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-60 {}
convert .tmp/*fs8.png -loop 0 -layers optimize output.gif
rm -r '.tmp'
10秒850, 7.8 MB, 一部領域を拡大
CASE6
処理速度は速い方。サイズ大きい。
- convert
layers optimize
ffmpeg -threads 0 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" -c:v pam -f image2pipe - | convert -delay $((100 / ${FPS})) - -loop 0 -layers optimize output.gif
5秒330, 8.0 MB, 一部領域を拡大
結果2: GIF動画の見た目比較
検証のため重いGIFを貼り付けています。ご了承ください。
CASE1
CASE2
CASE3-1
CASE3-2
CASE3-3
CASE4-1
CASE4-2
CASE4-3
CASE4-4
CASE5
CASE6
今後の改善点
- Gifsicle1.92以降の
-lossy
をつけた場合のサイズ・画質変化の調査
Install
試されたい方はこちらからどうぞ。
sudo install gifsicle parallel ffmpeg imagemagick pngquant pulseaudio-utils libnotify-bin
全体のコード
code
#!/bin/bash
# 初期設定 ###########
FPS=10
WIDTH=600
FILE="input.mp4"
# ####################
CASE="GIFSICLE1-1"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --optimize=3 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE1-2"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --optimize=2 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE1-3"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method sample --optimize=1 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE1-4"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method box --optimize=1 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE1-5"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method lanczos3 --optimize=1 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE1-6"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=floyd-steinberg --optimize=1 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE2-1"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --optimize=3 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="GIFSICLE2-2"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --optimize=3 > output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
# CASE="TEST1"
# set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
# ffmpeg -threads 0 -t 5 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
# ffmpeg -threads 0 -t 5 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
# paletteuse=dither=bayer:bayer_scale=1:diff_mode=rectangle"\
# -y -loop 0 output.gif
# gifsicle --loopcount=0 --optimize=3 output.gif
# time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
# convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
# Gifsicle other options ------------------------------- <Gifsicle1.91のコストが高いため検証から除外>
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method catrom
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mitchell
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=ro64
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=o3
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=o4
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=ordered
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=halftone,10,3
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=squarehalftone
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --dither=diagonal
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --color-method blend-diversity
# ffmpeg -threads 0 -t 5 -i "${FILE}" -pix_fmt rgb8 -f gif - | gifsicle --loopcount=0 --resize-width ${WIDTH} --resize-method mix --color-method median-cut
# ------------------------------------------------------
CASE="1"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" -loop 0 -y output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="2"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 -y output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="3-1"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -t 5 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=bayer:bayer_scale=1:diff_mode=rectangle"\
-y -loop 0 output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="3-2"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -t 5 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"\
-y -loop 0 output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="3-3"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i $FILE -vf "palettegen=stats_mode=diff" -y palette.png
ffmpeg -threads 0 -t 5 -i $FILE -i palette.png -lavfi "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos [x]; [x][1:v]\
paletteuse=dither=floyd_steinberg"\
-y -loop 0 output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="4-1"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
mkdir '.tmp'
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-5 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm .tmp/*.png
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="4-2"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
mkdir '.tmp'
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-20 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm .tmp/*.png
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="4-3"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
mkdir '.tmp'
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-40 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm .tmp/*.png
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="4-4"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
mkdir '.tmp'
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-60 {}
convert .tmp/*fs8.png -loop 0 output.gif
rm .tmp/*.png
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="5"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
mkdir '.tmp'
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" .tmp/%04d.png
find .tmp/ -maxdepth 1 -type f -name '*.png' -not -name '*fs8.png' -print0 | parallel -0 pngquant --quality=0-60 {}
convert .tmp/*fs8.png -loop 0 -layers optimize output.gif
rm .tmp/*.png
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
CASE="6"
set_START() { local dummy; read START dummy < /proc/uptime; }; get_ELAPS() { local dummy; read END dummy < /proc/uptime; let ELAPS=${END/./}0-${START/./}0; }; time { set_START; }; START=$START;
ffmpeg -threads 0 -t 5 -i "${FILE}" -vf "fps=${FPS},scale=${WIDTH}:(ow/a/2)*2:flags=lanczos" -c:v pam -f image2pipe - | convert -delay $((100 / ${FPS})) - -loop 0 -layers optimize output.gif
time { get_ELAPS; }; END=$END; ELAPS=START-END=$ELAPS; mv output.gif ${CASE}_${ELAPS}.gif;
convert ${CASE}_${ELAPS}.gif[0] gif:- | convert -crop 200x100+200+50 gif:- ${CASE}_${ELAPS}.jpg;
paplay "Positive.ogg"
notify-send "Measurement test" "Done."
Reference
-
Gifsicleの性能が出なかったのは想定された使用方法とは異なるからと思います。 ↩︎
Discussion