yolo8+阿里千問圖片理解(華為簡易版小藝看世界)

? 實現目標

  1. 按下空格鍵 → 獲取攝像頭當前畫面;

  2. 將圖片上傳給 大模型 接口,讓其“看圖說話”;

  3. 獲取返回描述后,以字幕形式展示在圖像畫面上;

  4. 持續顯示識別結果,直到下次按空格。

🧠 需要準備的

  • ? Python 環境(3.8+)

  • ? OpenCV (cv2)

  • ? 阿里千問 API 的 Key

  • ? 安裝以下依賴:

pip install opencv-python requests pillow

需要同時保留 YOLOv8 的實時目標檢測,并在按下空格時截屏上傳給 大模型 接口做“看圖說話”,然后將返回的中文描述以字幕形式疊加在畫面中。

? 總體功能整合:

功能項描述
? YOLOv8 實時檢測保留,檢測物體并標注
? 空格觸發 千問 描述按下空格鍵拍照并上傳
? 顯示中文字幕將 DeepSeek 返回文字疊加到圖像上
? 中文字體支持使用 PIL 實現中文繪圖支持

import cv2
import numpy as np
from ultralytics import YOLO
from PIL import ImageFont, ImageDraw, Image
import requests
from io import BytesIO
import os
from openai import OpenAI
import base64
from typing import Optional, Union
import logging
import json
import time
from collections import deque
import threading# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# 文字
current_caption = ""
# 存儲字幕:(text, timestamp)
captions = deque()def img2text(image_path: str, prompt: str = "這張圖片是什么,一句話來描述") -> Optional[Union[str, dict]]:global current_caption  # 關鍵:修改全局變量"""將圖像轉換為文本描述Args:image_path (str): 圖像文件路徑prompt (str): 提示詞,默認為"這張圖片是什么"Returns:Optional[Union[str, dict]]: API響應結果,失敗時返回None"""# base64_image = image_to_base64(image_path)base64_image = image_pathif not base64_image:return Noneclient = OpenAI(api_key="sk-02128251fc324da9800c2553d67fa2ca",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",)try:completion = client.chat.completions.create(model="qwen-vl-plus-2025-01-25",messages=[{"role": "user","content": [{"type": "text", "text": prompt},{"type": "image_url","image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}]}],# stream=False,stream=True,stream_options={"include_usage": False})# print(completion.model_dump_json())full_text = ""# for chunk in completion:#     result = json.loads(chunk.model_dump_json())#     text = result["choices"][0]["delta"]["content"]#     current_caption = text#     print("響應:",current_caption)current_line = ""for chunk in completion:result = json.loads(chunk.model_dump_json())word = result["choices"][0]["delta"]["content"]current_line += wordprint("響應:", word)if "。" in word or "?" in word or "!" in word:timestamp = time.time()captions.append((current_line.strip(), timestamp))current_line = ""if current_line.strip():  # 最后一段未結束的也保留captions.append((current_line.strip(), time.time()))except Exception as e:logger.error(f"API調用錯誤: {e}")return None# ========== 中文字體 ==========
font_path = "font/AlimamaDaoLiTi-Regular.ttf"  # MacOS 示例
font = ImageFont.truetype(font_path, 28)# ========== 加載 YOLO 模型 ==========
model = YOLO("/Users/lianying/Desktop/yolo/yolov8n.pt")  # 可換為 yolov8n.pt/yolov8s.pt 等# ========== 攝像頭 ==========
cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()if not ret:break# YOLOv8 實時檢測results = model(frame, verbose=False)[0]annotated_frame = results.plot()  # 獲取帶標注的幀# 疊加 DeepSeek 返回的字幕(如果有)frame_pil = Image.fromarray(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(frame_pil)# 繪制字幕(只顯示近3秒的)now = time.time()frame_pil = Image.fromarray(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(frame_pil)y_offset = 30for caption, ts in list(captions):if now - ts <= 3:draw.rectangle([20, y_offset - 10, 1200, y_offset + 35], fill=(0, 0, 0, 128))draw.text((30, y_offset), caption, font=font, fill="yellow")print(caption)y_offset += 50else:captions.popleft()  # 刪除過期字幕frame_final = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR)cv2.imshow("YOLOv8 實時檢測 + 圖像描述(按空格)", frame_final)key = cv2.waitKey(1)if key == 27:  # ESC 退出breakelif key == 32:  # 空格觸發print("正在發送當前幀給 DeepSeek,請稍等...")_, buffer = cv2.imencode(".jpg", frame)image_bytes = BytesIO(buffer.tobytes())base64_str = base64.b64encode(buffer).decode('utf-8')print("Base64 (PNG):", base64_str[:50] + "...")img2text(base64_str)cap.release()
cv2.destroyAllWindows()

