IIIF AV(Audio/Visual)の概要

IIIF Presentation API 3.0では、画像だけでなく音声・動画コンテンツも統一的なフレームワークで扱えるようになりました。これがIIIF AV(Audio/Visual)と呼ばれる機能群です。

画像の場合、Canvasにはwidthheightが設定されますが、AV資料ではduration(再生時間、秒単位)が加わります。動画の場合はwidthheightdurationの3つを、音声の場合はdurationのみを指定します。

{
  "id": "https://example.com/canvas",
  "type": "Canvas",
  "height": 480,
  "width": 640,
  "duration": 619.62
}

AV資料に対しても、画像と同様にアノテーションを付与できます。時間軸上の特定区間を指定するには、targetにMedia Fragment形式(#t=開始,終了)を使用します。

https://example.com/canvas#t=10.5,25.3

これにより、字幕、タグ、目次(Range/Structure)など、時間に紐づいたメタデータを柔軟に表現できます。

iiif-prezi3を使った動画マニフェストの作成

iiif-prezi3はPythonでIIIF Presentation API v3のマニフェストを生成するためのライブラリです。IIIF Cookbookにも実装例が公開されています。

基本的な動画マニフェストの作成

from iiif_prezi3 import Manifest, AnnotationPage, Annotation, ResourceItem, config

config.configs['helpers.auto_fields.AutoLang'].auto_lang = "ja"

manifest = Manifest(
    id="https://example.com/manifest.json",
    label="動画サンプル"
)

canvas = manifest.make_canvas(id="https://example.com/canvas")

anno_body = ResourceItem(
    id="https://example.com/video.mp4",
    type="Video",
    format="video/mp4"
)

anno_page = AnnotationPage(id="https://example.com/canvas/page")
anno = Annotation(
    id="https://example.com/canvas/page/annotation",
    motivation="painting",
    body=anno_body,
    target=canvas.id
)

# 高さ・幅・再生時間を設定
hwd = {"height": 480, "width": 640, "duration": 619.62}
anno_body.set_hwd(**hwd)
canvas.set_hwd(**hwd)

anno_page.add_item(anno)
canvas.add_item(anno_page)

print(manifest.json(indent=2))

ResourceItemtypeには、動画の場合は"Video"、音声の場合は"Sound"を指定します。formatにはMIMEタイプを指定します。set_hwd()メソッドにより、bodyとcanvasの両方にサイズと再生時間を設定します。

字幕(WebVTT)の組み込み

動画や音声に字幕を付与するには、WebVTT形式の字幕ファイルを作成し、マニフェストにアノテーションとして組み込みます。

WebVTTファイルの作成

OpenAIのWhisper APIを使って音声からWebVTT形式のトランスクリプトを自動生成できます。

from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
audio_file = open("video.mp4", "rb")
transcript = client.audio.transcriptions.create(
    model="whisper-1",
    file=audio_file,
    response_format="vtt"
)

with open("video.vtt", "w", encoding="utf-8") as f:
    f.write(transcript)

生成されるWebVTTファイルは以下のような形式です。

W00E00B::V00T00T::0005..0500000000::0000::0152..500000

マニフェストへの字幕の追加

字幕はsupplementing motivationのアノテーションとして、Canvasのannotationsに追加します。

def add_vtt(manifest, canvas, vtt_url):
    vtt_anno_page = AnnotationPage(id=f"{canvas.id}/page2")

    vtt_body = ResourceItem(
        id=vtt_url,
        type="Text",
        format="text/vtt"
    )
    vtt_anno = Annotation(
        id=f"{vtt_anno_page.id}/a1",
        motivation="supplementing",
        body=vtt_body,
        target=canvas.id,
        label="WebVTT Transcript (machine-generated)"
    )

    vtt_anno_page.add_item(vtt_anno)
    canvas.annotations = [vtt_anno_page]

生成されるマニフェストでは、annotations配列内にWebVTTファイルへの参照が格納されます。

{
  "annotations": [{
    "id": "https://example.com/canvas/page2",
    "type": "AnnotationPage",
    "items": [{
      "id": "https://example.com/canvas/page2/a1",
      "type": "Annotation",
      "motivation": "supplementing",
      "body": {
        "id": "https://example.com/video.vtt",
        "type": "Text",
        "format": "text/vtt"
      },
      "target": "https://example.com/canvas"
    }]
  }]
}

Theseus Viewer、Ramp、Clover IIIFなどのビューアで、字幕がトランスクリプトとして表示されます。

目次(Range/Structure)の設定

動画内のシーンやセグメントに目次を付けるには、IIIF Presentation APIのstructures(Range)を使用します。

セグメント情報の取得

Amazon Rekognitionのビデオセグメント検出を利用すると、動画のショット境界を自動検出できます。検出された各セグメントの開始・終了時間を使ってRange構造を構築します。

マニフェストへの目次の追加

def add_segment(manifest, canvas_id, segments):
    range_toc = manifest.make_range(
        id=f"{prefix}/range/r",
        label="Table of Contents"
    )

    for s in segments:
        if s["Type"] != "SHOT":
            continue

        index = s["Shot Index"]
        start = s["Start Timestamp (milliseconds)"] / 1000
        end = s["End Timestamp (milliseconds)"] / 1000

        range_seg = range_toc.make_range(
            id=f"{prefix}/range/r{index}",
            label=f"Segment {index}",
        )

        range_seg.items.append({
            "id": f"{canvas_id}#t={start},{end}",
            "type": "Canvas"
        })

生成されるマニフェストのstructuresは以下のような構造になります。

{
  "structures": [{
    "id": "https://example.com/range/r",
    "type": "Range",
    "label": { "ja": ["Table of Contents"] },
    "items": [
      {
        "id": "https://example.com/range/r0",
        "type": "Range",
        "label": { "ja": ["Segment 0"] },
        "items": [{
          "id": "https://example.com/canvas#t=0.0,5.338",
          "type": "Canvas"
        }]
      },
      {
        "id": "https://example.com/range/r1",
        "type": "Range",
        "label": { "ja": ["Segment 1"] },
        "items": [{
          "id": "https://example.com/canvas#t=5.372,6.74",
          "type": "Canvas"
        }]
      }
    ]
  }]
}

各Rangeのitemsに含まれるCanvasのIDには#t=開始,終了のフラグメントが付加されており、これにより目次の各項目が動画内の特定時間区間に紐づきます。Theseus ViewerやRampなどのビューアでは、目次項目をクリックすると該当区間に自動的にジャンプします。

アノテーションの付与

動画に対してもアノテーションを付与できます。時間軸上の特定区間に対するタグ付けや、時空間上の特定領域に対するラベル付けが可能です。

時間区間のタグ付け

Amazon Rekognitionのラベル検出を使い、動画内の各時間区間に対してオブジェクトラベルを自動付与する例です。

def add_label_segment(manifest, canvas, labels):
    anno_page = AnnotationPage(id=f"{canvas.id}/page1")
    canvas.annotations.append(anno_page)

    for i, label in enumerate(labels):
        start = label["StartTimestamp"] / 1000
        end = label["EndTimestamp"] / 1000
        name = label["Label"]["Name"]

        anno = Annotation(
            id=f"{anno_page.id}/a{i}",
            motivation="tagging",
            target=f"{canvas.id}#t={start},{end}",
            body={
                "type": "TextualBody",
                "value": name,
                "format": "text/plain",
            }
        )
        anno_page.add_item(anno)

target#t=開始,終了を指定することで、アノテーションが特定の時間区間に紐づきます。

時空間領域のアノテーション

BoundingBox情報が得られる場合は、時間指定と空間指定を組み合わせたアノテーションも可能です。target#t=時間&xywh=x,y,w,hの形式を使用します。ただし、Rampビューアでは#t=を先に記述し、開始点のみを含む形式が必要です。

{
  "target": "https://example.com/canvas#t=21.021&xywh=33,23,555,422"
}

音声資料の対応

音声資料のマニフェスト作成は、動画と基本的に同じ手順ですが、いくつかの違いがあります。

音声マニフェストの基本構造

音声の場合、ResourceItemtype"Sound"format"audio/mp4"(または適切なMIMEタイプ)に設定します。Canvasにはdurationのみを指定します。

canvas = manifest.make_canvas(
    id=f"{prefix}/canvas",
    duration=duration
)
anno_body = ResourceItem(
    id=mp4_url,
    type="Sound",
    format="audio/mp4",
    duration=duration
)

accompanyingCanvasによる画像の追加

音声資料にはデフォルトで表示する画像がありませんが、IIIF Presentation API v3のaccompanyingCanvasを使って、音声再生中に表示する画像を指定できます。IIIF Cookbookの「Audio Presentation with Accompanying Image」レシピに準拠した実装です。

def create_accompanying_canvas(prefix, image_path):
    im = Image.open(image_path)
    w, h = im.size

    accompanying = iiif_prezi3.Canvas(
        id=f"{prefix}/canvas/accompanying"
    )
    accompanying.set_hwd(height=h, width=w)

    anno_page = iiif_prezi3.AnnotationPage(
        id=f"{prefix}/canvas/accompanying/annotation/page"
    )
    anno = iiif_prezi3.Annotation(
        id=f"{prefix}/canvas/accompanying/annotation/image",
        motivation="painting",
        body=iiif_prezi3.ResourceItem(
            id=f"{prefix}/image.jpg",
            type="Image",
            format="image/jpeg",
            height=h,
            width=w
        ),
        target=anno_page.id
    )
    anno_page.add_item(anno)
    accompanying.add_item(anno_page)

    return accompanying

# Canvasに関連づける
canvas.accompanyingCanvas = create_accompanying_canvas(prefix, image_path)

Clover IIIFなどのビューアでは、このaccompanyingCanvasで指定された画像がメディアプレイヤーに表示されます。例えば、音声資料の内容を要約した画像をDALL-Eで生成してサムネイルとして設定するといった応用も考えられます。

summaryの活用

マニフェストのsummaryプロパティに要約テキストを格納することで、音声資料の内容をテキストとしても提示できます。

{
  "summary": {
    "ja": ["日本語のアクセントは、言葉の声の上げ下げを指しますが..."]
  }
}

複数字幕トラックの管理

多言語対応や複数の字幕トラック(例: 日本語字幕と英語字幕)を1つのマニフェストに含める方法を解説します。

マニフェストへの記述

複数のWebVTTファイルは、同一のAnnotationPage内にそれぞれ個別のAnnotationとして記述します。各アノテーションのlabelで言語やトラックの種類を明示します。

{
  "annotations": [{
    "id": "https://example.com/canvas/page/2",
    "type": "AnnotationPage",
    "items": [
      {
        "id": "https://example.com/canvas/annotation/webvtt",
        "type": "Annotation",
        "label": { "ja": ["日本語 (machine-generated)"] },
        "motivation": "supplementing",
        "body": {
          "id": "https://example.com/transcript_ja.vtt",
          "type": "Text",
          "format": "text/vtt",
          "label": { "ja": ["日本語 (machine-generated)"] }
        },
        "target": "https://example.com/canvas"
      },
      {
        "id": "https://example.com/canvas/annotation/webvtt/2",
        "type": "Annotation",
        "label": { "ja": ["English (machine-generated)"] },
        "motivation": "supplementing",
        "body": {
          "id": "https://example.com/transcript_en.vtt",
          "type": "Text",
          "format": "text/vtt",
          "label": { "ja": ["English (machine-generated)"] }
        },
        "target": "https://example.com/canvas"
      }
    ]
  }]
}

英語字幕の自動生成

OpenAIのWhisper(GitHub版)を使って、日本語音声から英語への翻訳字幕を生成できます。

import whisper

def translate(input_path, output_path):
    model = whisper.load_model('medium')
    result = model.transcribe(
        input_path,
        language="ja",
        task="translate"
    )
    write_vtt(result, output_path)

注意点として、API版Whisperのtranslationsエンドポイントでは日本語音声から英語への翻訳がうまく動作しない場合があり、GitHub版Whisperのtask="translate"オプションを使用する方が安定します。

ビューアごとの表示の違い

  • Ramp: 複数の字幕トラックを切り替え可能なタブとして表示。各トラックを個別に選択できる
  • Clover IIIF: 複数の字幕が連続して表示される形式。切り替えではなく、すべてのトラックが並列表示される

Rampビューアでの表示

Rampは、IIIF AV資料の閲覧に最も適したビューアの一つです。主要な機能と表示例を紹介します。

Transcriptsタブ

supplementing motivationのアノテーション(WebVTTファイル)は、Transcriptsタブにテキストとして表示されます。テキストをクリックすると、該当する時間位置に再生がジャンプします。

Markersタブ

highlighting motivationのアノテーションは、Markersタブに表形式で表示されます。各マーカーに付与されたリンクをクリックすることで、指定時間に遷移します。注意点として、Rampでは1つのAnnotationPageのみが処理対象となるため、supplementingアノテーションとhighlightingアノテーションを同じAnnotationPage内に格納する必要があります。

Filesタブ

マニフェストのrenderingプロパティで指定されたリソースが、Filesタブに表示されます。関連データセットやAPIエンドポイントへのリンクを提供できます。

その他のビューアでの表示

  • Theseus Viewer: 字幕や目次の表示に対応しており、AV資料の閲覧に利用できる
  • Clover IIIF: accompanyingCanvasで指定した画像の表示、renderingプロパティの表示に対応
  • Aviary: IIIF AVに対応しているが、マニフェストの形式によっては字幕が表示されないケースもある

まとめ

IIIF AVにより、音声・動画資料も画像と同じIIIFの枠組みで管理・配信・閲覧できるようになりました。iiif-prezi3ライブラリを使えば、Pythonで動画・音声マニフェストの生成を自動化でき、OpenAIのWhisperによる字幕自動生成やAmazon Rekognitionによるセグメント検出・ラベル付与と組み合わせることで、AV資料のIIIF化を効率的に進められます。

マニフェスト構築の要点をまとめると、以下の通りです。

  • 字幕はsupplementing motivationのアノテーションとしてWebVTTファイルを参照する
  • 目次はstructuresのRange構造で、キャンバスの時間フラグメント(#t=開始,終了)を使って表現する
  • タグやラベルはtagginghighlighting motivationのアノテーションとして付与する
  • 音声資料にはaccompanyingCanvasで表示画像を関連づけられる
  • 複数の字幕トラックは同一AnnotationPage内に複数のAnnotationとして記述する

ビューアの選択は、必要な機能と対象コンテンツに応じて判断してください。AV資料に特化した機能が必要であればRamp、汎用的な表示にはClover IIIFやTheseus Viewerが適しています。