みらいテックラボ

音声・画像認識や機械学習など, 週末プログラマである管理人が興味のある技術の紹介や実際にトライしてみた様子などメモしていく.

VLMは金魚を見分けられるか?

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で金魚検出サーバ...