0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

顔出しなしショート動画を公開前に検査する実装メモ

0
Posted at

顔出しなしショート動画を公開前に検査するための実装メモ

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. 最終ファイルを対象にする

編集画面だけを確認すると、書き出し時のフォント置換、字幕ずれ、圧縮、音量変化を見逃します。公開予定の実ファイルに対して次を確認します。

  1. 解像度、縦横比、フレームレート
  2. 音声ピークと無音区間
  3. 字幕の開始・終了時刻
  4. セーフエリア内の表示
  5. クレジットと開示文
  6. ファイルハッシュまたはバージョンID

検査対象ファイルのハッシュを承認記録に残せば、承認後に別ファイルへ差し替わる事故を防ぎやすくなります。

まとめ

顔出しなし動画の品質管理では、生成工程を増やすより、工程間の対応関係を明確にすることが重要です。シーンID、主張台帳、素材出典、字幕検査、最終ファイルの承認記録を組み合わせると、問題の場所と修正理由を追跡できます。

自動検査は人間の責任を置き換えるものではありません。事実、権利、開示、安全性を人が確認し、その判断を再現できる形で残すことが、継続的に改善できる制作パイプラインにつながります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?