切換語言為:簡體
使用自然語言 Python 呼叫 Geimini 介面實現圖片內容檢測

使用自然語言 Python 呼叫 Geimini 介面實現圖片內容檢測

  • 爱糖宝
  • 2024-10-14
  • 2050
  • 0
  • 0

專案概述

一般檢測圖片上的物體我們會選用OpenCV、YOLO之類解決方案,但是他們都需要在本機安裝很多東西,這裏講解如何使用AI API檢測圖片上的物體,這樣基本可以0配置就實現該功能。另外,利用了大模型的特性,我們可以追加自然語言對識別結果進行限定。

Google Geimini 1.5 Pro多模態功能,不僅能檢測圖片上的物體,還能輸出物體的邊框座標,這樣理論上來說你可以在輸入的圖片上畫上框和標註目標內容,很實用的功能。

注意提示詞裡面要限制輸出的格式是JSON這樣的方便解析的格式,例如:

檢測圖片上的物體,將邊界框作為 JSON 陣列返回,陣列的名稱為物件名稱及其邊界框 [ymin, xmin, ymax, xmax]。例如 'name_1': [ymin, xmin, ymax, xmax],只返回JSON格式,一定不要使用Markdown格式

可以直接呼叫Google Geimini 1.5 Pro的API實現,輸入如上的題詞,會得到這樣的結果,然後自己在圖片上畫框即可

{'tiger': [121, 52, 852, 769], 'man': [164, 681, 821, 934]}

使用自然語言 Python 呼叫 Geimini 介面實現圖片內容檢測

有網友已經做了開源專案:AlexZhangji/bonding_w_geimini: experiments with different llms (github.com),上傳圖片就可以顯示檢測結果,但是需要自備 Gemini 的 Api Key。

本文只是對該專案做了簡化,以及題詞的最佳化。

程式碼實現

  • 自備 Gemini 的 Api Key。

  • 網路要能訪問 Gemini

這裏我們可以對檢測物體追加自然語言的要求,比如: “僅識別圖中的大型車輛”

使用自然語言 Python 呼叫 Geimini 介面實現圖片內容檢測

程式碼執行後可以看到大模型準確的識別了我們的意圖

如果沒有追加限定條件會返回

{'car_1': [573, 64, 791, 253], 'car_2': [605, 233, 722, 322], 'car_3': [622, 307, 696, 368], 'truck_1': [506, 368, 714, 554], 'truck_2': [523, 453, 728, 567], 'car_4': [622, 351, 685, 401]}

追加限定條件 “僅識別圖中的大型車輛” 會返回

{'Truck_1': [515, 371, 732, 467], 'Truck_2': [531, 460, 725, 566]}

使用自然語言 Python 呼叫 Geimini 介面實現圖片內容檢測

安裝依賴

pip install Pillow google-generativeai

配置檔案config/gemini.config.json

{
    "api_key": ""
}

原始碼

import json
from PIL import Image, ImageDraw, ImageFont
import google.generativeai as genai
import random
import os
from google.api_core.exceptions import GoogleAPIError


def resize_image(image, max_size=800):
    """
    調整影象大小,保持縱橫比。如果任何一個維度超過 max_size,則將其縮小。
    """
    width, height = image.size
    if width > height:
        if width > max_size:
            height = int((height * max_size) / width)
            width = max_size
    else:
        if height > max_size:
            width = int((width * max_size) / height)
            height = max_size
    return image.resize((width, height))


def generate_random_color():
    """
    生成十六進制格式的隨機顏色。
    """
    return "#{:06x}".format(random.randint(0, 0xFFFFFF))


def get_font(size=20):
    """
    獲取用於繪製文字的字型物件。嘗試載入 NotoSansCJK-Regular.ttc。
    如果不可用,則回退到預設字型。
    """
    font_files = ["NotoSansCJK-Regular.ttc"]

    for font_file in font_files:
        if os.path.exists(font_file):
            try:
                return ImageFont.truetype(font_file, size)
            except IOError:
                continue

    return ImageFont.load_default()


