FFmpegで動画からGIFを作るときは、palettegen + paletteuse の2パスで変換します。GIFの色が汚くなる原因は、パレットを最適化していないことです。2パスにすれば品質が向上し、ファイルサイズも最小化できます。
なぜ2パスが必要か
GIF形式はフレームごとに256色パレットしか持てません。ffmpeg -i input.mp4 output.gif とだけ実行すると、FFmpegは素材に関係なく決まった固定の256色パレットを使うため、色が薄くなり、バンディング(帯状ノイズ)が発生します。
解決策:
- パス1(palettegen): 動画を分析して、その素材専用の256色最適パレットを生成
- パス2(paletteuse): 最適パレットを各フレームに適用(ディザリングで色の遷移を滑らかに)
コマンド例
1. 最もシンプル:1コマンドでpalettegen+paletteuse(lavfi方式)
ffmpeg -i input.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif
fps=15… 15fpsに落としてサイズ削減scale=480:-1:flags=lanczos… 幅480px・Lanczosリサンプリング(高品質)split[s0][s1]… ストリームを2つに分岐[s0]palettegen[p]… 一方でパレット生成[s1][p]paletteuse… 他方にパレットを適用
2. 特定区間だけをGIFに変換
# 5秒から3秒間を変換(ループ無限・幅320px)
ffmpeg -ss 00:00:05 -i input.mp4 -t 3 \
-vf "fps=12,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop 0 output.gif
3. 高品質:2ファイル2パス方式(推奨)
長い動画や、設定を細かく調整したい場合に向いています。
# パス1:パレット生成(PNG保存)
ffmpeg -i input.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,palettegen=stats_mode=full" palette.png
# パス2:パレット適用してGIF生成
ffmpeg -i input.mp4 -i palette.png \
-lavfi "fps=15,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5" \
output.gif
stats_mode=full … 全フレームを均等にサンプリング(デフォルトはfull。diffは前のフレームから変化した部分だけで集計するので、背景が動かない動画で動く部分の色を優先したいときに使います)
4. 小サイズ最優先(Slack・メッセージアプリ向け)
ffmpeg -ss 00:00:10 -i input.mp4 -t 5 \
-vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse=dither=bayer" \
-loop 0 output_small.gif
5. 1回だけ再生するGIF(ループなし)
ffmpeg -i input.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop -1 output_once.gif
-loop -1 … 1回再生で停止(0は無限ループ)
6. バッチGIF生成(フォルダ内の全mp4)
for f in *.mp4; do
ffmpeg -i "$f" \
-vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
"${f%.mp4}.gif" -y
done
ファイルサイズ比較(fps × 解像度)
FFmpeg 8.1 で、Big Buck Bunny(CC BY 3.0)の 1080p・30fps の動画の最初の5秒を変換した実測値です。どれも上の「1.」のコマンドの fps と幅だけを変えています。
| fps | 幅 | ファイルサイズ | 用途 |
|---|---|---|---|
| 24 | 640px | 大(17.5MB) | 映像品質重視(大容量許容) |
| 15 | 480px | 中(6.5MB) | バランス(一般的な用途) |
| 12 | 320px | 小(2.3MB) | SNS・チャット共有 |
| 10 | 240px | 最小(1.1MB) | アイコン・ミニGIF |
数値は素材により大きく変動します。動きが多い素材ほどサイズは大きくなります。
ディザリング比較
| ディザリングモード | 見た目 | ファイルサイズ | 向いているコンテンツ |
|---|---|---|---|
bayer |
規則的なドットパターン | 最小(圧縮に有利) | 動きの多い動画 |
floyd_steinberg |
滑らかなグラデーション | やや大きい | 写真・グラデーション多め |
sierra2 |
floyd_steinbergのバランス型 |
中程度 | バランス重視 |
none |
シャープなエッジ | 小(均一色に最小) | アイコン・フラットデザイン |
dither を指定しないときの既定値は sierra2_4a です。
# floyd_steinbergを試す場合
ffmpeg -i input.mp4 -i palette.png \
-lavfi "fps=15,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=floyd_steinberg" \
output_smooth.gif
ループ制御
-loop 値 |
動作 |
|---|---|
0 |
無限ループ(FFmpeg の既定値) |
-1 |
1回再生で停止 |
N(N ≥ 1) |
N回ループして停止 |
オプション詳解
| オプション | 意味 | 推奨値 |
|---|---|---|
fps=N |
出力フレームレート | 10〜15が実用的 |
scale=W:-1 |
幅指定・高さ自動(奇数になる可能性あり) | scale=480:-2 も使用可 |
flags=lanczos |
リサンプリングアルゴリズム | 品質最高。速度優先ならbilinear |
palettegen=stats_mode=full |
パレット生成方法 | full(デフォルト)/ diff(動き重視) |
paletteuse=dither=bayer |
ディザリング方式 | bayer(サイズ小)/ floyd_steinberg(品質高) |
bayer_scale=N |
bayerの模様の出方(0〜5、既定値2。小さいほど模様が目立つ) | 5(パターンは目立たないが、色の段差が出やすい) |
トラブルシューティング
エラー1: GIFファイルが数十MB以上になった
原因: 高fps・高解像度・ディザリングがファイルサイズを増大させている。
解決策:
# fpsを10に、幅を320に落とす
ffmpeg -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse=dither=bayer" output.gif
エラー2: 色が薄い・バンディング(帯状ノイズ)が出る
原因: palettegen を使わず直接 output.gif に変換している(汎用パレット使用)。
解決策: 必ず palettegen → paletteuse の2パスパイプラインを使用してください。
エラー3: パス2で Error opening input file palette.png.
原因: パス1のパレットPNGのパスがパス2と一致していない。
解決策: 両方のコマンドで絶対パスを使います。Windowsの場合:
ffmpeg -i input.mp4 -vf "fps=15,scale=320:-1:flags=lanczos,palettegen" C:/tmp/palette.png
ffmpeg -i input.mp4 -i C:/tmp/palette.png -lavfi "fps=15,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gif
エラー4: 同じフィルターでMP4を作ると height not divisible by 2 が出る(奇数高さ)
原因: scale=480:-1 で計算された高さが奇数(例: 271)になっています。GIFは奇数の高さでもそのまま出力できますが、H.264(libx264・yuv420p)は奇数の高さをエンコードできません。
解決策: -1 の代わりに -2(偶数に丸める)を使います:
-vf "fps=15,scale=480:-2:flags=lanczos,..."
エラー5: GIFが一部のアプリで正しくループしない
原因: -loop の指定が意図した動作と異なる、またはアプリ側の実装依存。
解決策: 多くのブラウザ・アプリでは -loop 0(無限ループ)が最も互換性が高いです。
FAQ
Q1. ffmpeg -i input.mp4 output.gif ではなく2パスが必要なのはなぜですか?
A. GIFは256色しか持てないため、素材に合わせた最適パレットが必要です。2パスなしでは汎用パレットで代替され、色が大きく劣化します。
Q2. GIFとWebPやMP4ループ、どれを選ぶべきですか?
A. GIFは最も互換性が高く、メールやSlack等でも問題なく再生されます。ファイルサイズを重視するなら WebP(非対応環境あり)やMP4の loop 属性(Webページ限定)が有効です。
Q3. GIFにアルファ透過は使えますか?
A. GIFの透過は1bit(完全に透明か、不透明か)だけです。半透明は使えません。透過付きの素材は、palettegen と paletteuse で変換すれば透過が残ります。透明にするかどうかは paletteuse の alpha_threshold(既定値128)で決まり、アルファ値がこれより小さい画素が透明になります。半透明が必要な場合は WebP/APNG を検討してください。
Q4. fps=15 と fps=24、品質の違いはどれほどですか?
A. 5秒以下の短いGIFならfps=15でもほぼ自然に見えます。fps=24は滑らかですが、フレーム数が1.6倍(24÷15)になるぶん、ファイルサイズも大きくなります(増え方は素材次第です)。動きが緩やかな素材はfps=10〜12で十分です。
Q5. スケール指定なしで元の解像度のままGIFにできますか?
A. できますが、HD動画(1920px幅)をそのままGIFにするとサイズが巨大になります。scaleを省略する場合は少なくともfpsを落としてください:
ffmpeg -i input.mp4 -vf "fps=10,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" output.gif