likes
comments
collection
share

使用自然语言进行图片物体检测一般检测图片上的物体会选用OpenCV、YOLO之类解决方案,这里讲解使用AI API检测图

作者站长头像
站长
· 阅读数 60

项目概述

一般检测图片上的物体我们会选用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]}

使用自然语言进行图片物体检测一般检测图片上的物体会选用OpenCV、YOLO之类解决方案,这里讲解使用AI API检测图

有网友已经做了开源项目:AlexZhangji/bonding_w_geimini: experiments with different llms (github.com),上传图片就可以显示检测结果,但是需要自备 Gemini 的 Api Key。

本文只是对该项目做了简化,以及题词的优化。

代码实现

  • 自备 Gemini 的 Api Key。
  • 网络要能访问 Gemini

这里我们可以对检测物体追加自然语言的要求,比如: “仅识别图中的大型车辆”

使用自然语言进行图片物体检测一般检测图片上的物体会选用OpenCV、YOLO之类解决方案,这里讲解使用AI API检测图

代码运行后可以看到大模型准确的识别了我们的意图

如果没有追加限定条件会返回

{'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]}

使用自然语言进行图片物体检测一般检测图片上的物体会选用OpenCV、YOLO之类解决方案,这里讲解使用AI API检测图

安装依赖

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()

转载自:https://juejin.cn/post/7425136905469116442
评论
请登录