あきろぐ

いろいろめもするよ🐈🐈🐈

Google Cloud Vision APIとOpenAIのFunction Callingを使ってみた

今回はGoogleのCloud Vision APIを使って画像からテキストを検出し、そのテキストとOpenAIのFunction Callingを用いて構造化データを抽出していきたいと思います。

Cloud Vision API

Googleが提供している画像を分析し情報を抽出できるサービスです。OCRによって画像からテキストを検出したり、ランドマークやロゴなども検出することが可能となっています。

cloud.google.com

テキスト検出の場合、最初の1000ユニット(画像)までは無料で使えます。

cloud.google.com

OpenAI Function Calling

OpenAIが提供しているChat APIに追加された機能です。

APIを呼び出すときに関数を定義することで構造化されたデータ、つまりJSONオブジェクトとしてレスポンスを受け取ることができます。

自分達のアプリケーションコードに組み込む時にJSONオブジェクトとしてレスポンスを受け取ることができれば、何かと便利ですよね。

https://platform.openai.com/docs/guides/function-callingplatform.openai.com

コストは使用するモデルによって異なりますが、InputとOutputの両方でコストがかかります。(1000トークンあたり)

openai.com

トークンはOpenAIの自然言語モデルでテキストを処理するときの単位のようなもので、以下のサイトからトークン数を確認することができます。

platform.openai.com

日本語の場合、ひらがなやカタカナは1文字=1トークンですが、漢字は2-3トークン、絵文字は3トークンほど消費するようです。

Cloud Vision APIOCR処理を実装する

では、実際にOCRでテキスト検出するためのコードをPython3.9で書いていきます。

まずCloud Visionクライアントライブラリをインストールし、Google Cloud上でサービスアカウントを発行し、APIキーをJSONファイルとしてローカルにダウンロードします。

サービスアカウントの発行手順等はこちらの記事は分かりやすかったです。

dev.classmethod.jp

$ pip install --upgrade google-cloud-vision

クレデンシャルファイルのパスを環境変数として設定します。

# .zshrc
export GOOGLE_APPLICATION_CREDENTIALS='<json_file_path>'

今回テキスト検出する対象は、私の好きな長谷川あかりさんのこちらのレシピ画像を使ってみました。(このレシピお気に入りの1つです)

実際に書いたコードはこちら。

from google.cloud import vision

client = vision.ImageAnnotatorClient()
file_paths = [
    '/Users/user_a/Downloads/recipe01.jpeg',
    '/Users/user_a/Downloads/recipe02.jpeg',
    '/Users/user_a/Downloads/recipe03.jpeg',
    '/Users/user_a/Downloads/recipe04.jpeg'
]

output_text = ''

for file_path in file_paths:
    with open(file_path, 'rb') as image_file:
        content = image_file.read()

    image = vision.Image(content=content)

    response = client.document_text_detection(
        image=image,
        image_context={'language_hints': ['ja']}
    )
    output_text += response.full_text_annotation.text + '\n'

print(output_text)

出力結果はこちら。

パッケージの細かい文字まで検出してくれたようです。画像にない文字も検出されていることもありますが、これだけの精度で検出されれば問題ないですね。