? 按下空格的時候為什么卡頓?

你這段代碼里,空格按下時調用的是這個同步過程:img2text(base64_str)

這一段是網絡請求 + AI處理 + 流式輸出 + 截圖處理,在本地和遠程服務器之間來回傳輸數據,因此:

  • YOLO 視頻幀不再刷新

  • cv2.imshow() 阻塞;

  • 顯示“卡住”不動,直到接口響應。

? 如何解決卡頓問題?

?? 使用 線程 異步處理圖片描述,避免主線程阻塞:

在你主程序中添加多線程工具包:

import threading

將觸發圖像描述的代碼改成:

elif key == 32:  # 空格觸發print("正在發送當前幀給 DeepSeek,請稍等...")# 異步處理圖像上傳與字幕生成def async_caption():_, buffer = cv2.imencode(".jpg", frame)base64_str = base64.b64encode(buffer).decode('utf-8')img2text(base64_str)  # 你之前寫的函數threading.Thread(target=async_caption, daemon=True).start()

? 整體流程優化后效果

  • 主線程負責視頻幀采集 + YOLO 檢測 + 字幕顯示

  • 圖像描述調用單獨在線程中執行,后臺上傳+接收,不影響主線程

  • 效果:按下空格后,攝像頭不會卡頓;字幕幾秒后自動出現

? 完整代碼

import cv2
import numpy as np
from ultralytics import YOLO
from PIL import ImageFont, ImageDraw, Image
import requests
from io import BytesIO
import os
from openai import OpenAI
import base64
from typing import Optional, Union
import logging
import json
import time
from collections import deque
import threading# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# 文字
current_caption = ""
# 存儲字幕:(text, timestamp)
captions = deque()def img2text(image_path: str, prompt: str = "這張圖片是什么,一句話來描述") -> Optional[Union[str, dict]]:global current_caption  # 關鍵:修改全局變量"""將圖像轉換為文本描述Args:image_path (str): 圖像文件路徑prompt (str): 提示詞,默認為"這張圖片是什么"Returns:Optional[Union[str, dict]]: API響應結果,失敗時返回None"""# base64_image = image_to_base64(image_path)base64_image = image_pathif not base64_image:return Noneclient = OpenAI(api_key="sk-02128251fc324da9800c2553d67fa2ca",base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",)try:completion = client.chat.completions.create(model="qwen-vl-plus-2025-01-25",messages=[{"role": "user","content": [{"type": "text", "text": prompt},{"type": "image_url","image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}]}],# stream=False,stream=True,stream_options={"include_usage": False})# print(completion.model_dump_json())full_text = ""# for chunk in completion:#     result = json.loads(chunk.model_dump_json())#     text = result["choices"][0]["delta"]["content"]#     current_caption = text#     print("響應:",current_caption)current_line = ""for chunk in completion:result = json.loads(chunk.model_dump_json())word = result["choices"][0]["delta"]["content"]current_line += wordprint("響應:", word)if "。" in word or "?" in word or "!" in word:timestamp = time.time()captions.append((current_line.strip(), timestamp))current_line = ""if current_line.strip():  # 最后一段未結束的也保留captions.append((current_line.strip(), time.time()))except Exception as e:logger.error(f"API調用錯誤: {e}")return None# ========== 中文字體 ==========
font_path = "font/AlimamaDaoLiTi-Regular.ttf"  # MacOS 示例
font = ImageFont.truetype(font_path, 28)# ========== 加載 YOLO 模型 ==========
model = YOLO("/Users/lianying/Desktop/yolo/yolov8n.pt")  # 可換為 yolov8n.pt/yolov8s.pt 等# ========== 攝像頭 ==========
cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()if not ret:break# YOLOv8 實時檢測results = model(frame, verbose=False)[0]annotated_frame = results.plot()  # 獲取帶標注的幀# 疊加 DeepSeek 返回的字幕(如果有)frame_pil = Image.fromarray(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(frame_pil)# 繪制字幕(只顯示近3秒的)now = time.time()frame_pil = Image.fromarray(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(frame_pil)y_offset = 30for caption, ts in list(captions):if now - ts <= 3:draw.rectangle([20, y_offset - 10, 1200, y_offset + 35], fill=(0, 0, 0, 128))draw.text((30, y_offset), caption, font=font, fill="yellow")print(caption)y_offset += 50else:captions.popleft()  # 刪除過期字幕# if current_caption:#     print(current_caption)# draw.text((30, 30), current_caption, font=font, fill="yellow")frame_final = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR)cv2.imshow("YOLOv8 實時檢測 + 圖像描述(按空格)", frame_final)key = cv2.waitKey(1)if key == 27:  # ESC 退出breakelif key == 32:  # 空格觸發print("正在發送當前幀給 DeepSeek,請稍等...")# 異步處理圖像上傳與字幕生成def async_caption():_, buffer = cv2.imencode(".jpg", frame)base64_str = base64.b64encode(buffer).decode('utf-8')img2text(base64_str)  # 你之前寫的函數threading.Thread(target=async_caption, daemon=True).start()# elif key == 32:  # 空格觸發#     print("正在發送當前幀給 DeepSeek,請稍等...")#     _, buffer = cv2.imencode(".jpg", frame)#     image_bytes = BytesIO(buffer.tobytes())#     base64_str = base64.b64encode(buffer).decode('utf-8')#     print("Base64 (PNG):", base64_str[:50] + "...")#     img2text(base64_str)cap.release()
cv2.destroyAllWindows()

