「ダウンロードした動画の上下に黒帯が入っている」「4:3 ソースを 16:9 のディスプレイで見たら左右が真っ黒」——FFmpeg の cropdetect で黒帯のサイズを自動検出して、crop フィルタで一気に切り落とせます。掲示板でよく見る「-vf crop=W:H:X:Y を使え」だけでは、値の出し方・偶数サイズの罠・再エンコード品質劣化が抜けがちです。本記事はそれら全てを順を追ってカバーします。

動作確認: FFmpeg 8.1(Windows / macOS / Linux)


この記事でわかること

  • cropdetect フィルタで黒帯領域を自動検出する方法
  • 検出結果を crop フィルタにそのまま貼り付ける流れ
  • libx264 / yuv420p で必須の 偶数サイズ調整
  • 黒帯が「真っ黒(0,0,0)」でない場合の閾値(limit)チューニング
  • レターボックス(上下黒帯)/ピラーボックス(左右黒帯)の違い
  • 逆に pad フィルタで黒帯を追加する(YouTube などの 16:9 強制対応)
  • 複数ファイルの一括バッチ処理スクリプト

黒帯が発生する理由

ケース状況表示結果
レターボックス16:9 ディスプレイで 2.35:1 シネスコ作品を再生上下に黒帯
ピラーボックス16:9 ディスプレイで 4:3 (1.33:1) ソースを再生左右に黒帯
両方(ウィンドウボックス)4:3 ソースをさらに 2.35:1 で出力上下+左右
エンコード時に焼き込まれた黒帯ソース動画ファイルに黒帯ピクセルとして含まれる物理的に黒ピクセル

最後のケース(ピクセルとして焼き込まれた黒帯)が、本記事で扱う「crop で削除すべき」対象です。表示時の黒帯(プレイヤーがアスペクト比を保つために動的に追加するもの)は動画ファイル自体には含まれていないので、crop の出番はありません。


cropdetect で自動的に検出する

基本の検出コマンド

ffmpeg -i input.mp4 -vf cropdetect -f null -
部分説明
-vf cropdetect各フレームを解析して黒帯領域を検出
-f null -出力ファイルを書かずに解析だけ実行

実行すると標準エラーに大量のログが流れ、各フレームの最後に crop=W:H:X:Y という形でそのまま使える値が表示されます。最後の数行を採用するのが基本です。

[Parsed_cropdetect_0 @ 0x...] x1:0 x2:1919 y1:140 y2:939 w:1920 h:800 x:0 y:140 crop=1920:800:0:140

この行から読み取るのは末尾の crop=1920:800:0:140 部分。これが次のステップで -vf に貼り付ける値です。

短時間サンプリングで高速化

長尺動画で全フレーム解析すると時間がかかります。冒頭に黒帯がない(オープニングロゴが画面いっぱい)場合は、動画の中盤から数秒だけサンプリングします。

ffmpeg -ss 60 -i input.mp4 -t 5 -vf cropdetect -f null -

-ss 60 で 1 分地点にシークし、-t 5 で 5 秒だけ解析。これで本編の代表シーンを高速にチェックできます。

閾値(limit)と丸め(round)の調整

cropdetect には主要な引数が 2 つあります。

引数デフォルト説明
limit24 (8bit)「黒」と判定する輝度の上限。低いほど厳しく、高いほど緩い
round16検出サイズをこの倍数に丸める。エンコーダ互換性のため
reset0N フレーム毎に検出をリセット。シーン変化対応
# 閾値を下げて (limit=16)、丸めを 2 にして (round=2) 厳密に検出
ffmpeg -ss 60 -i input.mp4 -t 5 -vf cropdetect=24:16:0 -f null -

limit を下げすぎると本編のダーク領域まで黒帯と誤検出することがあるので、デフォルト 24 から始めて段階的に調整します。


検出した値を crop フィルタに適用する

cropdetect が出した crop=W:H:X:Y をそのまま -vf に渡します。下記は入力解像度に依存しない汎用形で、実際には cropdetect の検出値(例: crop=1920:800:0:140)を貼り付けます。

ffmpeg -i input.mp4 -vf "crop=in_w:in_h-40:0:20" -c:a copy output.mp4
引数意味
crop=W:H:X:Y幅 W × 高さ H を、左上 (X, Y) から切り出す
in_w / in_h入力幅 / 入力高さの式変数(解像度を知らなくても書ける)
-c:a copy音声は再エンコードせずコピー(劣化なし、高速)

上の例は「上下 20 ピクセルずつ削る」コマンドです。1920×1080 ソースで上下 140px の黒帯を削るなら数値で crop=1920:800:0:140 と書けます。

