🖼️

OpenAI の Chat Completions API に投げられる画像のサイズを調べる

2025/01/28に公開

概要

表題の通りですが、自分の知る限りではドキュメントに画像サイズの制限について書かれていないため、どの程度のサイズの画像ファイルまで分析できるのか調べました。(一応GPT-4 Turbo with Visionのときは20MB制限という記述があるのですが、GPT-4oでは20MBを下回っていてもエラーが出ることがあります)

結果

ファイルサイズとしては5~10MB程度のファイルサイズを超える辺りでエラーが出る傾向が確認できました。しかし、今回試した方法で、画像がこうだとエラーが出る、というのを正確に測ることは出来ませんでした。
画像サイズを大きくしていくとエラーが出ることは確認できるのですが、プロンプトに渡す画像ファイルサイズ、画像のピクセル数、image_urlの長さなどを比較しても、画像によってエラーが出るタイミングが異なりました。ブラックボックス化された圧縮プロセス等があり、決まった制限をローカル側で知る方法はもしかしたらないかもしれません。(もしご存じの方がいらっしゃれば教えていただけると幸いです)

画像サイズが大きいときに発生するエラー
Error code: 400 - {'error': {'code': 'BadRequest', 'message': 'Invalid image data.', 'param': None, 'type': None}}

検証コード

Azure OpenAIでデプロイしたGPT-4oを使っています。

from io import BytesIO
import base64

from PIL import Image
from openai import AzureOpenAI

AZURE_OPENAI_API_KEY = "<API_KEY>"
AZURE_OPENAI_SERVICE_NAME = "<サービス名>"
AZURE_OPENAI_API_VERSION = "2024-06-01"
AZURE_OPENAI_MODEL_NAME = "<デプロイしたモデル名>"

def image_to_data_url(image: Image.Image) -> str:
    buffered = BytesIO()
    image.save(buffered, format="JPEG", quality=100)
    base64_encoded_data = base64.b64encode(buffered.getvalue()).decode("utf-8")

    print(f"size: {len(buffered.getvalue())/1000} KB")
    print(f"encoded data length: {len(base64_encoded_data)}")

    return f"data:image/jpeg;base64,{base64_encoded_data}"

def create_gpt_response(image_data_url):
    client = AzureOpenAI(
        azure_endpoint=f'https://{AZURE_OPENAI_SERVICE_NAME}.openai.azure.com/',
        api_key=AZURE_OPENAI_API_KEY,
        api_version=AZURE_OPENAI_API_VERSION
    )
    system_message = "あなたは画像分析のエキスパートです"
    user_message = "この画像には何が写っていますか。"
    system_message = {
        "role": "system", 
        "content": system_message
    }
    user_message = {
        "role": "user", 
        "content": [{"type": "text", "text": user_message}] 
    }
    if image:
        user_message["content"].append({ "type": "image_url", "image_url": { "url": image_data_url } })

    response = client.chat.completions.create(
        model=AZURE_OPENAI_MODEL_NAME,
        messages=[
            system_message,
            user_message
        ],
        max_tokens=512
    )

    content = response.choices[0].message.content
    usage = response.usage

    print(f"usage: completion {usage.completion_tokens}, prompt {usage.prompt_tokens}")
    print(f"content: {content}")

if __name__ == "__main__":
    image = Image.open("./images/test.png")
    print(f"original size: {image.width} x {image.height}")
    for i in range(10, 50, 5): # 倍率範囲とstepはエラーが出るいい感じの値を入れる
        print(f"=== ratio: {i} ===")
        try:
            new_size = (int(image.width * i), int(image.height * i))
            resized_image = image.resize(new_size, Image.Resampling.LANCZOS)
            data_url = image_to_data_url(resized_image)
            create_gpt_response(data_url)
        except Exception as e:
            print(e)
            break

余談

調べているときにわかった副産物

  • 画像サイズを大きくすると使用トークン数が増えますが、上限があります。おそらくインプット画像の縦横サイズに上限を設けており、サーバー側でリサイズされているのだと思います。(ならサイズを大きくしてもエラー出ないのでは?という気もしますが、通信量の問題で制限をかけているのだと思われます)
  • RGBAのPNGファイルをPNG形式でバイナリ化しても、RGBに変換してから使っても使用トークン数は変わりませんでした。それはそうという話ではありますが、透過度αの情報は使われていないようです。

まとめ

あまり起きない例だとは思いますが、~10MBレベルの大きいサイズの画像を使う場合はエラーが出る可能性があるということは認識しておきたいです。

ヘッドウォータース

Discussion