動画の上下や左右に入った黒帯は、FFmpeg の cropdetect でサイズを自動検出し、crop フィルタで切り落とせます。-vf crop=W:H:X:Y を使うときは、値の出し方・偶数サイズの罠・再エンコードによる画質劣化に注意が必要です。

左が上下に黒帯のある入力(実演のため意図的に付けたもの)、右が crop=1920:800:0:140 を適用した出力です。
素材: Tears of Steel (CC) Blender Foundation | mango.blender.org、CC BY 3.0。映画の1フレームを切り出し、フィルターで加工しています。
黒帯が発生する理由
| ケース | 状況 | 表示結果 |
|---|---|---|
| レターボックス | 16:9 ディスプレイで 2.35:1 シネスコ作品を再生 | 上下に黒帯 |
| ピラーボックス | 16:9 ディスプレイで 4:3 (1.33:1) ソースを再生 | 左右に黒帯 |
| 両方(ウィンドウボックス) | 16:9 作品を上下に黒帯を付けて 4:3 にし、それを 16:9 ディスプレイで再生 | 上下+左右 |
| エンコード時に焼き込まれた黒帯 | ソース動画ファイルに黒帯ピクセルとして含まれる | 物理的に黒ピクセル |
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 pts:29696 t:1.933333 limit:0.094118 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 の主な引数は次の 3 つです。
| 引数 | デフォルト | 説明 |
|---|---|---|
limit |
24 (8bit) |
「黒」と判定する輝度の上限。低いほど厳しく、高いほど緩い |
round |
16 |
検出サイズをこの倍数に丸める。エンコーダ互換性のため |
reset |
0 |
N フレーム毎に検出をリセット。シーン変化対応 |
# round=16・reset=0(既定値)を明示して検出。limit は省くと映像のビット深度に合った既定値になる。調整するときはこの数字を変える
ffmpeg -ss 60 -i input.mp4 -t 5 -vf cropdetect=round=16:reset=0 -f null -
limit を上げすぎると本編の暗い部分まで黒帯と判定されるので、既定の 24 から少しずつ調整します。10bit の動画では、limit=0.1 のように 0〜1 の割合で指定します(24 は約 0.094 にあたります)。
検出した値を 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)
[vost#0:0/libx264 @ 0x...] [enc:libx264 @ 0x...] Error while opening encoder - maybe incorrect parameters such as bit_rate, rate, width or height.
cropdetect の round(既定 16、2 以上の偶数)を使えばこの問題は起きません。また入力が yuv420p なら、crop は奇数の幅・高さを自動で偶数に切り下げます(crop=1920:801:0:140 は 1920×800 になります)。エラーになるのは、scale の -1 や crop の exact=1 などで奇数のサイズになったときです。その場合は次の方法で対処します。
解決策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 要件を緩める
yuv444p なら幅・高さとも奇数でも通りますが、再生互換性が下がります(yuv422p で奇数にできるのは高さだけです)。
# 先に yuv444p へ変換してから crop する(yuv420p のままだと奇数が切り下げられる)
ffmpeg -i input.mp4 -vf "format=yuv444p,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 を 0.25(8bit の 64 にあたる割合)まで緩めて検出
ffmpeg -ss 60 -i input.mp4 -t 5 -vf cropdetect=0.25:2:0 -f null -
第 1 引数(0.25)が limit、第 2 引数(2)が round、第 3 引数(0)が reset です。limit を上げすぎると本編の暗い部分まで「黒帯」として削られてしまうので、検出された矩形を ffplay などで目視確認すると安心です。
# 検出値を仮当てして再生確認(再エンコードなしのプレビュー)
ffplay -vf "crop=1920:800:0:140" input.mp4
逆に黒帯を追加する(pad)
1920×1080 のような決まったサイズの画面に、映像を切らずに収めたいときは、pad フィルタで黒帯を追加します。YouTube ではこの作業は不要です。プレーヤーが動画の縦横比に合わせて表示を変えるため、YouTube は黒帯を自分で付けないよう案内しています。
4:3 → 16:9 ピラーボックス追加
ffmpeg -i input.mp4 -vf "scale=1920: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 |
アスペクト比を保ったまま、1920×1080 に収まる最大のサイズにする(4:3 なら 1440×1080) |
pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black |
1920×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:reset の reset パラメータに 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 not divisible by 2 エラーが出ます
A. yuv420p で奇数幅・奇数高さは使えません。scale で奇数になった場合は、-1 を -2 に変えます。crop の値が原因なら、1 ピクセル削って偶数化するか、crop=floor(in_w/2)*2:floor(in_h/2)*2 のように数式で強制偶数化します。cropdetect のデフォルト round=16 を維持していれば自動的に 16 の倍数(つまり偶数)になります。
Q6. cropdetect の結果が途中で変わるのですが
A. 既定の reset=0 では、検出される範囲は広がる方向にしか変わりません。暗いシーンでは映像の暗い部分も黒と判定されるため、最初は範囲が小さく出て、明るいフレームが来ると広がります。中盤の代表的な明るいシーンを -ss + -t でサンプリングし、最後の値を採用してください。