重要: crop を使うと映像は必ず再エンコードされます。デフォルトは入力と同じコーデックではなく、出力拡張子から推定された既定コーデック(mp4 なら libx264)になります。品質を保ちたい場合は -c:v libx264 -crf 18 のように明示してください。

品質指定付きの推奨形

ffmpeg -i input.mp4 -vf "crop=in_w:in_h-40:0:20" \
  -c:v libx264 -crf 18 -preset slow \
  -c:a copy output.mp4

-crf 18 は視覚的にほぼロスレスとされる品質。-preset slow で圧縮効率も向上します。アーカイブ用途ならこの組み合わせが安心です。

コーデックコピーで切り抜けないか?

# 間違い
ffmpeg -i input.mp4 -vf "crop=in_w:in_h-40:0:20" -c copy output.mp4

-c copy はストリームコピー指定なので、-vf(フィルタ)と同時には使えません。フィルタはピクセルデータの書き換えを伴うため、必ず再エンコードが発生します。


手動で計算する場合

cropdetect が誤検出する場合や、特定の比率に合わせて切りたい場合は手計算します。

レターボックス(上下黒帯)の例

1920×1080 ソース、上下にそれぞれ 140px の黒帯がある場合、高さ = 1080 − 140×2 = 800、Y オフセット = 140 と計算できます。汎用的に「上下 N px 削る」と書く形がこちらです:

# 上下 20px ずつ削る(1920×1080 → 1920×1040 と同じ考え方)
ffmpeg -i input.mp4 -vf "crop=in_w:in_h-40:0:20" -c:a copy output.mp4

ピラーボックス(左右黒帯)の例

1920×1080 表示の中に 4:3 (1440×1080) コンテンツがあり、左右にそれぞれ 240px の黒帯がある場合は、幅 = 1920 − 240×2 = 1440、X オフセット = 240。汎用形は次のとおり:

# 左右 40px ずつ削る
ffmpeg -i input.mp4 -vf "crop=in_w-80:in_h:40:0" -c:a copy output.mp4

中央クロップ(簡略記法)

X / Y を省略すると中央が自動採用されます。

# 入力の中央 4:3 領域を切り出す(in_h*4/3 が in_w を超えないことが前提)
ffmpeg -i input.mp4 -vf "crop=in_h*4/3:in_h" -c:a copy output.mp4

偶数サイズの罠と解決策

H.264 / H.265 で yuv420p ピクセルフォーマット(最も互換性が高い)を使う場合、幅と高さの両方が 2 の倍数でなければエンコード時に下記エラーが出ます。

[libx264 @ 0x...] height not divisible by 2 (1920x801)
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0

cropdetect のデフォルト round=16 ならこの問題は起きませんが、round=2 などにした際や、手計算で奇数になった場合の対処を覚えておきます。

解決策1: 数式で「偶数に強制」する

# floor(W/2)*2 で必ず偶数化
ffmpeg -i input.mp4 -vf "crop=floor(in_w/2)*2:floor(in_h/2)*2" -c:a copy output.mp4

in_w / in_h は入力幅・入力高さ。これは「奇数なら 1 引いて偶数にする」イディオムで、汎用スクリプトに使えます。

解決策2: yuv420p 要件を緩める

yuv444pyuv422p なら奇数でも通りますが、再生互換性が下がります。

# 奇数サイズを許容するため yuv444p を指定
ffmpeg -i input.mp4 -vf "crop=in_w-1:in_h-1:0:0" \
  -c:v libx264 -pix_fmt yuv444p -c:a copy output.mp4

ただし古い再生環境(テレビ・スマートフォン標準プレイヤー)では再生できないことがあるので、配布用には yuv420p + 偶数サイズが安全です。


黒帯が真っ黒でない場合の対処

ノイズの多いソースや、圧縮で黒が歪んだ動画では、黒帯ピクセルが純粋な (0, 0, 0) ではなく (16, 16, 16) や (24, 24, 24) のような「ほぼ黒」になっていることがあります。デフォルトの limit=24 でも検出されないとき、閾値を緩めます。

# limit を 64 まで緩めて検出
ffmpeg -ss 60 -i input.mp4 -t 5 -vf cropdetect=64:2:0 -f null -

第 1 引数(64)が limit、第 2 引数(2)が round、第 3 引数(0)が reset です。limit を上げすぎると本編の暗い部分まで「黒帯」として削られてしまうので、検出された矩形を ffplay などで目視確認すると安心です。

# 検出値を仮当てして再生確認(再エンコードなしのプレビュー)
ffplay -vf "crop=1920:800:0:140" input.mp4

