見出し画像

AzureからGPTを使ってみる | GPTに画像中の物体を数えさせる(Agent-YOLO編)

以前、GPTは画像中の物体の数を把握できないと記事にしました。

それなら物体検出で優秀なYOLOの力を借りればいい!
ということで、agentにYOLOを設定してGPTをパワーアップさせようと思います。


YOLOを動かしてみる

agentに組み込む前に、YOLO単体をpythonで動かしてみます。
ver.は、現在最新のYOLO10を、Googel colab上で動かします。
(モデルのバリエーションなど、詳しくはこちらを参照してください。)
使う画像は、以前と同じリンゴの画像を使います。

まずは、ultralyticsをインストールします。

!pip install ultralytics

最も軽量なモデルを設定しています。

from ultralytics import YOLO

# Load a pre-trained YOLOv10n model
model = YOLO("yolov10n.pt")

img = "/content/drive/MyDrive/"画像までのpath"/apple.jpg"
results = model(img)
for result in results:
    result.save(filename="result_yolo10_test01.jpg")  # save to disk

バッチリですね!CPU環境でも十分です。
何を何個検出できたかを書き出してみます。

from collections import Counter

classes = results[0].boxes.cls.tolist()
names = results[0].names

# カウント用のCounterを使用
class_counts = Counter()

# Iterate through the results and count occurrences
for cls in classes:
    name = names[int(cls)]
    class_counts[name] += 1

# 結果を表示
for name, count in class_counts.items():
    print(f"{name}: {count}個")

#apple: 25個

注意しないといけない点は、使用している学習済みモデルが検出できる物体は限られているので、教育に使われていない物体は検出できません。

LangchainでAgentにYOLOを設定する

agentの設定はこちらも参考にしてください。
GPT4oを使います。

 #langchain =0.2.7   #langchain -core=0.2.12   #langchain -openai=0.1.14   

from langchain_openai import AzureChatOpenAI
from dotenv import load_dotenv
import os

# OpenAI APIキーの設定
dotenv_path = ".env"
load_dotenv(dotenv_path)

OPENAI_API_BASE = os.getenv('OPENAI_API_BASE')
OPENAI_API_VERSION = os.getenv('OPENAI_API_VERSION')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')

TAVILY_API_KEY = os.getenv('TAVILY_API_KEY')