???效果圖:

展示部分截圖,其他不方便展示

視頻:

yolo8實現小藝看世界

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916963.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916963.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916963.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【ee類保研面試】數學類---線性代數

25保研er&#xff0c;希望將自己的面試復習分享出來&#xff0c;供大家參考 part0—英語類 part1—通信類 part2—信號類 part3—高數類 part100—self項目準備 文章目錄線性代數知識點大全**1. 余子式與代數余子式****2. 行列式的含義****3. 矩陣的秩&#xff08;Rank&#xf…

在 Scintilla 中為 Squirrel 語言設置語法解析器的方法

Scintilla 作為一個強大的開源文本編輯控件&#xff0c;通過配置語法解析器&#xff0c;能夠對多種編程語言實現語法高亮、代碼折疊等實用功能。若要為新語言 Squirrel 設置語法解析器&#xff0c;可參考以下步驟&#xff1a;?創建 Lexer 源文件&#xff1a;Scintilla 通過 Le…

Go語言核心知識點補充

Go語言核心知識點補充 make函數、for循環與輸入處理詳解 在前幾章的內容中&#xff0c;我們介紹了Go語言的基礎語法、變量聲明、切片、循環等核心概念。但在實際開發中&#xff0c;一些細節性的知識點往往決定了代碼的健壯性與效率。 本文將針對前幾章涉及到的變量聲明與初始化…

AI服務器中,EEPROM有哪些部件使用,需要存儲哪些信息?

在AI服務器中&#xff0c;EEPROM&#xff08;電可擦可編程只讀存儲器&#xff09;主要用于存儲關鍵組件的配置數據、身份信息和校準參數。以下是主要組件及其存儲內容&#xff1a; 一、核心組件及存儲數據主板&#xff08;Baseboard Management Controller, BMC&#xff09; FR…

It學習資源下載

一.UI 8個高質量UI設計網站&#xff0c;靈感收集必備&#xff01;

Docker Compose :從入門到企業級部署

Docker Compose &#xff1a;從入門到企業級部署1. Docker Compose 核心概念1.1 Compose 架構全景圖2. 完整開發工作流2.1 典型開發流程2.2 多服務示例項目結構3. 核心配置詳解3.1 服務配置矩陣3.2 網絡拓撲示例4. 企業級部署方案4.1 多環境配置管理4.2 擴展部署架構5. 高級技巧…

1.2.vue插值表達式

在 Vue.js 中&#xff0c;插值表達式是用于在模板中顯示數據的一種方式。它使用雙大括號語法 {{ }} 來包裹需要輸出的變量或表達式的值。Vue 會自動將這些表達式的值插入到 HTML 文檔中相應的位置。插值表達式基本用法最基本的插值表達式形式就是直接在模板中引用 Vue 實例中的…

Python數據處理基礎(學習筆記分享)

Python數據處理入門 常用庫學習 numpy NumPy&#xff08;Numerical Python&#xff09; 是 Python 中用于高效數值計算的庫&#xff0c;核心是提供一個強大的 ndarray?&#xff08;多維數組&#xff09;對象&#xff0c;類似于 C/C 中的數組&#xff0c;但支持更豐富的操作&a…

力扣面試150題--顛倒二進制位

Day 89 題目描述思路 二進制的算法&#xff0c;將十進制轉化為二進制&#xff0c;有一點需要注意&#xff0c;直接采取庫函數轉化為二進制再反轉會出現問題&#xff08;這也是為什么我要補0的原因&#xff09;&#xff0c;因為轉化過去不滿足32位的二進制&#xff0c;前面不會當…

