AI Dev Lab
WhatCam

WhatCam ができるまで — CLIP のゼロショット画像分類をブラウザで動かす

CLIP (Xenova/clip-vit-base-patch32) を transformers.js の CDN ESM で side-load し、 写真と候補ラベルの近さをブラウザ内で計算。 日本語ラベルを英語プロンプトに変換し、 図鑑と自由入力の両モードで「これ何?」を判定する設計記録。

#WhatCam ができるまで — CLIP のゼロショット画像分類で「これ何?」をブラウザだけで当てる

WhatCam は、写真を選ぶかカメラで写すだけで AI が「これは何か」を判定 し、候補の中でいちばん近いものと一致度を出す Webサービスです。画像認識モデル CLIP を transformers.js で読み込み、写真とことばの候補の近さをブラウザの中で計算します。動物・料理・果物・乗り物などの図鑑から選ぶモードと、候補のことばを自分で決めるゼロショット判定モードの2本立て。画像はサーバーに送りません。

#なぜこの形にしたか

普通の画像分類モデルは「学習した1000クラスのどれか」しか答えられません。CLIP が面白いのは、その場で渡した任意のことば と写真の近さを測れる点です。「犬 / 猫 / きつね」でも「本物 / 偽物」でも「晴れ / 曇り / 雨」でも、事前学習なしに候補にできる — これがゼロショット分類です。

「これ何?」という素朴な問いは、子どもから大人まで刺さる入口です。図鑑モードで手軽に試せて、慣れたら自分で候補を作って AI の見え方を覗ける、という二段構えにしました。

#見た目の方針

