チュートリアル

TwelveLabsを使って自動でGDPR準拠のビデオ個人情報マスキングを構築する方法

リシケシュ・ヤダフ

このチュートリアルでは、Twelve Labs上でプロダクション対応のGDPR準拠ビデオ匿名化(モザイク処理)アプリケーションを構築する手順を解説します。エンティティベースの検索を行うMarengo 3.0、タイムスタンプ付きの構造化されたプライバシーメタデータを生成するPegasus 1.5、そして安定した個人特定・追跡モザイク処理のためのローカル顔検出パイプラインを組み合わせます。これにより、レビュー担当者は動画のアップロードから監査対応済みのモザイク処理出力までを単一のインターフェースで完結でき、すべての判断ステップにおける根拠もドキュメント化されます。

このチュートリアルでは、Twelve Labs上でプロダクション対応のGDPR準拠ビデオ匿名化(モザイク処理)アプリケーションを構築する手順を解説します。エンティティベースの検索を行うMarengo 3.0、タイムスタンプ付きの構造化されたプライバシーメタデータを生成するPegasus 1.5、そして安定した個人特定・追跡モザイク処理のためのローカル顔検出パイプラインを組み合わせます。これにより、レビュー担当者は動画のアップロードから監査対応済みのモザイク処理出力までを単一のインターフェースで完結でき、すべての判断ステップにおける根拠もドキュメント化されます。

この記事の内容

No headings found on page

ニュースレターに登録する

ニュースレターに登録する

ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします

ビデオ理解に関する最新の技術進歩、チュートリアル、業界の動向をお届けします

AIを活用してビデオを検索、分析、探索します。

2026/05/28

13分

記事へのリンクをコピー

イントロダクション

大規模なプライバシーレビューは、単なる編集の問題ではありません。それはインテリジェンス(情報と理解)の問題です。

法的要請が発生し、チームが数時間もの映像から特定の人物の登場箇所をすべて特定し、評価し、マスキング(黒塗り/ぼかし)する必要がある場合、フレームごとの手作業による確認は機能しません。GDPR Enforcement Trackerの記録によると、2026年3月までに2,793件の事例で累計60.6億ユーロ以上の制裁金が科されています。第83条では、最も深刻な違反に対して、最大2,000万ユーロまたは全世界の年間売上高の4%のいずれか高い方の制裁金が依然として認められています。運用のプレッシャーは現実のものであり、手動のワークフローはそれを吸収できるようには作られていません。

GDPRに準拠したマスキングには、3つの要素の連携が必要です。対象となる人物やオブジェクトの正確な特定、コーパス全体からの関連するすべての登場箇所の検索、そして法的な目的に実際に必要な範囲に限定したマスキングです。すべてをただぼかすだけでは、防御可能な戦略とは言えません。規制の「データの最小化」の原則は、全面的な抑制ではなく、正確性を求めています。

このチュートリアルでは、TwelveLabsをベースに構築された、本番環境対応のGDPRビデオマスキングアプリケーションについて説明します。このアプリケーションは、マルチモーダル検索とエンティティベースの検索にMarengo 3.0を、構造化されたプライバシーメタデータにPegasus 1.5を、そしてフレームレベルのアイデンティティクラスタリングにローカルの顔検出パイプラインを使用しています。その結果、ビデオのアップロードからエクスポート可能なマスキング済み出力までを単一のインターフェースでレビュー担当者が実行できるワークフローが実現します。

デモは tl-gdpr-compliance.vercel.app で体験でき、ソースコードは github.com/Hrishikesh332/tl-GDPR-compliance-redaction で公開されています。


アプリケーションの機能

ほとんどのマスキングツールは手動選択を前提に構築されています。レビュー担当者が映像を視聴し、バウンディングボックスを描き、ぼかし処理されたクリップをエクスポートします。このアプローチは、コーパスが大規模な場合や、人物が複数のクリップにまたがって登場する場合、あるいは法的義務により各マスキングの決定理由を文書化する必要がある場合にはスケールしません。

このアプリケーションは、この問題に対して異なるアプローチをとります。TwelveLabsはワークフロー全体を通じてビデオ理解レイヤーとして機能します。レビュー担当者は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、インデックス化された映像全体を検索できます。Pegasus 1.5は、タイムスタンプ付きのプライバシーメタデータを生成し、レビュー担当者が1フレームも見る前にリスクの高い瞬間を表面化させます。ローカルの顔検出は、検出された顔を安定した個人ごとのアイデンティティにクラスタリングし、エクスポートパイプライン全体で維持します。ぼかしの追跡は、物体の動き、横顔への変化、カメラのカット割りを超えて特定の人物を追跡します。

このワークフローは、映像のアップロードとインデックス登録、自然言語や顔画像を使用した対象の追跡、インタラクティブなタイムライン上でのAI生成プライバシーシステムリスクセグメントのレビュー、個人アイデンティティによるマスキング対象の選択、そして安定した顔固定のマスキングビデオのエクスポートにいたる、レビューサイクル全体をカバーしています。


マスキングパイプラインの内部構造

アプリケーションは、4つの異なる機能を単一のレビューワークフローに接続します。

Marengo 3.0によるマルチモーダル検索により、レビュー担当者はテキストクエリ、画像のアップロード、または登録済みの顔エンティティを使用して、任意の人物、オブジェクト、またはシーンを特定できます。Marengoは関連するクリップセグメントを信頼度スコアとともに返し、インターフェースはこれを最も一致度の高い箇所にレビューマーカーを配置した視覚的なタイムラインレーンとしてレンダリングします。

Pegasus 1.5によるプライバシーメタデータは、プライバシーリスクに焦点を当てたタイムスタンプ付きのセグメントデータを生成します。ビデオ全体の状況を説明するのではなく、顔、文書、ナンバープレート、スクリーン、機密性の高いオブジェクト、保護対象の個人などの特定のカテゴリをターゲットにしたスキーマがPegasusに提供されます。各セグメントには、リスクレベル、マスキング理由、推奨されるアクション、シーン内での役割が含まれます。

ローカル検出とアイデンティティクラスタリングは、Pegasusのタイムスタンプを使用してキーフレームの抽出をガイドします。その後、アプリケーションはローカルの顔検出とInsightFaceエンベディングを実行し、検出された顔をビデオ全体で一貫したIDを持つ安定した個人ごとのアイデンティティにクラスタリングします。

顔固定ぼかしとエクスポートマスキングは、選択された個人の検出履歴をフレームインデックス付きのバウンディングボックスレーンに変換します。エクスポート中、レンダラーは一致する各フレームのそのレーンからぼかしを適用し、安定した、アイデンティティを認識したマスキングビデオを生成します。


環境のセットアップ

構築を開始する前に、以下の前提条件を完了してください。

  1. TwelveLabsアカウントを作成し、APIキーを生成します。Marengo 3.0とPegasus 1.5を有効にしたインデックスを作成し、インデックスIDを記録します。

  2. 顔登録とエンティティ検索をサポートするために、TwelveLabsエンティティコレクションを作成します。

  3. Flaskバックエンド用にPython 3を、Reactフロントエンド用にNode.js/npmをインストールします。エクスポート時のビデオ再エンコードにはFFmpegを推奨します。

  4. リポジトリをクローンし、READMEのセットアップ手順に従います。

.env.exampleで定義されている変数を使用して、backend/.envを作成します。


セクション1:TwelveLabsによるエンティティ管理

インデックス化された映像全体から特定の既知の人物を特定するには、単なるテキスト検索以上のものが必要です。顔をTwelveLabsエンティティとして登録すると、再利用可能なアイデンティティ参照が作成され、コンテキストクエリ(ドキュメントの近くにいるこの人物、特定のシーンタイプ内にいる人物、または特定のアクションを行っている人物を見つけるなど)と組み合わせることができます。

これはマスキングの精度にとって重要です。ビデオ内のすべての顔にフラグを立てる代わりに、レビュー担当者は検索を既知のアイデンティティに固定し、その人物が表示されている瞬間のみを抽出できます。


1.1 - エンティティインデックスへの顔アセットの登録

ユーザーが顔画像をアップロードして名前を入力すると、バックエンドはResNet-10顔検出器を使用して顔を検出し、プレビュークロップを生成して、TwelveLabsへの登録用の顔アセットを準備します。

フローには2つのステップがあります。まず顔画像をアセットとしてアップロードし、次に返されたアセットIDを参照する名前付きエンティティを作成します。これにより、視覚的な参照が検索可能なアイデンティティにリンクされます。

asset_id = twelvelabs_service.upload_face_asset(tmp.name)
metadata = {"name": name}
if preview_base64:
    metadata["face_snap_base64"] = preview_base64

