動画ロゴ除去・字幕除去(video-watermark-remover)
ProPainter を使用して、動画内の固定位置にあるロゴ、角マーク、ウォーターマーク、焼き込み字幕を修復します。対象要素の周囲のみをROIとして切り出して修復し、羽根で元の映像に貼り付けます。ROI外のピクセルは変更しません。
⚠ ProPainter のライセンスは S-Lab License 1.0 です:非商用利用のみ許可。商用利用の場合は原作者に連絡して許諾を得てください。 権利処理済みの素材のみに使用してください。
0. 環境(初回使用時)
スクリプトは scripts/ 内にあります。作業ディレクトリはこのスキルディレクトリ(SKILL.md があるディレクトリ)に設定してください。
python scripts/setup_env.py # ffmpeg / PyTorch(CUDA) の確認、固定バージョンの ProPainter をクローン、config.json を生成
python scripts/selftest.py # テスト動画を合成して全プロセスを実行、正解データと比較し PASS/FAIL を出力
config.jsonが存在しない場合、setup_env.pyが不足しているものとそのインストール方法を表示します。PyTorch はユーザーが各自の GPU に合わせて CUDA 版をインストールする必要があります。- セルフテストが通らなければ続行しないでください。表示された指示に従って環境を修正してください。
- 以降の
pythonはsetup_env.pyで config に書き込まれたインタプリタ(つまりセットアップを実行した環境)を指します。
ユーザーが提供するもの
| 項目 | 必須 | 説明 |
|---|---|---|
| 動画ファイル | ✅ | 一つまたは複数 |
| 除去するもの | ✅ | 「左上のロゴ」「中央の大きなロゴ」「下部の字幕」など平易な表現で可 |
| 位置のヒント | 任意 | 同じ種類の要素が複数ある場合のみ必要 |
| 時間範囲 | 任意 | デフォルトでは自動検出。一部だけ残したい場合のみ必要 |
| 今後の予定 | 質問推奨 | 新しいロゴを重ねるか、字幕を追加するか、新しい字幕に下地があるか。下地がある場合は修復せずぼかしで対応可能 |
必須項目が不足している場合はユーザーに質問してください。任意項目は追及しないでください。
機能の範囲(作業前に確認し、範囲外の場合は事前にユーザーに伝えてください)
- ✅ 固定位置のロゴ、角マーク、ウォーターマーク(半透明、フェードイン/アウト、オープニング/エンディングのトランジションでぼやけるものも含む)
- ✅ 固定位置、水平中央、単一行の字幕(映像に焼き付いたもの、白文字・濃色文字、影付きでも可)
- ⚠️ 移動・拡大縮小するロゴ、スクロール字幕、複数行字幕、中央にない字幕:スクリプトは自動追跡できません。ユーザーが時間範囲を指定し、
--block、--grow-fromで手動処理する必要がありますが、効果は低下します。 - ⚠️ 映像内の元々ある文字(看板、画面上のテキスト)と除去対象の要素が重なる場合、一緒にぼかされます。認識リストでユーザーに警告してください。
- NVIDIA GPU が必要で、VRAM は 4GB 以上空いていることが望ましい。CPU でも動作しますが非常に低速です。
処理フロー(8ステップ、順番に実行)
中間ファイルは一時作業ディレクトリ $W に保存されます。完成品は元の動画の隣に、品質チェック画像は <動画ディレクトリ>/qc/ に保存されます。
1. プローブ
python scripts/probe.py IN1.mp4 [IN2.mp4] --out-dir $W
抽出したフレームのコラージュを確認し、以下を判断します:
- 要素の位置
- 角マークか中央の大きなロゴか
- フェードインがあるか(ロゴが安定して表示されるフレーム範囲を記録)、黒画面、ズーム・ぼかしトランジション
- 字幕があるか
2. 位置特定(静的ロゴ)
python scripts/locate_static.py --video IN1.mp4@安定開始フレーム:終了フレーム [--video IN2.mp4@a:b] --out-dir $W
- 開始・終了フレームはフェードイン、黒画面、トランジションを避けてください。
- 複数の動画に同じロゴがある場合、一緒に渡してください。 スクリプトは「各動画で静止している」ピクセルのみを取得するため、背景の偶然静止しているブロックを除外できます。
static_rel.png(黒いほど静止している)を確認し、それに基づいてロゴのおおまかな枠--boxと閾値を決定します:実体ロゴは 0.3–0.5、半透明ロゴは 0.6–0.75。
3. マスク作成、かつ必ずユーザーに確認させる
静的ロゴ:
python scripts/mask_logo.py --rel $W/static_rel.npy --box x0,y0,x1,y1 --thresh 0.5 \
[--block 矩形] [--clear 領域 --block 領域] --preview IN1.mp4@フレーム [--preview IN2.mp4@フレーム] --out-dir $W
- 細い要素(文字、線、ライン)は筆画ごとに切り抜きます。筆画間の実際の背景はモデルの参照用に残します。
- 以下の二つの場合のみ
--block矩形を使用します:一つは実体の大きなブロック、もう一つは細すぎて切り抜ききれない小さな文字(小さな英文字の行など)。 - ❌ 細い要素を全体で覆わないでください。 カメラがゆっくりズームインするとき、覆われた部分には実際の背景がほとんど露出せず、モデルはゼロから生成するしかなくなり、「鱗状のテクスチャ」が現れます。
- プレビュー画像で要素の端や尻尾が漏れていないか確認してください。漏れた部分は修復時に引きずり込まれ、色の線として残ります。
字幕:
python scripts/mask_subtitle.py IN.mp4 --out-dir $W [--rows y0,y1] [--dark] [--shadow 0,0]
--rowsを指定しない場合、画面下部 40% の範囲で自動的に字幕行を検索します。誤検出の場合は手動で指定してください。- 出力に「画面端に近い」という警告がないか注意してください。
ユーザーに確認させる: mask_preview.png または subtitle_mask_preview.png をユーザーに表示し、認識リストも添付します。座標は mask_logo.py が出力した値を使用します。例:
① 左上ロゴ:修復枠 (21,15)–(124,50)、全編出現 ② 下部字幕:y485–524、計 47 文 ③ 1:34 のシーン内の看板が修復領域にかかるため、一緒にぼかされます
ユーザー確認後にステップ5へ進みます。
4. セグメント分割
python scripts/shots.py IN.mp4 --out $W/bounds.json [--extra 178]
ハードカットとフェードイン/アウトのトランジションの両方が分割されます。分割せずに実行すると、静止画内に隣接するシーンの色の「薬莢状の色ブロック」が現れます。
5+6. 修復 + 合成
長い動画はバックグラウンドで実行し、定期的にログを確認して進捗を報告してください。
python scripts/inpaint.py --video IN.mp4 (--mask $W/mask.npy | --masks $W/masks.npz) --bounds $W/bounds.json \
--workdir $W/run --out OUT.mp4 [--mask-dilation 2] [--grow-from 178:14] [--workers 2]
--mask-dilation:デフォルト 2;半透明・光暈のあるロゴは 4。--grow-from フレーム:ピクセル:特定のフレームからマスクを外側に指定ピクセル拡張し、ズーム・ぼかしトランジションで散らばったロゴをカバーします。- 同じ動画でロゴと字幕の両方を除去する場合、二回に分けて実行してください。 二回目の
--videoには一回目の出力を使用し、二つの ROI を一つに結合しないでください。 - 中断後は同じコマンドで再実行できます。既に完了したセグメントはスキップされます。
- 出力ファイル名は
元の名前-nologo.mp4または元の名前-nologo-nosub.mp4となります。スクリプトは元のファイルを上書きしません。 - 時間の目安(RTX 3060、2並列):960×540 で 126 秒の動画、左上ロゴ約 17 分、字幕あり約 21 分;5 秒の中央大きなロゴ約 1 分。
7. 品質チェック
python scripts/qc.py --orig IN.mp4 --out OUT.mp4 (--mask .. | --masks ..) --bounds $W/bounds.json --workdir $W/run \
[--residue blue] [--times 3,12,20] --out-dir <動画ディレクトリ>/qc/xxx
数値指標:
- 残像:完成品の分散は外側の分散とほぼ等しいこと。
- ちらつき:比率の中央値が約1以下で、急激なジャンプフレームが少ないこと。
- 継ぎ目:比率が約1であること。
- 領域外誤差:2未満。
- 残留色:ほぼ0まで低下。
- 全黒フレーム:0フレーム。
⚠ 数値がすべて基準を満たしていても局所的な問題がある可能性があるため、必ず sheet_XX.png を1ページずつ目視で確認してください(色ブロック、鱗状テクスチャ、残字、色線)。問題が見つかった場合は、該当するステップに戻って修正します:
- 色ブロック:
shots.py --extraでカットポイントを追加。 - 鱗状テクスチャ:筆画ごとの切り抜きに変更。
- 残線:
--blockを追加。
修正後は影響を受けた部分だけを再実行し、再度品質チェックを行います。
8. 納品
- 完成品と比較画像の絶対パスを提供し、
before_after.pngをユーザーに表示します。 - 複数の動画を結合する必要がある場合は
python scripts/concat.py A.mp4 B.mp4 C.mp4 --out FULL.mp4を使用します。スクリプトは総フレーム数が各動画の合計と等しいことを検証します。 - 座標ドキュメントを作成します。テンプレートは
references/positions-template.mdにあります。各要素に対して三層の枠を記述します:元の要素範囲、実際の修復範囲、羽根を含む最も外側の範囲(新しいロゴがカバーすべき推奨範囲)。字幕はsubtitle_info.jsonのunion_boxを使用します。 - 既知の欠陥はタイムコード付きで明記し、隠さないでください。
ハードルール(実践で得られた教訓)
- ROI の幅と高さは両方とも 160 以上でなければなりません。 高さ 96 の場合、ProPainter がマスク領域に全黒を出力します。fp32 でも同様です。
inpaint.pyは自動的に ROI を選択する際にこれを保証しています。 - トランジションは必ず分割する。 ハードカットのみで分割するとフェードイン/アウトを見逃し、ProPainter がシーンを跨いでピクセルを借用し、色ブロックが発生します。マスクを拡大したり fp32 に変更しても解決しません。
- 細い要素は筆画ごとに切り抜き、全体を覆わない。
- 字幕は中央対称にトリミングし、同じ字幕文には同じマスクを使用する。 対称トリミングをしないと背景の明るい線がマスクに含まれます。異なるマスクを使うとちらつきが発生し、フェードイン時に漏れが生じます。
- 数値指標では問題を見逃すことがあるので、必ず連絡画像を1ページずつ目視確認する。
- 修復を開始する前に必ずユーザーにマスクを確認させる。 元のファイルを上書きせず、ユーザーのファイルを削除しない。
- バックグラウンド子プロセスはウィンドウを表示しません(スクリプト内で対応済み)。長時間のタスクはバックグラウンドで実行し、能動的に進捗を報告します。