観察図鑑 / ハーバリウム (植物標本) のフィールドガイド。

  • セージがかった紙 (#eef1e6) に、植物学グリーン (#3a6b4d) と標本ラベルの朱 (#c0452e) と真鍮 (#9a7b3f)
  • 学名を思わせるセリフ体イタリックで結果を見せる
  • 「観察して名前をつける」という題材から引いた、緑主役のパレット。既存の紙系 (和紙・グレー・ラベンダー) とは別アクセント

#実装の見どころ

#1. 日本語ラベルを英語プロンプトに変換して CLIP に渡す

CLIP は英語で学習しているので、日本語ラベルをそのまま渡すと精度が落ちます。そこで 表示は日本語・判定は英語 に分けました。図鑑の各項目は日本語名と英語プロンプトのペアで持ち、CLIP には英語だけを渡します。

ts
{ ja: "犬", en: "dog" }
{ ja: "ラーメン", en: "ramen noodles" }
{ ja: "救急車", en: "ambulance" }

transformers.js のゼロショット画像分類パイプラインは、候補ラベルを This is a photo of {} のテンプレートに差し込んでスコアリングします。返ってきたスコアを日本語名に戻して一致度バーで見せます。

ts
const out = await classifier(imageURL, candidateLabels, {
 hypoジャンル_template: "This is a photo of {}.",
})
// out: [{ label, score }, ...] を softmax 済みで降順に

自由入力モードでは、全図鑑項目から作った日本語→英語の辞書で、打った日本語をできる範囲で英語に変換します。辞書に無い語は英単語で入れると精度が上がる、と画面で案内しています。

#2. 図鑑モードと自由入力モードで候補セットを切り替える

候補の作り方だけがモードで変わり、判定ロジックは共通です。カテゴリを絞ると候補が少なくなるぶん、当たりやすくなります。

ts
// 図鑑: カテゴリの項目 or「おまかせ」の横断セット
// 自由: カンマ区切りの入力を辞書変換して候補化 (2語以上)

#3. 写真アップロードとカメラ撮影の両対応

getUserMedia で背面カメラを開き、フレームをキャンバスに焼いてそのまま判定に回します。カメラが使えない端末では、そっと「写真をアップしてください」に倒れるようにしています。

#苦労したところ

  • 手描きのサンプルを CLIP が誤判定した: 最初、お手本に「りんご」と「車」をコードで描きました。ところが CLIP は りんごを トマト 63%、車を タクシー/トラック と判定。しかも車のほうは全車種にスコアが割れて勝者がはっきりしませんでした。原因は深度推定サービスと同じで、合成の手描きイラストはモデルが期待どおりに反応しないこと。赤くて丸い絵はトマトとも解釈できてしまう。

そこで、モデルが確実に見分けられる 特徴的な形 に差し替えました。実際にプレビューでモデルを一度読み込むと、以降はブラウザにキャッシュされて何度でも試せるので、各サンプルの実際の出力を見ながら選びました。結果、バナナ 71% / ひまわり 91% / 傘 89% と、はっきり正解する題材に落ち着きました。三日月・放射状の花・ドーム状の傘は、色ではなく輪郭で決まるので混同されにくい。本領はあくまで実際の写真で、と画面でも明示しています。

  • 自動テストは「正解クラス固定」だと脆い: ライブテストで「サンプル → 正解ラベル」を毎回断定すると、モデル次第で落ちます。そこで実測で安定した題材 (ひまわり・傘) だけ正解ラベルを検証し、それ以外は「一致度が降順に並んだ実分布」「画像の内容で勝者が変わる」= モデルが本当に画像を読んでいる、という不変量で検証する形にしました。

  • MDX 本文に < を書くと壊れる: これは別サービスの記事で踏んだ罠ですが、Set<number> のような表記を地の文にそのまま置くと、MDX が <number> をタグと解釈してページが500になります。コードは必ずコードブロックか行内コードに入れる、という運用に統一しました。

#今後の拡張

  • 多ラベル / 複数被写体: 1枚に複数の物がある写真で、上位を同時に提示
  • 図鑑カテゴリの拡充: 犬種・魚・鉱物など、より専門的な語彙セット
  • 結果の共有カード: 判定結果を画像カードにして共有

#このサービスから言える事

「AIに画像を認識させる」には専用の学習とサーバーが要る、という前提は、CLIP のような対照学習モデルがブラウザで動くようになって崩れました。WhatCam は、事前学習なしで任意のことばと写真の近さを測れる CLIP のゼロショット性を、日本語で・端末内で・図鑑と自由入力の両方で触れる形にしたものです。分類だけでなく検索・仕分け・重複検出の土台にもなる技術なので、この「近さを測る」仕組みは今後いろいろな入口になります。

[ ./next_action ]

読んだら、 WhatCam を実際に動かす。

この開発ログは WhatCam をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ToonCast

ToonCast ができるまで — AnimeGANv2 をブラウザで動かす

AnimeGANv2 の小さな ONNX (約9MB) を onnxruntime-web (単一スレッド WASM=COOP/COEP不要、 color-revive で承認済みライブラリの再利用) で実行。 512x512・[-1,1] 正規化で推論し、 結果を元解像度に戻して表示する設計記録。 写真は端末内処理。

read log →
ColorRevive

ColorRevive ができるまで — onnxruntime-web で白黒写真をカラー化

DeOldify の量子化 ONNX を onnxruntime-web (CDN side-load・単一スレッド WASM=COOP/COEP不要) で実行。 256x256 でモデル推論し、 輝度は元写真・色だけ AI を YCbCr で再合成して輪郭を保つ設計記録。 写真は端末内処理。

read log →
PhotoTwin

PhotoTwin ができるまで — CLIP画像埋め込みで似た写真を見つける

CLIP (Xenova/clip-vit-base-patch32) の image-feature-extraction を transformers.js の CDN ESM で side-load し、 各写真を正規化ベクトル化。 cosine 類似度で重複・似た写真をブラウザ内で検出する設計記録 (新ライブラリ追加なし=what-cam と同じ CLIP の再利用)。

read log →
AkinFind

AkinFind ができるまで — ブラウザ内で完結する embeddings 意味検索

多言語の文章埋め込みモデル (Xenova/multilingual-e5-small) を transformers.js の CDN ESM で side-load し、 各文を正規化ベクトル化。 cosine 類似度で意味検索と似ている文ペア検出を全て端末内で行う設計記録。

read log →
DepthCast

DepthCast ができるまで — 1枚の写真をAIの深度推定で立体にする

Depth Anything (transformers.js) を CDN ESM で side-load し、 1枚の写真から深度マップを推定。 WebGL2 フラグメントシェーダで深度に比例した視差 (iterative backward parallax) を作り、 赤青アナグリフ / WebM 書き出しまで端末内で完結させた設計記録。

read log →
BioWave

BioWave ができるまで — バイオリズムを正弦波で描く

バイオリズム占いの開発ログ。 生年月日からの経過日数をもとに身体 23 日・感情 28 日・知性 33 日の正弦波で調子を出し、 ゼロ交差を要注意日として拾い、 2 人の誕生日差から相性を計算するまでを記録しています。

read log →