def draw_text_with_outline(draw, text, position, font, text_color, outline_color):
    """
    在影象上繪製帶有輪廓的文字。
    """
    x, y = position
    # 繪製輪廓
    draw.text((x - 1, y - 1), text, font=font, fill=outline_color)
    draw.text((x + 1, y - 1), text, font=font, fill=outline_color)
    draw.text((x - 1, y + 1), text, font=font, fill=outline_color)
    draw.text((x + 1, y + 1), text, font=font, fill=outline_color)
    # 繪製文字
    draw.text(position, text, font=font, fill=text_color)


def draw_bounding_boxes(image, bboxes):
    """
    使用 bboxes 字典中提供的座標在影象上繪製邊界框。
    """
    draw = ImageDraw.Draw(image)
    width, height = image.size

    font = get_font(20)

    for label, bbox in bboxes.items():
        color = generate_random_color()
        ymin, xmin, ymax, xmax = [
            coord / 1000 * dim
            for coord, dim in zip(bbox, [height, width, height, width])
        ]

        draw.rectangle([xmin, ymin, xmax, ymax], outline=color, width=3)

        # 計算標籤所需的區域並新增填充
        label_bbox = font.getbbox(label)
        label_width = label_bbox[2] - label_bbox[0] + 10  # 新增填充
        label_height = label_bbox[3] - label_bbox[1] + 10  # 新增填充

        if xmax - xmin < label_width:
            xmax = xmin + label_width
        if ymax - ymin < label_height:
            ymax = ymin + label_height

        draw.rectangle(
            [xmin, ymin, xmin + label_width, ymin + label_height], fill=color
        )
        draw_text_with_outline(
            draw,
            label,
            (xmin + 5, ymin + 5),
            font,
            text_color="white",
            outline_color="black",
        )  # 為白色文字新增黑色輪廓
    return image


def extract_bounding_boxes(text):
    """
    從給定的文字中提取邊界框,該文字應為 JSON 格式。
    """
    try:
        bboxes = json.loads(text)
        return bboxes
    except json.JSONDecodeError:
        import re

        pattern = r'"([^"]+)":\s*[(\d+),\s*(\d+),\s*(\d+),\s*(\d+)]'
        matches = re.findall(pattern, text)
        return {label: list(map(int, coords)) for label, *coords in matches}


def main():
    api_key = ""
    with open("./config/gemini.config.json", "r") as f:
        config = json.load(f)
        api_key = config.get("api_key")
    # 定義傳送給 Google Gemini 的提示,要求其檢測影象中的物體並以 JSON 格式返回邊界框。
    prompt = "檢測圖片上的物體,將邊界框作為 JSON 陣列返回,陣列的名稱為物件名稱及其邊界框 [ymin, xmin, ymax, xmax]。例如 'name_1': [ymin, xmin, ymax, xmax],只返回JSON格式,一定不要使用Markdown格式。"
    # 追加自然語言要求
    prompt += "\n僅識別圖中的大型車輛"

    # 開啟要進行物體檢測的影象。
    original_image = Image.open("./data/物體檢測2.jpg")

    # 調整影象大小以加快處理速度。
    resized_image = resize_image(original_image)
    # 配置 Google Gemini API 金鑰。
    genai.configure(api_key=api_key)
    # 選擇要使用的 Google Gemini 模型。
    model = genai.GenerativeModel("gemini-1.5-pro-exp-0827")

    try:
        # 將提示和調整大小後的影象傳送到 Google Gemini API。
        response = model.generate_content([prompt, resized_image])
    except GoogleAPIError:
        return

    # 從 Google Gemini API 的響應中提取邊界框。
    bboxes = extract_bounding_boxes(response.text)
    print(bboxes)
    # 如果檢測到任何邊界框,則在影象上繪製它們並顯示影象。
    if bboxes:
        image_with_boxes = draw_bounding_boxes(resized_image.copy(), bboxes)
        image_with_boxes.show()


if __name__ == "__main__":
    main()


0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.