gpt-4-vision-preview APIを使って画像からmdやjsonを生成してみる

2023.11.07

はじめに

こんにちは。データアナリティクス事業本部ビッグデータチームのyosh-kです。
今回は、OpenAI DevDayで発表された内容から特に私が気になったgpt-4-vision-preview APIを使ってみたいと思います。
OpenAIの公式ドキュメントにクイックドキュメントがあったのでその内容を参考に試してみます。
詳細については後ほど確認していきますが、今回はとりあえず試してみたブログとなります。

Visionのクイックスタートの概要

本ブログは試してみるブログになりますので、詳細はOpenAIの公式ドキュメントをご確認いただければと思います。どれも可能性に満ち溢れた内容が多いですね。

Vision APIの公式ガイド

  • ビデオからgpt-4-vision-preview APIを用いてナレーションを生成する方法
  • リンクの画像を渡す実装の紹介
  • Base 64 でエンコードされた画像を渡す実装の紹介
  • 複数画像の入力
  • 低忠実度または高忠実度の画像理解
  • 画像の管理
    • 画像はモデルによって処理された後、OpenAI サーバーから削除され、保持されません。モデルのトレーニングに OpenAI API 経由でアップロードされたデータは使用しません。
  • 制限事項
    • 英語以外: 日本語や韓国語など、非ラテン文字のテキストを含む画像を処理する場合、モデルは最適に動作しない可能性があります。
  • コストの計算
    • 画像入力は、テキスト入力と同様に従量制でトークンで課金されます。
  • よくある質問

クイックスタートを日本語指示で動かしてみた

クイックスタートの内容を単純に日本語指示に修正してみました。image_urlの内容は検索すれば出てきますが、風景画像となっております。

事前にopenaiライブラリのupgradeとOpenAI Keyを設定します。

pip install --upgrade openai
export OPENAI_API_KEY="sk-xxxxxxxx"
from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4-vision-preview",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "この画像を説明してください。"},
                {
                    "type": "image_url",
                    "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
                },
            ],
        }
    ],
    max_tokens=300,
)

print(response.choices[0])

以下、出力結果となります。問題なく日本語識別できていることを確認しました。

(openai_env) @27_gpt_4_vision_preview % python url_image.py 
Choice(finish_reason=None, index=0, message=ChatCompletionMessage(content='この画像には、高く茂った緑の草原の中を通る木製の遊歩道が映っています。遊歩道は画像の下側から中央を向いており、遠くまで続いているように見えます。遊歩道の両側には豊かな草が生い茂り、後方には低木や木々が点在しています。空は晴れており、そこにはいくつかの白い雲が浮かんでいます。太陽の光は草原を明るく照らし、全体に穏やかで平和な雰囲気を与えています。自然の美しさと静けさを感じさせる景色です。', role='assistant', function_call=None, tool_calls=None), finish_details={'type': 'stop', 'stop': '<|fim_suffix|>'})
(openai_env) @27_gpt_4_vision_preview %

画像の表をmd形式に変換してみた

今回は以前ブログで使用した画像の表をmd形式に変換することを試してみたいと思います。

実際の画像は以下になります。

import os
import base64
import requests

# OpenAI API Key
api_key = os.environ["OPENAI_API_KEY"]


# Function to encode the image
def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")


# 読み取りたい画像は実行するpythonファイルと同じ階層にimgフォルダを格納し配置。
image_path = "./img/Screenshot 2023-11-07 at 7.44.25.png"

# Getting the base64 string
base64_image = encode_image(image_path)

headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}

payload = {
    "model": "gpt-4-vision-preview",
    "messages": [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "画像の表をmd形式に変換してください。"},
                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}},
            ],
        }
    ],
    "max_tokens": 300,
}

response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)

print(response.json())

以下、出力結果です。出力結果からcontentにレスポンスとなるmdが返されているので、|部分のmd箇所だけを取得してmdファイルに起こしてみます。

(openai_env) @27_gpt_4_vision_preview % python file_image.py
{'id': 'chatcmpl-8I2m1eDTJOjLJdF20T6Qwy8NWC3qoaxxx', 'object': 'chat.completion', 'created': 1699311257, 'model': 'gpt-4-1106-vision-preview', 'usage': {'prompt_tokens': 788, 'completion_tokens': 300, 'total_tokens': 1088}, 'choices': [{'message': {'role': 'assistant', 'content': '以下は、画像に表示されている表をMarkdown形式で記述したものです。\n\n```markdown\n|  番号  |  回答  |  備考  |\n| ----- | ------ | ----- |\n|  ○  | 我々の祖先として知られる肉食恐竜はどの公園 | 我々の祖先として有名です。 |\n|  △  | 日本橋をわりの会場となる日本橋最大級公園 | 日本橋よりは動植物にあまりためになると言う、会場は日本橋をものでした。 |\n|  ○  | 東京タワーで開催されている「333光の塔のぼりセッションリード」 | 3/24 - 5/7まで開催しています。 |\n|  △  | 本公園では「さくらフェスティバル」が開催 | Bingの5月頃からはさくらフェスティバルは使用できます、東京マリンスポ'}, 'finish_details': {'type': 'max_tokens'}, 'index': 0}]}

