▍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 →
ShadowTwin

立体パズルの別解を作る:同じ影と最少の積み木

ShadowTwinの開発記録。3方向の図から別々の最少配置を2つ探す仕組み、5問の独立全探索、スマホの横並び比較、保存・再編集と入力検査を解説します。実照明の計算や能力診断ではありません。

read log →
SlitCard

スリットアニメーションを作る:4コマの合成と透明原稿の設計

SlitCardの開発記録。4コマの帯を交互に合成し、すき間と黒帯で別のコマを見せる仕組みを解説。同縮尺の台紙・透明シートSVG、50mm確認線、手動プレビュー・初期非表示、再編集と実出力検査を整理します。

read log →
WindowLight

日当たりシミュレーションを作る:窓の光を床へ投影する幾何モデル

WindowLightの開発記録。太陽位置近似から窓の4隅を床へ投影し、凸多角形を部屋で切り取る設計を解説。国立天文台の16時刻との限定照合、大気差・低高度・実際の採光との違い、SVGと設定の再編集を整理します。

read log →
GridLights

電源構成の比較を作る:割合と実量を100個の電球で読み解く

電源構成比較GridLightsの開発記録。12か国の年次発電データを最大剰余法で100個の電球に変換し、割合とTWhを比較する設計を解説。欠損を0補完しない7分類、予想と実値、出典付きSVGと実データ検証を整理します。

read log →
SoundLoom

音のお絵かきを作る:実WAVから絵を測り直す設計

音のお絵かきの開発記録。描画を正弦波のPCMとWAVへ変換し、保存音声の周波数をGoertzel法で測って比較する仕組みを解説。音量・測定窓・量子化の限界と、編集・離脱時の古い結果を抑止する検証を整理します。

read log →
MazeLoom

宝探し迷路を作る:寄り道まで含めた最短ルートの探索

宝探し迷路の開発記録。通路を木として生成し、現在地と3つの宝の取得状態を組み合わせて最短ルートを探す仕組みを解説。問題コードの版固定、画面と解答付きPDFの一致、手数と印刷の検証を整理します。

read log →