チュートリアル

Twelve Labsのシンプルな検索APIを使用して、ビデオ内の特定の瞬間を見つける方法

アンキット・カレ

開発者はTwelve Labs Search APIを使用することで、手作業で映像をスクラブすることなく、自然言語のクエリを使用して動画内の正確な瞬間をピンポイントで特定できます。このチュートリアルでは、動画のインデックス作成、視覚、会話、画面上のテキストにわたるセマンティック検索の実行、およびFlaskアプリでの検索結果の表示について手順を追って説明します。

開発者はTwelve Labs Search APIを使用することで、手作業で映像をスクラブすることなく、自然言語のクエリを使用して動画内の正確な瞬間をピンポイントで特定できます。このチュートリアルでは、動画のインデックス作成、視覚、会話、画面上のテキストにわたるセマンティック検索の実行、およびFlaskアプリでの検索結果の表示について手順を追って説明します。

この記事の内容

No headings found on page

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

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

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

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

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

2023/04/07

15分

記事へのリンクをコピー

最近、Twelve LabsはGTC 2023におけるマルチモーダルAIのパイオニアとして取り上げられました。GTC 2023のビデオを見ている時、共同創業者のSoyoungが、自社が紹介されているセグメントを探すのに苦労して髪をかきむしっているのを目にしました。この経験から、自社のAPIを使ってビデオ内を検索するという課題に取り組むモチベーションが湧きました。そこで、今週末の楽しさ満載の個人プロジェクトのチュートリアルとして、Twelve Labsの検索APIを使用してビデオ内の特定の瞬間を見つけるプロセスをご案内します。

                     ビデオ内の検索をより快適にするためのシンプルな設計図😎

はじめに

Twelve Labsは、ビデオ理解のパワーを活用したアプリケーションの作成を支援するために設計された、一連のAPIの形でマルチモーダル基盤モデルを提供しています。このブログ記事では、Twelve Labs APIを使用して、自然言語のクエリでビデオ内の特定の興味深い瞬間をシームレスに見つける方法を探ります。大学院時代に作成した、「Machine Learning is Everywhere(機械学習はどこにでもある)」というタイトルの面白いビデオをローカルドライブからアップロードします。その名の通り、この80秒のビデオは、生活のあらゆる側面におけるMLの偏在性を示しています。このビデオでは、プロの卓球選手がKukaロボットと対戦する様子や、スケートボードのトリックを行う人物のイベント要約にMLが使用されている様子などのMLアプリケーションが紹介されています。Twelve Labs APIの力を借りて、シンプルな自然言語クエリを使用してビデオ内の特定のシーンを見つける方法をデモします。

このチュートリアルでは、シンプルな検索APIについての緩やかな導入を目的としているため、シングルビデオ内の瞬間を検索することに焦点を当て、軽量でユーザーフレンドリーなFlaskフレームワークを使用してデモアプリを作成するという、最小限の構成にしています。しかし、このプラットフォームは、数百、あるいは数千のビデオのアップロードに対応し、それらの中から特定の瞬間を見つけ出せるようにスケールする十分な機能を備えています。それでは、本格的な楽しみに向けて飛び込んでいきましょう!

クイックオーバービュー

  • 前提条件: Twelve Labs APIスイートを使用するためのサインアップと、デモアプリケーションを作成するために必要なパッケージのインストール

  • ビデオのアップロード: 素晴らしいビデオをお持ちですか?それらをTwelve Labsプラットフォームに送信すれば、効率的にインデックスが作成され、検索体験が格段に簡単になります!

  • セマンティックビデオ検索: シンプルな自然言語クエリで、ビデオ内の思い出の瞬間を探し出します

  • デモアプリの作成: Flaskを使用してHTMLテンプレートをレンダリングする便利なPythonスクリプトを作成し、検索結果を表示する洗練されたHTMLページをデザインします

💡 ところで、開発者ではない方がこの記事を読まれていても、心配は無用です!Jupyter Notebookのすぐに使えるリンクを用意しており、プロセス全体を実行して結果を得ることができます。さらに、コードを1行も書かずにセマンティックビデオ検索の力を体験できる弊社のPlaygroundもぜひお試しください。無料クレジットが必要な場合は、お気軽にご連絡ください😄

前提条件

このチュートリアルでは、Jupyter Notebookを使用します。ローカルコンピューターにすでにJupyter、Python、およびPipがセットアップされていることを前提としています。何か問題が発生した場合は、最も迅速にレスポンスが可能な弊社のDiscordサーバーでお気軽にご相談ください 🚅🏎️⚡️。Discordがお好みに合わない場合は、メールでお問い合わせいただくこともできます。Twelve Labsのアカウントを作成すると、APIダッシュボードにアクセスしてAPIキーを取得できます。このデモでは既存のアカウントを使用します。API呼び出しを行うには、シークレットキーを使用し、API URLを指定するだけです。さらに、環境変数を使用して、アプリケーションに設定の詳細を渡すことができます。

<pre><code class="bash">%env API_KEY=<your_API_key>
%env API_URL=https://api.twelvelabs.io/v1.1
</code></pre>


依存関係のインストール:

<pre><code class="python">pip install requests
pip install flask
</code></pre>

ビデオのアップロード

最初のステップとして、ビデオ理解機能を活用するために、ローカルコンピューターからTwelve Labsプラットフォームにビデオをアップロードした方法を解説します。

インポート:

<pre><code class="python">import os
import requests
import glob
from pprint import pprint
</code></pre

以下のようにして、APIのURLとAPIキーを取得します。

<pre><code class="python">API_URL = os.getenv("API_URL")
assert API_URL
</code></pre

<pre><code class="python">API_KEY = os.getenv("API_KEY")
assert API_KEY
</code></pre

Index API

次のステップでは、Index APIを使用してビデオインデックスを作成します。ビデオインデックスは、1つ以上のビデオをグループ化し、いくつかの共通の検索プロパティを設定する方法であり、これにより、インデックスにアップロードされたビデオに対してセマンティック検索を実行できるようになります。

インデックスは以下のフィールドによって定義されます:

<ul>
<li><b>名前</b></li>
<li><b>エンジン</b> - 現在、ビデオ理解のための最新のマルチモーダル基盤モデルであるMarengo2を提供しています</li>
<li>1つ以上の<b>インデックス作成オプション</b>:</li>
<ul>
<li><b>visual</b>: このオプションを選択すると、APIサービスはビデオのマルチモーダルな視覚・音響分析を実行し、オブジェクト、アクション、音、動き、場所、状況に応じたイベント、および複雑な視覚・音響テキストの説明によって検索できるようになります。ビジュアル検索の例としては、大歓声の群衆やオフィスを去る疲れ切った開発者などが挙げられます😆。</li>
<li><b>conversation</b>: このオプションを選択すると、APIはビデオから説明(書き起こしテキスト)を抽出し、そのテキストに対してセマンティックな自然言語処理(NLP)分析を実行します。これにより、検索している会話が行われているビデオ内の正確な瞬間を特定できます。インデックス化されたビデオ内で行われる会話を検索する例としては、兄弟に嘘をついた瞬間などが挙げられます😜。</li>
<li><b>text_in_video</b>: このオプションを選択すると、APIサービスは文字認識(OCR)を実行し、標識、ラベル、字幕、ロゴ、プレゼンテーション、ドキュメントなど、ビデオ内に表示されるテキストを検索できるようにします。この場合、サッカーの試合中に登場するブランドなどを検索することができます🏈。</li></ul></ul>

インデックスの作成:

<pre><code class="python"># `/indexes` エンドポイントの URL を構築します
INDEXES_URL = f"{API_URL}/indexes"

# インデックスの名前を指定します
INDEX_NAME = "My University Days"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}

# data という名前の辞書を宣言します
data = {
    "engine_id": "marengo2",  
    "index_options": ["visual", "conversation", "text_in_video"], 
    "index_name": INDEX_NAME,
}

# インデックスを作成します
response = requests.post(INDEXES_URL, headers=headers, json=data)

# インデックスの一意の識別子を保存します
INDEX_ID = response.json().get('_id')

# ステータスコードとレスポンスをプリントします
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

ビデオをアップロードするためのTask API

Twelve Labs プラットフォームは、作成されたインデックスにビデオをアップロードし、アップロードプロセスのステータスを監視するためのTask APIを提供しています。

<pre><code class="python">TASKS_URL = f"{API_URL}/tasks"

file_name = "Machine Learning is Everywhere" # インデックス化されたビデオはこのファイル名になります
file_path = "ml.mp4" # アップロードするビデオのファイル名
file_stream = open(file_path,"rb")

data = {
    "index_id": INDEX_ID, 
    "language": "en"
}