以下のように改行コードがそのままの状態であるため、改行コードを改行に変換したいと思います。

VS Code上で置換設定を開き、検索文字列に\\nを入力し、正規表現をオプションを有効にします。置換後は\nと入力します。

問題なく変換できました。 変換された内容を確認したところ、トークン数の最大値を超えたため、画像の表全体を再現てきておらず、日本語文字についても所々誤りがあることがわかりました。

画像をjson形式に変換してみた

パッと良い画像が見つからなかったので、私のジョインブログの画像からjsonファイルに起こしたいと思います。

先ほどからの修正点は、画像パスとプロンプトになります。jsonはさらにわかりづらいので、startとendをつけていただきます。

# 読み取りたい画像は実行するpythonファイルと同じ階層にimgフォルダを格納し配置。
image_path = "./img/Screenshot 2023-11-07 at 7.09.18.png"

:
:
:

payload = {
    "model": "gpt-4-vision-preview",
    "messages": [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "画像の物体をjsonデータにしてください。jsonファイル出力結果はstart,endで終わるようにしてください。"},
                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}},
            ],
        }
    ],
    "max_tokens": 300,
}

以下、出力になります。こちらもstartendの箇所を抜き出してjsonファイルとして確認します。

(openai_env) @27_gpt_4_vision_preview % python file_image.py
{'id': 'chatcmpl-8I39o2qrJ6l4BOcUUdOG5ONAdZNoI', 'object': 'chat.completion', 'created': 1699312732, 'model': 'gpt-4-1106-vision-preview', 'usage': {'prompt_tokens': 1150, 'completion_tokens': 213, 'total_tokens': 1363}, 'choices': [{'message': {'role': 'assistant', 'content': 'start\n{\n  "人物": {\n    "性別": "特定不可",\n    "位置": "壁にもたれている",\n    "アクション": "海を見ているか地図を見ている",\n    "衣服": {\n      "帽子": "あり",\n      "上着": "ジャケット",\n      "バックパック": "あり"\n    }\n  },\n  "環境": {\n    "場所": "岩の上",\n    "壁": {\n      "素材": "石",\n      "状態": "風化している"\n    },\n    "天気": "霧が濃い",\n    "視認可能な地理的特徴": {\n      "海": "あり",\n      "平地": "遠景にあり"\n    }\n  }\n}\nend'}, 'finish_details': {'type': 'stop', 'stop': '<|fim_suffix|>'}, 'index': 0}]}
(openai_env) @27_gpt_4_vision_preview %

大まかな情報であれば抽出できているので素晴らしいですね。

今度は英語で、同じように試してみます。

payload = {
    "model": "gpt-4-vision-preview",
    "messages": [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Output the details of the objects in the image as JSON data. The resulting JSON file should start with 'start--' and end with '--end'.",
                },
                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}},
            ],
        }
    ],
    "max_tokens": 300,
}
(openai_env) @27_gpt_4_vision_preview % python file_image.py
{'id': 'chatcmpl-8I432NtTZXKEqQCyqfsCGRchV1HDYhsme', 'object': 'chat.completion', 'created': 1699316156, 'model': 'gpt-4-1106-vision-preview', 'usage': {'prompt_tokens': 1142, 'completion_tokens': 193, 'total_tokens': 1335}, 'choices': [{'message': {'role': 'assistant', 'content': 'start--{\n  "person": {\n    "gender": "ambiguous",\n    "clothing": [\n      {\n        "type": "hoodie",\n        "color": "light brown"\n      },\n      {\n        "type": "pants",\n        "color": "dark"\n      },\n      {\n        "type": "backpack",\n        "color": "light brown"\n      }\n    ],\n    "posture": "standing",\n    "activity": "looking at map"\n  },\n  "environment": {\n    "weather": "foggy",\n    "location": "outdoors",\n    "terrain": "rocky"\n  },\n  "structures": [\n    {\n      "type": "stone wall",\n      "condition": "intact"\n    }\n  ],\n  "background": {\n    "landscape": "coastal",\n    "visibility": "low",\n    "terrain": "rocky"\n  }\n}--end'}, 'finish_details': {'type': 'stop', 'stop': '<|fim_suffix|>'}, 'index': 0}]}
(openai_env) @27_gpt_4_vision_preview %

当たり前ですが、出力結果にはばらつきがあるのでプロンプトの出し方が重要になります。

最後に

Webからだと試せたGPT-4 VisionがいよいよAPIが出て朝から興奮していました。公式ドキュメントにも記載がありましたが、画像に日本語文字を含むと最適な回答が得られないこともあるそうでした。それを差し引いても様々なことに応用できそうな気がしました。公式のCookbookにビデオファイルからAPIを用いて文字起こしする記事もあったので、そちらも引き続き試していきたいと思います。

Processing and narrating a video with GPT's visual capabilities and the TTS API