Claude Media
Claudeで画像をリサイズすると座標がずれる原因と対処法

Claudeで画像をリサイズすると座標がずれる原因と対処法

Claudeは長辺だけでなくビジュアルトークンの上限でも画像を自動縮小し、返す座標は縮小後の画像基準になります。A4スキャンの実例と、事前リサイズ・エラー化・座標逆算という3つの対処法をまとめます。

A4を130DPIでスキャンした1075×1520pxの画像は、長辺(1568px)の上限には収まっているのに、Claudeに送ると924×1307pxへ縮小されます。原因は長辺の制限とは別に、ビジュアルトークン数の上限も同時にチェックされているためです。Claudeが返す座標は、この縮小後の画像を基準にした値なので、元画像にそのまま重ねるとズレます。対処法は3つあります。アップロード前に自分でリサイズする、リサイズが起きたらエラーとして検知する、リサイズ後の寸法から座標を逆算する、のいずれかです。

長辺の上限だけでは説明がつかない理由

Claudeは画像を送る前に、モデルのネイティブ解像度に収まるようアスペクト比を保ったまま縮小します。判定条件は2つです。1つは長辺がモデルの上限(標準ティアで1568px、高解像度ティアで2576px)を超えないこと、もう1つはビジュアルトークン数(⌈幅 / 28⌉ × ⌈高さ / 28⌉)が上限(標準ティアで1568トークン、高解像度ティアで4784トークン)を超えないことです。

ほとんどの写真・スクリーンショットではビジュアルトークンの上限の方が効きます。長辺の上限が効くのはパノラマや縦長のスマホ画面のような、極端に細長い画像だけです。1920×1080pxのスクリーンショットは標準ティアで1456×819pxに縮小されますが、これは長辺1568pxに合わせた1568×882pxではありません。長辺だけで手計算すると、この差の分だけ座標が実際とズレます。

元の画像サイズ標準ティアの縮小後高解像度ティアの縮小後
1000×1000px(100万画素)標準ティアの縮小後縮小なし高解像度ティアの縮小後縮小なし
1092×1092px(119万画素)標準ティアの縮小後縮小なし高解像度ティアの縮小後縮小なし
1920×1080px(207万画素)標準ティアの縮小後1456×819px高解像度ティアの縮小後縮小なし
2000×1500px(300万画素)標準ティアの縮小後1269×952px高解像度ティアの縮小後縮小なし
3840×2160px(829万画素)標準ティアの縮小後1456×819px高解像度ティアの縮小後2576×1449px

標準ティアでは約120万画素(1092×1092px)を超えると縮小が始まり、高解像度ティアでも800万画素クラスの4K画像は縮小されます。「自分の画像サイズが上限に近いかどうか」を確認せずに座標処理を実装すると、テスト画像ではズレず、本番の別サイズの画像だけズレるという再現しにくい不具合になります

A4スキャンが縮小される具体例

130DPIでスキャンしたA4サイズの画像は1075×1520pxです。長辺1520pxは標準ティアの上限1568px未満なので、一見縮小されないように見えます。ところがビジュアルトークン数を計算すると⌈1075/28⌉ × ⌈1520/28⌉ = 39 × 55 = 2,145トークンとなり、標準ティアの上限1568トークンを超えます。この結果、Claudeは画像を924×1307pxへ縮小してから処理します。長辺の上限だけを見ていると気づけない縮小です。

同じ画像でも、高解像度ティアのモデルに送ると話は変わります。2,145トークンは高解像度ティアの上限4784トークン以内に収まるため、縮小されず1075×1520pxのまま処理されます。座標がズレるかどうかは、送り先モデルのティア次第で変わります。

パディングは右端・下端だけに入る

