
Article
Qwenで写真と動画をテキスト化し、人物ラベルまで検索に入れる
前回は、GX10 Galleryをどういう構成で作っているかを書いた。
今回は、実際に写真と動画をどうインデックス化しているかを書く。ポイントは、画像をQwenに説明させるだけで終わらせず、撮影日時、GPS由来の大まかな場所、動画の音声、人物ラベルまでまとめて、検索できる情報にしているところだ。
写真や動画は、そのままだと検索しにくい。ファイル名はカメラやスマホが付けた連番に近いし、内容を表していない。フォルダ名はイベントのヒントにはなるが、「誰が写っているか」「何をしているか」「どんな雰囲気か」まではわからない。
そこで、GX10 Galleryでは各メディアごとにAI用の観測情報を作ることにした。
1ファイルごとに観測情報を積む
写真は1枚ごと、動画は1本ごとにレコードを作る。イベントは、そのレコードのまとまりとして扱う。
各レコードには、だいたい次のような情報を持たせている。
metadata:
ファイル種別
撮影日時
解像度
動画の長さ
カメラ情報
vision:
Qwenによる説明文
検索タグ
動画キーフレームの説明
audio:
短い音声区間のSTT結果
location:
GPS由来の大まかな場所
people:
顔クラスタ
匿名化した人物ラベル
search:
SQLite FTS
embedding
これをSQLiteに入れて、FTSとembeddingで検索できるようにする。凝った分散DBを立てているわけではない。ローカルの単一ユーザーアプリなので、SQLiteの扱いやすさを優先した。
全体の流れはこんな感じだ。
写真はサムネイルをQwenに渡す
写真の場合は比較的素直だ。
元ファイルは触らず、まず表示用のサムネイルを作る。そのサムネイルをQwenに渡して、日本語の説明文と検索タグを作る。
最初のころは、説明文に少し変な表現が混ざることがあった。写真の内容説明ではなく、「動画編集素材として活用できる」のような素材レビューっぽい文章になってしまう。検索画面に出す説明としては邪魔なので、プロンプトと後処理を調整した。
検索用のテキストに必要なのは、派手なコピーではない。見えているものを素直に書いてほしい。誰が、どこで、何をしていて、どんな雰囲気か。それだけで十分に使える。
ただし、公開記事では実名や生のPeopleラベルは扱わない。内部の検索では家族メンバーのラベルを使えるようにしても、記事では人物A、人物B、子どものように丸める。この分離はかなり大事だ。
動画は全フレームを見ない
動画は写真より難しい。
全フレームをQwenに見せれば、理屈の上では細かく理解できる。ただ、家庭内動画を全部そうやって処理するのは現実的ではない。動画は数が多いし、長さもまちまちだ。フレーム単位で処理すると、処理時間も再実行の負荷も膨らみすぎる。
なので、現時点では次の情報を組み合わせている。
- 動画のメタデータ
- 代表キーフレーム
- 短い音声区間のSTT
- 撮影日時
- 大まかな場所
- 匿名化した人物ラベル
動画から数枚のキーフレームを切り出し、それぞれをQwenに見せる。さらに、動画の中から短い音声窓を取り出してSTTにかける。会話や反応が入っていると、映像だけではわからない文脈が拾える。
たとえば、見た目にはただ歩いているだけの動画でも、音声に「これ見て」「楽しいね」のような反応が入っていれば、イベント動画の候補として少し強く扱える。これは後の自動Vlog生成でも効いてくる。
EXIFとGPSは地味に効く
写真にはEXIFがある。撮影日時、カメラ、レンズ、場合によってはGPSも入っている。
この情報は地味だが、検索ではかなり効く。
撮影日時がわかると、イベント内の時系列を作れる。GPSがあれば、大まかな場所名に変換できる。カメラ情報があると、動画プロファイルの扱いにも使える。
GPSをそのまま緯度経度として検索画面や記事に出したいわけではない。むしろ公開記事では出すべきではない。アプリ内部では逆ジオコーディングして、検索に使える大まかな場所の観測情報として持つ。
そして重要なのは、場所の文脈が後から入ったらQwen説明文も更新することだ。
最初にQwenが写真を見た時点では、「店内で子どもが座っている」くらいしか言えない。でも、後から大まかな場所がわかれば、説明文にもその文脈を反映できる。もちろん、詳細すぎる住所や実イベント名を公開記事に出すことはしない。
このために、場所や人物ラベルが変わったら、Qwen説明文だけを再実行する仕組みを入れた。STTやメタデータ抽出を全部やり直す必要はない。変わった文脈だけを署名化して、説明文を更新する。
人物識別はQwenに名前を当てさせない
一番面白くて、一番気を使うのが人物識別だ。
Qwenだけに画像を見せても、基本的には「幼い子ども」「女性」「男性」のような一般的な表現になる。これはモデルとしては自然だ。外部の画像から個人名を当てるべきではないし、そもそも知らない。
でも家庭内のメディアライブラリでは、少なくとも家族メンバーについては、誰が写っているかで検索できると便利だ。
そこで、顔検出と顔特徴量は別の処理として行う。
流れはこう。
1. 写真や動画キーフレームから顔を検出する
2. 顔特徴量を取り、似た顔をクラスタリングする
3. People画面でクラスタにラベルを付ける
4. 同じラベルのクラスタを同一人物として扱う
5. 人物ラベルをメディアの観測情報に入れる
6. 必要に応じてQwen説明文を再実行する
ここで大事なのは、人物名をQwenに推測させているわけではないことだ。ラベルはユーザーが付ける。AIは顔の近さを使って候補を出すだけだ。
公開記事では実名を出さない。本文では人物A、人物B、子どものように匿名化する。スクリーンショットを使う場合も、顔とラベルは必ずぼかす。今回は安全側に倒して、実スクリーンショットは使わず、抽象図だけにした。
People画面を独立させた
最初は、各イベントページにPeopleのクラスタを出していた。
ただ、これはすぐに重くなった。顔サムネイルが多いイベントでは、メディアカードとPeopleグリッドが同じページに並び、ブラウザの負荷が上がる。候補を保存するたびにイベント全体を再読み込みするのも無駄だった。
そこで、Peopleは独立したメニューに分けた。
People画面では、未確定のクラスタ、候補付きのクラスタ、確定済みのクラスタをフィルタできる。候補の信頼度が高いものは、一括確定できるようにした。候補を押すと、そのクラスタにラベルを入れて保存する。
別クラスタでも同じラベルを付ければ、同じ人物として扱う。これは厳密な意味でモデルを学習させているというより、ラベル付きクラスタを統合している感覚に近い。以後の候補提示では、そのラベルが手がかりになる。
幼い子どもから人物Aへ
人物識別を後から入れると、Qwenの説明文も変えたくなる。
たとえば、最初の説明文では「明るい服を着た幼い子どもが椅子に座っている」となる。画像だけを見た説明としては自然だ。
でも、その顔が人物ラベルと結びついているなら、内部的には「人物Aが椅子に座っている」と扱える。検索でも、人物Aで探せる。
この順番は重要だった。
最初にQwenで説明を作り、その後に人物ラベルを付けると、検索用の説明文には一般名が残る。そこで、人物ラベルや場所の観測情報が更新された時点で、Qwen説明文も再実行するようにした。
これにより、単なる画像説明ではなく、ライブラリの文脈を持った説明になる。
検索はファイル名ではなく文脈を見る
検索結果には、写真や動画のプレビューを出す。
これは必須だった。テキスト検索だけで結果が出ても、メディアライブラリとしては弱い。写真はサムネイルで見たいし、動画はその場で再生したい。
検索語は自由だ。
水族館 ショー
夜 風船
キャンプ 料理
人物A 笑顔
公園 自転車
ファイル名ではなく、Qwen説明文、STT、場所、人物ラベル、タグを横断して探せる。家庭内メディアの検索としては、このくらい自然言語寄りの方が使いやすい。
さらに、イベントごとにMarkdown exportも作るようにした。Web UIは人間向け、HTTP APIはアプリ向け、MarkdownはAIエージェント向け、という棲み分けにしている。あとから別のエージェントに「このイベントの候補を見て」と渡すとき、Markdownがあると扱いやすい。
Renderロックは地味だけど必要
インデックスが途中のイベントで動画生成を押すと、中途半端な素材で動画ができてしまう。
そこで、イベント内の処理が終わっていない場合はRenderできないようにした。単に処理済みフラグが入っているかだけではなく、GPS由来の場所、人物ラベル、Qwen再説明の状態も見る。
場所や人物ラベルが後から入ったのに、Qwen説明文が古いままならRenderはロックされる。Web UI上では理由を表示し、APIでも409を返す。
これは地味だが、かなり重要だった。動画生成は目立つ機能なので、押せるボタンがあると押してしまう。処理途中なら押せない方がいい。
ここまでで見えてきたこと
画像説明だけでは足りない。
写真や動画を検索可能にするには、複数の情報を束ねる必要がある。
- Qwenが見た内容
- STTが拾った会話や反応
- EXIFの撮影日時
- GPSから得た大まかな場所
- 顔識別で付けた人物ラベル
- イベント単位の文脈
これらが揃うと、検索の精度だけでなく、説明文の質も上がる。単なる「子どもが座っている写真」ではなく、内部的には「人物Aが旅行イベントで座っている写真」として扱える。
もちろん、公開するときはこの情報をそのまま出さない。家族の実名、顔、詳細な地名、ファイル名、ローカル環境の情報は外に出さない。検索に必要な内部情報と、記事に書ける公開情報は分ける。
次回は、このインデックスを使ってイベント動画を自動生成する話を書く。Qwenでクリップ候補を見たり、STTで反応のある場面を拾ったり、人物ラベルを使って候補を加点したりする。検索のために作った観測情報が、そのまま動画生成の材料になる。