os.environ["AZURE_OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["AZURE_OPENAI_ENDPOINT"] = OPENAI_API_BASE

llm = AzureChatOpenAI(
    api_version=OPENAI_API_VERSION, 
    azure_deployment="gpt4o" # azure_deployment = "deployment_name"
    )

デコレーターを使ってtoolを設定します。
toolには、YOLOとGPT4oのマルチモーダルLLMを設定して、画像の情景と物体数を回答できるようにしてみます。

import base64
from ultralytics import YOLO
from collections import Counter

from langchain_core.output_parsers import StrOutputParser
from langchain.tools import tool# tool デコレーターをインポート


#####物体を検出するagentをgpt4oで設定#####

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

@tool
def OjectDetectTool(image_path: str = None):
  """A tool that recognizes images and describes what they depict.Input should be a Image path."""

  base64_image = encode_image(image_path)
  image_template = {
                "type": "image_url",
                "image_url": {
                  "url": f"data:image/jpeg;base64,{base64_image}"
                }
              }  
  
  system_prompt = "提供された画像について説明してください。"
  
  messages=[
          {"role": "system", "content": system_prompt},
          {"role": "user", "content": [
              #{"type": "text", "text": "Describe the images as an alternative text"},
              image_template
          ]}
      ]  
  
  chain = llm | StrOutputParser()
     
  return chain.invoke(messages)


#####物体の数をカウントするagentをyoloで設定#####

def detect_objects_in_image(image_path):
    # YOLOモデルのロード
    #model = YOLO("yolov9c.pt")
    model = YOLO("yolov10n.pt")
    
    # 画像に対する推論を実行
    results = model(image_path)
    
    # 検出結果を取得
    classes = results[0].boxes.cls.tolist()
    names = results[0].names

    # カウント用のCounterを使用
    class_counts = Counter()
    # Iterate through the results and count occurrences
    for cls in classes:
        name = names[int(cls)]
        class_counts[name] += 1    
        
    # 各クラスごとの物体数をカウント
    object_counts = {}  # 辞書として定義

    # 結果を辞書に追加
    for name, count in class_counts.items():
        object_counts[name] = f"{count}個"

    print(object_counts) 
    
    return object_counts


@tool
def OjectCountTool(image_path: str = None):
    """A tool that count the number of objects in the image and return the counts.Input should be a Image path."""
    object_counts = detect_objects_in_image(image_path)
    return object_counts

Agentを実行します

from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate

tools = [OjectCountTool,OjectDetectTool ]

prompt = ChatPromptTemplate.from_messages([
    ("system", "you're a helpful assistant"), 
    ("human", "{input}"), 
    ("placeholder", "{agent_scratchpad}"),
])

# Tool Calling エージェントの準備
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Tool Calling エージェントの実行
agent_executor.invoke({"input": "'apple.jpg'には何が映っていますか?数も教えてください。", })
> Entering new AgentExecutor chain...

Invoking: `OjectDetectTool` with `{'image_path': 'apple.jpg'}`


The image appears to show apples arranged in a rectangular frame. The apples are of various types, including red, green, and yellow varieties, and are evenly spaced around the blurred central area. The arrangement is neatly organized, creating a visually appealing border around the central blurred portion.
Invoking: `OjectCountTool` with `{'image_path': 'apple.jpg'}`



image 1/1 c:\Users\....\apple.jpg: 640x640 25 apples, 50.5ms
Speed: 6.0ms preprocess, 50.5ms inference, 1.0ms postprocess per image at shape (1, 3, 640, 640)
{'apple': '25個'}
{'apple': '25個'}画像には、赤、緑、黄色の様々な種類のリンゴが長方形の枠に並べられている様子が映っています。リンゴの数は25個です。

> Finished chain.

{'input': "'apple.jpg'には何が映っていますか?数も教えてください。",
 'output': '画像には、赤、緑、黄色の様々な種類のリンゴが長方形の枠に並べられている様子が映っています。リンゴの数は25個です。'}

いいですね!ちゃんと正確に回答できています。
複数の物体が映る画像でも試してみます。使ったのは以下の画像です。

> Entering new AgentExecutor chain...

Invoking: `OjectDetectTool` with `{'image_path': 'test01.jpg'}`


The image depicts a busy urban street scene. A person riding a bicycle is in the foreground, crossing the street. The bicycle has a large basket in the front filled with flowers and greenery. Several cars are waiting at the intersection, including a white truck, a silver car, a white car, and a black Toyota Prius. The background shows tall buildings, some with glass facades, and there are street signs and traffic lights visible. The street is bustling with activity, typical of a city environment.
Invoking: `OjectCountTool` with `{'image_path': 'test01.jpg'}`



image 1/1 c:\Users\.......\test01.jpg: 448x640 1 person, 1 bicycle, 7 cars, 1 truck, 22.0ms
Speed: 2.0ms preprocess, 22.0ms inference, 2.0ms postprocess per image at shape (1, 3, 448, 640)
{'car': '7個', 'person': '1個', 'bicycle': '1個', 'truck': '1個'}
{'car': '7個', 'person': '1個', 'bicycle': '1個', 'truck': '1個'}画像には、以下のものが映っています:

- 車: 7台
- 人: 1人
- 自転車: 1台
- トラック: 1台

情景の説明:
この画像は賑やかな都市の街並みを描いています。手前には自転車に乗った人が道を横断しており、自転車の前かごには花や緑が詰まっています。交差点では、白いトラック、銀色の車、白い車、そして黒いトヨタ・プリウスを含む数台の車が待っています。背景にはガラスのファサードがある高い建物が見え、街路標識や信号機も確認できます。このような街の情景は、典型的な都市環境の賑わいを表しています。

> Finished chain.

{'input': "'test01.jpg'には何が、何個映っているかの情報を含めて、画像の情景を説明したください。",
 'output': '画像には、以下のものが映っています:\n\n- 車: 7台\n- 人: 1人\n- 自転車: 1台\n- トラック: 1台\n\n情景の説明:\nこの画像は賑やかな都市の街並みを描いています。手前には自転車に乗った人が道を横断しており、自転車の前かごには花や緑が詰まっています。交差点では、白いトラック、銀色の車、白い車、そして黒いトヨタ・プリウスを含む数台の車が待っています。背景にはガラスのファサードがある高い建物が見え、街路標識や信号機も確認できます。このような街の情景は、典型的な都市環境の賑わいを表しています。'}

数の情報と情景が分割されている印象がありますが、なかなかいい感じかと思うのですが、どうでしょうか?

YOLOのモデルは独自データを使ってカスタムできるので、使い方を限定させれば、かなりおもしろいLLMができるのではないでしょうか?

この記事が気に入ったらサポートをしてみませんか?