file_param = [
    ("video_file", (file_name, file_stream, "application/octet-stream")),]

response = requests.post(TASKS_URL, headers=headers, data=data, files=file_param
</code></pre

ビデオをアップロードすると、システムは自動的にビデオ・インデックス作成プロセスを開始します。Twelve Labsは、「ビデオ・インデックス作成」の概念について、マルチモーダル基盤モデルを使用して時間的コンテキストを組み込み、動き、オブジェクト、音、画面上のテキスト、ビデオからの音声などの情報を抽出して、強力なビデオ埋め込み(embeddings)を生成することだと説明しています。これにより、日常的な言葉を使ってビデオ内の特定の瞬間を見つけたり、指定されたラベルやプロンプトに基づいてビデオセグメントを分類したりすることが可能になります。

ビデオ・インデックス作成プロセスの監視:

<pre><code class="python">import time

# 開始時間を定義します
start = time.time()
print("Start uploading video")

# インデックスプロセスを監視します
TASK_STATUS_URL = f"{API_URL}/tasks/{TASK_ID}"
while True:
    response = requests.get(TASK_STATUS_URL, headers=headers)
    STATUS = response.json().get("status")
    if STATUS == "ready":
        print(f"Status code: {STATUS}")
        break
    time.sleep(10)

# 終了時間を定義します
end = time.time()
print("Finish uploading video")
print("Time elapsed (in seconds): ", end - start)
</code></pre

<pre><code class="python"># ビデオの一意の識別子を取得します
VIDEO_ID = response.json().get('video_id')

# ステータスコード、ビデオの一意の識別子、
およびレスポンスをプリントします
print(f"VIDEO ID: {VIDEO_ID}")
pprint(response.json())
</code></pre

<pre><code class="language-plaintext">VIDEO ID: 642621ffffa3551fb6d2f###
{'_id': '642621fc3205dc8a48ba8###',
 'created_at': '2023-03-30T23:57:48.877Z',
 'estimated_time': '2023-03-30T23:59:58.312Z',
 'index_id': '###621fb7b1f2230dfcd6###',
 'metadata': {'duration': 80.32,
              'filename': 'Machine Learning is Everywhere',
              'height': 720,
              'width': 1280},
 'status': 'ready',
 'type': 'index_task_info',
 'updated_at': '2023-03-31T00:00:34.412Z',
 'video_id': '642621ffffa3551fb6d2f###'}
</code></pre

アプリに既存インデックスの一意の識別子を渡すための別の環境変数を作成します:

<pre><code class="python">%env ENV_INDEX_ID = ###621fb7b1f2230dfcd6###
</code></pre

こちらはインデックス内のすべてのビデオの一覧です。今回はシンプルにするためにビデオを1つだけインデックス化しましたが、無料クレジットを使用すれば最大10時間分のビデオコンテンツをアップロードできます。

<pre><code class="python"># 既存のインデックスの一意の識別子を取得します
INDEX_ID = os.getenv("ENV_INDEX_ID")

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY,
}

# インデックス内のすべてのビデオをリストします
INDEXES_VIDEOS_URL = f"{API_URL}/indexes/{INDEX_ID}/videos"
response = requests.get(INDEXES_VIDEOS_URL, headers=headers)
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

<pre><code class="python">Status code: 200
{'data': [{'_id': '642621ffffa3551fb6d2f###',
           'created_at': '2023-03-30T23:57:48Z',
           'metadata': {'duration': 80.32,
                        'engine_id': 'marengo2',
                        'filename': 'Machine Learning is Everywhere',
                        'fps': 25,
                        'height': 720,
                        'size': 11877525,
                        'width': 1280},
           'updated_at': '2023-03-30T23:57:51Z'}],
 'page_info': {'limit_per_page': 10,
               'page': 1,
               'total_duration': 80.32,
               'total_page': 1,
               'total_results': 1}}
</code></pre

セマンティックビデオ検索、すなわち特定の瞬間の検索

システムがビデオのインデックス作成とビデオ埋め込みの生成を完了すると、これらを活用して検索APIで特定の瞬間を見つけることができます。このAPIは、入力したクエリのセマンティックな意味に対応する、関連ビデオ内の正確な開始・終了タイムコードを特定します。選択したインデックスの作成オプションに応じて、セマンティックビデオ検索に同じオプションのサブセットから選択できます。例えば、インデックスのすべてのオプションを有効にした場合、視覚・聴覚的な検索、会話の検索、ビデオ内に表示されるテキストの検索が可能になります。インデックスレベルと検索レベルの両方で同じオプションのセットを提供する理由は、ビデオコンテンツの分析にプラットフォームをどのように利用したいか、また現在のコンテキストに適したオプションを組み合わせてビデオコンテンツ全体をどのように検索したいかを柔軟に決定できるようにするためです。

まずは、シンプルな自然言語クエリ「a guy doing a trick on a skateboard(スケートボードでトリックを決める男)」を使ったビジュアル検索から始めましょう。

<pre><code class="bash">Status code: 200
{'data': [{'confidence': 'high',
           'end': 49.34375,
           'metadata': [{'type': 'visual'}],
           'score': 83.24,
           'start': 41.65625,
           'video_id': '642621ffffa3551fb6d2f###'}],
 'page_info': {'limit_per_page': 10,
               'page_expired_at': '2023-03-31T22:41:42Z',
               'total_results': 1},
 'search_pool': {'index_id': '642621fb7b1f2230dfcd####',
                 'total_count': 1,
                 'total_duration': 80}}
</code></pre

対応するビデオセグメント:

この機能は、モデルがビデオコンテンツを人間のように理解している様子を示しているため、とても興奮します。上のスクリーンショットでわかるように、システムは抽出したかった正確な瞬間を見事に特定しています。

別のクエリとして「a guy playing table tennis with a robotic arm(ロボットアームと卓球をする男)」を試して、システムがさらに魔法のような力を発揮する様子を見てみましょう。

<pre><code class="python"># `/search` エンドポイントの URL を構築します
SEARCH_URL = f"{API_URL}/search/"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}
query = "a guy playing table tennis with a robotic arm"
# `data` という名前の辞書を宣言します
data = {
    "query": query,  # 検索クエリを指定します
    "index_id": INDEX_ID,  # インデックスの一意の識別子を指定します
    "search_options": ["visual"],  # 検索オプションを指定します
}

# 検索リクエストを行います
response = requests.post(SEARCH_URL, headers=headers, json=data)
print(f"Status code: {response.status_code}")
pprint(response.json())
</code></pre


出力:

<pre><code class="python">{'data': [{'confidence': 'high',
           'end': 14.6875,
           'metadata': [{'type': 'visual'}],
           'score': 90.62,
           'start': 9.75,
           'video_id': '642621ffffa3551fb6d2####'},
          {'confidence': 'high',
           'end': 9.75,
           'metadata': [{'type': 'visual'}],
           'score': 89.74,
           'start': 4.5,
           'video_id': '642621ffffa3551fb6d2####'}],
</code></pre


対応するビデオセグメント:

ビンゴ!今回もシステムは魅力的な瞬間を正確に特定しました。

💡ここで、楽しいミニ課題です。"a breakthrough in machine learning would be worth ten Microsofts(機械学習におけるブレイクスルーはマイクロソフト10社分の価値がある)"を検索し、検索オプションを `["text_in_video"]` に設定してみてください。

これらのJSONレスポンスを、開始点と終了点を手動で確認することなく最大限に活用するには、愛情込めて作られた素晴らしいインデックスページが必要です。そうすれば、検索リクエストのJSON出力を対応するビデオに直接送信できます。さあ、取りかかりましょう!

デモアプリの作成

この素晴らしいビデオ理解の冒険にお付き合いいただきありがとうございます 🎉🥳👏!最後のステップに到達しました。ここでは、Flaskベースのシンプルなアプリを作成し、前のステップの検索結果を取得して美しいウェブページに表示し、リクエストした正確な瞬間を披露します。ちなみに、私はデータサイエンスのバックグラウンドがあり、Pythonが大好きなのでFlaskを選びました。さらに、Flaskは軽量なPythonベースのフレームワークであり、このチュートリアルのニーズに合致しています。もちろん、お好みに合わせて他のフレームワークを選んでいただいてもかまいません。


最初のステップは、Jupyter Notebookに必要なインポートを行うことです。

<pre><code class="python">import json
import pickle
</code></pre


検索APIから収集したすべての開始および終了タイムスタンプを保持する、"starts "と "ends "という2つのリストを生成します。

<pre><code class="python">data = response.json()

starts = []
ends = []

for item in data['data']:
    starts.append(item['start'])
    ends.append(item['end'])

print("starts:", starts)
print("ends:", ends)
</code></pre

<pre><code class="bash">starts: [34.90625, 0, 62.5625]
ends: [39.21875, 4.5, 65.90625]
</code></pre


必要なタイムスタンプを取得し、同じビデオを自身のYouTubeチャンネルにアップロードしたので、それらを使用する方法はいくつかあります。ローカルディスクからビデオを取得してウェブページでお気に入りのクリップを表示することもできますし、YouTubeチャンネルのビデオURLをそのまま使用して同じ結果を得ることもできます。後者の方がより魅力的なので、アップロードしたビデオと同じYouTubeの埋め込みコードを使用し、開始と終了のタイムスタンプを渡すことにします。このようにして、検索した正確なビデオセグメントが表示されるようになります。少し注意点として、YouTubeの埋め込みコードは、開始および終了パラメータに整数値のみをサポートしているため、これらの値を丸める必要があります。

<pre><code class="bash">starts_int = [int(f) for f in starts]
ends_int = [int(f) for f in ends]
</code></pre


作成するFlaskアプリのファイルに渡せるよう、入力したクエリと一緒にこれらのリストを素早くシリアライズ(pickle)しておきましょう。これは、これから作成するFlaskアプリのファイルにパラメータを渡す際に役立ちます。

<pre><code class="python">with open("lists.pkl", "wb") as f:
    pickle.dump((starts_int, ends_int, query), f)
</code></pre>

これで、Flaskを使用してこれらのパラメータを渡す設定がすべて整いました。

Flaskアプリを作成する手順

1. 新しいFlaskプロジェクトの作成: プロジェクト用の新しいディレクトリを作成し、Flaskアプリのメインファイルとなる新しいPythonファイルを作成します。

<pre><code class="bash">mkdir my_flask_app
cd my_flask_app
touch app.py
</code></pre>

Flaskアプリファイルとテンプレートの準備ができると、ディレクトリ構造は次のようになります。

<pre><code class="markdown">my_flask_app/
│   app.py
│   ml.mp4
│
└───templates/
    │   index.html
</code></pre>

アップロードするビデオファイルを `my_flask_app` ディレクトリ内に保存しておきます。

2. Flaskアプリのコードを作成する: **app.py** ファイルに、Flaskアプリのコードを記述する必要があります。以下は、Jinja2テンプレートを使用して、タイムスタンプのリストが使用されている 'index.html' ファイルをレンダリングするFlaskアプリです。

<pre><code class="python">from flask import Flask, render_template
import pickle

with open("lists.pkl", "rb") as f:
    starts, ends, query = pickle.load(f)

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html", starts=starts, ends=ends, query=query)

if __name__ == "__main__":
    app.run(debug=True)
</code></pre

3. templatesディレクトリの作成: FlaskでJinja2テンプレートを使用するには、Flaskアプリと同じディレクトリに **templates** ディレクトリを作成する必要があります。このディレクトリにJinja2テンプレートを保存します。

<pre><code class="bash">mkdir templates
</code></pre>

パズルの最後のピースは、検索クエリに一致したすべてのビデオセグメントを表示する `index.html` ページです。HTMLファイルを作成する前に、私のYouTubeチャンネルから「ビデオの埋め込み」コードを素早く取得しましょう。

4. Jinja2テンプレートの作成: Jinja2テンプレートを作成するには、**templates** ディレクトリにHTMLファイルを作成する必要があります。

<pre><code class="bash">touch index.html
</code></pre>

以下は、Jinja2テンプレートのシンプルな例です。HTMLファイル内に、アプリファイルから渡されたリストとクエリ文字列を反復処理できるようにするコードが組み込まれています。

<pre><code class="language-html"><html>
  <head>
    <style>
      body {
        background-color: #F2F2F2;
        font-family: Arial, sans-serif;
        text-align: center;
      }
      h1 {
        margin-top: 40px;
      }
      .video-container {
        display: flex;
        flex-wrap: wrap;
        padding: 40px;
        justify-content: center;
      }
      .video-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 50%;
        height: 600px;
        margin: 20px;
        text-align: center;
      }
      .video-item iframe {
        width: 80%;
        height: 380px;
        margin: 20px;
      }
      .video-item p {
        font-size: 16px;
        margin-top: 10px;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <h1>My Favorite Scenes</h1>
    <div class="video-container">
      {% for i in range(starts|length) %}
        <div class="video-item">
          <iframe width="560" height="315" src="https://www.youtube.com/embed/hdZ_tNtdB4c?start={{ starts[i] }}&amp;end={{ ends[i] }}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in- picture" allowfullscreen></iframe>
          <p>Start: {{ starts[i] }} | End: {{ ends[i] }}</p>
          <p>Query: {{ query }}</p>
        </div>
      {% endfor %}
    </div>
  </body>
</html>
</code></pre>

パーフェクト!Jupyter Notebookの最後のセルを実行しましょう。

<pre><code class="python">%run app.py
</code></pre>

すべてが期待通りに進んでいることを示す、以下のような出力が表示されるはずです😊:

URLリンク http://127.0.0.1:5000 をクリックすると、検索クエリに応じて、以下のように出力されます。

ビデオを再生すると、指定したタイムスタンプに従って再生され、ビデオ内で探し出したかった特定の瞬間やセグメントが強調表示されます。

Jupyter Notebookを含むフォルダと、手元のコンピュータでローカルにチュートリアルを実行するために必要なすべてのファイルへのリンクはこちらです - https://tinyurl.com/twelvelabs

探求してほしい楽しいアクティビティ:

  1. 検索オプション(visual、conversation、text-in-video)のさまざまな組み合わせを試してみて、結果がどのように変化するかを確認してください。

  2. 複数のビデオをアップロードし、それに応じてコードを調整して、これらすべてのビデオを同時に検索できるようにします。

  3. プロの開発者スキルを発揮して、ユーザーが `index.html` ページからクエリを入力し、リアルタイムで結果を取得できるようにコードを強化します。

‍次にすること

次回の記事では、演算子のセットを使用して複数のシンプルなクエリを組み合わせたり、ビデオのコレクション全体を検索したりする方法について詳しく説明します。今後の投稿を楽しみにしていてください!

最後に、マルチモーダル基盤モデルに関心を共有する同志たちとつながるために、当社のDiscordコミュニティにぜひご参加ください。アイデアを交換したり、質問したり、お互いから学び合ったりするのに最適な場所です!

最近、Twelve LabsはGTC 2023におけるマルチモーダルAIのパイオニアとして取り上げられました。GTC 2023のビデオを見ている時、共同創業者のSoyoungが、自社が紹介されているセグメントを探すのに苦労して髪をかきむしっているのを目にしました。この経験から、自社のAPIを使ってビデオ内を検索するという課題に取り組むモチベーションが湧きました。そこで、今週末の楽しさ満載の個人プロジェクトのチュートリアルとして、Twelve Labsの検索APIを使用してビデオ内の特定の瞬間を見つけるプロセスをご案内します。

                     ビデオ内の検索をより快適にするためのシンプルな設計図😎

はじめに

Twelve Labsは、ビデオ理解のパワーを活用したアプリケーションの作成を支援するために設計された、一連のAPIの形でマルチモーダル基盤モデルを提供しています。このブログ記事では、Twelve Labs APIを使用して、自然言語のクエリでビデオ内の特定の興味深い瞬間をシームレスに見つける方法を探ります。大学院時代に作成した、「Machine Learning is Everywhere(機械学習はどこにでもある)」というタイトルの面白いビデオをローカルドライブからアップロードします。その名の通り、この80秒のビデオは、生活のあらゆる側面におけるMLの偏在性を示しています。このビデオでは、プロの卓球選手がKukaロボットと対戦する様子や、スケートボードのトリックを行う人物のイベント要約にMLが使用されている様子などのMLアプリケーションが紹介されています。Twelve Labs APIの力を借りて、シンプルな自然言語クエリを使用してビデオ内の特定のシーンを見つける方法をデモします。

このチュートリアルでは、シンプルな検索APIについての緩やかな導入を目的としているため、シングルビデオ内の瞬間を検索することに焦点を当て、軽量でユーザーフレンドリーなFlaskフレームワークを使用してデモアプリを作成するという、最小限の構成にしています。しかし、このプラットフォームは、数百、あるいは数千のビデオのアップロードに対応し、それらの中から特定の瞬間を見つけ出せるようにスケールする十分な機能を備えています。それでは、本格的な楽しみに向けて飛び込んでいきましょう!

クイックオーバービュー

  • 前提条件: Twelve Labs APIスイートを使用するためのサインアップと、デモアプリケーションを作成するために必要なパッケージのインストール

  • ビデオのアップロード: 素晴らしいビデオをお持ちですか?それらをTwelve Labsプラットフォームに送信すれば、効率的にインデックスが作成され、検索体験が格段に簡単になります!

  • セマンティックビデオ検索: シンプルな自然言語クエリで、ビデオ内の思い出の瞬間を探し出します

  • デモアプリの作成: Flaskを使用してHTMLテンプレートをレンダリングする便利なPythonスクリプトを作成し、検索結果を表示する洗練されたHTMLページをデザインします

💡 ところで、開発者ではない方がこの記事を読まれていても、心配は無用です!Jupyter Notebookのすぐに使えるリンクを用意しており、プロセス全体を実行して結果を得ることができます。さらに、コードを1行も書かずにセマンティックビデオ検索の力を体験できる弊社のPlaygroundもぜひお試しください。無料クレジットが必要な場合は、お気軽にご連絡ください😄

前提条件

このチュートリアルでは、Jupyter Notebookを使用します。ローカルコンピューターにすでにJupyter、Python、およびPipがセットアップされていることを前提としています。何か問題が発生した場合は、最も迅速にレスポンスが可能な弊社のDiscordサーバーでお気軽にご相談ください 🚅🏎️⚡️。Discordがお好みに合わない場合は、メールでお問い合わせいただくこともできます。Twelve Labsのアカウントを作成すると、APIダッシュボードにアクセスしてAPIキーを取得できます。このデモでは既存のアカウントを使用します。API呼び出しを行うには、シークレットキーを使用し、API URLを指定するだけです。さらに、環境変数を使用して、アプリケーションに設定の詳細を渡すことができます。

<pre><code class="bash">%env API_KEY=<your_API_key>
%env API_URL=https://api.twelvelabs.io/v1.1
</code></pre>


依存関係のインストール:

<pre><code class="python">pip install requests
pip install flask
</code></pre>

ビデオのアップロード

最初のステップとして、ビデオ理解機能を活用するために、ローカルコンピューターからTwelve Labsプラットフォームにビデオをアップロードした方法を解説します。

インポート:

<pre><code class="python">import os
import requests
import glob
from pprint import pprint
</code></pre

以下のようにして、APIのURLとAPIキーを取得します。

<pre><code class="python">API_URL = os.getenv("API_URL")
assert API_URL
</code></pre

<pre><code class="python">API_KEY = os.getenv("API_KEY")
assert API_KEY
</code></pre

Index API

次のステップでは、Index APIを使用してビデオインデックスを作成します。ビデオインデックスは、1つ以上のビデオをグループ化し、いくつかの共通の検索プロパティを設定する方法であり、これにより、インデックスにアップロードされたビデオに対してセマンティック検索を実行できるようになります。

インデックスは以下のフィールドによって定義されます:

<ul>
<li><b>名前</b></li>
<li><b>エンジン</b> - 現在、ビデオ理解のための最新のマルチモーダル基盤モデルであるMarengo2を提供しています</li>
<li>1つ以上の<b>インデックス作成オプション</b>:</li>
<ul>
<li><b>visual</b>: このオプションを選択すると、APIサービスはビデオのマルチモーダルな視覚・音響分析を実行し、オブジェクト、アクション、音、動き、場所、状況に応じたイベント、および複雑な視覚・音響テキストの説明によって検索できるようになります。ビジュアル検索の例としては、大歓声の群衆やオフィスを去る疲れ切った開発者などが挙げられます😆。</li>
<li><b>conversation</b>: このオプションを選択すると、APIはビデオから説明(書き起こしテキスト)を抽出し、そのテキストに対してセマンティックな自然言語処理(NLP)分析を実行します。これにより、検索している会話が行われているビデオ内の正確な瞬間を特定できます。インデックス化されたビデオ内で行われる会話を検索する例としては、兄弟に嘘をついた瞬間などが挙げられます😜。</li>
<li><b>text_in_video</b>: このオプションを選択すると、APIサービスは文字認識(OCR)を実行し、標識、ラベル、字幕、ロゴ、プレゼンテーション、ドキュメントなど、ビデオ内に表示されるテキストを検索できるようにします。この場合、サッカーの試合中に登場するブランドなどを検索することができます🏈。</li></ul></ul>

インデックスの作成:

<pre><code class="python"># `/indexes` エンドポイントの URL を構築します
INDEXES_URL = f"{API_URL}/indexes"

# インデックスの名前を指定します
INDEX_NAME = "My University Days"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}

