顔出しなしショート動画を公開前に検査するための実装メモ
AIを利用したショート動画制作では、生成そのものよりも「複数の工程で内容がずれていないか」を確認する処理が重要です。スクリプト、シーン表、素材、ナレーション、字幕、最終書き出しを別々に扱うと、固有名詞、数値、URL、字幕タイミング、素材権利などの不整合が発生しやすくなります。
この記事では、顔出しなし動画の制作データを構造化し、公開前に機械検査と人間のレビューを組み合わせる方法をまとめます。Faceless Reels AI のようなブラウザ型ワークスペースで工程をまとめる場合にも、同じチェック設計を適用できます。
1. シーンを安定したIDで管理する
最初に、各シーンへ S001 のような固定IDを付けます。編集順が変わってもIDは変更しません。
{
"scene_id": "S003",
"narration": "字幕は音声と同じ意味を保つ必要があります。",
"caption": "字幕は音声と同じ意味を保つ必要があります",
"visual": "caption-timeline-v2.png",
"duration_sec": 4.8,
"source_id": "ASSET-014",
"review_status": "pending"
}
この形式なら、字幕エラーが見つかったときに、対象シーン、素材、承認状態を追跡できます。配列の位置だけを識別子にすると、シーン追加のたびに参照が壊れるため避けます。
2. スクリプトと字幕を正規化して比較する
完全一致だけでは、句読点や改行の違いを誤検出します。比較用の正規化関数を用意します。
import re
import unicodedata
def normalize_text(value: str) -> str:
value = unicodedata.normalize("NFKC", value)
value = value.lower().strip()
value = re.sub(r"[、。,.!?!?::;;]", "", value)
value = re.sub(r"\s+", "", value)
return value
def same_meaning_candidate(narration: str, caption: str) -> bool:
return normalize_text(narration) == normalize_text(caption)
この判定は一次フィルターです。否定語、数値、単位が変わる誤りは重大なので、別ルールで強く検査します。
3. 数値と単位を抽出する
「15」が「50」になる誤変換は、文章全体の類似度が高くても見逃せません。
NUMBER_UNIT = re.compile(
r"(?P<number>-?\d+(?:\.\d+)?)\s*"
r"(?P<unit>%|秒|分|時間|px|MB|GB|m|cm|kg)?"
)
def extract_numbers(text: str):
return [m.groupdict() for m in NUMBER_UNIT.finditer(text)]
def validate_numbers(narration: str, caption: str):
return extract_numbers(narration) == extract_numbers(caption)
実運用では、日付、通貨、範囲、倍率も別パターンにします。単位変換を許可する場合は、変換後の値が同じか確認し、単に文字列が違うだけで失敗にしない設計が必要です。
4. 完全なURLを検査する
動画内テキストや説明文にURLを含める場合、ドメインのタイプミスを検査します。
from urllib.parse import urlparse
def validate_https_url(value: str, allowed_hosts: set[str]) -> bool:
parsed = urlparse(value)
return (
parsed.scheme == "https"
and parsed.hostname in allowed_hosts
and not parsed.username
and not parsed.password
)
表示文字列だけでなく、実際のリンク先も確認します。短縮URLや意図しないリダイレクトは、最終ページまで追跡して許可ドメインと一致するかを検査します。
5. 素材の出典ログを必須にする
各画像、動画、音声、フォント、生成素材に対して、次の情報を保存します。
- 素材ID
- 元ページまたは内部資産ID
- 作成者・提供者
- ライセンスまたは許可の根拠
- 取得日
- 必要なクレジット
- 加工内容
- 使用シーンID
REQUIRED_ASSET_KEYS = {
"asset_id", "source", "rights_basis", "acquired_at", "scene_ids"
}
def validate_asset(asset: dict) -> list[str]:
missing = REQUIRED_ASSET_KEYS - asset.keys()
errors = []
if missing:
errors.append(f"missing keys: {sorted(missing)}")
if not asset.get("scene_ids"):
errors.append("asset is not connected to a scene")
return errors
「無料でダウンロードできた」は利用許可の根拠とは限りません。商用利用、改変、再配布、人物・商標・場所の権利を分けて確認します。
6. 字幕の表示時間をチェックする
字幕の読みやすさは文字数と表示時間の両方で決まります。単純な目安でも、極端に速い字幕を検出できます。
def reading_speed(caption: str, start: float, end: float) -> float:
duration = max(end - start, 0.001)
chars = len(normalize_text(caption))
return chars / duration
def caption_warning(caption: str, start: float, end: float):
speed = reading_speed(caption, start, end)
if speed > 12:
return f"too fast: {speed:.1f} chars/sec"
if end <= start:
return "invalid time range"
return None
閾値は言語、対象視聴者、画面内の他の情報量に合わせて調整します。自動判定だけで合格にせず、スマートフォン相当の表示で通常速度のレビューを行います。
7. レビューレポートを生成する
検査結果はコンソールに流すだけでなく、シーン単位のレポートへまとめます。
def review_scene(scene: dict, assets: dict[str, dict]) -> dict:
issues = []
if not same_meaning_candidate(scene["narration"], scene["caption"]):
issues.append("caption differs from narration")
if not validate_numbers(scene["narration"], scene["caption"]):
issues.append("number or unit mismatch")
asset = assets.get(scene.get("source_id"))
if asset is None:
issues.append("source asset not found")
else:
issues.extend(validate_asset(asset))
return {
"scene_id": scene["scene_id"],
"status": "pass" if not issues else "review",
"issues": issues,
}
pass は公開許可ではなく、「自動検査で問題候補がなかった」という意味にします。最終判断者、判断日時、対象の書き出しバージョンを別に記録します。
8. 最終ファイルを対象にする
編集画面だけを確認すると、書き出し時のフォント置換、字幕ずれ、圧縮、音量変化を見逃します。公開予定の実ファイルに対して次を確認します。
- 解像度、縦横比、フレームレート
- 音声ピークと無音区間
- 字幕の開始・終了時刻
- セーフエリア内の表示
- クレジットと開示文
- ファイルハッシュまたはバージョンID
検査対象ファイルのハッシュを承認記録に残せば、承認後に別ファイルへ差し替わる事故を防ぎやすくなります。
まとめ
顔出しなし動画の品質管理では、生成工程を増やすより、工程間の対応関係を明確にすることが重要です。シーンID、主張台帳、素材出典、字幕検査、最終ファイルの承認記録を組み合わせると、問題の場所と修正理由を追跡できます。
自動検査は人間の責任を置き換えるものではありません。事実、権利、開示、安全性を人が確認し、その判断を再現できる形で残すことが、継続的に改善できる制作パイプラインにつながります。