「動画の一部をGIFにしてSlackやSNSで共有したい」「でもFFmpegで作ったGIFが汚い……」——この悩みの原因はパレット最適化の欠如です。palettegen + paletteuse の2パスパイプラインを使えば、劇的に品質が向上しファイルサイズも最小化できます。この記事で最適なGIF作成コマンドを習得しましょう。所要時間:10分。
動作確認: FFmpeg 6.1(ubuntu-latest / GitHub Actions CI検証済み)
この記事でわかること
- なぜナイーブな変換(
ffmpeg -i input.mp4 output.gif)は品質が悪いのか palettegen → paletteuse2パスパイプラインの仕組み- 1コマンド版(lavfi分岐)vs 2ファイル2パス版の使い分け
- fps・解像度・ディザリングの最適値とファイルサイズ比較表
- 動画クリップから特定区間をGIFに変換する方法
- バッチGIF生成(複数ファイル一括)
- よくあるエラーと解決策5件
- FAQ 5問
なぜ2パスが必要か
GIFフォーマットはフレームごとに256色パレットしか持てません。ffmpeg -i input.mp4 output.gif と単純に実行すると、FFmpegは汎用のWebセーフパレットで代替するため、色が薄くバンディング(帯状ノイズ)が発生します。
解決策:
- パス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 × 解像度)
同一素材(30fps/1080pの5秒動画)を変換した場合の目安:
| fps | 幅 | ファイルサイズ目安 | 用途 |
|---|---|---|---|
| 24 | 640px | 大(8〜20MB) | 映像品質重視(大容量許容) |
| 15 | 480px | 中(3〜8MB) | バランス(一般的な用途) |
| 12 | 320px | 小(1〜3MB) | SNS・チャット共有 |
| 10 | 240px | 最小(0.5〜1.5MB) | アイコン・ミニGIF |
数値は素材により大きく変動します。動きが多い素材ほどサイズは大きくなります。
ディザリング比較
| ディザリングモード | 見た目 | ファイルサイズ | 向いているコンテンツ |
|---|---|---|---|
bayer(デフォルト) | 規則的なドットパターン | 最小(圧縮に有利) | 動きの多い動画 |
floyd_steinberg | 滑らかなグラデーション | やや大きい | 写真・グラデーション多め |
sierra2 | floyd_steinbergのバランス型 | 中程度 | バランス重視 |
none | シャープなエッジ | 小(均一色に最小) | アイコン・フラットデザイン |
# 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 | 無限ループ(ブラウザデフォルト) |
-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パターンのサイズ(1〜5) | 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で No such file or directory: 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: scale=480:-1 でエラーが出る(奇数高さ)
原因: 計算された高さが奇数になるとGIFエンコーダーがエラーを出すことがある。
解決策: -1 の代わりに -2(偶数に丸める)を使用:
-vf "fps=15,scale=480:-2:flags=lanczos,..."
エラー5: GIFが一部のアプリで正しくループしない
原因: -loop の指定が意図した動作と異なる、またはアプリ側の実装依存。
解決策: 多くのブラウザ・アプリでは -loop 0(無限ループ)が最も互換性が高いです。Discordなどでは -loop -1 でも正しく再生できます。
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透過(完全透明 or 不透明)のみ対応です。半透明は使えません。透過が必要な場合は palettegen=transparency_color=オプションか、WebP/APNGを検討してください。
Q4. fps=15 と fps=24、品質の違いはどれほどですか?
A. 5秒以下の短いGIFならfps=15でもほぼ自然に見えます。fps=24は滑らかですがファイルサイズが1.6倍になります。動きが緩やかなコンテンツは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
関連記事
- 動画のスケーリング・リサイズ — scaleフィルター完全ガイド
- 動画のトリミング・カット — -ss/-to/-t 完全ガイド
- 透かし・ロゴのオーバーレイ合成
- シェルスクリプトでバッチ変換を自動化する
テスト環境: ffmpeg 6.1.1 / Ubuntu 24.04(GitHub Actions)
一次ソース: ffmpeg.org/ffmpeg-filters.html#palettegen / ffmpeg.org/ffmpeg-filters.html#paletteuse