# data という名前の辞書を宣言します
data = {
    "engine_id": "marengo2",  
    "index_options": ["visual", "conversation", "text_in_video"], 
    "index_name": INDEX_NAME,
}

# インデックスを作成します
response = requests.post(INDEXES_URL, headers=headers, json=data)

# インデックスの一意の識別子を保存します
INDEX_ID = response.json().get('_id')

# ステータスコードとレスポンスをプリントします
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

ビデオをアップロードするためのTask API

Twelve Labs プラットフォームは、作成されたインデックスにビデオをアップロードし、アップロードプロセスのステータスを監視するためのTask APIを提供しています。

<pre><code class="python">TASKS_URL = f"{API_URL}/tasks"

file_name = "Machine Learning is Everywhere" # インデックス化されたビデオはこのファイル名になります
file_path = "ml.mp4" # アップロードするビデオのファイル名
file_stream = open(file_path,"rb")

data = {
    "index_id": INDEX_ID, 
    "language": "en"
}

file_param = [
    ("video_file", (file_name, file_stream, "application/octet-stream")),]

response = requests.post(TASKS_URL, headers=headers, data=data, files=file_param
</code></pre

ビデオをアップロードすると、システムは自動的にビデオ・インデックス作成プロセスを開始します。Twelve Labsは、「ビデオ・インデックス作成」の概念について、マルチモーダル基盤モデルを使用して時間的コンテキストを組み込み、動き、オブジェクト、音、画面上のテキスト、ビデオからの音声などの情報を抽出して、強力なビデオ埋め込み(embeddings)を生成することだと説明しています。これにより、日常的な言葉を使ってビデオ内の特定の瞬間を見つけたり、指定されたラベルやプロンプトに基づいてビデオセグメントを分類したりすることが可能になります。

ビデオ・インデックス作成プロセスの監視:

<pre><code class="python">import time

# 開始時間を定義します
start = time.time()
print("Start uploading video")

# インデックスプロセスを監視します
TASK_STATUS_URL = f"{API_URL}/tasks/{TASK_ID}"
while True:
    response = requests.get(TASK_STATUS_URL, headers=headers)
    STATUS = response.json().get("status")
    if STATUS == "ready":
        print(f"Status code: {STATUS}")
        break
    time.sleep(10)

# 終了時間を定義します
end = time.time()
print("Finish uploading video")
print("Time elapsed (in seconds): ", end - start)
</code></pre

<pre><code class="python"># ビデオの一意の識別子を取得します
VIDEO_ID = response.json().get('video_id')

# ステータスコード、ビデオの一意の識別子、
およびレスポンスをプリントします
print(f"VIDEO ID: {VIDEO_ID}")
pprint(response.json())
</code></pre

<pre><code class="language-plaintext">VIDEO ID: 642621ffffa3551fb6d2f###
{'_id': '642621fc3205dc8a48ba8###',
 'created_at': '2023-03-30T23:57:48.877Z',
 'estimated_time': '2023-03-30T23:59:58.312Z',
 'index_id': '###621fb7b1f2230dfcd6###',
 'metadata': {'duration': 80.32,
              'filename': 'Machine Learning is Everywhere',
              'height': 720,
              'width': 1280},
 'status': 'ready',
 'type': 'index_task_info',
 'updated_at': '2023-03-31T00:00:34.412Z',
 'video_id': '642621ffffa3551fb6d2f###'}
</code></pre

アプリに既存インデックスの一意の識別子を渡すための別の環境変数を作成します:

<pre><code class="python">%env ENV_INDEX_ID = ###621fb7b1f2230dfcd6###
</code></pre

こちらはインデックス内のすべてのビデオの一覧です。今回はシンプルにするためにビデオを1つだけインデックス化しましたが、無料クレジットを使用すれば最大10時間分のビデオコンテンツをアップロードできます。

<pre><code class="python"># 既存のインデックスの一意の識別子を取得します
INDEX_ID = os.getenv("ENV_INDEX_ID")

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY,
}

# インデックス内のすべてのビデオをリストします
INDEXES_VIDEOS_URL = f"{API_URL}/indexes/{INDEX_ID}/videos"
response = requests.get(INDEXES_VIDEOS_URL, headers=headers)
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

