画像認識が向上した ChatGPT-4o で名刺を読み取る

ChatGPT-4oですが、日本語の精度も向上し、かつ画像認識、OCRの制度も向上したとのことで、名刺を読み取ってデータ化したいと思います。

GPT-4-Turboでも画像認識はできましたが、GPT-4oになり実感としてはかなり精度が上がっています。ただ文字を読み取るだけではなく、ほぼ正しく会社名や部署名、役職などを読み取って構造化してくれます。

APIでChatGPT-4oに名刺画像を渡して、画像を認識してもらいます。画像をAPIで渡すには、Base64でエンコードして渡すか、画像のURLを渡すかのどちらかですが、今回はローカルにある画像を利用するので、Base64でエンコードします。

JSONで書き出すため、systemのcontentsには「The response should be output in JSON format.」と記載してあります。またリクエストには「response_format={"type": "json_object"}」を追記してください。

また読み取った文字を構造化するために、userのcontentsには、「この名刺の画像から「氏名」、「会社名」、「住所」、「郵便番号」、「電話番号」、「FAX番号」、「メールアドレス」、「部署名」、「役職・肩書」、「URL」を読み取ってください。複数ある場合はそれらを「,」でつないで入れてください。画像が名刺じゃない場合はErrorを返してください。」としています。

```Python:read_card.py
from openai import OpenAI
import base64
import argparse

PROJECT_ID = '<YOUR-PROJECT-ID>'
API_KEY = '<YOUR-API-KEY>'
ORGANIZATION = '<YOUR-ORGANIZATION-ID>'

def encode_image(image_path):
  with open(image_path, 'rb') as image_file:
    return base64.b64encode(image_file.read()).decode('utf-8')

def main(args: argparse.Namespace):
    
    image_path = args.target
    image_path = check_image_format(image_path)
    
    client = OpenAI(
        organization = ORGANIZATION,
        project = PROJECT_ID,
        api_key = API_KEY
    )
    
    base64_image = encode_image(image_path)

    res = client.chat.completions.create(
        model = "gpt-4o-2024-05-13",
        messages = [
            {
                "role": "system",
                "content": "You are an Optical Character Recognition (OCR) machine. You will extract all the characters from the image file provided by the user, and you will only privide the extracted text in your response. As an OCR machine, You can only respond with the extracted text.The response should be output in JSON format."},
            {
                "role": "user", 
                "content": [
                  {
                      "type":"text",
                      "text":"この名刺の画像から「氏名」、「会社名」、「住所」、「郵便番号」、「電話番号」、「FAX番号」、「メールアドレス」、「部署名」、「役職・肩書」、「URL」を読み取ってください。複数ある場合はそれらを「,」でつないで入れてください。画像が名刺じゃない場合はErrorを返してください。", 
                  },
                  {
                      "type":"image_url",
                      "image_url":{
                          "url": f"data:image/jpeg;base64,{base64_image}",
                      },
                  },
                ],
            },      
        ],
        response_format={"type": "json_object"},
        temperature=0.0
    )

    return res.choices[0].message.content
            
if __name__ == '__main__':
    
    parser = argparse.ArgumentParser()
    parser.add_argument('target', type=str, help='target image')
    args = parser.parse_args()

    print(main(args))
```
エラー処理は省いていますので、必要に応じてエラー処理などは入れてください。
ダミーの画像（手作り！）を読み取ってみます。名刺はスマホのカメラで撮ることが多いかと思いますので、手作りの名刺の写真を撮りました。
※お試しの場合はご自由にご利用ください
![sample_card.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191954/70d56f0b-aa7e-8ed4-f032-346ac3142003.jpeg)

実行プログラムと同じディレクトリに画像をおいて、実行します。
```bash
$ python3 read_card.py sample_card.jpg
```
実行結果がJSONで返ってきます。
```json:GTP-4oでの名刺の読み取り結果
{
  "氏名": "名刺 太郎",
  "会社名": "株式会社名刺サンプル",
  "住所": "東京都品川区千代田1-2-3千代田ビル5階",
  "郵便番号": "123-4567",
  "電話番号": "03-1234-5677",
  "FAX番号": "03-1234-5678",
  "メールアドレス": "taro.meishi@example.com",
  "部署名": "営業部営業2課",
  "役職・肩書": "課長",
  "URL": "http://example.com"
}
```
今回のサンプルの名刺ではほぼ正しく読み取れていますね。冒頭でも書きましたが、文字を読み取ってくれるだけではなく、会社名や部署名、役職など構造化して返してくれるところがChatGPTのすごいところです。名刺には「〒」のような郵便番号を示す記載がないのに、正しく郵便番号として読み取ってくれています。ただし、写真の撮り方によって文字が薄い場合や小さい場合は誤読があるようですのでご注意ください。まだ試していないのですが、読み取れなかった場合はNoneを入れるように指示すると誤読も減るかもしれません。

名刺に似たような病院の診察券を読み取ろうとすると、Errorが返ってきます。
```json:GTP-4oでの診察券の読み取り結果
{
  "Error": "画像が名刺ではありません。"
}
```
また、GPT-4 Turboで試しに実行してみると、構造化はしてくれているのですが、一部間違っていたり、読み取れていなかったりします。
```json:GTP-4-Turboでの名刺の読み取り結果
{
  "氏名": "名刺 太郎",
  "会社名": "株式会社名刺サンプル",
  "住所": "東京都新宿区下宿町2-3-2F共同ビル5階",
  "郵便番号": "123-4567",
  "電話番号": "Tel:03-1234-5677",
  "FAX番号": "Fax:03-1234-5678",
  "メールアドレス": "taro.meishi@example.com",
  "部署名": "",
  "役職・肩書": "",
  "URL": "https://example.com"
}
```
手作り名刺だけでなく、実在する名刺でも試しましたがほぼ同じような結果でした。みなさんもGPT-4oでの名刺読み取りをお試しください。


補足：費用としては、今回のサンプルの名刺では、トークン数は、prompt_tokens が 576、completion_tokens が140でしたので、GPT-4oですと1回あたりおおよそ0.7～0.8円程度かと思います（$1=150円換算）。

追記：Claude 3.5 Sonnetでも名刺を読み取ってみました

https://qiita.com/kanekoyuichi/items/ed822735c381a3c6fae8