專案概述
一般檢測圖片上的物體我們會選用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]}
有網友已經做了開源專案:AlexZhangji/bonding_w_geimini: experiments with different llms (github.com),上傳圖片就可以顯示檢測結果,但是需要自備 Gemini 的 Api Key。
本文只是對該專案做了簡化,以及題詞的最佳化。
程式碼實現
自備 Gemini 的 Api Key。
網路要能訪問 Gemini
這裏我們可以對檢測物體追加自然語言的要求,比如: “僅識別圖中的大型車輛”
程式碼執行後可以看到大模型準確的識別了我們的意圖
如果沒有追加限定條件會返回
{'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]}
安裝依賴
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()