パディングが必要になるのは、Claudeが画像を28×28pxのパッチ単位で処理しているためです。縮小後の寸法が28の倍数ちょうどになるとは限らないので、Claudeは縮小したかどうかに関わらず、すべての画像を28pxの倍数へ切り上げてパディングします。924×1307pxの例では924×1316pxまでパディングされます。縮小されなかった画像でも、寸法が28の倍数でなければ同じようにパディングされます。パディング部分にコンテンツは含まれず、Claudeが実際に認識するページ内容は縮小後の924×1307pxの範囲だけです。パディングは右端と下端にだけ追加され、原点(0, 0)や左端・上端の位置は変わりません。正規化やリスケールの計算では、パディング後の寸法ではなく縮小後の寸法で割ることが重要です。パディング後の寸法で割ると、すべての座標がわずかにズレます。

対処法1: アップロード前に自分でリサイズする

最も確実な対処は、Claudeが縮小する前に、自分で同じ寸法へリサイズしてからアップロードすることです。手元の画像がClaudeの見る画像と一致するので、座標変換が一切不要になり、後続コードもシンプルなまま保てます。

import math
 
 
def count_image_tokens(width: int, height: int) -> int:
    """28x28ピクセルのパッチ1つ = 1ビジュアルトークン"""
    return math.ceil(width / 28) * math.ceil(height / 28)
 
 
def resized_size(
    width: int,
    height: int,
    max_edge: int = 1568,
    max_tokens: int = 1568,
) -> tuple[int, int]:
    """Claudeがパディング前にリサイズする寸法。
    高解像度ティアのモデルではmax_edge=2576, max_tokens=4784を渡す。
    """
 
    def fits(w: int, h: int) -> bool:
        return (
            math.ceil(w / 28) * 28 <= max_edge
            and math.ceil(h / 28) * 28 <= max_edge
            and count_image_tokens(w, h) <= max_tokens
        )
 
    if fits(width, height):
        return (width, height)
    if height > width:
        resized_h, resized_w = resized_size(height, width, max_edge, max_tokens)
        return (resized_w, resized_h)
 
    aspect_ratio = width / height
    lo, hi = 1, width
    while lo + 1 < hi:
        mid = (lo + hi) // 2
        if fits(mid, max(round(mid / aspect_ratio), 1)):
            lo = mid
        else:
            hi = mid
    return (lo, max(round(lo / aspect_ratio), 1))
 
 
# A4スキャンの例
print(resized_size(1075, 1520))  # (924, 1307)
 
# Pillowで実際にリサイズする場合
# image.resize(resized_size(*image.size))

このアルゴリズムはClaude側の実装と同じ二分探索でサイズを決めます。手計算でおおよそのサイズを見積もると、この関数の結果とズレることがあるため、実装に組み込んで使います。端数の丸め方にも注意します。アスペクト比を保ったまま短辺を計算すると、ちょうど.5になる端数が出ることがあり、Claude本体は四捨五入ではなく偶数丸め(0.5をどちらへ丸めるかを、丸めた結果が偶数になる方に決める方式)を使います。多くの言語の標準的なround()とは挙動が異なるため、上のコード例のように丸め方を明示した実装を使わないと、ごくまれに1px単位でズレる計算結果になります。

対処法2: リサイズが起きたらエラーにする

事前リサイズは、パイプラインが常に正しい寸法を出力している間だけ効果があります。新しい画像ソースやモデルのティア変更で、気づかないうちにサーバー側リサイズが復活することもあります。この静かなズレを見える化するには、画像コンテンツブロックにtransformationsフィールドを付けます。

{
  "type": "image",
  "source": { "type": "base64", "media_type": "image/png", "data": "..." },
  "transformations": { "oversized_image": "error" }
}

このフィールドを付けた画像がリサイズされるはずのリクエストは、400のinvalid_request_errorで拒否され、画像の寸法と収まる最大寸法がエラーメッセージに含まれます。デフォルト("downsize")は今までどおり自動縮小です。設定は画像ブロックごとなので、座標が重要なスクリーンショットにはエラー化を付け、ロゴのような縮小されても問題ない画像には付けない、という使い分けもできます。PDFのdocumentブロックはこのフィールドを受け付けません。ページはサーバー側でラスタライズされるためです。