<pre><code class="python">Status code: 200
{'data': [{'_id': '642621ffffa3551fb6d2f###',
           'created_at': '2023-03-30T23:57:48Z',
           'metadata': {'duration': 80.32,
                        'engine_id': 'marengo2',
                        'filename': 'Machine Learning is Everywhere',
                        'fps': 25,
                        'height': 720,
                        'size': 11877525,
                        'width': 1280},
           'updated_at': '2023-03-30T23:57:51Z'}],
 'page_info': {'limit_per_page': 10,
               'page': 1,
               'total_duration': 80.32,
               'total_page': 1,
               'total_results': 1}}
</code></pre

セマンティックビデオ検索、すなわち特定の瞬間の検索

システムがビデオのインデックス作成とビデオ埋め込みの生成を完了すると、これらを活用して検索APIで特定の瞬間を見つけることができます。このAPIは、入力したクエリのセマンティックな意味に対応する、関連ビデオ内の正確な開始・終了タイムコードを特定します。選択したインデックスの作成オプションに応じて、セマンティックビデオ検索に同じオプションのサブセットから選択できます。例えば、インデックスのすべてのオプションを有効にした場合、視覚・聴覚的な検索、会話の検索、ビデオ内に表示されるテキストの検索が可能になります。インデックスレベルと検索レベルの両方で同じオプションのセットを提供する理由は、ビデオコンテンツの分析にプラットフォームをどのように利用したいか、また現在のコンテキストに適したオプションを組み合わせてビデオコンテンツ全体をどのように検索したいかを柔軟に決定できるようにするためです。

まずは、シンプルな自然言語クエリ「a guy doing a trick on a skateboard(スケートボードでトリックを決める男)」を使ったビジュアル検索から始めましょう。

<pre><code class="bash">Status code: 200
{'data': [{'confidence': 'high',
           'end': 49.34375,
           'metadata': [{'type': 'visual'}],
           'score': 83.24,
           'start': 41.65625,
           'video_id': '642621ffffa3551fb6d2f###'}],
 'page_info': {'limit_per_page': 10,
               'page_expired_at': '2023-03-31T22:41:42Z',
               'total_results': 1},
 'search_pool': {'index_id': '642621fb7b1f2230dfcd####',
                 'total_count': 1,
                 'total_duration': 80}}
</code></pre

対応するビデオセグメント:

この機能は、モデルがビデオコンテンツを人間のように理解している様子を示しているため、とても興奮します。上のスクリーンショットでわかるように、システムは抽出したかった正確な瞬間を見事に特定しています。

別のクエリとして「a guy playing table tennis with a robotic arm(ロボットアームと卓球をする男)」を試して、システムがさらに魔法のような力を発揮する様子を見てみましょう。

<pre><code class="python"># `/search` エンドポイントの URL を構築します
SEARCH_URL = f"{API_URL}/search/"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}
query = "a guy playing table tennis with a robotic arm"
# `data` という名前の辞書を宣言します
data = {
    "query": query,  # 検索クエリを指定します
    "index_id": INDEX_ID,  # インデックスの一意の識別子を指定します
    "search_options": ["visual"],  # 検索オプションを指定します
}

# 検索リクエストを行います
response = requests.post(SEARCH_URL, headers=headers, json=data)
print(f"Status code: {response.status_code}")
pprint(response.json())
</code></pre


出力:

<pre><code class="python">{'data': [{'confidence': 'high',
           'end': 14.6875,
           'metadata': [{'type': 'visual'}],
           'score': 90.62,
           'start': 9.75,
           'video_id': '642621ffffa3551fb6d2####'},
          {'confidence': 'high',
           'end': 9.75,
           'metadata': [{'type': 'visual'}],
           'score': 89.74,
           'start': 4.5,
           'video_id': '642621ffffa3551fb6d2####'}],
</code></pre


対応するビデオセグメント:

ビンゴ!今回もシステムは魅力的な瞬間を正確に特定しました。

💡ここで、楽しいミニ課題です。"a breakthrough in machine learning would be worth ten Microsofts(機械学習におけるブレイクスルーはマイクロソフト10社分の価値がある)"を検索し、検索オプションを `["text_in_video"]` に設定してみてください。

これらのJSONレスポンスを、開始点と終了点を手動で確認することなく最大限に活用するには、愛情込めて作られた素晴らしいインデックスページが必要です。そうすれば、検索リクエストのJSON出力を対応するビデオに直接送信できます。さあ、取りかかりましょう!

デモアプリの作成

この素晴らしいビデオ理解の冒険にお付き合いいただきありがとうございます 🎉🥳👏!最後のステップに到達しました。ここでは、Flaskベースのシンプルなアプリを作成し、前のステップの検索結果を取得して美しいウェブページに表示し、リクエストした正確な瞬間を披露します。ちなみに、私はデータサイエンスのバックグラウンドがあり、Pythonが大好きなのでFlaskを選びました。さらに、Flaskは軽量なPythonベースのフレームワークであり、このチュートリアルのニーズに合致しています。もちろん、お好みに合わせて他のフレームワークを選んでいただいてもかまいません。


最初のステップは、Jupyter Notebookに必要なインポートを行うことです。

<pre><code class="python">import json
import pickle
</code></pre


検索APIから収集したすべての開始および終了タイムスタンプを保持する、"starts "と "ends "という2つのリストを生成します。

<pre><code class="python">data = response.json()

starts = []
ends = []

for item in data['data']:
    starts.append(item['start'])
    ends.append(item['end'])

print("starts:", starts)
print("ends:", ends)
</code></pre

<pre><code class="bash">starts: [34.90625, 0, 62.5625]
ends: [39.21875, 4.5, 65.90625]
</code></pre


必要なタイムスタンプを取得し、同じビデオを自身のYouTubeチャンネルにアップロードしたので、それらを使用する方法はいくつかあります。ローカルディスクからビデオを取得してウェブページでお気に入りのクリップを表示することもできますし、YouTubeチャンネルのビデオURLをそのまま使用して同じ結果を得ることもできます。後者の方がより魅力的なので、アップロードしたビデオと同じYouTubeの埋め込みコードを使用し、開始と終了のタイムスタンプを渡すことにします。このようにして、検索した正確なビデオセグメントが表示されるようになります。少し注意点として、YouTubeの埋め込みコードは、開始および終了パラメータに整数値のみをサポートしているため、これらの値を丸める必要があります。

<pre><code class="bash">starts_int = [int(f) for f in starts]
ends_int = [int(f) for f in ends]
</code></pre


作成するFlaskアプリのファイルに渡せるよう、入力したクエリと一緒にこれらのリストを素早くシリアライズ(pickle)しておきましょう。これは、これから作成するFlaskアプリのファイルにパラメータを渡す際に役立ちます。

<pre><code class="python">with open("lists.pkl", "wb") as f:
    pickle.dump((starts_int, ends_int, query), f)
</code></pre>

これで、Flaskを使用してこれらのパラメータを渡す設定がすべて整いました。

Flaskアプリを作成する手順

1. 新しいFlaskプロジェクトの作成: プロジェクト用の新しいディレクトリを作成し、Flaskアプリのメインファイルとなる新しいPythonファイルを作成します。

<pre><code class="bash">mkdir my_flask_app
cd my_flask_app
touch app.py
</code></pre>

Flaskアプリファイルとテンプレートの準備ができると、ディレクトリ構造は次のようになります。

<pre><code class="markdown">my_flask_app/
│   app.py
│   ml.mp4
│
└───templates/
    │   index.html
</code></pre>

アップロードするビデオファイルを `my_flask_app` ディレクトリ内に保存しておきます。

2. Flaskアプリのコードを作成する: **app.py** ファイルに、Flaskアプリのコードを記述する必要があります。以下は、Jinja2テンプレートを使用して、タイムスタンプのリストが使用されている 'index.html' ファイルをレンダリングするFlaskアプリです。

<pre><code class="python">from flask import Flask, render_template
import pickle

with open("lists.pkl", "rb") as f:
    starts, ends, query = pickle.load(f)

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html", starts=starts, ends=ends, query=query)

if __name__ == "__main__":
    app.run(debug=True)
</code></pre

3. templatesディレクトリの作成: FlaskでJinja2テンプレートを使用するには、Flaskアプリと同じディレクトリに **templates** ディレクトリを作成する必要があります。このディレクトリにJinja2テンプレートを保存します。

<pre><code class="bash">mkdir templates
</code></pre>

パズルの最後のピースは、検索クエリに一致したすべてのビデオセグメントを表示する `index.html` ページです。HTMLファイルを作成する前に、私のYouTubeチャンネルから「ビデオの埋め込み」コードを素早く取得しましょう。

4. Jinja2テンプレートの作成: Jinja2テンプレートを作成するには、**templates** ディレクトリにHTMLファイルを作成する必要があります。

<pre><code class="bash">touch index.html
</code></pre>

以下は、Jinja2テンプレートのシンプルな例です。HTMLファイル内に、アプリファイルから渡されたリストとクエリ文字列を反復処理できるようにするコードが組み込まれています。

<pre><code class="language-html"><html>
  <head>
    <style>
      body {
        background-color: #F2F2F2;
        font-family: Arial, sans-serif;
        text-align: center;
      }
      h1 {
        margin-top: 40px;
      }
      .video-container {
        display: flex;
        flex-wrap: wrap;
        padding: 40px;
        justify-content: center;
      }
      .video-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 50%;
        height: 600px;
        margin: 20px;
        text-align: center;
      }
      .video-item iframe {
        width: 80%;
        height: 380px;
        margin: 20px;
      }
      .video-item p {
        font-size: 16px;
        margin-top: 10px;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <h1>My Favorite Scenes</h1>
    <div class="video-container">
      {% for i in range(starts|length) %}
        <div class="video-item">
          <iframe width="560" height="315" src="https://www.youtube.com/embed/hdZ_tNtdB4c?start={{ starts[i] }}&amp;end={{ ends[i] }}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in- picture" allowfullscreen></iframe>
          <p>Start: {{ starts[i] }} | End: {{ ends[i] }}</p>
          <p>Query: {{ query }}</p>
        </div>
      {% endfor %}
    </div>
  </body>
</html>
</code></pre>

パーフェクト!Jupyter Notebookの最後のセルを実行しましょう。

<pre><code class="python">%run app.py
</code></pre>

すべてが期待通りに進んでいることを示す、以下のような出力が表示されるはずです😊:

URLリンク http://127.0.0.1:5000 をクリックすると、検索クエリに応じて、以下のように出力されます。

ビデオを再生すると、指定したタイムスタンプに従って再生され、ビデオ内で探し出したかった特定の瞬間やセグメントが強調表示されます。

Jupyter Notebookを含むフォルダと、手元のコンピュータでローカルにチュートリアルを実行するために必要なすべてのファイルへのリンクはこちらです - https://tinyurl.com/twelvelabs

探求してほしい楽しいアクティビティ:

  1. 検索オプション(visual、conversation、text-in-video)のさまざまな組み合わせを試してみて、結果がどのように変化するかを確認してください。

  2. 複数のビデオをアップロードし、それに応じてコードを調整して、これらすべてのビデオを同時に検索できるようにします。

  3. プロの開発者スキルを発揮して、ユーザーが `index.html` ページからクエリを入力し、リアルタイムで結果を取得できるようにコードを強化します。

‍次にすること

次回の記事では、演算子のセットを使用して複数のシンプルなクエリを組み合わせたり、ビデオのコレクション全体を検索したりする方法について詳しく説明します。今後の投稿を楽しみにしていてください!

最後に、マルチモーダル基盤モデルに関心を共有する同志たちとつながるために、当社のDiscordコミュニティにぜひご参加ください。アイデアを交換したり、質問したり、お互いから学び合ったりするのに最適な場所です!

最近、Twelve LabsはGTC 2023におけるマルチモーダルAIのパイオニアとして取り上げられました。GTC 2023のビデオを見ている時、共同創業者のSoyoungが、自社が紹介されているセグメントを探すのに苦労して髪をかきむしっているのを目にしました。この経験から、自社のAPIを使ってビデオ内を検索するという課題に取り組むモチベーションが湧きました。そこで、今週末の楽しさ満載の個人プロジェクトのチュートリアルとして、Twelve Labsの検索APIを使用してビデオ内の特定の瞬間を見つけるプロセスをご案内します。

                     ビデオ内の検索をより快適にするためのシンプルな設計図😎

はじめに

Twelve Labsは、ビデオ理解のパワーを活用したアプリケーションの作成を支援するために設計された、一連のAPIの形でマルチモーダル基盤モデルを提供しています。このブログ記事では、Twelve Labs APIを使用して、自然言語のクエリでビデオ内の特定の興味深い瞬間をシームレスに見つける方法を探ります。大学院時代に作成した、「Machine Learning is Everywhere(機械学習はどこにでもある)」というタイトルの面白いビデオをローカルドライブからアップロードします。その名の通り、この80秒のビデオは、生活のあらゆる側面におけるMLの偏在性を示しています。このビデオでは、プロの卓球選手がKukaロボットと対戦する様子や、スケートボードのトリックを行う人物のイベント要約にMLが使用されている様子などのMLアプリケーションが紹介されています。Twelve Labs APIの力を借りて、シンプルな自然言語クエリを使用してビデオ内の特定のシーンを見つける方法をデモします。

このチュートリアルでは、シンプルな検索APIについての緩やかな導入を目的としているため、シングルビデオ内の瞬間を検索することに焦点を当て、軽量でユーザーフレンドリーなFlaskフレームワークを使用してデモアプリを作成するという、最小限の構成にしています。しかし、このプラットフォームは、数百、あるいは数千のビデオのアップロードに対応し、それらの中から特定の瞬間を見つけ出せるようにスケールする十分な機能を備えています。それでは、本格的な楽しみに向けて飛び込んでいきましょう!

クイックオーバービュー

  • 前提条件: Twelve Labs APIスイートを使用するためのサインアップと、デモアプリケーションを作成するために必要なパッケージのインストール

  • ビデオのアップロード: 素晴らしいビデオをお持ちですか?それらをTwelve Labsプラットフォームに送信すれば、効率的にインデックスが作成され、検索体験が格段に簡単になります!

  • セマンティックビデオ検索: シンプルな自然言語クエリで、ビデオ内の思い出の瞬間を探し出します

  • デモアプリの作成: Flaskを使用してHTMLテンプレートをレンダリングする便利なPythonスクリプトを作成し、検索結果を表示する洗練されたHTMLページをデザインします

💡 ところで、開発者ではない方がこの記事を読まれていても、心配は無用です!Jupyter Notebookのすぐに使えるリンクを用意しており、プロセス全体を実行して結果を得ることができます。さらに、コードを1行も書かずにセマンティックビデオ検索の力を体験できる弊社のPlaygroundもぜひお試しください。無料クレジットが必要な場合は、お気軽にご連絡ください😄

前提条件

このチュートリアルでは、Jupyter Notebookを使用します。ローカルコンピューターにすでにJupyter、Python、およびPipがセットアップされていることを前提としています。何か問題が発生した場合は、最も迅速にレスポンスが可能な弊社のDiscordサーバーでお気軽にご相談ください 🚅🏎️⚡️。Discordがお好みに合わない場合は、メールでお問い合わせいただくこともできます。Twelve Labsのアカウントを作成すると、APIダッシュボードにアクセスしてAPIキーを取得できます。このデモでは既存のアカウントを使用します。API呼び出しを行うには、シークレットキーを使用し、API URLを指定するだけです。さらに、環境変数を使用して、アプリケーションに設定の詳細を渡すことができます。

<pre><code class="bash">%env API_KEY=<your_API_key>
%env API_URL=https://api.twelvelabs.io/v1.1
</code></pre>


依存関係のインストール:

<pre><code class="python">pip install requests
pip install flask
</code></pre>

ビデオのアップロード

最初のステップとして、ビデオ理解機能を活用するために、ローカルコンピューターからTwelve Labsプラットフォームにビデオをアップロードした方法を解説します。

インポート:

<pre><code class="python">import os
import requests
import glob
from pprint import pprint
</code></pre

以下のようにして、APIのURLとAPIキーを取得します。

<pre><code class="python">API_URL = os.getenv("API_URL")
assert API_URL
</code></pre

<pre><code class="python">API_KEY = os.getenv("API_KEY")
assert API_KEY
</code></pre

Index API

次のステップでは、Index APIを使用してビデオインデックスを作成します。ビデオインデックスは、1つ以上のビデオをグループ化し、いくつかの共通の検索プロパティを設定する方法であり、これにより、インデックスにアップロードされたビデオに対してセマンティック検索を実行できるようになります。

インデックスは以下のフィールドによって定義されます:

<ul>
<li><b>名前</b></li>
<li><b>エンジン</b> - 現在、ビデオ理解のための最新のマルチモーダル基盤モデルであるMarengo2を提供しています</li>
<li>1つ以上の<b>インデックス作成オプション</b>:</li>
<ul>
<li><b>visual</b>: このオプションを選択すると、APIサービスはビデオのマルチモーダルな視覚・音響分析を実行し、オブジェクト、アクション、音、動き、場所、状況に応じたイベント、および複雑な視覚・音響テキストの説明によって検索できるようになります。ビジュアル検索の例としては、大歓声の群衆やオフィスを去る疲れ切った開発者などが挙げられます😆。</li>
<li><b>conversation</b>: このオプションを選択すると、APIはビデオから説明(書き起こしテキスト)を抽出し、そのテキストに対してセマンティックな自然言語処理(NLP)分析を実行します。これにより、検索している会話が行われているビデオ内の正確な瞬間を特定できます。インデックス化されたビデオ内で行われる会話を検索する例としては、兄弟に嘘をついた瞬間などが挙げられます😜。</li>
<li><b>text_in_video</b>: このオプションを選択すると、APIサービスは文字認識(OCR)を実行し、標識、ラベル、字幕、ロゴ、プレゼンテーション、ドキュメントなど、ビデオ内に表示されるテキストを検索できるようにします。この場合、サッカーの試合中に登場するブランドなどを検索することができます🏈。</li></ul></ul>

インデックスの作成:

<pre><code class="python"># `/indexes` エンドポイントの URL を構築します
INDEXES_URL = f"{API_URL}/indexes"

# インデックスの名前を指定します
INDEX_NAME = "My University Days"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}

