【正點原子STM32MP257開發板試用】基于 YOLO 模型的物體識別
本文介紹了正點原子 STM32MP257 開發板基于 YOLO-v5 模型實現物體識別的項目設計。
項目介紹
YOLO 模型介紹;
官方 Demo 測試;
動態識別:加載 USB 攝像頭實現動態畫面的物體識別,結果顯示在板載LCD屏;
靜態識別:通過加載本地圖片并執行板端推理實現靜態圖片的識別,結果保存至本地路徑。
YOLO 模型
YOLOv5 是 YOLO 目標檢測算法系列的版本之一。作為計算機視覺領域的重要進展,YOLOv5 在目標檢測方面取得了顯著的性能提升。
與之前的版本相比,YOLOv5 采用了單階段檢測策略,通過一次前向傳播即可實現物體的定位和分類。它引入了 CSPDarknet53 架構作為基礎網絡,以提取豐富的特征信息,從而有效地增強了檢測的準確性。
YOLOv5 還在多尺度檢測上做出了改進,能夠檢測不同尺度的目標,從小物體到大物體。這在處理多樣化場景時表現出色。
此外,數據增強技術也被廣泛應用,通過隨機裁剪、顏色變換等方式增加了訓練數據的多樣性,提升了模型的泛化能力。
在計算效率方面,YOLOv5 在保持高檢測性能的同時,優化了模型的大小和速度,適用于嵌入式設備、移動端以及實時應用。其開源性質使得研究人員和開發者可以自由訪問代碼和預訓練模型,進行研究和定制。
總之,YOLOv5 通過單階段檢測、高效網絡架構、多尺度策略和數據增強等技術,極大地提升了目標檢測的性能。它在計算機視覺、自動駕駛、安防監控等領域具有廣泛的應用前景,為實時目標檢測任務帶來了新的解決方案。
YOLOv5 模型詳見: ultralytics/yolov5 .
模型檢測 Demo
下載例程并解壓,路徑為 01、程序源碼/05、AI例程源碼/07、yolov5n_weston/atk_yolov5_weston_demo.zip ;
該 Demo 例程使用 python 語言,因此將解壓后的 python 程序和模型等文件傳輸至開發板;
進入例程可執行文件對應目錄,終端執行如下指令
cd atk_yolov5_weston_demo/
python3 atk_yolov5_weston.py -m yolov5n-uint.nb
將目標識別物體置于攝像頭前,即可在屏幕上顯示識別結果、包括物體外框、推理時間以及置信度等信息。
動態識別
介紹了使用 USB 攝像頭獲取動態畫面并進行推理,進而實現物體識別的主要流程。
流程圖
代碼
終端執行指令 touch object_yolov5_camera.py 新建 python 程序文件,添加如下代碼
import argparse
from yolov5_pp import NeuralNetwork
import cv2
# YOLOv5的80個類別標簽
CLASS_NAMES = [
\"person\", \"bicycle\", \"car\", \"motorcycle\", \"airplane\", \"bus\", \"train\", \"truck\", \"boat\",
\"traffic light\", \"fire hydrant\", \"stop sign\", \"parking meter\", \"bench\", \"bird\", \"cat\",
\"dog\", \"horse\", \"sheep\", \"cow\", \"elephant\", \"bear\", \"zebra\", \"giraffe\", \"backpack\",
\"umbrella\", \"handbag\", \"tie\", \"suitcase\", \"frisbee\", \"skis\", \"snowboard\", \"sports ball\",
\"kite\", \"baseball bat\", \"baseball glove\", \"skateboard\", \"surfboard\", \"tennis racket\",
\"bottle\", \"wine glass\", \"cup\", \"fork\", \"knife\", \"spoon\", \"bowl\", \"banana\", \"apple\",
\"sandwich\", \"orange\", \"broccoli\", \"carrot\", \"hot dog\", \"pizza\", \"donut\", \"cake\",
\"chair\", \"couch\", \"potted plant\", \"bed\", \"dining table\", \"toilet\", \"tv\", \"laptop\",
\"mouse\", \"remote\", \"keyboard\", \"cell phone\", \"microwave\", \"oven\", \"toaster\", \"sink\",
\"refrigerator\", \"book\", \"clock\", \"vase\", \"scissors\", \"teddy bear\", \"hair drier\", \"toothbrush\"
]
def main():
# 參數解析
parser = argparse.ArgumentParser()
parser.add_argument(\"-m\", \"--model_file\", required=True, help=\"Path to YOLOv5 model file\")
args = parser.parse_args()
# 初始化神經網絡
nn = NeuralNetwork(
model_file=args.model_file,
score_threshold=0.45,
iou_threshold=0.5
)
# 攝像頭初始化
cap = cv2.VideoCapture(\"/dev/video7\")
if not cap.isOpened():
print(\"無法打開攝像頭\")
return
try:
while True:
ret, frame = cap.read()
if not ret:
break
# 預處理
input_img = cv2.resize(frame, (nn.width, nn.height))
input_img = cv2.cvtColor(input_img, cv2.COLOR_BGR2RGB)
# 執行推理
nn.launch_inference(input_img)
detections = nn.get_results()
# 繪制結果
for det in detections:
x, y, w, h, score, cls_id = det
x1, y1 = int(x * frame.shape[1]), int(y * frame.shape[0])
x2, y2 = int((x + w) * frame.shape[1]), int((y + h) * frame.shape[0])
# 獲取類別名稱
class_name = CLASS_NAMES[int(cls_id)] if int(cls_id) < len(CLASS_NAMES) else str(cls_id)
label = f\"{class_name}: {score:.2f}\"
# 繪制邊界框和標簽
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, label, (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 顯示結果
cv2.imshow(\"YOLOv5 Detection\", frame)
if cv2.waitKey(1) & 0xFF == ord(\'q\'):
break
finally:
cap.release()
cv2.destroyAllWindows()
if __name__ == \"__main__\":
main()
終端執行指令 touch yolov5_pp.py 新建 python 程序文件,添加如下代碼
from stai_mpu import stai_mpu_network
import numpy as np
class NeuralNetwork:
def __init__(self, model_file, score_threshold=0.45, iou_threshold=0.5):
\"\"\"初始化YOLOv5模型\"\"\"
self.model = stai_mpu_network(model_path=model_file)
self.score_thres = score_threshold
self.iou_thres = iou_threshold
# 獲取模型輸入輸出信息
input_info = self.model.get_input_infos()[0]
self.input_shape = input_info.get_shape()# (C,H,W)格式
self.height, self.width = self.input_shape[1], self.input_shape[2]
# 輸出處理參數(根據實際模型調整)
self.output_scale = 0.005922
self.output_zero_point = 3
def get_img_size(self):
\"\"\"返回模型輸入尺寸 (width, height, channels)\"\"\"
return (self.width, self.height, self.input_shape[0])
def launch_inference(self, img):
\"\"\"執行推理\"\"\"
input_data = np.expand_dims(img, axis=0)# 添加batch維度
self.model.set_input(0, input_data)
self.model.run()
def get_results(self):
\"\"\"獲取并后處理檢測結果\"\"\"
# 獲取原始輸出并反量化
raw_output = self.model.get_output(0)[0]
pred = (raw_output - self.output_zero_point) * self.output_scale
# 提取邊界框、置信度和類別
boxes = pred[:, :4]# [x_center, y_center, width, height]
conf = pred[:, 4]# 物體置信度
cls_probs = pred[:, 5:]# 類別概率
# 合并分數并過濾低置信度檢測
class_ids = np.argmax(cls_probs, axis=1)
scores = conf * cls_probs[np.arange(len(cls_probs)), class_ids]
mask = scores > self.score_thres
if not np.any(mask):
return []
boxes, scores, class_ids = boxes[mask], scores[mask], class_ids[mask]
# 轉換框格式 (center -> corner) 并執行NMS
boxes[:, :2] -= boxes[:, 2:] / 2# xywh to xyxy
keep = self.nms(boxes, scores)
return np.column_stack([boxes[keep], scores[keep], class_ids[keep]]).tolist()
def nms(self, boxes, scores):
\"\"\"簡化的非極大值抑制\"\"\"
x1, y1 = boxes[:, 0], boxes[:, 1]
x2, y2 = boxes[:, 0] + boxes[:, 2], boxes[:, 1] + boxes[:, 3]
areas = (x2 - x1) * (y2 - y1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w, h = np.maximum(0, xx2 - xx1), np.maximum(0, yy2 - yy1)
iou = (w * h) / (areas[i] + areas[order[1:]] - w * h)
order = order[np.where(iou <= self.iou_thres)[0] + 1]
return keep
連接 USB 攝像頭,終端執行指令 python3 object_yolov5_camera.py -m yolov5n-uint.nb ;
開啟攝像頭并運行畫面采集、物體識別推理和識別結果顯示等。
效果
動態效果見頂部視頻。
靜態識別
介紹了通過對本地圖片的 板端推理 實現物體識別的主要流程。
流程圖
代碼
終端執行指令 touch yolov5_inference.py 新建 python 程序文件,添加如下代碼
代碼中調用了前面動態識別建立的 yolov5_pp.py 文件
import argparse
import cv2
import os
from yolov5_pp import NeuralNetwork
# YOLOv5的80個類別標簽
CLASS_NAMES = [
\"person\", \"bicycle\", \"car\", \"motorcycle\", \"airplane\", \"bus\", \"train\", \"truck\", \"boat\",
\"traffic light\", \"fire hydrant\", \"stop sign\", \"parking meter\", \"bench\", \"bird\", \"cat\",
\"dog\", \"horse\", \"sheep\", \"cow\", \"elephant\", \"bear\", \"zebra\", \"giraffe\", \"backpack\",
\"umbrella\", \"handbag\", \"tie\", \"suitcase\", \"frisbee\", \"skis\", \"snowboard\", \"sports ball\",
\"kite\", \"baseball bat\", \"baseball glove\", \"skateboard\", \"surfboard\", \"tennis racket\",
\"bottle\", \"wine glass\", \"cup\", \"fork\", \"knife\", \"spoon\", \"bowl\", \"banana\", \"apple\",
\"sandwich\", \"orange\", \"broccoli\", \"carrot\", \"hot dog\", \"pizza\", \"donut\", \"cake\",
\"chair\", \"couch\", \"potted plant\", \"bed\", \"dining table\", \"toilet\", \"tv\", \"laptop\",
\"mouse\", \"remote\", \"keyboard\", \"cell phone\", \"microwave\", \"oven\", \"toaster\", \"sink\",
\"refrigerator\", \"book\", \"clock\", \"vase\", \"scissors\", \"teddy bear\", \"hair drier\", \"toothbrush\"
]
def process_image(model, image_path, input_width, input_height):
# 讀取圖片
frame = cv2.imread(image_path)
if frame is None:
print(f\"Error: 無法讀取圖片 {image_path}\")
return None, []
original_height, original_width = frame.shape[:2]
# 預處理
input_img = cv2.resize(frame, (input_width, input_height))
input_img = cv2.cvtColor(input_img, cv2.COLOR_BGR2RGB)
# 執行推理
model.launch_inference(input_img)
detections = model.get_results()
# 轉換坐標到原始圖像尺寸并繪制結果
results = []
for det in detections:
x, y, w, h, score, cls_id = det
x_abs = int(x * original_width)
y_abs = int(y * original_height)
w_abs = int(w * original_width)
h_abs = int(h * original_height)
class_name = CLASS_NAMES[int(cls_id)] if int(cls_id) < len(CLASS_NAMES) else str(cls_id)
results.append({
\"class\": class_name,
\"confidence\": float(score),
\"bbox\": [x_abs, y_abs, w_abs, h_abs]
})
# 在圖片上繪制檢測結果
cv2.rectangle(frame, (x_abs, y_abs), (x_abs+w_abs, y_abs+h_abs), (0, 255, 0), 2)
label = f\"{class_name}: {score:.2f}\"
cv2.putText(frame, label, (x_abs, y_abs-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
return frame, results
def main():
# 參數解析
parser = argparse.ArgumentParser()
parser.add_argument(\"-m\", \"--model_file\", required=True, help=\"YOLOv5模型文件路徑\")
parser.add_argument(\"-p\", \"--image_path\", required=True, help=\"待檢測圖片路徑\")
args = parser.parse_args()
# 檢查文件是否存在
if not os.path.exists(args.model_file):
print(f\"錯誤: 模型文件 {args.model_file} 不存在\")
return
if not os.path.exists(args.image_path):
print(f\"錯誤: 圖片文件 {args.image_path} 不存在\")
return
# 初始化模型
nn = NeuralNetwork(
model_file=args.model_file,
score_threshold=0.45,
iou_threshold=0.5
)
# 獲取模型輸入尺寸
input_width, input_height, _ = nn.get_img_size()
# 處理圖片
result_img, detections = process_image(nn, args.image_path, input_width, input_height)
if result_img is not None:
# 生成輸出文件名
dir_name, file_name = os.path.split(args.image_path)
name, ext = os.path.splitext(file_name)
output_path = f\"{name}_detected{ext}\"
# 保存帶標注的結果圖片
cv2.imwrite(output_path, result_img)
# 打印結構化檢測結果
print(\"\\\\n===== 檢測結果 =====\")
print(f\"輸入圖片: {args.image_path}\")
print(f\"輸出圖片: {output_path}\")
print(f\"檢測到 {len(detections)} 個對象:\\\\n\")
for i, det in enumerate(detections, 1):
print(f\"{i}. 類別: {det[\'class\']}\")
print(f\"置信度: {det[\'confidence\']:.4f}\")
print(f\"位置: x={det[\'bbox\'][0]}, y={det[\'bbox\'][1]}\")
print(f\"尺寸: w={det[\'bbox\'][2]}, h={det[\'bbox\'][3]}\\\\n\")
print(\"===================\")
if __name__ == \"__main__\":
main()
終端執行指令 python3 yolov5_inference.py -m ./model/yolov5n-uint.nb -p ./model/test3.jpg ;
加載模型和目標圖片、執行物體識別的推理、識別結果保存和打印等。
效果
運行指令結束后,生成識別結果圖片
同時終端打印識別結果信息
詳見底部視頻。
更多場景的推理測試如下
總結
本文介紹了正點原子 STM32MP257 開發板基于 YOLO-v5n 模型實現物體識別的項目設計,包括YOLOv5模型介紹、官方Demo例程測試、攝像頭采集畫面的動態識別、板端圖片靜態識別等,為該開發板在人工智能等相關領域的開發、產品的設計和應用等方面提供了參考。
發表于 06-21 16:32
評論