transformationsが防ぐのは自動縮小だけです。画像の最長辺が8000pxを超える場合や、多数の画像を含むリクエストの上限を超える場合は、"oversized_image"の設定に関係なく別のバリデーションエラーで拒否されます。URLやfile_idで渡した画像は、バイト列を取得できた時点でチェックされるため、エラーメッセージにリクエスト内での位置(何番目の画像か)が付きません。埋め込みBase64画像だけが位置つきで報告されます。

推論そのものを実行する前に確認したいなら、Token countingエンドポイントもtransformationsを尊重します。Messages APIを実際に呼ばなくても、埋め込み画像がリサイズされずに収まるかどうかを事前に確認できます。ただしURLやfile_idで渡す画像は、Token counting時点ではバイト列を取得しないため、この事前チェックの対象になりません。実際にMessages APIを呼んだタイミングで初めて検証されます。

対処法3: 事前リサイズできないときは座標を逆算する

上流システムから受け取った画像で、自分で事前リサイズできない場合は、resized_size関数でClaudeが見たサイズを再計算し、返ってきた座標を相対座標に変換してから元画像へ戻します。

def to_relative_coordinates(
    x: float,
    y: float,
    original_width: int,
    original_height: int,
    max_edge: int = 1568,
    max_tokens: int = 1568,
) -> tuple[float, float]:
    """Claudeが返したピクセル座標を、元画像上の[0, 1]の相対座標に変換する"""
    resized_w, resized_h = resized_size(
        original_width, original_height, max_edge, max_tokens
    )
    return (x / resized_w, y / resized_h)
 
 
# 縮小後のA4ページ上で(462, 653.5)を返した場合、元の1075x1520画像上では
rel_x, rel_y = to_relative_coordinates(462, 653.5, 1075, 1520)
print((rel_x * 1075, rel_y * 1520))  # (537.5, 760.0)

元画像の寸法と、実際に送ったモデルのティア(標準か高解像度か)を正しく渡すことが前提です。ティアを取り違えると、間違った縮小後サイズを逆算してしまい、座標がまた別方向にズレます。モデルを切り替えるたびに、この2つの引数を更新し忘れないようにします。

PDFではこの方法が使えない

PDFはページがサーバー側でラスタライズされ、その寸法をあらかじめこちらで制御できません。元画像のピクセル寸法を前提とする逆算方式は、そもそも「元画像」という概念が存在しないPDFには適用できません。PDFの内容に対して座標を扱いたいなら、自分でページを画像へラスタライズしてからアップロードし、対処法1のアップロード前リサイズの手順に合わせます。PDF全般の扱い方はClaudeのPDF読み込み・画像解析の使い方にまとめています。

Computer Useツールのスクリーンショットも別枠です。自動リサイズの対象外で、上限を超えると拒否されるため、この記事の縮小ロジックではなく専用のスケーリング計算が必要になります。詳細はComputer Useの座標ズレを直すスケーリング計算式にまとめています。

リサイズを直しても座標は近似値のまま

ここまで見た対処法1〜3は、いずれも「Claudeがどの寸法の画像を見ているか」というリサイズ由来のズレを解消する方法です。これを直しても、座標そのものの精度が完璧になるわけではありません。Claudeの空間認識には限界があり、低品質な画像・回転した画像・極端に小さい画像では見積もりの誤差が別途乗ります。リサイズ計算を正しく合わせたうえで、重要な用途では出力をそのまま信頼せず、一定割合をサンプリングして目視確認する運用を組み合わせます。

まとめ

画像リサイズで座標がズレる最大の原因は、長辺の上限だけでなくビジュアルトークンの上限も画像を縮小させることです。A4スキャンのように長辺だけなら収まって見える画像でも、トークン数で縮小される場合があります。確実なのはアップロード前に自分で同じ寸法へリサイズすることで、それが難しければtransformationsでリサイズをエラー化するか、返ってきた座標を相対座標に逆算します。丸め方の違いや、テスト画像だけが偶然縮小の境界を跨がないサイズだったという再現しにくい不具合も、原因を疑う候補に加えておきます。座標をそもそもどう取得するかはClaudeで座標・バウンディングボックスを正確に取得する方法、画像処理全般の得意・苦手はClaude画像解析でできることで扱っています。

この記事を共有:XはてブLinkedIn