项目概述
一般检测图片上的物体我们会选用OpenCV、YOLO之类解决方案,但是他们都需要在本机安装很多东西,这里讲解如何使用AI API检测图片上的物体,这样基本可以0配置就实现该功能。另外,利用了大模型的特性,我们可以追加自然语言对识别结果进行限定。
Google Geimini 1.5 Pro多模态功能,不仅能检测图片上的物体,还能输出物体的边框坐标,这样理论上来说你可以在输入的图片上画上框和标注目标内容,很实用的功能。
注意提示词里面要限制输出的格式是JSON这样的方便解析的格式,例如:
javascript 代码解读复制代码检测图片上的物体,将边界框作为 JSON 数组返回,数组的名称为对象名称及其边界框 [ymin, xmin, ymax, xmax]。例如 'name_1': [ymin, xmin, ymax, xmax],只返回JSON格式,一定不要使用Markdown格式
可以直接调用Google Geimini 1.5 Pro的API实现,输入如上的题词,会得到这样的结果,然后自己在图片上画框即可
json 代码解读复制代码{'tiger': [121, 52, 852, 769], 'man': [164, 681, 821, 934]}
有网友已经做了开源项目:AlexZhangji/bonding_w_geimini: experiments with different llms (github.com),上传图片就可以显示检测结果,但是需要自备 Gemini 的 Api Key。
本文只是对该项目做了简化,以及题词的优化。
代码实现
- 自备 Gemini 的 Api Key。
- 网络要能访问 Gemini
这里我们可以对检测物体追加自然语言的要求,比如: “仅识别图中的大型车辆”
代码运行后可以看到大模型准确的识别了我们的意图
如果没有追加限定条件会返回
json 代码解读复制代码{'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]}
追加限定条件 “仅识别图中的大型车辆”
会返回
json 代码解读复制代码{'Truck_1': [515, 371, 732, 467], 'Truck_2': [531, 460, 725, 566]}
安装依赖
代码解读复制代码pip install Pillow google-generativeai
配置文件config/gemini.config.json
json 代码解读复制代码{
"api_key": ""
}
源代码
python 代码解读复制代码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()
评论记录:
回复评论: