9月27日、Anthropicが「Giving Claude a zoom tool for reading fine image detail」と題した記事を公開した。グラフの凡例、財務表の数字、回路図のラベル——画像内の「細部」を読み取れないというマルチモーダルAIの弱点を、モデル自身にズームさせることで突破する実装レシピだ。APIのツール使用機能を活用し、Claudeが必要と判断した領域を自律的にクロップ・拡大して再参照するループ構造を、PillowとAnthropic SDKだけで実現する。
なぜ「ズーム」が必要なのか
ClaudeはAnthropicが開発するLLMで、画像入力にも対応している。しかし、高解像度の画像を渡した場合でも、モデルが内部で参照する解像度には上限がある。具体的には、1辺最大2,576px、かつ28×28pxパッチ換算で最大4,784トークンという制約がある(標準ティアでは1,568px / 1,568トークン)。
この制約のため、グラフの凡例、財務表の数字、回路図のラベルといった細部はClaudeの視野では潰れてしまうことがある。GPT-4oをはじめとする他のマルチモーダルモデルも同種の解像度制限を抱えており、「画像は渡せるが細部は読めない」という問題はマルチモーダルAI全般に共通する課題だ。
そこで本レシピが提案するのが、「Claudeが必要と判断した領域を自分でクロップ・拡大して再送する」というループ構造だ。金融レポートのグラフ自動解析や、医療画像の注釈読み取りといった、細部の正確さが求められるユースケースで特に有効なアプローチといえる。
実装の核心:ズームツールの仕組み
Anthropic APIのツール使用機能を使い、Claudeに zoom というツールを定義する。クロップしたい矩形領域を x1/y1/x2/y2 のピクセル座標で指定させる構造で、複数画像が会話に混在する場合は image_index で対象を指定できる。
ZOOM_TOOL: ToolParam = {
"name": "zoom",
"description": (
"Zoom into a rectangular region of an image. Returns the region cropped "
"from the full-resolution original and magnified, so that small details "
"become legible."
),
"input_schema": {
"type": "object",
"properties": {
"x1": {"type": "integer", "description": "Left edge of the region, in pixels"},
"y1": {"type": "integer", "description": "Top edge of the region, in pixels"},
"x2": {"type": "integer", "description": "Right edge of the region, in pixels"},
"y2": {"type": "integer", "description": "Bottom edge of the region, in pixels"},
"image_index": {
"type": "integer",
"description": "Which image to zoom into, counting images in the conversation from 0.",
},
},
"required": ["x1", "y1", "x2", "y2"],
},
}
Claudeがこのツールを呼び出すと、アプリ側でクロップ・拡大を実行し、結果をツール応答として会話に戻す。これをClaudeが「十分に読み取れた」と判断するまで繰り返す自律ループが成立する。Anthropicのマルチモーダル対応ドキュメントも併せて参照すると、画像入力全般の制約や推奨サイズを確認できる。
実装上のポイント3点
1. 座標系の整合が最重要
Claudeが参照する座標系は、サーバー側で自動リサイズされた後の画像に基づく。そのため、クライアント側でも同じリサイズ処理(prepare_image())を事前に適用しておき、Claudeの座標がそのままクロップ計算に使えるようにする必要がある。ここがずれると、Claudeが指定した座標と実際のクロップ位置がかみ合わなくなる。
resized_size() はバイナリサーチで「モデルの上限に収まる最大サイズ」を求める関数で、アスペクト比を保持したままリサイズ先を決定する。prepare_image() はこれを呼び出し、必要な場合のみLANCZOSフィルタでリサイズを行う。
2. ズーム後の画像は「限界まで引き伸ばす」
クロップした小領域は、再度 resized_size() に渡してモデルが受け取れる最大サイズまで拡大する。zoom_size() が元サイズの10,000倍を渡すことで、上限まで目一杯引き伸ばす設計になっている。
def zoom_size(width: int, height: int) -> tuple[int, int]:
return resized_size(width * 10_000, height * 10_000)
これにより、小さなクロップ領域でも28×28pxパッチが最大限に割り当てられ、細部の視認性が上がる。小さな凡例テキストや細かい目盛り数値も、この拡大を経ることで読み取り精度が大幅に改善する。
3. ズーム結果はJPEGで返す
元画像はPNGで渡すが、ズーム結果のツール応答はJPEGで返す。会話に複数のズーム結果が積み重なると、リクエストサイズがAPIの32MBの上限を超えるリスクがあるためだ。ただし色情報の劣化を抑えるため、subsampling=0(4:4:4)、quality=92 を指定している。透過背景(Webのチャートに多い)は白背景に合成してから変換する。この処理により、ズームを繰り返してもリクエストサイズを現実的な範囲に抑えられる。
依存関係の少なさも特徴
このズームツールの実装に必要なライブラリはPillowとAnthropic SDKのみだ。コードは1セルで完結しており、そのまま既存プロジェクトにコピーして使えるよう設計されている。ツール使用の基本的なパターンを理解しておくと応用しやすく、Anthropicが公開している他のマルチモーダル関連クックブックも参考になる。
詳細はGiving Claude a zoom tool for reading fine image detailを参照していただきたい。