「動画の一部をGIFにしてSlackやSNSで共有したい」「でもFFmpegで作ったGIFが汚い……」——この悩みの原因はパレット最適化の欠如です。palettegen + paletteuse の2パスパイプラインを使えば、劇的に品質が向上しファイルサイズも最小化できます。この記事で最適なGIF作成コマンドを習得しましょう。所要時間:10分。

動作確認: FFmpeg 6.1(ubuntu-latest / GitHub Actions CI検証済み)


この記事でわかること

  1. なぜナイーブな変換(ffmpeg -i input.mp4 output.gif)は品質が悪いのか
  2. palettegen → paletteuse 2パスパイプラインの仕組み
  3. 1コマンド版(lavfi分岐)vs 2ファイル2パス版の使い分け
  4. fps・解像度・ディザリングの最適値とファイルサイズ比較表
  5. 動画クリップから特定区間をGIFに変換する方法
  6. バッチGIF生成(複数ファイル一括)
  7. よくあるエラーと解決策5件
  8. FAQ 5問

なぜ2パスが必要か

GIFフォーマットはフレームごとに256色パレットしか持てません。ffmpeg -i input.mp4 output.gif と単純に実行すると、FFmpegは汎用のWebセーフパレットで代替するため、色が薄くバンディング(帯状ノイズ)が発生します。

解決策:

  1. パス1 — palettegen: 動画を分析して、そのコンテンツ専用の256色最適パレットを生成
  2. パス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ファイルサイズ目安用途
24640px大(8〜20MB)映像品質重視(大容量許容)
15480px中(3〜8MB)バランス(一般的な用途)
12320px小(1〜3MB)SNS・チャット共有
10240px最小(0.5〜1.5MB)アイコン・ミニGIF

数値は素材により大きく変動します。動きが多い素材ほどサイズは大きくなります。


ディザリング比較

ディザリングモード見た目ファイルサイズ向いているコンテンツ
bayer(デフォルト)規則的なドットパターン最小(圧縮に有利)動きの多い動画
floyd_steinberg滑らかなグラデーションやや大きい写真・グラデーション多め
sierra2floyd_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無限ループ(ブラウザデフォルト)
-11回再生で停止
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=Nbayerパターンのサイズ(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

関連記事


テスト環境: ffmpeg 6.1.1 / Ubuntu 24.04(GitHub Actions)
一次ソース: ffmpeg.org/ffmpeg-filters.html#palettegen / ffmpeg.org/ffmpeg-filters.html#paletteuse