# data という名前の辞書を宣言します
data = {
    "engine_id": "marengo2",  
    "index_options": ["visual", "conversation", "text_in_video"], 
    "index_name": INDEX_NAME,
}

# インデックスを作成します
response = requests.post(INDEXES_URL, headers=headers, json=data)

# インデックスの一意の識別子を保存します
INDEX_ID = response.json().get('_id')

# ステータスコードとレスポンスをプリントします
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

ビデオをアップロードするためのTask API

Twelve Labs プラットフォームは、作成されたインデックスにビデオをアップロードし、アップロードプロセスのステータスを監視するためのTask APIを提供しています。

<pre><code class="python">TASKS_URL = f"{API_URL}/tasks"

file_name = "Machine Learning is Everywhere" # インデックス化されたビデオはこのファイル名になります
file_path = "ml.mp4" # アップロードするビデオのファイル名
file_stream = open(file_path,"rb")

data = {
    "index_id": INDEX_ID, 
    "language": "en"
}

file_param = [
    ("video_file", (file_name, file_stream, "application/octet-stream")),]

response = requests.post(TASKS_URL, headers=headers, data=data, files=file_param
</code></pre

ビデオをアップロードすると、システムは自動的にビデオ・インデックス作成プロセスを開始します。Twelve Labsは、「ビデオ・インデックス作成」の概念について、マルチモーダル基盤モデルを使用して時間的コンテキストを組み込み、動き、オブジェクト、音、画面上のテキスト、ビデオからの音声などの情報を抽出して、強力なビデオ埋め込み(embeddings)を生成することだと説明しています。これにより、日常的な言葉を使ってビデオ内の特定の瞬間を見つけたり、指定されたラベルやプロンプトに基づいてビデオセグメントを分類したりすることが可能になります。

ビデオ・インデックス作成プロセスの監視:

<pre><code class="python">import time

# 開始時間を定義します
start = time.time()
print("Start uploading video")

# インデックスプロセスを監視します
TASK_STATUS_URL = f"{API_URL}/tasks/{TASK_ID}"
while True:
    response = requests.get(TASK_STATUS_URL, headers=headers)
    STATUS = response.json().get("status")
    if STATUS == "ready":
        print(f"Status code: {STATUS}")
        break
    time.sleep(10)

# 終了時間を定義します
end = time.time()
print("Finish uploading video")
print("Time elapsed (in seconds): ", end - start)
</code></pre

<pre><code class="python"># ビデオの一意の識別子を取得します
VIDEO_ID = response.json().get('video_id')

# ステータスコード、ビデオの一意の識別子、
およびレスポンスをプリントします
print(f"VIDEO ID: {VIDEO_ID}")
pprint(response.json())
</code></pre

<pre><code class="language-plaintext">VIDEO ID: 642621ffffa3551fb6d2f###
{'_id': '642621fc3205dc8a48ba8###',
 'created_at': '2023-03-30T23:57:48.877Z',
 'estimated_time': '2023-03-30T23:59:58.312Z',
 'index_id': '###621fb7b1f2230dfcd6###',
 'metadata': {'duration': 80.32,
              'filename': 'Machine Learning is Everywhere',
              'height': 720,
              'width': 1280},
 'status': 'ready',
 'type': 'index_task_info',
 'updated_at': '2023-03-31T00:00:34.412Z',
 'video_id': '642621ffffa3551fb6d2f###'}
</code></pre

アプリに既存インデックスの一意の識別子を渡すための別の環境変数を作成します:

<pre><code class="python">%env ENV_INDEX_ID = ###621fb7b1f2230dfcd6###
</code></pre

こちらはインデックス内のすべてのビデオの一覧です。今回はシンプルにするためにビデオを1つだけインデックス化しましたが、無料クレジットを使用すれば最大10時間分のビデオコンテンツをアップロードできます。

<pre><code class="python"># 既存のインデックスの一意の識別子を取得します
INDEX_ID = os.getenv("ENV_INDEX_ID")

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY,
}

# インデックス内のすべてのビデオをリストします
INDEXES_VIDEOS_URL = f"{API_URL}/indexes/{INDEX_ID}/videos"
response = requests.get(INDEXES_VIDEOS_URL, headers=headers)
print(f'Status code: {response.status_code}')
pprint(response.json())
</code></pre

<pre><code class="python">Status code: 200
{'data': [{'_id': '642621ffffa3551fb6d2f###',
           'created_at': '2023-03-30T23:57:48Z',
           'metadata': {'duration': 80.32,
                        'engine_id': 'marengo2',
                        'filename': 'Machine Learning is Everywhere',
                        'fps': 25,
                        'height': 720,
                        'size': 11877525,
                        'width': 1280},
           'updated_at': '2023-03-30T23:57:51Z'}],
 'page_info': {'limit_per_page': 10,
               'page': 1,
               'total_duration': 80.32,
               'total_page': 1,
               'total_results': 1}}
</code></pre

セマンティックビデオ検索、すなわち特定の瞬間の検索

システムがビデオのインデックス作成とビデオ埋め込みの生成を完了すると、これらを活用して検索APIで特定の瞬間を見つけることができます。このAPIは、入力したクエリのセマンティックな意味に対応する、関連ビデオ内の正確な開始・終了タイムコードを特定します。選択したインデックスの作成オプションに応じて、セマンティックビデオ検索に同じオプションのサブセットから選択できます。例えば、インデックスのすべてのオプションを有効にした場合、視覚・聴覚的な検索、会話の検索、ビデオ内に表示されるテキストの検索が可能になります。インデックスレベルと検索レベルの両方で同じオプションのセットを提供する理由は、ビデオコンテンツの分析にプラットフォームをどのように利用したいか、また現在のコンテキストに適したオプションを組み合わせてビデオコンテンツ全体をどのように検索したいかを柔軟に決定できるようにするためです。

まずは、シンプルな自然言語クエリ「a guy doing a trick on a skateboard(スケートボードでトリックを決める男)」を使ったビジュアル検索から始めましょう。

<pre><code class="bash">Status code: 200
{'data': [{'confidence': 'high',
           'end': 49.34375,
           'metadata': [{'type': 'visual'}],
           'score': 83.24,
           'start': 41.65625,
           'video_id': '642621ffffa3551fb6d2f###'}],
 'page_info': {'limit_per_page': 10,
               'page_expired_at': '2023-03-31T22:41:42Z',
               'total_results': 1},
 'search_pool': {'index_id': '642621fb7b1f2230dfcd####',
                 'total_count': 1,
                 'total_duration': 80}}
</code></pre

対応するビデオセグメント:

この機能は、モデルがビデオコンテンツを人間のように理解している様子を示しているため、とても興奮します。上のスクリーンショットでわかるように、システムは抽出したかった正確な瞬間を見事に特定しています。

別のクエリとして「a guy playing table tennis with a robotic arm(ロボットアームと卓球をする男)」を試して、システムがさらに魔法のような力を発揮する様子を見てみましょう。

<pre><code class="python"># `/search` エンドポイントの URL を構築します
SEARCH_URL = f"{API_URL}/search/"

# リクエストのヘッダーを設定します
headers = {
    "x-api-key": API_KEY
}
query = "a guy playing table tennis with a robotic arm"
# `data` という名前の辞書を宣言します
data = {
    "query": query,  # 検索クエリを指定します
    "index_id": INDEX_ID,  # インデックスの一意の識別子を指定します
    "search_options": ["visual"],  # 検索オプションを指定します
}