逆に黒帯を追加する(pad)

YouTube や Instagram など、特定アスペクト比を要求するプラットフォームに 4:3 や縦動画をアップロードする際、pad フィルタで黒帯を追加します。

4:3 → 16:9 ピラーボックス追加

ffmpeg -i input.mp4 -vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black" -c:a copy output.mp4
部分説明
scale=...:force_original_aspect_ratio=decreaseアスペクト比を保ったまま 1080 高さに収める
pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black1920×1080 の枠に中央配置、余白を黒で塗る

(ow-iw)/2 は「出力幅と入力幅の差を 2 で割った値」、つまり中央配置の X オフセットです。

縦動画 → 16:9 横向きパディング

スマートフォン縦撮り (1080×1920) を 16:9 (1920×1080) 動画として出すケース:

ffmpeg -i input.mp4 -vf "scale=-2:1080,pad=1920:1080:(ow-iw)/2:0:black" -c:a copy output.mp4

縦動画を高さ 1080 にスケールしてから、左右に黒帯を加えて 1920 幅に揃えます。

黒以外の色で塗る

ffmpeg -i input.mp4 -vf "pad=1920:1080:(ow-iw)/2:(oh-ih)/2:white" -c:a copy output.mp4

pad の最後の引数が色名。white / red / 0x00ff00 のような 16 進数指定も可能です。


一括処理スクリプト

複数ファイルにまとめて同じクロップ値を適用する場合の Bash スクリプト例です。

for f in *.mp4; do
  ffmpeg -nostdin -i "$f" -vf "crop=1920:800:0:140" -c:v libx264 -crf 20 -c:a copy "cropped_${f}" -y
done

各ファイルごとに cropdetect → crop を自動化したい場合:

for f in *.mp4; do
  CROP=$(ffmpeg -ss 60 -i "$f" -t 3 -vf cropdetect -f null - 2>&1 | \
    grep -oE 'crop=[0-9:]+' | tail -1)
  ffmpeg -nostdin -i "$f" -vf "$CROP" -c:v libx264 -crf 20 -c:a copy "cropped_${f}" -y
done

grep -oE 'crop=[0-9:]+' でログから crop= 行だけを抽出し、最後の検出結果を採用します。動画ごとに黒帯サイズが違っても自動対応できます。


よくある質問(FAQ)

Q1. なぜ crop だけなのに画質が落ちるのですか? A. crop フィルタはピクセルデータを書き換えるため、必ず再エンコードが発生します。出力時のコーデック・品質設定(-crf 値など)次第で画質が変わります。-crf 18(視覚的ほぼロスレス)以下を指定するのが安全です。-c copy はフィルタと併用できません。

Q2. 黒帯のサイズが時間で変わる動画にはどう対応しますか? A. cropdetect=limit:round:resetreset パラメータに 1 以上を指定すると、N フレーム毎に検出をリセットします。ただしクロップ自体はシーンごとに変えられないので、最大の安全値(最も小さい矩形)を採用するか、シーンごとにファイルを分割して個別処理します。

Q3. 縦動画を横動画用にパディングしたい A. 「逆に黒帯を追加する(pad)」セクションの「縦動画 → 16:9 横向きパディング」コマンドを使います。scale=-2:1080,pad=1920:1080:(ow-iw)/2:0:black で縦動画を中央配置の横動画にできます。

Q4. crop と scale はどちらを先に書くべきですか? A. 一般には crop → scale の順です。先に crop で不要領域を捨ててから scale すると、scale するピクセル数が減って高速かつ品質劣化が少なくなります。例: -vf "crop=1920:800:0:140,scale=1280:-2"

Q5. Width is not divisible by 2 エラーが出ます A. yuv420p で奇数幅・奇数高さは使えません。crop の値を 1 ピクセル削って偶数化するか、crop=floor(in_w/2)*2:floor(in_h/2)*2 のように数式で強制偶数化します。cropdetect のデフォルト round=16 を維持していれば自動的に 16 の倍数(→ 当然偶数)になります。

Q6. cropdetect の結果がほぼ毎フレーム変わるのですが A. 暗いシーンや動きの多いシーンで一時的に検出が揺らぐためです。中盤の代表的な明るいシーンで -ss-t でサンプリングし、その値を採用するのが実用的です。


関連記事


動作確認: ffmpeg 8.1 / Windows 11 一次ソース: ffmpeg.org/ffmpeg-filters.html#crop / ffmpeg.org/ffmpeg-filters.html#cropdetect / ffmpeg.org/ffmpeg-filters.html#pad