7, 8年前に, CNNモデルを用いて金魚の品種判定をやっていたことがある. [1][2][3][4]
当時は金魚の画像データを集めモデル学習していたのだが, 品種によってはデータがあまり集まらなかったり, 見た目が割と似た感じの品種もあり, 結構判定の精度をあげるの苦労した覚えがある.
ここ1, 2年VLM(Vision Language Model)の進歩が凄まじいので, 久ぶりに金魚ネタで, VLMが金魚の検出及び品種の違いを見分けられるのかを試してみた.
1. 前提条件
VLMによる予備実験の条件は以下の通り.
モデル : gemini 3.5-flash
分類対象 : 22品種(従来と同じ)
テストデータ : 22品種について各1サンプル
2. 検出&分類
geminiによる金魚の検出及び分類を行うにあたり, LLMの構造化出力を利用するとともに, 以下のようなプロンプトを用いた.
[コードの一部]
# 構造化出力のためのデータクラス class NormalizedLLMResult(BaseModel): """LLMの生出力(0-1000正規化座標)を格納するデータクラス""" no: int = Field(..., description="番号(1から連番)") variety_name: str = Field(..., description="品種判定結果") position_x: int = Field(..., description="金魚の左上X座標(0-1000正規化スケール)") position_y: int = Field(..., description="金魚の左上Y座標(0-1000正規化スケール)") width: int = Field(..., description="金魚の幅(0-1000正規化スケール)") height: int = Field(..., description="金魚の高さ(0-1000正規化スケール)") rationale_for_variety: Optional[str] = Field(None, description="品種判定の根拠(LLMが出力した理由)") class NormalizedLLMDetectionResult(BaseModel): """1枚の画像から検出された金魚一覧(0-1000正規化座標)を格納するデータクラス""" goldfish: List[NormalizedLLMResult] = Field(..., description="画像内で検出された金魚のリスト(正規化座標)") # 金魚の品種リスト VARIETY_LIST: List[str] = [ "東錦(アズマニシキ)", "茶金(チャキン)", "珍珠鱗(チンシュリン)", "蝶尾(チョウビ)", "頂点眼(チョウテンガン)", "コメット", "出目金(デメキン)", "江戸錦(エドニシキ)", "浜錦(ハマニシキ)", "キャリコ", "ナンキン", "オランダ獅子頭(オランダシシガシラ)", "ランチュウ", "琉金(リュウキン)", "桜錦(サクラニシキ)", "青文魚(セイブンギョ)", "朱文金(シュブンキン)", "水泡眼(スイホウガン)", "丹頂(タンチョウ)", "土佐錦(トサニシキ)", "和金(ワキン)", "地金(ジキン)", ] def build_system_prompt() -> str: """金魚の品種リストを組み込んだシステムプロンプトを作成する""" variety_lines = "\n".join(f"- {name}" for name in VARIETY_LIST) return f"""あなたは金魚の品種鑑定のスペシャリストです。 【目的】 画像から金魚を抽出し、各金魚の品種、位置情報を特定することが目的です。 【品種リスト】 {variety_lines} 【出力ルール】 - 画像内に写っている金魚をすべて検出してください。 - variety_name は【品種リスト】の中から最も近いものを選んでください。リストにない金魚は「不明」としてください。 - position_x, position_y は、金魚を囲む矩形(バウンディングボックス)の左上角の座標です。画像の左上を原点(0, 0)、右下を(1000, 1000)とする0〜1000の正規化スケールで出力してください。画像の実際のピクセルサイズに関わらず、必ずこの0〜1000スケールを使用してください。 - width, height は、そのバウンディングボックスの幅と高さを同じ0〜1000正規化スケールで出力してください(実ピクセル値ではありません)。 - no は検出順に1から始まる連番を付与してください。 - 出力は指定されたスキーマ(JSON)に厳密に従ってください。""" SYSTEM_PROMPT = build_system_prompt() def build_human_content(image_path: str) -> List[Dict[str, Any]]: """画像を含む、人間メッセージ用のコンテンツを作成する""" image_b64, mime_type = encode_image_to_base64(image_path) instruction_text = ( "この画像に写っている金魚を検出し、" "各金魚の品種・位置(0-1000正規化座標) を" "システムプロンプトのルールに従ってJSONで出力してください。" ) return [ {"type": "text", "text": instruction_text}, {"type": "image_url", "image_url": f"data:{mime_type};base64,{image_b64}"}, ]
[動作確認]
File : ./data\azumanishiki-00077.jpg Drawing label for goldfish no 1: 東錦 image_file no variety_name is_known_variety_name position_x position_y width height rationale_for_variety 0 azumanishiki-00077.jpg 1 東錦(アズマニシキ) True 301 322 917 570 頭部に肉瘤が発達したオランダ型の体型に、赤・白・黒(青)のモザイク透明鱗(キャリコ柄)を持つ...
入力の東錦の画像に対し, 正しく金魚の位置を検出し, 品種も「東錦」と判定.
そして, 判定理由もそれなりにしっかりとした内容が出力された.

[実装上のポイント]
geminiは入力画像のサイズ(幅, 高さ)が大きいと画像をresizeして処理するようで, 当初Bounding Boxの表示位置がずれることがあった.
そこで, 矩形情報を0~1000の正規化スケールで出力してもらい, 後で元画像に対する矩形情報に変換するようにした.
3. 実験結果
残りの21品種について試した結果は, 以下の通り.
File : ./data\chakin-00074.jpg Drawing label for goldfish no 1: 茶金
正解

File : ./data\chinshurin-00064.jpg Drawing label for goldfish no 1: 珍珠鱗
正解

File : ./data\chobi-00074.jpg Drawing label for goldfish no 1: 蝶尾
正解

File : ./data\chotengan-00078.jpg Drawing label for goldfish no 1: 水泡眼
不正解, 正解は頂点眼

File : ./data\comet-00075.jpg Drawing label for goldfish no 1: コメット
正解

File : ./data\demekin-00073.jpg Drawing label for goldfish no 1: 出目金
正解

File : ./data\edonishiki-00077.jpg Drawing label for goldfish no 1: 江戸錦 Drawing label for goldfish no 2: 江戸錦

File : ./data\hamanishiki-00075.jpg Drawing label for goldfish no 1: 浜錦
正解

File : ./data\kyariko-00078.jpg Drawing label for goldfish no 1: キャリコ
正解

File : ./data\nankin-00080.jpg Drawing label for goldfish no 1: ランチュウ
不正解, 正解はナンキン

File : ./data\oranda-00078.jpg Drawing label for goldfish no 1: オランダ獅子頭 Drawing label for goldfish no 2: 出目金 Drawing label for goldfish no 3: 不明 Drawing label for goldfish no 4: 不明
正解

File : ./data\ranchu-00080.jpg Drawing label for goldfish no 1: ランチュウ
正解

File : ./data\ryukin-00056.jpg Drawing label for goldfish no 1: 琉金
正解

File : ./data\sakuranishiki-00072.jpg Drawing label for goldfish no 1: ランチュウ
不正解, 正解は桜錦

File : ./data\seibungyo-00074.jpg Drawing label for goldfish no 1: 青文魚
正解

File : ./data\shubunkin-00064.jpg Drawing label for goldfish no 1: 朱文金
正解

File : ./data\suihougan-00057.jpg Drawing label for goldfish no 1: 水泡眼
正解

File : ./data\tancho-00066.jpg Drawing label for goldfish no 1: 丹頂
正解

File : ./data\tosakin-00065.jpg Drawing label for goldfish no 1: 土佐錦
正解

File : ./data\wakin-00073.jpg Drawing label for goldfish no 1: 和金
正解

File : ./data\zikin-00075.jpg Drawing label for goldfish no 1: 和金
不正解, 正解は地金