ごはんに合う!
鶏むね肉とごぼうの柚子胡椒クリームシチュー
※2人分
鶏むね肉150g (1cmくらいの角切り)
ごぼう150g (四つ割りにして端から1cmに切る)
小麦粉大さじ2
バター10g
料理酒大さじ2
牛乳200ml
柚子胡椒小さじ1
塩適量
BAR
elna
000
100
日清
クッキング
粒 フラワー
薄力小麦粉
タイプの
M
Oxx
NET150g
柚子こしょう
24.10/8/201
S&B
牛乳
明治
ナチュラルテイスト
生乳100%使用
おいしい牛乳
新鮮な生乳のおいしさ、そのまま
要冷蔵(10℃以下)
450ml
450ml
鶏むね肉に
塩ひとつまみ (1g)を
ふって馴染ませたら、
小麦粉を満遍なくまぶす
1
水150mlと料理酒を加え、
煮立ったら鶏肉を加えて
よく混ぜる。
蓋をして弱めの中火で
5分煮込む。
フライパンに
バターを加えて
中火で溶かし
ごぼうを入れる。
塩ひとつまみをふって、
ごぼうのいい香りが
してくるまで炒める
2
4
牛乳と柚子胡椒を加えて
とろみがつくまで
強めの中火でおよそ
1分30秒~2分ほど煮詰めたら、
火を止める。
塩少々で味をつけたら完成。
※味を見て足りなければ
塩で調える。
ごぼうの香りとしっとり鶏むね肉、
文句なしのおいしさ!
柚子胡椒の塩気でごはんが進みます。

Function Callingで構造化データを抽出する

まず、必要なOpenAIのPythonライブラリをインストールします。

$ pip install --upgrade openai

APIキーを発行し、環境変数として設定します。

# .zshrc
export OPENAI_API_KEY='your-api-key-here'

OCRで得られたテキストデータを用いて、以下のようにFunctionを定義して構造化データを出力しています。

Functionの定義はこちらの記事を参考にしました。

gihyo.jp

import json
from openai import OpenAI

client = OpenAI()

# テキストデータからどのような構造化データを返してほしいか定義
functions = [
    {
        "name": "recipe_ingredients",
        "description": """これはテキストデータからレシピの情報を抽出する処理です。
        レシピ名、レシピに必要な材料名、材料の種類、材料の量を抽出します。
        """,
        "parameters": {
            "type": "object",
            "properties": {
                "recipe_name": {
                    "type": "string",
                    "description": "レシピ名"
                },
                "ingredients": {
                    "type": "array",
                    "description": "レシピに必要な材料一覧",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "description": "材料名"
                            },
                            "type": {
                                "type": "string",
                                "description": "材料の種類"
                            },
                            "amount": {
                                "type": "string",
                                "description": "材料の量"
                            }
                        }
                    }
                }
            },
            "required": ["recipe_name"]
        }
    }
]

# ここで渡すテキストデータを指定
# 先ほど出力したテキストデータを読み込む
messages = [
    {
      "role": "user",
      "content": output_text
    }
]

# openaiにモデル、function、messagesを指定しリクエスト送る
response = client.chat.completions.create(
  model="gpt-3.5-turbo-1106",
  messages=messages,
  functions=functions,
  temperature=1,
  max_tokens=2024,
  function_call={"name": "recipe_ingredients"}
)

# 構造化データ表示
str = response.choices[0].message.function_call.arguments
json_str = json.dumps(str)
print(json.loads(json_str))

結果はこちら。正確にレシピ名と材料を認識し、JSONオブジェクトを返してくれました! これはいいですね!

{
  "recipe_name": "鶏むね肉とごぼうの柚子胡椒クリームシチュー",
  "ingredients": [
    {
      "name": "鶏むね肉",
      "amount": "150g",
      "type": "肉"
    },
    {
      "name": "ごぼう",
      "amount": "150g",
      "type": "野菜"
    },
    {
      "name": "小麦粉",
      "amount": "大さじ2",
      "type": "調味料"
    },
    {
      "name": "バター",
      "amount": "10g",
      "type": "調味料"
    },
    {
      "name": "料理酒",
      "amount": "大さじ2",
      "type": "調味料"
    },
    {
      "name": "牛乳",
      "amount": "200ml",
      "type": "乳製品"
    },
    {
      "name": "柚子胡椒",
      "amount": "小さじ1",
      "type": "調味料"
    },
    {
      "name": "塩",
      "amount": "適量",
      "type": "調味料"
    }
  ]
}

期待以上の結果が得られたので、とても満足しました。様々な場面で活用できそうです。