# 検索リクエストを行います
response = requests.post(SEARCH_URL, headers=headers, json=data)
print(f"Status code: {response.status_code}")
pprint(response.json())
</code></pre


出力:

<pre><code class="python">{'data': [{'confidence': 'high',
           'end': 14.6875,
           'metadata': [{'type': 'visual'}],
           'score': 90.62,
           'start': 9.75,
           'video_id': '642621ffffa3551fb6d2####'},
          {'confidence': 'high',
           'end': 9.75,
           'metadata': [{'type': 'visual'}],
           'score': 89.74,
           'start': 4.5,
           'video_id': '642621ffffa3551fb6d2####'}],
</code></pre


対応するビデオセグメント:

ビンゴ!今回もシステムは魅力的な瞬間を正確に特定しました。

💡ここで、楽しいミニ課題です。"a breakthrough in machine learning would be worth ten Microsofts(機械学習におけるブレイクスルーはマイクロソフト10社分の価値がある)"を検索し、検索オプションを `["text_in_video"]` に設定してみてください。

これらのJSONレスポンスを、開始点と終了点を手動で確認することなく最大限に活用するには、愛情込めて作られた素晴らしいインデックスページが必要です。そうすれば、検索リクエストのJSON出力を対応するビデオに直接送信できます。さあ、取りかかりましょう!

デモアプリの作成

この素晴らしいビデオ理解の冒険にお付き合いいただきありがとうございます 🎉🥳👏!最後のステップに到達しました。ここでは、Flaskベースのシンプルなアプリを作成し、前のステップの検索結果を取得して美しいウェブページに表示し、リクエストした正確な瞬間を披露します。ちなみに、私はデータサイエンスのバックグラウンドがあり、Pythonが大好きなのでFlaskを選びました。さらに、Flaskは軽量なPythonベースのフレームワークであり、このチュートリアルのニーズに合致しています。もちろん、お好みに合わせて他のフレームワークを選んでいただいてもかまいません。


最初のステップは、Jupyter Notebookに必要なインポートを行うことです。

<pre><code class="python">import json
import pickle
</code></pre


検索APIから収集したすべての開始および終了タイムスタンプを保持する、"starts "と "ends "という2つのリストを生成します。

<pre><code class="python">data = response.json()

starts = []
ends = []

for item in data['data']:
    starts.append(item['start'])
    ends.append(item['end'])

print("starts:", starts)
print("ends:", ends)
</code></pre

<pre><code class="bash">starts: [34.90625, 0, 62.5625]
ends: [39.21875, 4.5, 65.90625]
</code></pre


必要なタイムスタンプを取得し、同じビデオを自身のYouTubeチャンネルにアップロードしたので、それらを使用する方法はいくつかあります。ローカルディスクからビデオを取得してウェブページでお気に入りのクリップを表示することもできますし、YouTubeチャンネルのビデオURLをそのまま使用して同じ結果を得ることもできます。後者の方がより魅力的なので、アップロードしたビデオと同じYouTubeの埋め込みコードを使用し、開始と終了のタイムスタンプを渡すことにします。このようにして、検索した正確なビデオセグメントが表示されるようになります。少し注意点として、YouTubeの埋め込みコードは、開始および終了パラメータに整数値のみをサポートしているため、これらの値を丸める必要があります。

<pre><code class="bash">starts_int = [int(f) for f in starts]
ends_int = [int(f) for f in ends]
</code></pre


作成するFlaskアプリのファイルに渡せるよう、入力したクエリと一緒にこれらのリストを素早くシリアライズ(pickle)しておきましょう。これは、これから作成するFlaskアプリのファイルにパラメータを渡す際に役立ちます。

<pre><code class="python">with open("lists.pkl", "wb") as f:
    pickle.dump((starts_int, ends_int, query), f)
</code></pre>

これで、Flaskを使用してこれらのパラメータを渡す設定がすべて整いました。

Flaskアプリを作成する手順

1. 新しいFlaskプロジェクトの作成: プロジェクト用の新しいディレクトリを作成し、Flaskアプリのメインファイルとなる新しいPythonファイルを作成します。

<pre><code class="bash">mkdir my_flask_app
cd my_flask_app
touch app.py
</code></pre>

Flaskアプリファイルとテンプレートの準備ができると、ディレクトリ構造は次のようになります。

<pre><code class="markdown">my_flask_app/
│   app.py
│   ml.mp4
│
└───templates/
    │   index.html
</code></pre>

アップロードするビデオファイルを `my_flask_app` ディレクトリ内に保存しておきます。

2. Flaskアプリのコードを作成する: **app.py** ファイルに、Flaskアプリのコードを記述する必要があります。以下は、Jinja2テンプレートを使用して、タイムスタンプのリストが使用されている 'index.html' ファイルをレンダリングするFlaskアプリです。

<pre><code class="python">from flask import Flask, render_template
import pickle

with open("lists.pkl", "rb") as f:
    starts, ends, query = pickle.load(f)

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html", starts=starts, ends=ends, query=query)

if __name__ == "__main__":
    app.run(debug=True)
</code></pre

3. templatesディレクトリの作成: FlaskでJinja2テンプレートを使用するには、Flaskアプリと同じディレクトリに **templates** ディレクトリを作成する必要があります。このディレクトリにJinja2テンプレートを保存します。

<pre><code class="bash">mkdir templates
</code></pre>

パズルの最後のピースは、検索クエリに一致したすべてのビデオセグメントを表示する `index.html` ページです。HTMLファイルを作成する前に、私のYouTubeチャンネルから「ビデオの埋め込み」コードを素早く取得しましょう。

4. Jinja2テンプレートの作成: Jinja2テンプレートを作成するには、**templates** ディレクトリにHTMLファイルを作成する必要があります。

<pre><code class="bash">touch index.html
</code></pre>

以下は、Jinja2テンプレートのシンプルな例です。HTMLファイル内に、アプリファイルから渡されたリストとクエリ文字列を反復処理できるようにするコードが組み込まれています。

<pre><code class="language-html"><html>
  <head>
    <style>
      body {
        background-color: #F2F2F2;
        font-family: Arial, sans-serif;
        text-align: center;
      }
      h1 {
        margin-top: 40px;
      }
      .video-container {
        display: flex;
        flex-wrap: wrap;
        padding: 40px;
        justify-content: center;
      }
      .video-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 50%;
        height: 600px;
        margin: 20px;
        text-align: center;
      }
      .video-item iframe {
        width: 80%;
        height: 380px;
        margin: 20px;
      }
      .video-item p {
        font-size: 16px;
        margin-top: 10px;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <h1>My Favorite Scenes</h1>
    <div class="video-container">
      {% for i in range(starts|length) %}
        <div class="video-item">
          <iframe width="560" height="315" src="https://www.youtube.com/embed/hdZ_tNtdB4c?start={{ starts[i] }}&amp;end={{ ends[i] }}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in- picture" allowfullscreen></iframe>
          <p>Start: {{ starts[i] }} | End: {{ ends[i] }}</p>
          <p>Query: {{ query }}</p>
        </div>
      {% endfor %}
    </div>
  </body>
</html>
</code></pre>

パーフェクト!Jupyter Notebookの最後のセルを実行しましょう。

<pre><code class="python">%run app.py
</code></pre>

すべてが期待通りに進んでいることを示す、以下のような出力が表示されるはずです😊:

URLリンク http://127.0.0.1:5000 をクリックすると、検索クエリに応じて、以下のように出力されます。

ビデオを再生すると、指定したタイムスタンプに従って再生され、ビデオ内で探し出したかった特定の瞬間やセグメントが強調表示されます。

Jupyter Notebookを含むフォルダと、手元のコンピュータでローカルにチュートリアルを実行するために必要なすべてのファイルへのリンクはこちらです - https://tinyurl.com/twelvelabs

探求してほしい楽しいアクティビティ:

  1. 検索オプション(visual、conversation、text-in-video)のさまざまな組み合わせを試してみて、結果がどのように変化するかを確認してください。

  2. 複数のビデオをアップロードし、それに応じてコードを調整して、これらすべてのビデオを同時に検索できるようにします。

  3. プロの開発者スキルを発揮して、ユーザーが `index.html` ページからクエリを入力し、リアルタイムで結果を取得できるようにコードを強化します。

‍次にすること

次回の記事では、演算子のセットを使用して複数のシンプルなクエリを組み合わせたり、ビデオのコレクション全体を検索したりする方法について詳しく説明します。今後の投稿を楽しみにしていてください!

最後に、マルチモーダル基盤モデルに関心を共有する同志たちとつながるために、当社のDiscordコミュニティにぜひご参加ください。アイデアを交換したり、質問したり、お互いから学び合ったりするのに最適な場所です!