Claudeで座標・バウンディングボックスを正確に取得する方法
Claudeに正確な座標を返させるコツは、0〜1000正規化ではなく絶対ピクセル座標を明示的に指示することです。structured outputsでJSON化する手順と、座標がリサイズ後の画像基準になる注意点を実装例付きでまとめます。
Claudeに正確な座標やバウンディングボックスを返させる最大のコツは、0〜1000のような正規化座標ではなく絶対ピクセル座標をプロンプトで明示的に指定することです。Claudeは正規化座標を求められると精度が落ちます。加えてstructured outputsでJSONスキーマを固定すれば、座標を散文ではなく機械可読な配列として取得できます。
Claudeの座標出力の前提
Claudeは画像やPDFに含まれる表・フォーム項目・チャート要素・UI部品などの領域を、バウンディングボックスや点として返せます。座標系は画像の標準的な向きで、原点(0, 0)は画像の左上、xは右方向、yは下方向に増えます。
ここで返ってくる座標は、アップロードした元画像の座標ではありません。Claudeがリサイズしたあとに実際に見ている画像の座標です。これを理解せずに使うと、見た目には合っているようで実際にはズレた位置を指す座標を取得することになります。基本的な画像・PDFの扱い方はClaudeのPDF読み込み・画像解析の使い方、得意・苦手な作業の見分け方はClaude画像解析でできることにまとめています。
ステップ1: 絶対ピクセル座標で明示的にプロンプトする
公式ドキュメントは、座標を求めるプロンプトの書き方を明確に指定しています。「各テーブルのバウンディングボックスを、ピクセル座標で[x1, y1, x2, y2](左上と右下の座標)として返してください」のように、絶対ピクセル座標であることを明示します。
「0から1000の範囲でバウンディングボックス座標を返してください」のような正規化座標の指示は避けます。Claudeは正規化座標のリクエストにうまく対応できません。正規化した値が最終的に必要でも、まずピクセル座標で受け取り、自分のコードで割り算する方が精度は安定します。
Claudeの空間認識には限界があります。座標・位置の特定は近似値にとどまるという前提で、期待するフォーマットをプロンプトで明示したうえで、実運用に載せる前に結果を目視で数件スポットチェックします。小さな要素は画像が縮小される過程で精度が落ちやすいので、細かい対象を扱うタスクほどスポットチェックの重要度が上がります。OCRパイプライン・フォーム抽出・チャート解析・UI要素の位置特定のように、特定領域に対して後続処理を実行するタスクほど、座標のズレがそのまま処理ミスに直結します。
ステップ2: structured outputsでJSON形式に固定する
座標を「テーブルはだいたい画像の中央あたりにあります」のような散文で受け取ると、後続処理でパースが必要になり不安定です。structured outputsでoutput_config.formatにJSON Schemaを渡すと、指定した形のJSONだけが返ります。以下は検出した要素ごとにlabelと[x1, y1, x2, y2]の配列を返すスキーマ例です。
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "<base64エンコードした画像>",
},
},
{
"type": "text",
"text": "画像内のすべてのテーブルを検出し、絶対ピクセル座標でバウンディングボックスを返してください。",
},
],
}
],
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"elements": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {"type": "string"},
"bbox": {
"type": "object",
"properties": {
"x1": {"type": "number"},
"y1": {"type": "number"},
"x2": {"type": "number"},
"y2": {"type": "number"},
},
"required": ["x1", "y1", "x2", "y2"],
"additionalProperties": False,
},
},
"required": ["label", "bbox"],
"additionalProperties": False,
},
}
},
"required": ["elements"],
"additionalProperties": False,
},
}
},
)
print(next(block.text for block in response.content if block.type == "text"))output_config.formatが現行のパラメータ名です。以前のベータ版にあったoutput_formatは移行期間中もリクエストとしては動きますが、Python SDK 1.0以降ではclient.beta.messages.create()に渡すとTypeErrorになるため、新しいコードでは使いません。Bedrock経由でstructured outputsを使う場合は、対応モデルがOpus 4.6・Sonnet 4.6・Sonnet 4.5・Opus 4.5・Haiku 4.5に限られる点も確認しておきます。
bboxをオブジェクトにしているのには理由があります。structured outputsのJSON Schemaは配列の要素数をminItemsの0・1以外では制約できず、maxItemsもサポート対象外です。[x1, y1, x2, y2]という4要素固定の配列をそのままスキーマ化しようとすると400エラーになります。座標を配列で扱いたい場合でも、まずオブジェクトで受け取ってから自分のコードで配列へ変換する方が安全です。プロパティの並び順は、スキーマに書いた順ではなく必須プロパティが先、任意プロパティが後という規則で決まるので、x1/y1/x2/y2をすべてrequiredに入れておくと出力順序も安定します。
Pydantic・Zodで型安全に取得する
生のJSON Schemaを自分で書く代わりに、PydanticやZodのモデル定義からスキーマを自動生成する方法もあります。Python SDKのclient.messages.parse()にPydanticモデルを渡すと、パース済みのオブジェクトがそのまま返り、辞書からのキー取り出しが不要になります。
from pydantic import BaseModel
from anthropic import Anthropic
class BoundingBox(BaseModel):
x1: float
y1: float
x2: float
y2: float
class DetectedElement(BaseModel):
label: str
bbox: BoundingBox
class DetectionResult(BaseModel):
elements: list[DetectedElement]
client = Anthropic()
response = client.messages.parse(
model="claude-opus-5",
max_tokens=1024,
messages=[...], # 画像ブロックとプロンプトはステップ2と同じ
output_format=DetectionResult,
)
print(response.parsed_output.elements)TypeScriptならZodスキーマをzodOutputFormat()に渡す形が同じ役割を果たします。output_format=はPython SDKのclient.messages.parse()専用の簡易記法で、内部ではoutput_config.formatに変換されます。他のSDKやCLI経由で呼ぶ場合はoutput_configを直接指定します。
バウンディングボックスを大量の画像に適用するときの注意
structured outputsは、内部でスキーマから制約付きサンプリング用の文法を生成してから使う仕組みです。同じスキーマを初めて使うリクエストでは、この文法をコンパイルする分だけレイテンシーが上乗せされます。コンパイル結果は最後に使ってから24時間キャッシュされるので、同じbboxスキーマを何百枚もの画像に繰り返し適用するようなバッチ処理では、2回目以降のリクエストは速くなります。スキーマの構造を変えるとキャッシュは無効化されますが、titleやdescriptionだけの変更なら無効化されません。
もう一点、structured outputsを使うとClaude側で出力形式を説明する追加のシステムプロンプトが自動的に挿入されるため、入力トークンがわずかに増えます。output_config.formatのスキーマ自体を変更すると、そのスレッドのプロンプトキャッシュも無効化されます。大量の画像を1つの会話で連続処理する設計なら、スキーマを途中で変えない方がキャッシュヒット率を保てます。
ステップ3: 返ってきた座標が指す画像を理解する
ステップ1・2で正確な座標を取得しても、どの画像に対して描画・クリック・切り出しをするかを間違えると意味がありません。Claudeが返す座標は、送った元画像ではなくClaudeがリサイズしたあとに見ている画像のピクセル位置です。長辺やビジュアルトークンの上限を超える画像は自動的に縮小されるため、元画像のサイズによっては座標をそのまま元画像に重ねると位置がズレます。
| ティア | 対象モデル | 長辺上限 | ビジュアルトークン上限 |
|---|---|---|---|
| 高解像度 | 対象モデルClaude 4.7以降のモデル | 長辺上限2576px | ビジュアルトークン上限4784 |
| 標準 | 対象モデルそれ以外のモデル | 長辺上限1568px | ビジュアルトークン上限1568 |
長辺・トークン数のどちらか一方の上限を超えるだけでも自動縮小がかかります。高解像度ティアのモデルに切り替えるだけで、同じ画像でも縮小されなくなり座標のズレが消えることがあります。このリサイズの仕組みと、座標を元画像へ正確に戻す方法は画像リサイズで座標がズレる原因と直し方で扱っています。
stop_reasonを確認してから使う
structured outputsはほとんどの場合スキーマどおりのJSONを返しますが、2つの例外があります。1つはstop_reason: "max_tokens"で、max_tokensに達して途中で応答が打ち切られたケースです。検出した要素が多い画像(細かい表のセルを1つずつバウンディングボックス化する場合など)では、配列の途中でJSONが切れて壊れたまま返ってくることがあります。バウンディングボックスの検出では要素数を事前に見積もりにくいため、レスポンスを使う前に必ずstop_reasonを確認し、max_tokensならより大きな値で再試行します。もう1つはstop_reason: "refusal"で、安全性の理由でClaudeが拒否した場合です。この場合はスキーマに従わない拒否メッセージが優先されるため、stop_reasonを見ずにパースだけ試みるとエラーハンドリングが漏れます。
よくあるつまずき
- 小さい要素は精度が落ちる: 小さい要素は画像が縮小される過程で情報が失われやすく、座標の精度も下がります。対象領域を先にクロップして送り、返ってきた座標にクロップ原点の座標を足し戻すか、高解像度ティアのモデルを使います。
- PDFの座標はページに正確に戻せない: PDFのページはサーバー側で寸法をコントロールできない形にラスタライズされるため、返ってきた座標を元のPDFページへ正確にマッピングできません。PDFで座標を扱うなら、自分でページを画像化してから送ります。
- Computer Useツールでのクリック座標は、この記事の一般的な画像座標とは別の設計です。スクリーンショットは自動リサイズの対象外で、上限を超えるとエラーで拒否されます。専用のスケーリング計算はComputer Useの座標ズレを直すスケーリング計算式にまとめています。
- 取得した座標をそのまま実運用のトリガーとして使うと、精度低下がサイレントに本番処理へ流れ込みます。信頼度が低いケースを弾く仕組みや、サンプリングでの目視チェックを挟みます。座標のズレは例外を投げないため、監視をエラーログだけに頼ると気づくのが遅れます。
まとめ
Claudeから正確な座標を引き出すコツは、正規化座標ではなく絶対ピクセル座標を明示的にプロンプトすること、structured outputsでJSONスキーマに固定すること、そして返ってくる座標がリサイズ後の画像基準であることを理解しておくことの3つです。座標がズレていると感じたら、プロンプトの書き方より先に、リサイズの有無を疑うのが近道です。本番投入前にはstop_reasonのチェックと、少量データでのスポットチェックを組み込んでおくと、要素数が多い画像でのJSON途中切れや精度低下に早く気づけます。JSON出力の基本的な使い方はClaude JSONモードの使い方でも扱っています。