【ResNet50圖像分類部署至RK3588】模型訓練→轉換RKNN→開發板部署

已在GitHub開源與本博客同步的ResNet50v2_RK3588_Classificationt項目&#xff0c;地址&#xff1a;https://github.com/A7bert777/ResNet50v2_RK3588_Classification 詳細使用教程&#xff0c;可參考README.md或參考本博客第八章 模型部署 文章目錄一、項目回顧二、模型選擇介…

C# _泛型

目錄 泛型是什么? 泛型的主要優勢 創建一個泛型類 泛型方法 泛型是什么? 泛型是通過參數化來實現同一份代碼上操作多種數據類型 利用參數類型將參數的類型抽象化 從而實現靈活的復用 總結: 通過泛型可以實現在同一份代碼上操作多種數據類型的邏輯 將類和類中的成員定義…

Vue路由鉤子完全指南

Vue.js中的路由導航鉤子&#xff08;Navigation Guards&#xff09;主要用于在路由導航過程中進行攔截和處理&#xff0c;確保訪問控制和狀態管理。以下是主要分類及使用方法&#xff1a; 1. 全局鉤子函數 作用于整個路由實例&#xff0c;需在路由配置外定義&#xff1a; befor…

RAGFlow 登錄界面點擊登錄無反應,控制臺報錯 502 Bad Gateway 解決方法

遇到的問題 在使用RAGFlow的時候&#xff0c;登錄不進去&#xff0c;但是之前能登錄。 還出現了輸入地址直接進入工作界面&#xff0c;但是進行不了任何操作的bug&#xff1b;以及無法上傳文檔的問題&#xff08;其實都是因為沒登錄&#xff09;。 登陸界面報錯如圖顯示。 …

數據結構第3問:什么是線性表?

線性表 線性表由具有相同數據類型的n個元素構成&#xff0c;這些元素之間存在一一對應的線性關系。其中n為表長&#xff0c;當n0的時候線性表是一個空表。簡單來說&#xff0c;線性表中的元素排列成一條線&#xff0c;每個元素最多有一個直接的前驅和后繼&#xff08;除第一個和…

常見CMS 靶場復現

一、wordpass1.修改模版文件getshell搭建網站登錄網站后臺更改網站模版的相關文件寫入一句話木馬憑借路徑訪問/wp-content/themes/twentyfifteen/404.php/?aphpinfo();2.上傳夾帶木馬的主題getshell外觀-->主題-->添加-->上傳-->瀏覽-->安裝-->訪問木馬文件…

Elasticsearch - 倒排索引原理和簡易實現

倒排索引的功能設計倒排索引&#xff08;Inverted Index&#xff09;是一種高效的數據結構&#xff0c;常用于全文搜索和信息檢索系統。它的核心思想是將文檔中每個關鍵字&#xff08;term&#xff09;與包含該關鍵字的文檔列表進行映射。以下是實現倒排索引功能的設計步驟和代…

C#開發的Panel里控件拖放例子 - 開源研究系列文章

上次寫了Panel的分頁滾動控件( C#開發的Panel滾動分頁控件&#xff08;滑動版&#xff09; - 開源研究系列文章 - Lzhdims Fashion - 博客園 )&#xff0c;但是主要是想寫一個Panel里控件拖放的效果&#xff0c;然后分頁控件用于Panel里控件的分頁。此文這次寫的是控件拖放效果…

Thinkph6中常用的驗證方式實例

我們在使用thinkphp6中的數據驗證時&#xff0c;如果使用不多的話&#xff0c;會經常遇到校驗不對&#xff0c;在這個小問題上折騰很多&#xff0c;索引就不用了。我還不如直接寫if條件來的迅捷&#xff01;&#xff01;下面把常見的校驗方法進行一下整理&#xff1a;protected…

分享一個FPGA寄存器接口自動化工具

FPGA模塊越寫越多&#xff0c;規范性和可移植性卻堪憂。要是有一個工具可以根據模塊接口描述文件生成verilog和c頭文件就好了。苦苦搜尋找到了幾款免費的工具&#xff0c;SystemRDL、cheby和rggen。筆者學習了下cheby和reksio&#xff0c;reksio是gui版的cheby&#xff0c;這是…

小程序中事件對象的屬性與方法

在小程序中&#xff0c;事件處理函數的參數為事件對象&#xff08;通常命名為 e&#xff09;&#xff0c;包含了事件相關的詳細信息&#xff08;如事件類型、觸發元素、傳遞的數據等&#xff09;。事件對象的屬性和方法因事件類型&#xff08;如點擊、輸入、觸摸等&#xff09;…