image_file no variety_name is_known_variety_name position_x position_y width height rationale_for_variety 0 azumanishiki-00077.jpg 1 東錦(アズマニシキ) True 301 322 917 570 頭部に肉瘤が発達したオランダ型の体型に、赤・白・黒(青)のモザイク透明鱗(キャリコ柄)を持つ... 1 chakin-00074.jpg 1 茶金(チャキン) True 312 127 1073 656 茶褐色の体色に、鼻先に発達した一対の大きな赤い房(鼻房)を持つ特徴から、茶金(茶金花房)と判... 2 chinshurin-00064.jpg 1 珍珠鱗(チンシュリン) True 373 187 699 643 体がピンポン玉のように丸く、鱗の一枚一枚が真珠をはめ込んだように半球状に突出している特徴から... 3 chobi-00074.jpg 1 蝶尾(チョウビ) True 272 78 702 1015 左右に大きく突出した眼(出目)と、上から見たときに蝶が羽を広げたように左右対称に美しく広がる... 4 chotengan-00078.jpg 1 水泡眼(スイホウガン) True 456 293 818 644 目の下に大きな水泡状の袋(角膜の一部が肥大化したもの)が左右に発達している特徴から、水泡眼(... 5 comet-00075.jpg 1 コメット True 16 0 1277 788 細長い和金型の体型に、非常に長く伸びた尾鰭(吹き流し尾)を持ち、赤と白の更紗模様があることか... 6 demekin-00073.jpg 1 出目金(デメキン) True 421 293 790 641 左右に大きく飛び出した眼球が特徴的であり、赤一色の体色と丸みのある体型から出目金(デメキン)... 7 edonishiki-00077.jpg 1 江戸錦(エドニシキ) True 240 238 1144 724 背びれがなく、丸みを帯びた体型で、頭部に肉瘤が発達しており、体色が赤・白・黒のキャリコ柄(モ... 8 edonishiki-00077.jpg 2 江戸錦(エドニシキ) True 1324 104 243 359 画面右端に一部のみ写っていますが、背びれがなく、キャリコ柄の色彩パターンから江戸錦と推測されます。 9 hamanishiki-00075.jpg 1 浜錦(ハマニシキ) True 105 12 1541 1002 体全体が半球状に盛り上がったパールスケール(珍珠鱗)で覆われており、頭頂部に非常に発達した大... 10 kyariko-00078.jpg 1 キャリコ True 226 86 1268 1068 赤、黒、白(青)のモザイク透明鱗(キャリコ柄)を持ち、体型が丸く、長い尾びれと背びれが発達し... 11 nankin-00080.jpg 1 ランチュウ True 25 269 1320 713 背鰭がなく、丸みを帯びた体型と、白とオレンジの更紗模様が特徴です。頭部の肉瘤は比較的滑らかで... 12 oranda-00078.jpg 1 オランダ獅子頭(オランダシシガシラ) True 294 116 1175 853 頭部に発達した肉瘤(にくりゅう)があり、背びれが存在し、丸みを帯びた体型と長く伸びた尾びれを... 13 oranda-00078.jpg 2 出目金(デメキン) True 81 97 507 426 全体が黒く、突出した眼の特徴が見られるため、出目金と推測されます。 14 oranda-00078.jpg 3 不明 False 551 44 338 203 背景で大きくぼやけて写っているため、品種の特定が困難です。 15 oranda-00078.jpg 4 不明 False 1263 0 206 281 画面端に体の一部のみが写っているため、品種の特定が困難です。 16 ranchu-00080.jpg 1 ランチュウ True 197 307 796 466 背びれがなく、丸みを帯びた体型と頭部の肉瘤の特徴からランチュウと判断されます。 17 ryukin-00056.jpg 1 琉金(リュウキン) True 141 105 1456 1056 体型が丸く、背中が大きく盛り上がっており、長い尾鰭を持つ典型的な琉金の特徴を示しているため。 18 sakuranishiki-00072.jpg 1 ランチュウ True 95 109 1012 713 背びれがなく、丸みを帯びた卵型の体型と、頭部に肉瘤が発達している特徴からランチュウと判定しました。 19 seibungyo-00074.jpg 1 青文魚(セイブンギョ) True 57 91 1330 851 頭部に発達した肉瘤があり、背鰭を持つオランダ型の体型で、体色が特徴的な青灰色(渋い銀灰色)で... 20 shubunkin-00064.jpg 1 朱文金(シュブンキン) True 120 98 1076 544 体型が和金のように細長く、体色が赤・黒・白のキャリコ柄(三色)で、尾鰭が長く伸びている特徴か... 21 suihougan-00057.jpg 1 水泡眼(スイホウガン) True 394 98 562 505 目の下に大きな水泡(液体の入った袋)が左右に発達しており、背びれがない特徴から水泡眼と判定し... 22 tancho-00066.jpg 1 丹頂(タンチョウ) True 157 152 954 583 頭部に鮮やかな赤色の肉瘤(頭巾)が発達しており、体全体が白から銀白色である特徴から、典型的な... 23 tosakin-00065.jpg 1 土佐錦(トサニシキ) True 118 284 793 605 上見において、尾鰭の先が前方に大きく反り返る独特の「反り尾」の特徴が顕著に見られるため、土佐... 24 wakin-00073.jpg 1 和金(ワキン) True 244 244 1081 673 フナ型の体型に、赤と白の更紗模様、そして三つ尾または四つ尾の特徴を持つことから、和金(ワキン... 25 zikin-00075.jpg 1 和金(ワキン) True 287 432 951 412 体型が細長くフナ型(和金型)で、尾鰭が長く三つ尾または四つ尾に分かれている特徴から、長尾の和..
結果としては, 以下の通り.
- 金魚の検出はほぼ正解.
- 品種分類は22品種中18品種が正解.
- 複数写っている場合も検出可.
今回, Geminiを使って金魚の検出及び品種判定を行ったが, 思っていた以上に金魚を見分けることができている感じ.
----
参照URL:
[1] 金魚って見分けられる?(1)~(4)
[2] Cloud AutoML Vision Edgeを試してみる(1), (2)
[3] Cloud AutoML Vision Object Detectionを試してみる
[4] YOLOv3で金魚検出サーバ...

