基于YOLOv8的手语手势识别系统:从环境配置到实时检测完整指南
在计算机视觉项目中,手语手势识别一直是个具有挑战性的应用场景。传统方法往往依赖复杂的特征工程,而基于深度学习的目标检测技术让这一任务变得更加高效准确。本文将基于YOLOv8构建完整的手语手势识别系统,从环境配置到模型训练,再到可视化界面开发,提供一站式解决方案。
无论你是刚接触深度学习的新手,还是希望将YOLOv8应用于实际项目的开发者,都能通过本文掌握完整的实现流程。我们将使用Python 3.8+、PyTorch 1.12+和Ultralytics YOLOv8框架,构建一个能够实时识别多种手语手势的检测系统。
1. 项目背景与技术选型
1.1 手语手势识别的意义与应用场景
手语是聋哑人士的主要交流方式,但大多数健听人士并不掌握手语,这造成了沟通障碍。基于计算机视觉的手语识别系统可以实时翻译手语动作,促进无障碍交流。该技术可应用于:
- 实时翻译系统:将手语实时转换为文字或语音
- 教育辅助工具:帮助学习手语的练习和评估
- 智能家居控制:通过特定手势控制智能设备
- 虚拟现实交互:在VR环境中实现自然的手势交互
1.2 YOLOv8的技术优势
YOLOv8是Ultralytics公司推出的最新目标检测模型,相比前代具有以下优势:
- 更高的检测精度:采用新的骨干网络和检测头设计
- 更快的推理速度:优化了网络结构和训练策略
- 更简单的使用接口:提供简洁的Python API
- 更好的扩展性:支持分类、检测、分割等多种任务
对于手语手势识别任务,YOLOv8能够平衡精度和速度,适合实时应用场景。
2. 环境配置与依赖安装
2.1 系统要求与基础环境
本项目支持Windows、Linux和macOS系统,推荐配置如下:
- 操作系统:Windows 10/11, Ubuntu 18.04+, macOS 10.15+
- Python版本:3.8-3.10(3.11可能存在兼容性问题)
- 内存:至少8GB,推荐16GB以上
- GPU:可选,但推荐NVIDIA GPU(CUDA 11.3+)
2.2 创建虚拟环境
为避免依赖冲突,建议使用conda或venv创建独立环境:
# 使用conda创建环境 conda create -n yolov8-signlang python=3.9 conda activate yolov8-signlang # 或使用venv python -m venv yolov8-signlang # Windows yolov8-signlang\Scripts\activate # Linux/macOS source yolov8-signlang/bin/activate2.3 安装核心依赖
创建requirements.txt文件,包含以下内容:
torch>=1.12.0 torchvision>=0.13.0 ultralytics>=8.0.0 opencv-python>=4.5.0 numpy>=1.21.0 pillow>=9.0.0 matplotlib>=3.5.0 seaborn>=0.11.0 pandas>=1.3.0 pyqt5>=5.15.0 qimage2ndarray>=1.9.0安装依赖包:
pip install -r requirements.txt2.4 GPU环境配置(可选)
如果使用NVIDIA GPU,需要安装对应版本的CUDA工具包:
# 检查CUDA版本 nvidia-smi # 安装GPU版本的PyTorch(根据CUDA版本选择) pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu1133. 数据集准备与预处理
3.1 手语手势数据集介绍
手语手势数据集应包含多种手势的标注信息,常见的类别包括:
- 数字手势(0-9)
- 字母手势(A-Z)
- 常用词汇手势(谢谢、你好、帮助等)
数据集结构应遵循YOLO格式:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── data.yaml3.2 数据标注格式
YOLO格式的标注文件为txt格式,每行表示一个目标:
<class_id> <x_center> <y_center> <width> <height>其中坐标值为归一化后的相对坐标(0-1之间)。
3.3 创建数据集配置文件
创建data.yaml文件,配置数据集信息:
# 数据集路径 path: /path/to/dataset train: images/train val: images/val # 类别数量 nc: 26 # 例如26个字母手势 # 类别名称 names: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']3.4 数据增强策略
为提高模型泛化能力,需要配置适当的数据增强:
from ultralytics import YOLO import albumentations as A from albumentations.pytorch import ToTensorV2 # 定义数据增强管道 train_transform = A.Compose([ A.RandomBrightnessContrast(p=0.5), A.HueSaturationValue(p=0.5), A.RandomGamma(p=0.5), A.Blur(blur_limit=3, p=0.3), A.MotionBlur(blur_limit=3, p=0.3), A.RandomRotate90(p=0.5), A.Flip(p=0.5), A.ShiftScaleRotate( shift_limit=0.0625, scale_limit=0.1, rotate_limit=15, p=0.5 ), ], bbox_params=A.BboxParams(format='yolo', label_fields=['class_labels']))4. YOLOv8模型训练
4.1 模型选择与初始化
YOLOv8提供多种规模的预训练模型:
from ultralytics import YOLO # 根据需求选择模型规模 # model = YOLO('yolov8n.pt') # 纳米版,速度最快 # model = YOLO('yolov8s.pt') # 小版 model = YOLO('yolov8m.pt') # 中版,推荐平衡精度和速度 # model = YOLO('yolov8l.pt') # 大版 # model = YOLO('yolov8x.pt') # 超大版 # 使用预训练权重初始化 model = YOLO('yolov8m.pt')4.2 训练参数配置
配置训练参数,优化模型性能:
# 训练配置 training_config = { 'data': 'data.yaml', # 数据集配置文件 'epochs': 100, # 训练轮数 'imgsz': 640, # 输入图像尺寸 'batch': 16, # 批次大小 'workers': 4, # 数据加载线程数 'device': '0', # 使用GPU('cpu'为CPU训练) 'optimizer': 'auto', # 优化器 'lr0': 0.01, # 初始学习率 'lrf': 0.01, # 最终学习率 'momentum': 0.937, # 动量 'weight_decay': 0.0005, # 权重衰减 'warmup_epochs': 3.0, # 热身轮数 'warmup_momentum': 0.8, # 热身动量 'box': 7.5, # 框损失权重 'cls': 0.5, # 分类损失权重 'dfl': 1.5, # DFL损失权重 'save_period': 10, # 保存间隔 'seed': 42, # 随机种子 'deterministic': True # 确定性训练 }4.3 开始模型训练
启动训练过程,并保存最佳模型:
# 开始训练 results = model.train( data='data.yaml', epochs=100, imgsz=640, batch=16, device='0', workers=4, save=True, exist_ok=True, pretrained=True, optimizer='auto', lr0=0.01, patience=10 # 早停耐心值 ) # 保存最终模型 model.save('runs/detect/train/weights/best.pt')4.4 训练过程监控
使用TensorBoard监控训练过程:
tensorboard --logdir runs/detect/train关键监控指标包括:
- 损失函数变化(box_loss, cls_loss, dfl_loss)
- 验证集精度(precision, recall, mAP50, mAP50-95)
- 学习率变化
5. 模型评估与性能分析
5.1 验证集评估
训练完成后,在验证集上评估模型性能:
# 加载最佳模型 model = YOLO('runs/detect/train/weights/best.pt') # 在验证集上评估 metrics = model.val( data='data.yaml', imgsz=640, batch=16, conf=0.25, # 置信度阈值 iou=0.6, # IoU阈值 device='0' ) print(f"mAP50: {metrics.box.map50}") print(f"mAP50-95: {metrics.box.map}") print(f"Precision: {metrics.box.mp}") print(f"Recall: {metrics.box.mr}")5.2 混淆矩阵分析
生成混淆矩阵,分析分类性能:
from ultralytics.utils import plots # 生成混淆矩阵 plots.confusion_matrix( model=model, save_dir='runs/detect/val', normalize=True, save=True )5.3 性能可视化
绘制PR曲线和检测示例:
import matplotlib.pyplot as plt # 绘制PR曲线 fig, ax = plt.subplots(1, 1, figsize=(9, 6)) for i in range(len(metrics.box.ap_class_index)): class_idx = metrics.box.ap_class_index[i] precision = metrics.box.p[:, i] # 所有阈值下的precision recall = metrics.box.r[:, i] # 所有阈值下的recall ax.plot(recall, precision, label=f'Class {class_idx}') ax.set_xlabel('Recall') ax.set_ylabel('Precision') ax.set_title('Precision-Recall Curve') ax.legend() plt.savefig('pr_curve.png', dpi=300, bbox_inches='tight')6. 推理检测与实时识别
6.1 单张图像检测
实现单张手语图像的检测功能:
import cv2 from ultralytics import YOLO class SignLanguageDetector: def __init__(self, model_path): self.model = YOLO(model_path) self.class_names = self.model.names def detect_image(self, image_path, conf_threshold=0.5): """检测单张图像""" results = self.model( source=image_path, conf=conf_threshold, imgsz=640, save=False ) # 处理检测结果 detections = [] for result in results: boxes = result.boxes if boxes is not None: for box in boxes: detection = { 'class_id': int(box.cls), 'class_name': self.class_names[int(box.cls)], 'confidence': float(box.conf), 'bbox': box.xyxy[0].tolist() # [x1, y1, x2, y2] } detections.append(detection) return detections, results[0].plot() # 返回标注后的图像 # 使用示例 detector = SignLanguageDetector('runs/detect/train/weights/best.pt') detections, annotated_img = detector.detect_image('test_image.jpg')6.2 实时视频流检测
实现摄像头实时手语检测:
import cv2 import time class RealTimeSignDetector: def __init__(self, model_path, camera_id=0): self.detector = SignLanguageDetector(model_path) self.cap = cv2.VideoCapture(camera_id) self.fps = 0 self.frame_count = 0 self.start_time = time.time() def run(self): """运行实时检测""" while True: ret, frame = self.cap.read() if not ret: break # 执行检测 detections, annotated_frame = self.detector.detect_frame(frame) # 计算FPS self.frame_count += 1 if self.frame_count >= 30: self.fps = 30 / (time.time() - self.start_time) self.start_time = time.time() self.frame_count = 0 # 显示FPS和检测结果 cv2.putText(annotated_frame, f'FPS: {self.fps:.1f}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) for i, det in enumerate(detections): label = f"{det['class_name']} {det['confidence']:.2f}" cv2.putText(annotated_frame, label, (10, 60 + i * 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow('Sign Language Detection', annotated_frame) # 按q退出 if cv2.waitKey(1) & 0xFF == ord('q'): break self.cap.release() cv2.destroyAllWindows() # 启动实时检测 detector = RealTimeSignDetector('runs/detect/train/weights/best.pt') detector.run()7. PyQt5图形界面开发
7.1 主界面设计
创建用户友好的图形界面:
import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QComboBox, QSlider, QGroupBox, QWidget, QMessageBox) from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QThread from PyQt5.QtGui import QPixmap, QImage import cv2 import numpy as np class DetectionThread(QThread): """检测线程,避免界面卡顿""" frame_processed = pyqtSignal(np.ndarray, list) def __init__(self, detector): super().__init__() self.detector = detector self.running = False self.frame = None def run(self): while self.running: if self.frame is not None: detections, processed_frame = self.detector.detect_frame(self.frame) self.frame_processed.emit(processed_frame, detections) self.msleep(30) # 控制检测频率 def update_frame(self, frame): self.frame = frame class SignLanguageApp(QMainWindow): def __init__(self): super().__init__() self.detector = SignLanguageDetector('runs/detect/train/weights/best.pt') self.init_ui() self.setup_camera() def init_ui(self): """初始化界面""" self.setWindowTitle('手语手势识别系统') self.setGeometry(100, 100, 1200, 800) # 中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout = QHBoxLayout() central_widget.setLayout(main_layout) # 左侧视频显示区域 left_layout = QVBoxLayout() # 视频显示标签 self.video_label = QLabel() self.video_label.setMinimumSize(640, 480) self.video_label.setStyleSheet("border: 2px solid gray;") self.video_label.setAlignment(Qt.AlignCenter) left_layout.addWidget(self.video_label) # 控制按钮 control_layout = QHBoxLayout() self.start_btn = QPushButton('开始检测') self.stop_btn = QPushButton('停止检测') self.capture_btn = QPushButton('拍照识别') self.start_btn.clicked.connect(self.start_detection) self.stop_btn.clicked.connect(self.stop_detection) self.capture_btn.clicked.connect(self.capture_image) control_layout.addWidget(self.start_btn) control_layout.addWidget(self.stop_btn) control_layout.addWidget(self.capture_btn) left_layout.addLayout(control_layout) # 右侧信息面板 right_layout = QVBoxLayout() # 检测结果组 result_group = QGroupBox('检测结果') result_layout = QVBoxLayout() self.result_label = QLabel('等待检测...') self.result_label.setStyleSheet("font-size: 14pt;") result_layout.addWidget(self.result_label) result_group.setLayout(result_layout) right_layout.addWidget(result_group) # 参数设置组 settings_group = QGroupBox('检测参数') settings_layout = QVBoxLayout() # 置信度阈值滑块 conf_layout = QHBoxLayout() conf_layout.addWidget(QLabel('置信度阈值:')) self.conf_slider = QSlider(Qt.Horizontal) self.conf_slider.setRange(10, 90) self.conf_slider.setValue(50) self.conf_slider.valueChanged.connect(self.update_conf_threshold) self.conf_label = QLabel('0.5') conf_layout.addWidget(self.conf_slider) conf_layout.addWidget(self.conf_label) settings_layout.addLayout(conf_layout) settings_group.setLayout(settings_layout) right_layout.addWidget(settings_group) # 添加到主布局 main_layout.addLayout(left_layout, 2) main_layout.addLayout(right_layout, 1) # 初始化状态 self.stop_btn.setEnabled(False) def setup_camera(self): """设置摄像头""" self.cap = cv2.VideoCapture(0) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 检测线程 self.detection_thread = DetectionThread(self.detector) self.detection_thread.frame_processed.connect(self.update_display) # 定时器用于读取摄像头帧 self.timer = QTimer() self.timer.timeout.connect(self.update_frame) def start_detection(self): """开始检测""" self.detection_thread.running = True self.detection_thread.start() self.timer.start(30) # 30ms间隔 self.start_btn.setEnabled(False) self.stop_btn.setEnabled(True) def stop_detection(self): """停止检测""" self.detection_thread.running = False self.timer.stop() self.start_btn.setEnabled(True) self.stop_btn.setEnabled(False) def update_frame(self): """更新摄像头帧""" ret, frame = self.cap.read() if ret: # 转换颜色空间 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) self.detection_thread.update_frame(frame) def update_display(self, frame, detections): """更新显示""" # 显示图像 h, w, ch = frame.shape bytes_per_line = ch * w q_img = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(q_img)) # 更新检测结果 if detections: result_text = "检测到手势:\n" for det in detections: result_text += f"{det['class_name']}: {det['confidence']:.3f}\n" self.result_label.setText(result_text) else: self.result_label.setText("未检测到手势") def update_conf_threshold(self, value): """更新置信度阈值""" conf = value / 100.0 self.conf_label.setText(f'{conf:.2f}') self.detector.conf_threshold = conf def capture_image(self): """拍照识别""" ret, frame = self.cap.read() if ret: # 保存图像并检测 filename = f"capture_{time.strftime('%Y%m%d_%H%M%S')}.jpg" cv2.imwrite(filename, frame) QMessageBox.information(self, "拍照成功", f"图像已保存为: {filename}") def closeEvent(self, event): """关闭事件""" self.stop_detection() if self.cap.isOpened(): self.cap.release() event.accept() if __name__ == '__main__': app = QApplication(sys.argv) window = SignLanguageApp() window.show() sys.exit(app.exec_())7.2 界面功能优化
增强用户体验的功能:
# 在SignLanguageApp类中添加以下方法 def create_shortcuts(self): """创建快捷键""" from PyQt5.QtWidgets import QShortcut from PyQt5.QtGui import QKeySequence # 空格键开始/停止 self.toggle_shortcut = QShortcut(QKeySequence(Qt.Key_Space), self) self.toggle_shortcut.activated.connect(self.toggle_detection) # ESC键退出 self.quit_shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.quit_shortcut.activated.connect(self.close) def toggle_detection(self): """切换检测状态""" if self.start_btn.isEnabled(): self.start_detection() else: self.stop_detection() def save_results(self, detections, image): """保存检测结果""" options = QFileDialog.Options() filename, _ = QFileDialog.getSaveFileName( self, "保存检测结果", "", "Images (*.png *.jpg);;All Files (*)", options=options) if filename: cv2.imwrite(filename, cv2.cvtColor(image, cv2.COLOR_RGB2BGR)) # 保存检测信息到文本文件 info_filename = filename.rsplit('.', 1)[0] + '_info.txt' with open(info_filename, 'w', encoding='utf-8') as f: f.write("检测结果:\n") for det in detections: f.write(f"{det['class_name']}: {det['confidence']:.3f}\n")8. 模型优化与部署
8.1 模型量化与加速
为了提升推理速度,可以进行模型量化:
def quantize_model(model_path, output_path): """模型量化""" model = YOLO(model_path) # 动态量化 model.export( format='onnx', imgsz=640, dynamic=True, simplify=True, opset=12 ) # 进一步优化(需要onnxruntime) import onnxruntime as ort from onnxruntime.quantization import quantize_dynamic quantize_dynamic( model_path.replace('.pt', '.onnx'), output_path, weight_type=ort.quantization.QuantType.QUInt8 ) # 使用量化模型 quantized_detector = YOLO('quantized_model.onnx')8.2 模型剪枝
减少模型参数,提升速度:
def prune_model(model, pruning_rate=0.2): """模型剪枝""" import torch.nn.utils.prune as prune # 对卷积层进行剪枝 for name, module in model.named_modules(): if isinstance(module, torch.nn.Conv2d): prune.l1_unstructured(module, name='weight', amount=pruning_rate) prune.remove(module, 'weight') return model8.3 多尺度检测优化
提升对不同大小手势的检测能力:
class MultiScaleDetector: def __init__(self, model_path, scales=[0.5, 1.0, 1.5]): self.model = YOLO(model_path) self.scales = scales def detect_multi_scale(self, image, conf_threshold=0.3): """多尺度检测""" all_detections = [] h, w = image.shape[:2] for scale in self.scales: # 调整图像尺寸 new_w, new_h = int(w * scale), int(h * scale) resized_img = cv2.resize(image, (new_w, new_h)) # 检测 results = self.model( source=resized_img, conf=conf_threshold, imgsz=640, verbose=False ) # 转换坐标回原始尺寸 for result in results: boxes = result.boxes if boxes is not None: for box in boxes: # 坐标转换 x1, y1, x2, y2 = box.xyxy[0].cpu().numpy() x1, x2 = x1 / scale, x2 / scale y1, y2 = y1 / scale, y2 / scale detection = { 'class_id': int(box.cls), 'class_name': self.model.names[int(box.cls)], 'confidence': float(box.conf), 'bbox': [x1, y1, x2, y2] } all_detections.append(detection) # 非极大值抑制去除重复检测 return self.nms(all_detections, iou_threshold=0.5) def nms(self, detections, iou_threshold=0.5): """非极大值抑制""" if not detections: return [] # 按置信度排序 detections.sort(key=lambda x: x['confidence'], reverse=True) keep = [] while detections: keep.append(detections[0]) if len(detections) == 1: break # 计算IoU ious = [] for i in range(1, len(detections)): iou = self.calculate_iou(keep[-1]['bbox'], detections[i]['bbox']) ious.append(iou) # 保留IoU低于阈值的检测 detections = [detections[i+1] for i, iou in enumerate(ious) if iou < iou_threshold] return keep def calculate_iou(self, box1, box2): """计算IoU""" x11, y1_1, x2_1, y2_1 = box1 x1_2, y1_2, x2_2, y2_2 = box2 # 计算交集区域 xi1 = max(x1_1, x1_2) yi1 = max(y1_1, y1_2) xi2 = min(x2_1, x2_2) yi2 = min(y2_1, y2_2) inter_area = max(0, xi2 - xi1) * max(0, yi2 - yi1) # 计算并集区域 box1_area = (x2_1 - x1_1) * (y2_1 - y1_1) box2_area = (x2_2 - x1_2) * (y2_2 - y1_2) union_area = box1_area + box2_area - inter_area return inter_area / union_area if union_area > 0 else 09. 常见问题与解决方案
9.1 训练过程中的常见问题
问题1:训练损失不下降或波动较大
解决方案:
- 检查学习率是否合适,尝试减小学习率
- 增加训练数据量或加强数据增强
- 检查数据标注质量,确保标注准确
- 调整批次大小,避免过小导致训练不稳定
# 学习率调度器配置 def configure_optimizer(model): optimizer = torch.optim.AdamW( model.parameters(), lr=0.001, # 更小的学习率 weight_decay=0.05 ) scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts( optimizer, T_0=10, T_mult=2 ) return optimizer, scheduler问题2:过拟合现象严重
解决方案:
- 增加正则化(Dropout、权重衰减)
- 使用早停机制
- 增加数据增强的多样性
- 减少模型复杂度或使用预训练权重
# 早停实现 class EarlyStopping: def __init__(self, patience=10, delta=0): self.patience = patience self.delta = delta self.best_score = None self.counter = 0 def __call__(self, val_loss): if self.best_score is None: self.best_score = val_loss elif val_loss > self.best_score - self.delta: self.counter += 1 if self.counter >= self.patience: return True else: self.best_score = val_loss self.counter = 0 return False9.2 推理检测中的问题
问题3:漏检或误检较多
解决方案:
- 调整置信度阈值和NMS阈值
- 检查训练数据是否覆盖所有场景
- 使用多尺度检测提升小目标检测能力
- 增加难例挖掘(Hard Negative Mining)
# 自适应阈值调整 class AdaptiveThreshold: def __init__(self, base_threshold=0.5, adaptation_rate=0.1): self.base_threshold = base_threshold self.adaptation_rate = adaptation_rate self.current_threshold = base_threshold def update(self, detection_count, target_count=3): """根据检测数量自适应调整阈值""" diff = detection_count - target_count self.current_threshold += self.adaptation_rate * diff self.current_threshold = max(0.1, min(0.9, self.current_threshold)) return self.current_threshold问题4:推理速度慢
解决方案:
- 使用更小的模型版本(YOLOv8n)
- 进行模型量化或剪枝
- 使用TensorRT加速
- 优化图像预处理和后处理
9.3 环境配置问题
问题5:CUDA内存不足
解决方案:
- 减小批次大小或图像尺寸
- 使用梯度累积模拟大批次
- 清理GPU缓存
import torch def clear_gpu_cache(): """清理GPU缓存""" if torch.cuda.is_available(): torch.cuda.empty_cache() torch.cuda.synchronize() # 在训练循环中定期清理 for epoch in range(epochs): if epoch % 10 == 0: clear_gpu_cache()10. 项目部署与生产化建议
10.1 桌面应用打包
使用PyInstaller打包为可执行文件:
# 创建打包脚本 package.py import PyInstaller.__main__ PyInstaller.__main__.run([ 'sign_language_app.py', '--name=手语识别系统', '--onefile', '--windowed', '--add-data=model_weights;model_weights', '--icon=app_icon.ico' ])10.2 Web服务部署
使用FastAPI创建REST API服务:
from fastapi import FastAPI, File, UploadFile from fastapi.responses import JSONResponse import uvicorn import cv2 import numpy as np app = FastAPI(title="手语识别API") # 全局模型实例 detector = None @app.on_event("startup") async def startup_event(): global detector detector = SignLanguageDetector('model_weights/best.pt') @app.post("/detect/image") async def detect_image(file: UploadFile = File(...)): """图像检测接口""" contents = await file.read() nparr = np.frombuffer(contents, np.uint8) image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) detections, _ = detector.detect_frame(image) return JSONResponse({ "detections": detections, "count": len(detections) }) @app.post("/detect/video") async def detect_video(file: UploadFile = File(...)): """视频检测接口""" # 保存临时文件 with open("temp_video.mp4", "wb") as f: f.write(await file.read()) # 处理视频 cap = cv2.VideoCapture("temp_video.mp4") results = [] while True: ret, frame = cap.read() if not ret: break detections, _ = detector.detect_frame(frame) results.append({ "frame": cap.get(cv2.CAP_PROP_POS_FRAMES), "detections": detections }) cap.release() return JSONResponse({"results": results}) if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)10.3 移动端部署考虑
对于移动端部署,需要考虑:
- 模型轻量化:使用YOLOv8n或自定义小模型
- 推理优化:使用NCNN、MNN等移动端推理框架
- 功耗控制:优化推理频率和图像分辨率
- 实时性保证:在性能和精度间找到平衡点
# 移动端优化配置 mobile_config = { 'imgsz': 320, # 更小的输入尺寸 'conf': 0.4, # 更高的置信