entity_result = twelvelabs_service.create_entity(
    name=name,
    asset_ids=[asset_id],
    description=description or f"Face entity: {name}",
    metadata=metadata,

登録後、エンティティはコレクション内のインデックス化されたすべてのビデオにわたって、アイデンティティ制約付き検索に使用できるようになります。


1.2 - エンティティとテキストによる検索

検索は、エンティティベース、テキストベース、画像ベースの3つのモードをサポートしています。レビュー担当者は、レビュー開始時に把握している情報量に応じてこれらを組み合わせることができます。

エンティティベースの検索の場合、バックエンドはエンティティIDをTwelveLabsのメンション形式(<@entity_id>)でラップします。ユーザーがテキスト修飾子を追加すると、双方が結合され、検索がアイデンティティとコンテキストの両方によって制限されます。

backend/services/twelvelabs_services (Line 1214)

def entity_search(entity_id, query_suffix="", index_id=None):
    client = get_client()
    idx = resolve_index_id(index_id)

    query_text = f"<@{entity_id}>"
    if query_suffix:
        query_text = f"<@{entity_id}> {query_suffix}"

    logger.info("Entity search: %s", query_text)
    response = client.search.query(
        index_id=idx,
        search_options=["visual"],
        query_text=query_text,
        group_by="video",
        sort_option="score",
        page_limit=50,
    )
    return serialize_search_results(response)

画像ベースの検索の場合、バックエンドはquery_media_type="image"として画像を渡し、query_media_fileまたはquery_media_urlのいずれかを指定します。

if image_url:
    response = client.search.query(
        **kwargs,
        query_media_type="image",
        query_media_url=image_url,
    )

if image_path:
    with open(image_path, "rb") as image_file:
        response = client.search.query(
            **kwargs,
            query_media_type="image",
            query_media_file=image_file,
        )


1.3 - 検索タイムラインレーンとレビューマーカー

検索結果は、順位付きリストとしてだけでなく、視覚的なタイムラインレーンとしてもレンダリングされます。各結果セグメントにはstart(開始)とend(終了)の時間があり、フロントエンドはこれをビデオスクラバーにマッピングします。信頼度の高い一致箇所は赤いレビューインジケーターでマークされるため、レビュー担当者は周囲のコンテキストから最も一致度の高い箇所を容易に区別できます。

これにより、検索がレビューワークフローへと変わります。レビュー担当者は検索を行い、タイムラインを検査し、マスキングの決定が必要になる可能性が最も高い瞬間に直接ジャンプできます。


セクション2:検出と顔固定マスキング

検出は、TwelveLabsのビデオ理解をローカルのコンピュータービジョンパイプラインに接続します。すべてのフレームで顔検出を実行するのではなく、Pegasus 1.5が最初に、ビデオ内で人が表示される場所に関する構造化されたメタデータを生成します。バックエンドはこれらのタイムスタンプを使用してキーフレームの抽出をガイドし、検出パスをより高速かつターゲットを絞ったものにします。

検出された顔は、安定した個人ごとのアイデンティティにクラスタリングされます。レビュー担当者は、個々のバウンディングボックスからではなく、それらのアイデンティティから選択します。選択により、エクスポートマスキングに使用される顔固定レーンが駆動されます。


2.1 - TwelveLabsのコンテキストとローカルな顔検出の組み合わせ

パイプラインは、Pegasus構造化分析タスクから始まります。スキーマは2つのセグメントタイプを定義します。face_redaction_target(マスキングが必要になる可能性のある人々用)と scene_segment(より曖昧なシーンのコンテキスト用)です。temperature: 0.1に設定することで、出力を一貫した状態に保ち、自動化されたパイプラインに適したものにします。

backend/services/twelvelabs_services (Line 102)

PIPELINE_METADATA_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "face_redaction_target",
            "description": (
                "Return people segments for face redaction decisions. Create one segment per "
                "distinct face/person for each continuous time range where their face is visible "
                "enough to matter for redaction."
            ),
            "fields": [
                {"name": "name", "type": "string"},
                {"name": "description", "type": "string"},
                {"name": "should_anonymize", "type": "boolean"},
                {"name": "is_official", "type": "boolean"},
                {"name": "review_required", "type": "boolean"},
                {"name": "redaction_reason", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
        {
            "id": "scene_segment",
            "fields": [
                {"name": "description", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
    ],
}
body = {
    "video": {
        "type": "asset_id",
        "asset_id": asset_id,
    },
    "model_name": "pegasus1.5",
    "analysis_mode": "time_based_metadata",
    "response_format": PIPELINE_METADATA_RESPONSE_FORMAT,
    "temperature": 0.1,
}

Pegasusが人物のメタデータを返すと、アプリケーションはタイムレンジを抽出し、それらのウィンドウからキーフレームをサンプリングします。各キーフレームは detect_faces(..., with_encodings=True) に渡され、InsightFaceを使用して顔を検出し、バウンディングボックスを計算し、鮮明度を評価し、アイデンティティの埋め込みを生成します。

for kf in keyframes:
    faces = detect_faces(kf["frame"], with_encodings=True)
    for f in faces:
        f["frame_idx"] = kf["frame_idx"]
        f["timestamp"] = kf["timestamp"]
        all_faces.append(f)

各検出データは、埋め込みとともにフレームインデックスとタイムスタンプを保存し、クラスタリングステップで一貫した人物アイデンティティにグループ化できる時間対応のマスキングメタデータを作成します。


2.2 - マスキングターゲットの選択

検出とクラスタリングの後、レビュー担当者は検出された個人のリストから人物のアイデンティティを選択します。その選択は、顔のターゲットとそれらの保存された埋め込みのリストに分解され、エクスポートパイプラインが顔固定追跡のために使用します。

if person_ids:
    enriched = get_enriched_faces(job_id) or {}
    unique_faces = job.get("unique_faces") or enriched.get("unique_faces", [])
    for index, face in enumerate(unique_faces):
        stable_person_id = ensure_face_identity(face, fallback_index=index)
        if stable_person_id not in person_ids:
            continue
        face_targets.append(face)
        encoding = face.get("encoding")
        if encoding:
            face_encodings.append(encoding)
        matched_ids.append(stable_person_id)

リバースマスキング(除くマスキング)は、逆方向で同じアイデンティティ解決を使用します。レビュー担当者が保護したい人を選択すると、エクスポート時に検出された他のすべての顔にぼかしが適用されます。


2.3 - 顔固定レーンの構築とマスキングされたエクスポートのレンダリング

選択された各人物アイデンティティについて、パイプラインは build_face_lock_lane(job_id, person_id) を呼び出します。レーンビルダーは、保存されたInsightFaceの外観レコード、TwelveLabsエンティティ検索のタイムレンジ、およびPegasusから保存されたセマンティック人物範囲の3つのソースを利用します。その結果、ビデオ内のそのアイデンティティの全スパンをカバーするレーンドキュメントが作成されます。

face_lock_tracks = {}
if face_targets and not reverse_face_redaction:
    from services.face_lock_track import build_face_lock_lane

    for face in face_targets:
        person_id = get_face_identity(face)
        if not person_id:
            continue

        lane_doc = build_face_lock_lane(job_id, person_id)
        if lane_doc:
            face_lock_tracks[person_id] = lane_doc
appearances = collect_person_appearances(selected_face)

video_id = str(job.get("twelvelabs_video_id") or "").strip()
entity_ranges = get_entity_search_ranges(selected_face, video_id)
saved_person_ranges = get_face_semantic_time_ranges(selected_face)
semantic_ranges = entity_ranges + saved_person_ranges

segments = build_face_lock_segments(
    appearances, semantic_ranges, fps, total_frames, duration_sec,
)

エクスポート中、レーンはフレームインデックス付きのバウンディングボックスルックアップテーブル(face_lock_bboxes_by_frame)に変換され、YOLOv8-Faceリファインメントでガイドされます。レンダラーはこのテーブルに対して各フレームをチェックし、顔固定バウンディングボックスが登録されている場所にはどこでも apply_detection_redaction を適用します。

if face_lock_bboxes_by_frame and not preview_only:
    for entry in face_lock_bboxes_by_frame.get(frame_idx, ()):
        lane_bbox = entry.get("bbox")
        if lane_bbox:
            apply_detection_redaction(frame, lane_bbox, "face")

出力は、フレームごとの手動修正を必要とせずに、動き、部分的なオクルージョン、およびカメラの動きを通じて選択されたアイデンティティを追跡する安定したぼかしです。


セクション3:Pegasus 1.5によるプライバシーメタデータ

Pegasus 1.5はスキーマ駆動型のタイムスタンプベースのメタデータをサポートしています。これは、検出するプライバシーリスクのタイプと、各セグメントに対して返す構造化フィールドを正確に定義できることを意味します。これが、レビューインターフェースの「Meta Insights(メタインサイト)」を動かす仕組みです。


3.1 - プライバシーリスクスキーマの構築

スキーマは、アクション可能なレビューターゲット、すなわち顔、文書、画面、ナンバープレート、機密性の高いテキスト、保護対象の個人にPegasusの出力を集中させる単一のセグメントタイプ privacy_risk_segment を定義します。risk_levelscene_roleredaction_decisionreason などのフィールドは、単にタイムスタンプだけでなく、フラグが立てられた各瞬間の文書化された明確な論拠をレビュー担当者に提供します。 

backend/services/pegasus_privacy (Line 75)

PEGASUS_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "privacy_risk_segment",
            "description": (
                f"{PEGASUS_PRIVACY_PROMPT} Do not create broad background or crowd segments. Each "
                "segment must be narrow, actionable, and tied to one visible target that should be "
                "redacted or reviewed with care."
            ),
            "fields": [
                {
                    "name": "privacy_category",
                    "type": "string",
                    "description": (
                        "One of person, face, screen, document, text, license_plate, logo, object, scene. "
                        "Use scene only when the whole frame contains sensitive material; do not use it "
                        "for ordinary courtroom background."
                    ),
                    "enum": ["person", "face", "screen", "document", "text", "license_plate", "logo", "object", "scene"],
                },
                {
                    "name": "risk_level",
                    "type": "string",
                    "description": "One of low, medium, high.",
                    "enum": ["low", "medium", "high"],
                },
                {
                    "name": "label",
                    "type": "string",
                    "description": "Short target name, for example Main verdict subject, Protected witness, Visible ID, Phone screen, or License plate.",
                },
                {
                    "name": "description",
                    "type": "string",
                    "description": "What is visible and why this exact target needs redaction or careful review.",
                },
                {
                    "name": "reason",
                    "type": "string",
                    "description": (
                        "Specific reason this item should be redacted. For courtroom people, state why this is "
                        "the main verdict subject or another protected/private person; do not include generic "
                        "courtroom observers."
                    ),
                },
                {
                    "name": "scene_role",
                    "type": "string",
                    "description": (
                        "Role of the target in context. Use verdict_subject, defendant, respondent, or accused for "
                        "the main person whose verdict is being discussed. Ordinary judges, lawyers, clerks, officers, "
                        "jury, audience, reporters, and bystanders should not be segmented."
                    ),
                    "enum": [
                        "verdict_subject",
                        "defendant",
                        "respondent",
                        "accused",
                        "protected_witness",
                        "victim",
                        "minor",
                        "private_non_party",
                        "sensitive_item",
                        "unknown",
                    ],
				# More Segments Defined ...
                }
            ],
        }
    ],
}


3.2 - タイムスタンプ付きプライバシーセグメントの生成

スキーマは analysis_mode: "time_based_metadata" を指定してPegasusに渡されます。これにより、Pegasusは単一のドキュメントの要約ではなく、構造化されたタイムラインとして応答を返すよう指示されます。temperature: 0.1に設定することで出力を決定論的な状態に保ちます。これは、繰り返しの実行において一貫性が求められるコンプライアンスワークフローに非常に重要です。

backend/services/twelvelabs_services (Line 1044)

def create_pegasus_privacy_task(asset_id, *, response_format):
    """Create a Pegasus 1.5 async structured-analysis task from an existing asset id."""
    body = {
        "video": {
            "type": "asset_id",
            "asset_id": asset_id,
        },
        "model_name": "pegasus1.5",
        "analysis_mode": "time_based_metadata",
        "response_format": response_format,
        "temperature": 0.1,
    }

バックエンドは情報が空の timeline_eventsrecommended_actions フィールドを持つ初期ジョブデータを保存し、タスクが完了するまでポーリングします。各Pegasusセグメントは、タイムラインイベント(start_secend_secseveritycategoryreason、および redaction_decision)と、次にレビュー担当者が行うべきことを示す推奨アクションの2つのオブジェクトに変換されます。

event = {
    "id": event_id,
    "start_sec": round(start_sec, 3),
    "end_sec": round(end_sec, 3),
    "severity": severity,
    "category": category,
    "label": label[:120],
    "description": description[:600],
    "reason": reason[:600],
    "redaction_target": redaction_target[:120] or None,
    "scene_role": scene_role[:120] or None,
    "redaction_decision": redaction_decision[:120] or None,
    "subject_selection": subject_selection[:120] or None,
    "confidence": round(confidence, 3),
    "review_required": True,
    "recommended_action_ids": [action_id],
}


3.3 - レビューインターフェースとしてのプライバシーメタデータのレンダリング

タイムラインレーンは、各Pegasusイベントを start_secend_sec に配置されたクリック可能なホットスポットとしてレンダリングします。ホットスポットをクリックすると、メタインサイトパネルが開き、イベントにフォーカスが当たり、ビデオがそのタイムスタンプにシークされます。

サイドバーには、深刻度、カテゴリ、理由、マスキングの決定、マスキングターゲット、対象の選択、シーンの役割、信頼度、および処理に関するメモなど、完全なイベントメタデータが表示されます。レビュー担当者は、何かが注意を必要とする場所だけでなく、その理由も確認できます。これは、GDPR監査が必要とする文書化された論拠となります。


セクション4:確認のためのオープンエンドなビデオ分析

構造化されたメタデータに加えて、アプリケーションはインデックス付きビデオに対する自由形式のコンテキストの質問をサポートしています。レビュー担当者は、映像全体にどのような機密情報が表示されるか、どの瞬間が最も高いコンプライアンスリスクを伴うか、または特定のタイムスタンプの周囲で何が起こっているかを質問できます。これは、レビュー担当者が探しているものがまだ分かっていない状況で役立ちます。

フロントエンドは、video_id とレビュー担当者のプロンプトを /api/analyze-custom に送信します。バックエンドは、特定の瞬間に言及されるたびにタイムスタンプを要求するフォーマット指示をプロンプトの先頭に追加し、結合されたプロンプトをTwelveLabsのAnalyze(分析)サービスに渡します。

backend/services/twelvelabs_services (Line 666)

def analyze_video_custom(video_id, prompt):
    client = get_client()
    logger.info("Custom analysis on video %s", video_id)
    enhanced_prompt = f"{ANALYZE_FORMAT_INSTRUCTION}\n\n{prompt}"
    result = client.analyze(
        video_id=video_id,
        prompt=enhanced_prompt,
        temperature=0.2,
        request_options={"timeout_in_seconds": TWELVELABS_ANALYZE_TIMEOUT_SEC},
    )
    return {"data": result.data, "id": result.id}

応答は、ビデオコンテンツに基づいた平易な言葉による分析を返し、タイムスタンプによってレビュー担当者に関連する瞬間に直接リンクします。これにより、「機密情報を見つける必要がある」状態から「見るべき正確な場所はここである」状態へのギャップが埋まります。


このアプローチが可能にすること

歴史的に、ビデオのGDPRコンプライアンスには2つのトレードオフのいずれかが必要でした。遅くて高価な手動レビュープロセスか、法律が必要とする以上のものをマスキングし、その決定理由を明確に説明できない、大雑把な自動化されたアプローチのいずれかです。

このアプリケーションは、異なる道を進みます。Marengo 3.0は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、コーパス全体から適切な瞬間を特定します。Pegasus 1.5は、フラグが立てられた各セグメントについて、文書化された明確な論拠を持つ構造化された、タイムスタンプ付きのプライバシーメタデータを生成します。ローカルの顔検出は、AIに基づいたキーフレームからアイデンティティをクラスタリングします。顔固定レーンは、エクスポート全体で安定したぼかしトラックを維持します。

その結果、マスキングワークフローが、網羅的ではなくピンポイントなものになり、不透明ではなく文書化され、そして手動ではなくスケーラブルなものになります。すべての決定基準点にはレビュー可能な記録があり、これこそが真のコンプライアンスにおいて実際に求められているものです。

参考情報

イントロダクション

大規模なプライバシーレビューは、単なる編集の問題ではありません。それはインテリジェンス(情報と理解)の問題です。

法的要請が発生し、チームが数時間もの映像から特定の人物の登場箇所をすべて特定し、評価し、マスキング(黒塗り/ぼかし)する必要がある場合、フレームごとの手作業による確認は機能しません。GDPR Enforcement Trackerの記録によると、2026年3月までに2,793件の事例で累計60.6億ユーロ以上の制裁金が科されています。第83条では、最も深刻な違反に対して、最大2,000万ユーロまたは全世界の年間売上高の4%のいずれか高い方の制裁金が依然として認められています。運用のプレッシャーは現実のものであり、手動のワークフローはそれを吸収できるようには作られていません。

GDPRに準拠したマスキングには、3つの要素の連携が必要です。対象となる人物やオブジェクトの正確な特定、コーパス全体からの関連するすべての登場箇所の検索、そして法的な目的に実際に必要な範囲に限定したマスキングです。すべてをただぼかすだけでは、防御可能な戦略とは言えません。規制の「データの最小化」の原則は、全面的な抑制ではなく、正確性を求めています。

このチュートリアルでは、TwelveLabsをベースに構築された、本番環境対応のGDPRビデオマスキングアプリケーションについて説明します。このアプリケーションは、マルチモーダル検索とエンティティベースの検索にMarengo 3.0を、構造化されたプライバシーメタデータにPegasus 1.5を、そしてフレームレベルのアイデンティティクラスタリングにローカルの顔検出パイプラインを使用しています。その結果、ビデオのアップロードからエクスポート可能なマスキング済み出力までを単一のインターフェースでレビュー担当者が実行できるワークフローが実現します。

デモは tl-gdpr-compliance.vercel.app で体験でき、ソースコードは github.com/Hrishikesh332/tl-GDPR-compliance-redaction で公開されています。


アプリケーションの機能

ほとんどのマスキングツールは手動選択を前提に構築されています。レビュー担当者が映像を視聴し、バウンディングボックスを描き、ぼかし処理されたクリップをエクスポートします。このアプローチは、コーパスが大規模な場合や、人物が複数のクリップにまたがって登場する場合、あるいは法的義務により各マスキングの決定理由を文書化する必要がある場合にはスケールしません。

このアプリケーションは、この問題に対して異なるアプローチをとります。TwelveLabsはワークフロー全体を通じてビデオ理解レイヤーとして機能します。レビュー担当者は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、インデックス化された映像全体を検索できます。Pegasus 1.5は、タイムスタンプ付きのプライバシーメタデータを生成し、レビュー担当者が1フレームも見る前にリスクの高い瞬間を表面化させます。ローカルの顔検出は、検出された顔を安定した個人ごとのアイデンティティにクラスタリングし、エクスポートパイプライン全体で維持します。ぼかしの追跡は、物体の動き、横顔への変化、カメラのカット割りを超えて特定の人物を追跡します。

このワークフローは、映像のアップロードとインデックス登録、自然言語や顔画像を使用した対象の追跡、インタラクティブなタイムライン上でのAI生成プライバシーシステムリスクセグメントのレビュー、個人アイデンティティによるマスキング対象の選択、そして安定した顔固定のマスキングビデオのエクスポートにいたる、レビューサイクル全体をカバーしています。


マスキングパイプラインの内部構造

アプリケーションは、4つの異なる機能を単一のレビューワークフローに接続します。

Marengo 3.0によるマルチモーダル検索により、レビュー担当者はテキストクエリ、画像のアップロード、または登録済みの顔エンティティを使用して、任意の人物、オブジェクト、またはシーンを特定できます。Marengoは関連するクリップセグメントを信頼度スコアとともに返し、インターフェースはこれを最も一致度の高い箇所にレビューマーカーを配置した視覚的なタイムラインレーンとしてレンダリングします。

Pegasus 1.5によるプライバシーメタデータは、プライバシーリスクに焦点を当てたタイムスタンプ付きのセグメントデータを生成します。ビデオ全体の状況を説明するのではなく、顔、文書、ナンバープレート、スクリーン、機密性の高いオブジェクト、保護対象の個人などの特定のカテゴリをターゲットにしたスキーマがPegasusに提供されます。各セグメントには、リスクレベル、マスキング理由、推奨されるアクション、シーン内での役割が含まれます。

ローカル検出とアイデンティティクラスタリングは、Pegasusのタイムスタンプを使用してキーフレームの抽出をガイドします。その後、アプリケーションはローカルの顔検出とInsightFaceエンベディングを実行し、検出された顔をビデオ全体で一貫したIDを持つ安定した個人ごとのアイデンティティにクラスタリングします。

顔固定ぼかしとエクスポートマスキングは、選択された個人の検出履歴をフレームインデックス付きのバウンディングボックスレーンに変換します。エクスポート中、レンダラーは一致する各フレームのそのレーンからぼかしを適用し、安定した、アイデンティティを認識したマスキングビデオを生成します。


環境のセットアップ

構築を開始する前に、以下の前提条件を完了してください。

  1. TwelveLabsアカウントを作成し、APIキーを生成します。Marengo 3.0とPegasus 1.5を有効にしたインデックスを作成し、インデックスIDを記録します。

  2. 顔登録とエンティティ検索をサポートするために、TwelveLabsエンティティコレクションを作成します。

  3. Flaskバックエンド用にPython 3を、Reactフロントエンド用にNode.js/npmをインストールします。エクスポート時のビデオ再エンコードにはFFmpegを推奨します。

  4. リポジトリをクローンし、READMEのセットアップ手順に従います。

.env.exampleで定義されている変数を使用して、backend/.envを作成します。


セクション1:TwelveLabsによるエンティティ管理

インデックス化された映像全体から特定の既知の人物を特定するには、単なるテキスト検索以上のものが必要です。顔をTwelveLabsエンティティとして登録すると、再利用可能なアイデンティティ参照が作成され、コンテキストクエリ(ドキュメントの近くにいるこの人物、特定のシーンタイプ内にいる人物、または特定のアクションを行っている人物を見つけるなど)と組み合わせることができます。

これはマスキングの精度にとって重要です。ビデオ内のすべての顔にフラグを立てる代わりに、レビュー担当者は検索を既知のアイデンティティに固定し、その人物が表示されている瞬間のみを抽出できます。


1.1 - エンティティインデックスへの顔アセットの登録

ユーザーが顔画像をアップロードして名前を入力すると、バックエンドはResNet-10顔検出器を使用して顔を検出し、プレビュークロップを生成して、TwelveLabsへの登録用の顔アセットを準備します。

フローには2つのステップがあります。まず顔画像をアセットとしてアップロードし、次に返されたアセットIDを参照する名前付きエンティティを作成します。これにより、視覚的な参照が検索可能なアイデンティティにリンクされます。

asset_id = twelvelabs_service.upload_face_asset(tmp.name)
metadata = {"name": name}
if preview_base64:
    metadata["face_snap_base64"] = preview_base64

entity_result = twelvelabs_service.create_entity(
    name=name,
    asset_ids=[asset_id],
    description=description or f"Face entity: {name}",
    metadata=metadata,

登録後、エンティティはコレクション内のインデックス化されたすべてのビデオにわたって、アイデンティティ制約付き検索に使用できるようになります。


1.2 - エンティティとテキストによる検索

検索は、エンティティベース、テキストベース、画像ベースの3つのモードをサポートしています。レビュー担当者は、レビュー開始時に把握している情報量に応じてこれらを組み合わせることができます。

エンティティベースの検索の場合、バックエンドはエンティティIDをTwelveLabsのメンション形式(<@entity_id>)でラップします。ユーザーがテキスト修飾子を追加すると、双方が結合され、検索がアイデンティティとコンテキストの両方によって制限されます。

backend/services/twelvelabs_services (Line 1214)

def entity_search(entity_id, query_suffix="", index_id=None):
    client = get_client()
    idx = resolve_index_id(index_id)

    query_text = f"<@{entity_id}>"
    if query_suffix:
        query_text = f"<@{entity_id}> {query_suffix}"

    logger.info("Entity search: %s", query_text)
    response = client.search.query(
        index_id=idx,
        search_options=["visual"],
        query_text=query_text,
        group_by="video",
        sort_option="score",
        page_limit=50,
    )
    return serialize_search_results(response)

画像ベースの検索の場合、バックエンドはquery_media_type="image"として画像を渡し、query_media_fileまたはquery_media_urlのいずれかを指定します。

if image_url:
    response = client.search.query(
        **kwargs,
        query_media_type="image",
        query_media_url=image_url,
    )

if image_path:
    with open(image_path, "rb") as image_file:
        response = client.search.query(
            **kwargs,
            query_media_type="image",
            query_media_file=image_file,
        )


1.3 - 検索タイムラインレーンとレビューマーカー

検索結果は、順位付きリストとしてだけでなく、視覚的なタイムラインレーンとしてもレンダリングされます。各結果セグメントにはstart(開始)とend(終了)の時間があり、フロントエンドはこれをビデオスクラバーにマッピングします。信頼度の高い一致箇所は赤いレビューインジケーターでマークされるため、レビュー担当者は周囲のコンテキストから最も一致度の高い箇所を容易に区別できます。

これにより、検索がレビューワークフローへと変わります。レビュー担当者は検索を行い、タイムラインを検査し、マスキングの決定が必要になる可能性が最も高い瞬間に直接ジャンプできます。


セクション2:検出と顔固定マスキング

検出は、TwelveLabsのビデオ理解をローカルのコンピュータービジョンパイプラインに接続します。すべてのフレームで顔検出を実行するのではなく、Pegasus 1.5が最初に、ビデオ内で人が表示される場所に関する構造化されたメタデータを生成します。バックエンドはこれらのタイムスタンプを使用してキーフレームの抽出をガイドし、検出パスをより高速かつターゲットを絞ったものにします。

検出された顔は、安定した個人ごとのアイデンティティにクラスタリングされます。レビュー担当者は、個々のバウンディングボックスからではなく、それらのアイデンティティから選択します。選択により、エクスポートマスキングに使用される顔固定レーンが駆動されます。


2.1 - TwelveLabsのコンテキストとローカルな顔検出の組み合わせ

パイプラインは、Pegasus構造化分析タスクから始まります。スキーマは2つのセグメントタイプを定義します。face_redaction_target(マスキングが必要になる可能性のある人々用)と scene_segment(より曖昧なシーンのコンテキスト用)です。temperature: 0.1に設定することで、出力を一貫した状態に保ち、自動化されたパイプラインに適したものにします。

backend/services/twelvelabs_services (Line 102)

PIPELINE_METADATA_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "face_redaction_target",
            "description": (
                "Return people segments for face redaction decisions. Create one segment per "
                "distinct face/person for each continuous time range where their face is visible "
                "enough to matter for redaction."
            ),
            "fields": [
                {"name": "name", "type": "string"},
                {"name": "description", "type": "string"},
                {"name": "should_anonymize", "type": "boolean"},
                {"name": "is_official", "type": "boolean"},
                {"name": "review_required", "type": "boolean"},
                {"name": "redaction_reason", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
        {
            "id": "scene_segment",
            "fields": [
                {"name": "description", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
    ],
}
body = {
    "video": {
        "type": "asset_id",
        "asset_id": asset_id,
    },
    "model_name": "pegasus1.5",
    "analysis_mode": "time_based_metadata",
    "response_format": PIPELINE_METADATA_RESPONSE_FORMAT,
    "temperature": 0.1,
}

Pegasusが人物のメタデータを返すと、アプリケーションはタイムレンジを抽出し、それらのウィンドウからキーフレームをサンプリングします。各キーフレームは detect_faces(..., with_encodings=True) に渡され、InsightFaceを使用して顔を検出し、バウンディングボックスを計算し、鮮明度を評価し、アイデンティティの埋め込みを生成します。

for kf in keyframes:
    faces = detect_faces(kf["frame"], with_encodings=True)
    for f in faces:
        f["frame_idx"] = kf["frame_idx"]
        f["timestamp"] = kf["timestamp"]
        all_faces.append(f)

各検出データは、埋め込みとともにフレームインデックスとタイムスタンプを保存し、クラスタリングステップで一貫した人物アイデンティティにグループ化できる時間対応のマスキングメタデータを作成します。


2.2 - マスキングターゲットの選択

検出とクラスタリングの後、レビュー担当者は検出された個人のリストから人物のアイデンティティを選択します。その選択は、顔のターゲットとそれらの保存された埋め込みのリストに分解され、エクスポートパイプラインが顔固定追跡のために使用します。

if person_ids:
    enriched = get_enriched_faces(job_id) or {}
    unique_faces = job.get("unique_faces") or enriched.get("unique_faces", [])
    for index, face in enumerate(unique_faces):
        stable_person_id = ensure_face_identity(face, fallback_index=index)
        if stable_person_id not in person_ids:
            continue
        face_targets.append(face)
        encoding = face.get("encoding")
        if encoding:
            face_encodings.append(encoding)
        matched_ids.append(stable_person_id)

リバースマスキング(除くマスキング)は、逆方向で同じアイデンティティ解決を使用します。レビュー担当者が保護したい人を選択すると、エクスポート時に検出された他のすべての顔にぼかしが適用されます。


2.3 - 顔固定レーンの構築とマスキングされたエクスポートのレンダリング

選択された各人物アイデンティティについて、パイプラインは build_face_lock_lane(job_id, person_id) を呼び出します。レーンビルダーは、保存されたInsightFaceの外観レコード、TwelveLabsエンティティ検索のタイムレンジ、およびPegasusから保存されたセマンティック人物範囲の3つのソースを利用します。その結果、ビデオ内のそのアイデンティティの全スパンをカバーするレーンドキュメントが作成されます。

face_lock_tracks = {}
if face_targets and not reverse_face_redaction:
    from services.face_lock_track import build_face_lock_lane

    for face in face_targets:
        person_id = get_face_identity(face)
        if not person_id:
            continue

        lane_doc = build_face_lock_lane(job_id, person_id)
        if lane_doc:
            face_lock_tracks[person_id] = lane_doc
appearances = collect_person_appearances(selected_face)

video_id = str(job.get("twelvelabs_video_id") or "").strip()
entity_ranges = get_entity_search_ranges(selected_face, video_id)
saved_person_ranges = get_face_semantic_time_ranges(selected_face)
semantic_ranges = entity_ranges + saved_person_ranges

segments = build_face_lock_segments(
    appearances, semantic_ranges, fps, total_frames, duration_sec,
)

エクスポート中、レーンはフレームインデックス付きのバウンディングボックスルックアップテーブル(face_lock_bboxes_by_frame)に変換され、YOLOv8-Faceリファインメントでガイドされます。レンダラーはこのテーブルに対して各フレームをチェックし、顔固定バウンディングボックスが登録されている場所にはどこでも apply_detection_redaction を適用します。

if face_lock_bboxes_by_frame and not preview_only:
    for entry in face_lock_bboxes_by_frame.get(frame_idx, ()):
        lane_bbox = entry.get("bbox")
        if lane_bbox:
            apply_detection_redaction(frame, lane_bbox, "face")

出力は、フレームごとの手動修正を必要とせずに、動き、部分的なオクルージョン、およびカメラの動きを通じて選択されたアイデンティティを追跡する安定したぼかしです。


セクション3:Pegasus 1.5によるプライバシーメタデータ

Pegasus 1.5はスキーマ駆動型のタイムスタンプベースのメタデータをサポートしています。これは、検出するプライバシーリスクのタイプと、各セグメントに対して返す構造化フィールドを正確に定義できることを意味します。これが、レビューインターフェースの「Meta Insights(メタインサイト)」を動かす仕組みです。


3.1 - プライバシーリスクスキーマの構築

スキーマは、アクション可能なレビューターゲット、すなわち顔、文書、画面、ナンバープレート、機密性の高いテキスト、保護対象の個人にPegasusの出力を集中させる単一のセグメントタイプ privacy_risk_segment を定義します。risk_levelscene_roleredaction_decisionreason などのフィールドは、単にタイムスタンプだけでなく、フラグが立てられた各瞬間の文書化された明確な論拠をレビュー担当者に提供します。 

backend/services/pegasus_privacy (Line 75)

PEGASUS_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "privacy_risk_segment",
            "description": (
                f"{PEGASUS_PRIVACY_PROMPT} Do not create broad background or crowd segments. Each "
                "segment must be narrow, actionable, and tied to one visible target that should be "
                "redacted or reviewed with care."
            ),
            "fields": [
                {
                    "name": "privacy_category",
                    "type": "string",
                    "description": (
                        "One of person, face, screen, document, text, license_plate, logo, object, scene. "
                        "Use scene only when the whole frame contains sensitive material; do not use it "
                        "for ordinary courtroom background."
                    ),
                    "enum": ["person", "face", "screen", "document", "text", "license_plate", "logo", "object", "scene"],
                },
                {
                    "name": "risk_level",
                    "type": "string",
                    "description": "One of low, medium, high.",
                    "enum": ["low", "medium", "high"],
                },
                {
                    "name": "label",
                    "type": "string",
                    "description": "Short target name, for example Main verdict subject, Protected witness, Visible ID, Phone screen, or License plate.",
                },
                {
                    "name": "description",
                    "type": "string",
                    "description": "What is visible and why this exact target needs redaction or careful review.",
                },
                {
                    "name": "reason",
                    "type": "string",
                    "description": (
                        "Specific reason this item should be redacted. For courtroom people, state why this is "
                        "the main verdict subject or another protected/private person; do not include generic "
                        "courtroom observers."
                    ),
                },
                {
                    "name": "scene_role",
                    "type": "string",
                    "description": (
                        "Role of the target in context. Use verdict_subject, defendant, respondent, or accused for "
                        "the main person whose verdict is being discussed. Ordinary judges, lawyers, clerks, officers, "
                        "jury, audience, reporters, and bystanders should not be segmented."
                    ),
                    "enum": [
                        "verdict_subject",
                        "defendant",
                        "respondent",
                        "accused",
                        "protected_witness",
                        "victim",
                        "minor",
                        "private_non_party",
                        "sensitive_item",
                        "unknown",
                    ],
				# More Segments Defined ...
                }
            ],
        }
    ],
}


3.2 - タイムスタンプ付きプライバシーセグメントの生成

スキーマは analysis_mode: "time_based_metadata" を指定してPegasusに渡されます。これにより、Pegasusは単一のドキュメントの要約ではなく、構造化されたタイムラインとして応答を返すよう指示されます。temperature: 0.1に設定することで出力を決定論的な状態に保ちます。これは、繰り返しの実行において一貫性が求められるコンプライアンスワークフローに非常に重要です。

backend/services/twelvelabs_services (Line 1044)

def create_pegasus_privacy_task(asset_id, *, response_format):
    """Create a Pegasus 1.5 async structured-analysis task from an existing asset id."""
    body = {
        "video": {
            "type": "asset_id",
            "asset_id": asset_id,
        },
        "model_name": "pegasus1.5",
        "analysis_mode": "time_based_metadata",
        "response_format": response_format,
        "temperature": 0.1,
    }

バックエンドは情報が空の timeline_eventsrecommended_actions フィールドを持つ初期ジョブデータを保存し、タスクが完了するまでポーリングします。各Pegasusセグメントは、タイムラインイベント(start_secend_secseveritycategoryreason、および redaction_decision)と、次にレビュー担当者が行うべきことを示す推奨アクションの2つのオブジェクトに変換されます。

event = {
    "id": event_id,
    "start_sec": round(start_sec, 3),
    "end_sec": round(end_sec, 3),
    "severity": severity,
    "category": category,
    "label": label[:120],
    "description": description[:600],
    "reason": reason[:600],
    "redaction_target": redaction_target[:120] or None,
    "scene_role": scene_role[:120] or None,
    "redaction_decision": redaction_decision[:120] or None,
    "subject_selection": subject_selection[:120] or None,
    "confidence": round(confidence, 3),
    "review_required": True,
    "recommended_action_ids": [action_id],
}


3.3 - レビューインターフェースとしてのプライバシーメタデータのレンダリング

タイムラインレーンは、各Pegasusイベントを start_secend_sec に配置されたクリック可能なホットスポットとしてレンダリングします。ホットスポットをクリックすると、メタインサイトパネルが開き、イベントにフォーカスが当たり、ビデオがそのタイムスタンプにシークされます。

サイドバーには、深刻度、カテゴリ、理由、マスキングの決定、マスキングターゲット、対象の選択、シーンの役割、信頼度、および処理に関するメモなど、完全なイベントメタデータが表示されます。レビュー担当者は、何かが注意を必要とする場所だけでなく、その理由も確認できます。これは、GDPR監査が必要とする文書化された論拠となります。


セクション4:確認のためのオープンエンドなビデオ分析

構造化されたメタデータに加えて、アプリケーションはインデックス付きビデオに対する自由形式のコンテキストの質問をサポートしています。レビュー担当者は、映像全体にどのような機密情報が表示されるか、どの瞬間が最も高いコンプライアンスリスクを伴うか、または特定のタイムスタンプの周囲で何が起こっているかを質問できます。これは、レビュー担当者が探しているものがまだ分かっていない状況で役立ちます。

フロントエンドは、video_id とレビュー担当者のプロンプトを /api/analyze-custom に送信します。バックエンドは、特定の瞬間に言及されるたびにタイムスタンプを要求するフォーマット指示をプロンプトの先頭に追加し、結合されたプロンプトをTwelveLabsのAnalyze(分析)サービスに渡します。

backend/services/twelvelabs_services (Line 666)

def analyze_video_custom(video_id, prompt):
    client = get_client()
    logger.info("Custom analysis on video %s", video_id)
    enhanced_prompt = f"{ANALYZE_FORMAT_INSTRUCTION}\n\n{prompt}"
    result = client.analyze(
        video_id=video_id,
        prompt=enhanced_prompt,
        temperature=0.2,
        request_options={"timeout_in_seconds": TWELVELABS_ANALYZE_TIMEOUT_SEC},
    )
    return {"data": result.data, "id": result.id}

応答は、ビデオコンテンツに基づいた平易な言葉による分析を返し、タイムスタンプによってレビュー担当者に関連する瞬間に直接リンクします。これにより、「機密情報を見つける必要がある」状態から「見るべき正確な場所はここである」状態へのギャップが埋まります。


このアプローチが可能にすること

歴史的に、ビデオのGDPRコンプライアンスには2つのトレードオフのいずれかが必要でした。遅くて高価な手動レビュープロセスか、法律が必要とする以上のものをマスキングし、その決定理由を明確に説明できない、大雑把な自動化されたアプローチのいずれかです。

このアプリケーションは、異なる道を進みます。Marengo 3.0は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、コーパス全体から適切な瞬間を特定します。Pegasus 1.5は、フラグが立てられた各セグメントについて、文書化された明確な論拠を持つ構造化された、タイムスタンプ付きのプライバシーメタデータを生成します。ローカルの顔検出は、AIに基づいたキーフレームからアイデンティティをクラスタリングします。顔固定レーンは、エクスポート全体で安定したぼかしトラックを維持します。

その結果、マスキングワークフローが、網羅的ではなくピンポイントなものになり、不透明ではなく文書化され、そして手動ではなくスケーラブルなものになります。すべての決定基準点にはレビュー可能な記録があり、これこそが真のコンプライアンスにおいて実際に求められているものです。

参考情報

イントロダクション

大規模なプライバシーレビューは、単なる編集の問題ではありません。それはインテリジェンス(情報と理解)の問題です。

法的要請が発生し、チームが数時間もの映像から特定の人物の登場箇所をすべて特定し、評価し、マスキング(黒塗り/ぼかし)する必要がある場合、フレームごとの手作業による確認は機能しません。GDPR Enforcement Trackerの記録によると、2026年3月までに2,793件の事例で累計60.6億ユーロ以上の制裁金が科されています。第83条では、最も深刻な違反に対して、最大2,000万ユーロまたは全世界の年間売上高の4%のいずれか高い方の制裁金が依然として認められています。運用のプレッシャーは現実のものであり、手動のワークフローはそれを吸収できるようには作られていません。

GDPRに準拠したマスキングには、3つの要素の連携が必要です。対象となる人物やオブジェクトの正確な特定、コーパス全体からの関連するすべての登場箇所の検索、そして法的な目的に実際に必要な範囲に限定したマスキングです。すべてをただぼかすだけでは、防御可能な戦略とは言えません。規制の「データの最小化」の原則は、全面的な抑制ではなく、正確性を求めています。

このチュートリアルでは、TwelveLabsをベースに構築された、本番環境対応のGDPRビデオマスキングアプリケーションについて説明します。このアプリケーションは、マルチモーダル検索とエンティティベースの検索にMarengo 3.0を、構造化されたプライバシーメタデータにPegasus 1.5を、そしてフレームレベルのアイデンティティクラスタリングにローカルの顔検出パイプラインを使用しています。その結果、ビデオのアップロードからエクスポート可能なマスキング済み出力までを単一のインターフェースでレビュー担当者が実行できるワークフローが実現します。

デモは tl-gdpr-compliance.vercel.app で体験でき、ソースコードは github.com/Hrishikesh332/tl-GDPR-compliance-redaction で公開されています。


アプリケーションの機能

ほとんどのマスキングツールは手動選択を前提に構築されています。レビュー担当者が映像を視聴し、バウンディングボックスを描き、ぼかし処理されたクリップをエクスポートします。このアプローチは、コーパスが大規模な場合や、人物が複数のクリップにまたがって登場する場合、あるいは法的義務により各マスキングの決定理由を文書化する必要がある場合にはスケールしません。

このアプリケーションは、この問題に対して異なるアプローチをとります。TwelveLabsはワークフロー全体を通じてビデオ理解レイヤーとして機能します。レビュー担当者は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、インデックス化された映像全体を検索できます。Pegasus 1.5は、タイムスタンプ付きのプライバシーメタデータを生成し、レビュー担当者が1フレームも見る前にリスクの高い瞬間を表面化させます。ローカルの顔検出は、検出された顔を安定した個人ごとのアイデンティティにクラスタリングし、エクスポートパイプライン全体で維持します。ぼかしの追跡は、物体の動き、横顔への変化、カメラのカット割りを超えて特定の人物を追跡します。

このワークフローは、映像のアップロードとインデックス登録、自然言語や顔画像を使用した対象の追跡、インタラクティブなタイムライン上でのAI生成プライバシーシステムリスクセグメントのレビュー、個人アイデンティティによるマスキング対象の選択、そして安定した顔固定のマスキングビデオのエクスポートにいたる、レビューサイクル全体をカバーしています。


マスキングパイプラインの内部構造

アプリケーションは、4つの異なる機能を単一のレビューワークフローに接続します。

Marengo 3.0によるマルチモーダル検索により、レビュー担当者はテキストクエリ、画像のアップロード、または登録済みの顔エンティティを使用して、任意の人物、オブジェクト、またはシーンを特定できます。Marengoは関連するクリップセグメントを信頼度スコアとともに返し、インターフェースはこれを最も一致度の高い箇所にレビューマーカーを配置した視覚的なタイムラインレーンとしてレンダリングします。

Pegasus 1.5によるプライバシーメタデータは、プライバシーリスクに焦点を当てたタイムスタンプ付きのセグメントデータを生成します。ビデオ全体の状況を説明するのではなく、顔、文書、ナンバープレート、スクリーン、機密性の高いオブジェクト、保護対象の個人などの特定のカテゴリをターゲットにしたスキーマがPegasusに提供されます。各セグメントには、リスクレベル、マスキング理由、推奨されるアクション、シーン内での役割が含まれます。

ローカル検出とアイデンティティクラスタリングは、Pegasusのタイムスタンプを使用してキーフレームの抽出をガイドします。その後、アプリケーションはローカルの顔検出とInsightFaceエンベディングを実行し、検出された顔をビデオ全体で一貫したIDを持つ安定した個人ごとのアイデンティティにクラスタリングします。

顔固定ぼかしとエクスポートマスキングは、選択された個人の検出履歴をフレームインデックス付きのバウンディングボックスレーンに変換します。エクスポート中、レンダラーは一致する各フレームのそのレーンからぼかしを適用し、安定した、アイデンティティを認識したマスキングビデオを生成します。


環境のセットアップ

構築を開始する前に、以下の前提条件を完了してください。

  1. TwelveLabsアカウントを作成し、APIキーを生成します。Marengo 3.0とPegasus 1.5を有効にしたインデックスを作成し、インデックスIDを記録します。

  2. 顔登録とエンティティ検索をサポートするために、TwelveLabsエンティティコレクションを作成します。

  3. Flaskバックエンド用にPython 3を、Reactフロントエンド用にNode.js/npmをインストールします。エクスポート時のビデオ再エンコードにはFFmpegを推奨します。

  4. リポジトリをクローンし、READMEのセットアップ手順に従います。

.env.exampleで定義されている変数を使用して、backend/.envを作成します。


セクション1:TwelveLabsによるエンティティ管理

インデックス化された映像全体から特定の既知の人物を特定するには、単なるテキスト検索以上のものが必要です。顔をTwelveLabsエンティティとして登録すると、再利用可能なアイデンティティ参照が作成され、コンテキストクエリ(ドキュメントの近くにいるこの人物、特定のシーンタイプ内にいる人物、または特定のアクションを行っている人物を見つけるなど)と組み合わせることができます。

これはマスキングの精度にとって重要です。ビデオ内のすべての顔にフラグを立てる代わりに、レビュー担当者は検索を既知のアイデンティティに固定し、その人物が表示されている瞬間のみを抽出できます。


1.1 - エンティティインデックスへの顔アセットの登録

ユーザーが顔画像をアップロードして名前を入力すると、バックエンドはResNet-10顔検出器を使用して顔を検出し、プレビュークロップを生成して、TwelveLabsへの登録用の顔アセットを準備します。

フローには2つのステップがあります。まず顔画像をアセットとしてアップロードし、次に返されたアセットIDを参照する名前付きエンティティを作成します。これにより、視覚的な参照が検索可能なアイデンティティにリンクされます。

asset_id = twelvelabs_service.upload_face_asset(tmp.name)
metadata = {"name": name}
if preview_base64:
    metadata["face_snap_base64"] = preview_base64

entity_result = twelvelabs_service.create_entity(
    name=name,
    asset_ids=[asset_id],
    description=description or f"Face entity: {name}",
    metadata=metadata,

登録後、エンティティはコレクション内のインデックス化されたすべてのビデオにわたって、アイデンティティ制約付き検索に使用できるようになります。


1.2 - エンティティとテキストによる検索

検索は、エンティティベース、テキストベース、画像ベースの3つのモードをサポートしています。レビュー担当者は、レビュー開始時に把握している情報量に応じてこれらを組み合わせることができます。

エンティティベースの検索の場合、バックエンドはエンティティIDをTwelveLabsのメンション形式(<@entity_id>)でラップします。ユーザーがテキスト修飾子を追加すると、双方が結合され、検索がアイデンティティとコンテキストの両方によって制限されます。

backend/services/twelvelabs_services (Line 1214)

def entity_search(entity_id, query_suffix="", index_id=None):
    client = get_client()
    idx = resolve_index_id(index_id)

    query_text = f"<@{entity_id}>"
    if query_suffix:
        query_text = f"<@{entity_id}> {query_suffix}"

    logger.info("Entity search: %s", query_text)
    response = client.search.query(
        index_id=idx,
        search_options=["visual"],
        query_text=query_text,
        group_by="video",
        sort_option="score",
        page_limit=50,
    )
    return serialize_search_results(response)

画像ベースの検索の場合、バックエンドはquery_media_type="image"として画像を渡し、query_media_fileまたはquery_media_urlのいずれかを指定します。

if image_url:
    response = client.search.query(
        **kwargs,
        query_media_type="image",
        query_media_url=image_url,
    )

if image_path:
    with open(image_path, "rb") as image_file:
        response = client.search.query(
            **kwargs,
            query_media_type="image",
            query_media_file=image_file,
        )


1.3 - 検索タイムラインレーンとレビューマーカー

検索結果は、順位付きリストとしてだけでなく、視覚的なタイムラインレーンとしてもレンダリングされます。各結果セグメントにはstart(開始)とend(終了)の時間があり、フロントエンドはこれをビデオスクラバーにマッピングします。信頼度の高い一致箇所は赤いレビューインジケーターでマークされるため、レビュー担当者は周囲のコンテキストから最も一致度の高い箇所を容易に区別できます。

これにより、検索がレビューワークフローへと変わります。レビュー担当者は検索を行い、タイムラインを検査し、マスキングの決定が必要になる可能性が最も高い瞬間に直接ジャンプできます。


セクション2:検出と顔固定マスキング

検出は、TwelveLabsのビデオ理解をローカルのコンピュータービジョンパイプラインに接続します。すべてのフレームで顔検出を実行するのではなく、Pegasus 1.5が最初に、ビデオ内で人が表示される場所に関する構造化されたメタデータを生成します。バックエンドはこれらのタイムスタンプを使用してキーフレームの抽出をガイドし、検出パスをより高速かつターゲットを絞ったものにします。

検出された顔は、安定した個人ごとのアイデンティティにクラスタリングされます。レビュー担当者は、個々のバウンディングボックスからではなく、それらのアイデンティティから選択します。選択により、エクスポートマスキングに使用される顔固定レーンが駆動されます。


2.1 - TwelveLabsのコンテキストとローカルな顔検出の組み合わせ

パイプラインは、Pegasus構造化分析タスクから始まります。スキーマは2つのセグメントタイプを定義します。face_redaction_target(マスキングが必要になる可能性のある人々用)と scene_segment(より曖昧なシーンのコンテキスト用)です。temperature: 0.1に設定することで、出力を一貫した状態に保ち、自動化されたパイプラインに適したものにします。

backend/services/twelvelabs_services (Line 102)

PIPELINE_METADATA_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "face_redaction_target",
            "description": (
                "Return people segments for face redaction decisions. Create one segment per "
                "distinct face/person for each continuous time range where their face is visible "
                "enough to matter for redaction."
            ),
            "fields": [
                {"name": "name", "type": "string"},
                {"name": "description", "type": "string"},
                {"name": "should_anonymize", "type": "boolean"},
                {"name": "is_official", "type": "boolean"},
                {"name": "review_required", "type": "boolean"},
                {"name": "redaction_reason", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
        {
            "id": "scene_segment",
            "fields": [
                {"name": "description", "type": "string"},
                {"name": "confidence", "type": "number"},
            ],
        },
    ],
}
body = {
    "video": {
        "type": "asset_id",
        "asset_id": asset_id,
    },
    "model_name": "pegasus1.5",
    "analysis_mode": "time_based_metadata",
    "response_format": PIPELINE_METADATA_RESPONSE_FORMAT,
    "temperature": 0.1,
}

Pegasusが人物のメタデータを返すと、アプリケーションはタイムレンジを抽出し、それらのウィンドウからキーフレームをサンプリングします。各キーフレームは detect_faces(..., with_encodings=True) に渡され、InsightFaceを使用して顔を検出し、バウンディングボックスを計算し、鮮明度を評価し、アイデンティティの埋め込みを生成します。

for kf in keyframes:
    faces = detect_faces(kf["frame"], with_encodings=True)
    for f in faces:
        f["frame_idx"] = kf["frame_idx"]
        f["timestamp"] = kf["timestamp"]
        all_faces.append(f)

各検出データは、埋め込みとともにフレームインデックスとタイムスタンプを保存し、クラスタリングステップで一貫した人物アイデンティティにグループ化できる時間対応のマスキングメタデータを作成します。


2.2 - マスキングターゲットの選択

検出とクラスタリングの後、レビュー担当者は検出された個人のリストから人物のアイデンティティを選択します。その選択は、顔のターゲットとそれらの保存された埋め込みのリストに分解され、エクスポートパイプラインが顔固定追跡のために使用します。

if person_ids:
    enriched = get_enriched_faces(job_id) or {}
    unique_faces = job.get("unique_faces") or enriched.get("unique_faces", [])
    for index, face in enumerate(unique_faces):
        stable_person_id = ensure_face_identity(face, fallback_index=index)
        if stable_person_id not in person_ids:
            continue
        face_targets.append(face)
        encoding = face.get("encoding")
        if encoding:
            face_encodings.append(encoding)
        matched_ids.append(stable_person_id)

リバースマスキング(除くマスキング)は、逆方向で同じアイデンティティ解決を使用します。レビュー担当者が保護したい人を選択すると、エクスポート時に検出された他のすべての顔にぼかしが適用されます。


2.3 - 顔固定レーンの構築とマスキングされたエクスポートのレンダリング

選択された各人物アイデンティティについて、パイプラインは build_face_lock_lane(job_id, person_id) を呼び出します。レーンビルダーは、保存されたInsightFaceの外観レコード、TwelveLabsエンティティ検索のタイムレンジ、およびPegasusから保存されたセマンティック人物範囲の3つのソースを利用します。その結果、ビデオ内のそのアイデンティティの全スパンをカバーするレーンドキュメントが作成されます。

face_lock_tracks = {}
if face_targets and not reverse_face_redaction:
    from services.face_lock_track import build_face_lock_lane

    for face in face_targets:
        person_id = get_face_identity(face)
        if not person_id:
            continue

        lane_doc = build_face_lock_lane(job_id, person_id)
        if lane_doc:
            face_lock_tracks[person_id] = lane_doc
appearances = collect_person_appearances(selected_face)

video_id = str(job.get("twelvelabs_video_id") or "").strip()
entity_ranges = get_entity_search_ranges(selected_face, video_id)
saved_person_ranges = get_face_semantic_time_ranges(selected_face)
semantic_ranges = entity_ranges + saved_person_ranges

segments = build_face_lock_segments(
    appearances, semantic_ranges, fps, total_frames, duration_sec,
)

エクスポート中、レーンはフレームインデックス付きのバウンディングボックスルックアップテーブル(face_lock_bboxes_by_frame)に変換され、YOLOv8-Faceリファインメントでガイドされます。レンダラーはこのテーブルに対して各フレームをチェックし、顔固定バウンディングボックスが登録されている場所にはどこでも apply_detection_redaction を適用します。

if face_lock_bboxes_by_frame and not preview_only:
    for entry in face_lock_bboxes_by_frame.get(frame_idx, ()):
        lane_bbox = entry.get("bbox")
        if lane_bbox:
            apply_detection_redaction(frame, lane_bbox, "face")

出力は、フレームごとの手動修正を必要とせずに、動き、部分的なオクルージョン、およびカメラの動きを通じて選択されたアイデンティティを追跡する安定したぼかしです。


セクション3:Pegasus 1.5によるプライバシーメタデータ

Pegasus 1.5はスキーマ駆動型のタイムスタンプベースのメタデータをサポートしています。これは、検出するプライバシーリスクのタイプと、各セグメントに対して返す構造化フィールドを正確に定義できることを意味します。これが、レビューインターフェースの「Meta Insights(メタインサイト)」を動かす仕組みです。


3.1 - プライバシーリスクスキーマの構築

スキーマは、アクション可能なレビューターゲット、すなわち顔、文書、画面、ナンバープレート、機密性の高いテキスト、保護対象の個人にPegasusの出力を集中させる単一のセグメントタイプ privacy_risk_segment を定義します。risk_levelscene_roleredaction_decisionreason などのフィールドは、単にタイムスタンプだけでなく、フラグが立てられた各瞬間の文書化された明確な論拠をレビュー担当者に提供します。 

backend/services/pegasus_privacy (Line 75)

PEGASUS_RESPONSE_FORMAT = {
    "type": "segment_definitions",
    "segment_definitions": [
        {
            "id": "privacy_risk_segment",
            "description": (
                f"{PEGASUS_PRIVACY_PROMPT} Do not create broad background or crowd segments. Each "
                "segment must be narrow, actionable, and tied to one visible target that should be "
                "redacted or reviewed with care."
            ),
            "fields": [
                {
                    "name": "privacy_category",
                    "type": "string",
                    "description": (
                        "One of person, face, screen, document, text, license_plate, logo, object, scene. "
                        "Use scene only when the whole frame contains sensitive material; do not use it "
                        "for ordinary courtroom background."
                    ),
                    "enum": ["person", "face", "screen", "document", "text", "license_plate", "logo", "object", "scene"],
                },
                {
                    "name": "risk_level",
                    "type": "string",
                    "description": "One of low, medium, high.",
                    "enum": ["low", "medium", "high"],
                },
                {
                    "name": "label",
                    "type": "string",
                    "description": "Short target name, for example Main verdict subject, Protected witness, Visible ID, Phone screen, or License plate.",
                },
                {
                    "name": "description",
                    "type": "string",
                    "description": "What is visible and why this exact target needs redaction or careful review.",
                },
                {
                    "name": "reason",
                    "type": "string",
                    "description": (
                        "Specific reason this item should be redacted. For courtroom people, state why this is "
                        "the main verdict subject or another protected/private person; do not include generic "
                        "courtroom observers."
                    ),
                },
                {
                    "name": "scene_role",
                    "type": "string",
                    "description": (
                        "Role of the target in context. Use verdict_subject, defendant, respondent, or accused for "
                        "the main person whose verdict is being discussed. Ordinary judges, lawyers, clerks, officers, "
                        "jury, audience, reporters, and bystanders should not be segmented."
                    ),
                    "enum": [
                        "verdict_subject",
                        "defendant",
                        "respondent",
                        "accused",
                        "protected_witness",
                        "victim",
                        "minor",
                        "private_non_party",
                        "sensitive_item",
                        "unknown",
                    ],
				# More Segments Defined ...
                }
            ],
        }
    ],
}


3.2 - タイムスタンプ付きプライバシーセグメントの生成

スキーマは analysis_mode: "time_based_metadata" を指定してPegasusに渡されます。これにより、Pegasusは単一のドキュメントの要約ではなく、構造化されたタイムラインとして応答を返すよう指示されます。temperature: 0.1に設定することで出力を決定論的な状態に保ちます。これは、繰り返しの実行において一貫性が求められるコンプライアンスワークフローに非常に重要です。

backend/services/twelvelabs_services (Line 1044)

def create_pegasus_privacy_task(asset_id, *, response_format):
    """Create a Pegasus 1.5 async structured-analysis task from an existing asset id."""
    body = {
        "video": {
            "type": "asset_id",
            "asset_id": asset_id,
        },
        "model_name": "pegasus1.5",
        "analysis_mode": "time_based_metadata",
        "response_format": response_format,
        "temperature": 0.1,
    }

バックエンドは情報が空の timeline_eventsrecommended_actions フィールドを持つ初期ジョブデータを保存し、タスクが完了するまでポーリングします。各Pegasusセグメントは、タイムラインイベント(start_secend_secseveritycategoryreason、および redaction_decision)と、次にレビュー担当者が行うべきことを示す推奨アクションの2つのオブジェクトに変換されます。

event = {
    "id": event_id,
    "start_sec": round(start_sec, 3),
    "end_sec": round(end_sec, 3),
    "severity": severity,
    "category": category,
    "label": label[:120],
    "description": description[:600],
    "reason": reason[:600],
    "redaction_target": redaction_target[:120] or None,
    "scene_role": scene_role[:120] or None,
    "redaction_decision": redaction_decision[:120] or None,
    "subject_selection": subject_selection[:120] or None,
    "confidence": round(confidence, 3),
    "review_required": True,
    "recommended_action_ids": [action_id],
}


3.3 - レビューインターフェースとしてのプライバシーメタデータのレンダリング

タイムラインレーンは、各Pegasusイベントを start_secend_sec に配置されたクリック可能なホットスポットとしてレンダリングします。ホットスポットをクリックすると、メタインサイトパネルが開き、イベントにフォーカスが当たり、ビデオがそのタイムスタンプにシークされます。

サイドバーには、深刻度、カテゴリ、理由、マスキングの決定、マスキングターゲット、対象の選択、シーンの役割、信頼度、および処理に関するメモなど、完全なイベントメタデータが表示されます。レビュー担当者は、何かが注意を必要とする場所だけでなく、その理由も確認できます。これは、GDPR監査が必要とする文書化された論拠となります。


セクション4:確認のためのオープンエンドなビデオ分析

構造化されたメタデータに加えて、アプリケーションはインデックス付きビデオに対する自由形式のコンテキストの質問をサポートしています。レビュー担当者は、映像全体にどのような機密情報が表示されるか、どの瞬間が最も高いコンプライアンスリスクを伴うか、または特定のタイムスタンプの周囲で何が起こっているかを質問できます。これは、レビュー担当者が探しているものがまだ分かっていない状況で役立ちます。

フロントエンドは、video_id とレビュー担当者のプロンプトを /api/analyze-custom に送信します。バックエンドは、特定の瞬間に言及されるたびにタイムスタンプを要求するフォーマット指示をプロンプトの先頭に追加し、結合されたプロンプトをTwelveLabsのAnalyze(分析)サービスに渡します。

backend/services/twelvelabs_services (Line 666)

def analyze_video_custom(video_id, prompt):
    client = get_client()
    logger.info("Custom analysis on video %s", video_id)
    enhanced_prompt = f"{ANALYZE_FORMAT_INSTRUCTION}\n\n{prompt}"
    result = client.analyze(
        video_id=video_id,
        prompt=enhanced_prompt,
        temperature=0.2,
        request_options={"timeout_in_seconds": TWELVELABS_ANALYZE_TIMEOUT_SEC},
    )
    return {"data": result.data, "id": result.id}

応答は、ビデオコンテンツに基づいた平易な言葉による分析を返し、タイムスタンプによってレビュー担当者に関連する瞬間に直接リンクします。これにより、「機密情報を見つける必要がある」状態から「見るべき正確な場所はここである」状態へのギャップが埋まります。


このアプローチが可能にすること

歴史的に、ビデオのGDPRコンプライアンスには2つのトレードオフのいずれかが必要でした。遅くて高価な手動レビュープロセスか、法律が必要とする以上のものをマスキングし、その決定理由を明確に説明できない、大雑把な自動化されたアプローチのいずれかです。

このアプリケーションは、異なる道を進みます。Marengo 3.0は、テキスト、画像、または登録されたアイデンティティエンティティを使用して、コーパス全体から適切な瞬間を特定します。Pegasus 1.5は、フラグが立てられた各セグメントについて、文書化された明確な論拠を持つ構造化された、タイムスタンプ付きのプライバシーメタデータを生成します。ローカルの顔検出は、AIに基づいたキーフレームからアイデンティティをクラスタリングします。顔固定レーンは、エクスポート全体で安定したぼかしトラックを維持します。

その結果、マスキングワークフローが、網羅的ではなくピンポイントなものになり、不透明ではなく文書化され、そして手動ではなくスケーラブルなものになります。すべての決定基準点にはレビュー可能な記録があり、これこそが真のコンプライアンスにおいて実際に求められているものです。

参考情報