YOLO目标检测实战:从数据标注到模型部署全流程指南
🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度
零基础训练自己的YOLO目标检测模型!从数据采集到本地部署,全流程保姆级教程
目标检测作为计算机视觉的核心技术之一,在安防监控、自动驾驶、工业质检等领域有着广泛应用。YOLO(You Only Look Once)作为单阶段目标检测算法的代表,以其速度快、精度高的特点深受开发者喜爱。很多初学者想入门YOLO却苦于不知从何开始,本文将带你完整走一遍YOLO模型训练的全流程,从数据采集到模型部署,每个步骤都提供详细的操作指南。
无论你是计算机视觉新手,还是有一定基础想系统学习YOLO的开发者,这篇教程都能帮你快速上手。我们将使用最流行的Ultralytics YOLOv8版本,结合Label Studio标注工具,打造一个完整的实战项目。
1. YOLO目标检测基础概念
1.1 什么是YOLO目标检测
YOLO(You Only Look Once)是一种基于深度学习的实时目标检测算法,与传统的两阶段检测方法不同,YOLO将目标检测任务视为一个回归问题,直接在单个神经网络中预测边界框和类别概率。
YOLO的核心思想是将输入图像划分为S×S的网格,每个网格负责预测固定数量的边界框。对于每个边界框,网络会预测5个值:边界框的中心坐标(x,y)、宽度w、高度h以及置信度分数。同时,每个网格还会预测C个类别概率。这种设计使得YOLO能够实现端到端的训练和推理,大大提高了检测速度。
1.2 YOLO版本演进与选择
YOLO算法自2015年提出以来,经历了多个版本的迭代:
- YOLOv1-v3:Joseph Redmon团队开发的经典版本
- YOLOv4:Alexey Bochkovskiy在YOLOv3基础上的优化
- YOLOv5:Ultralytics公司开发,采用PyTorch框架
- YOLOv6:美团视觉智能部研发的工业级目标检测框架
- YOLOv7:在速度和精度上的进一步优化
- YOLOv8:Ultralytics最新版本,在易用性和性能上都有显著提升
对于初学者,推荐使用YOLOv8,因为它有完善的文档、活跃的社区支持,且安装使用相对简单。YOLOv8支持目标检测、实例分割、图像分类等多种任务,满足大多数应用场景需求。
1.3 目标检测的核心评价指标
了解目标检测的评价指标有助于我们评估模型性能:
- mAP(mean Average Precision):综合衡量检测精度的重要指标,计算所有类别的平均精度
- Precision(精确率):预测为正样本中真正为正样本的比例
- Recall(召回率):实际正样本中被正确预测为正样本的比例
- F1 Score:精确率和召回率的调和平均数
- IoU(Intersection over Union):预测框与真实框的重叠程度
2. 环境准备与工具安装
2.1 硬件与软件要求
在开始之前,确保你的系统满足以下要求:
硬件要求:
- CPU:至少4核处理器
- 内存:8GB以上(推荐16GB)
- 存储:至少20GB可用空间
- GPU:可选,但推荐NVIDIA GPU(训练速度大幅提升)
软件要求:
- 操作系统:Windows 10/11, Ubuntu 18.04+, macOS 10.15+
- Python:3.8-3.10版本
- CUDA:如果使用GPU,需要安装对应版本的CUDA
2.2 创建Python虚拟环境
为避免包冲突,我们首先创建独立的Python环境:
# 创建名为yolo_env的虚拟环境 conda create -n yolo_env python=3.9 -y # 激活虚拟环境 conda activate yolo_env # 或者使用venv创建虚拟环境(Linux/macOS) python -m venv yolo_env source yolo_env/bin/activate2.3 安装YOLOv8及相关依赖
YOLOv8可以通过pip直接安装,非常方便:
# 安装YOLOv8 pip install ultralytics # 安装其他必要的依赖 pip install opencv-python pip install pillow pip install matplotlib pip install seaborn pip install pandas如果计划使用GPU进行训练,还需要安装PyTorch的GPU版本:
# 安装PyTorch GPU版本(根据你的CUDA版本选择) pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu1182.4 安装Label Studio标注工具
Label Studio是一款开源的数据标注工具,支持多种标注类型:
# 安装Label Studio pip install label-studio # 启动Label Studio(首次启动会引导初始化) label-studio start安装完成后,在浏览器中访问 http://localhost:8080 即可看到Label Studio的界面。
3. 数据采集与准备
3.1 数据采集策略
高质量的数据集是训练优秀模型的基础。数据采集可以考虑以下途径:
公开数据集:
- COCO:包含80个类别,33万张图片
- Pascal VOC:20个类别,1.1万张图片
- Open Images:600个类别,900万张图片
自定义数据采集:
- 网络爬虫:使用爬虫工具采集特定类别图片
- 自行拍摄:针对特定场景拍摄图片
- 视频抽帧:从视频中按时间间隔抽取帧作为训练数据
3.2 数据采集实战示例
以下是一个简单的图片爬虫示例,用于采集特定类别的图片:
import requests import os from bs4 import BeautifulSoup import time import urllib.parse class ImageCollector: def __init__(self, save_dir="dataset/images"): self.save_dir = save_dir os.makedirs(save_dir, exist_ok=True) def download_images(self, keywords, num_images=100): """下载指定关键词的图片""" for keyword in keywords: print(f"正在下载关键词: {keyword}") self._download_by_keyword(keyword, num_images) def _download_by_keyword(self, keyword, num_images): """根据关键词下载图片""" # 这里使用Bing图片搜索为例(实际使用时请遵守网站规则) search_url = f"https://www.bing.com/images/search?q={urllib.parse.quote(keyword)}" # 注意:实际爬虫需要处理反爬机制,这里仅为示例 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } # 实际项目中应使用合法的API或遵守robots.txt print(f"模拟搜索: {search_url}") # 这里省略具体的爬虫实现,建议使用合法的数据源 # 使用示例 if __name__ == "__main__": collector = ImageCollector() keywords = ["cat", "dog", "car"] # 根据你的需求修改关键词 collector.download_images(keywords, 50)重要提醒:在实际项目中,请确保数据采集的合法性,遵守网站的使用条款和robots.txt规定。
3.3 数据质量检查
采集到的数据需要进行质量检查:
import os from PIL import Image import cv2 def check_dataset_quality(image_dir): """检查数据集质量""" valid_images = [] corrupted_images = [] for img_file in os.listdir(image_dir): if img_file.lower().endswith(('.png', '.jpg', '.jpeg')): img_path = os.path.join(image_dir, img_file) try: # 尝试用PIL打开图片 with Image.open(img_path) as img: img.verify() # 用OpenCV检查图片是否可读 cv_img = cv2.imread(img_path) if cv_img is not None: valid_images.append(img_path) else: corrupted_images.append(img_path) except Exception as e: corrupted_images.append(img_path) print(f"损坏图片: {img_path}, 错误: {e}") print(f"有效图片: {len(valid_images)}") print(f"损坏图片: {len(corrupted_images)}") # 删除损坏图片 for corrupt_img in corrupted_images: os.remove(corrupt_img) print(f"已删除: {corrupt_img}") return valid_images # 检查图片质量 image_dir = "dataset/images" valid_images = check_dataset_quality(image_dir)4. 数据标注与预处理
4.1 使用Label Studio进行数据标注
Label Studio提供了友好的Web界面进行数据标注:
创建标注项目:
- 启动Label Studio:
label-studio start - 访问 http://localhost:8080
- 创建新项目,选择"Object Detection with Bounding Boxes"模板
- 设置标签名称(如:person, car, dog, cat等)
配置标注界面:
<View> <Image name="image" value="$image"/> <RectangleLabels name="label" toName="image"> <Label value="person" background="green"/> <Label value="car" background="blue"/> <Label value="dog" background="red"/> <Label value="cat" background="yellow"/> </RectangleLabels> </View>批量导入图片:
import os import json def prepare_label_studio_import(image_dir, output_file="import_list.json"): """准备Label Studio导入文件列表""" image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg'))] import_list = [] for img_file in image_files: import_list.append({ "image": f"/data/local-files/?d=images/{img_file}" }) with open(output_file, 'w') as f: json.dump(import_list, f, indent=2) print(f"生成导入文件: {output_file}, 包含 {len(image_files)} 张图片") # 准备导入文件 prepare_label_studio_import("dataset/images")4.2 半自动化标注技巧
利用预训练模型进行半自动化标注可以大幅提高效率:
from ultralytics import YOLO import cv2 import json import os class SemiAutoLabeling: def __init__(self, model_path='yolov8n.pt'): # 加载预训练模型 self.model = YOLO(model_path) def predict_and_export(self, image_dir, output_dir="auto_labels"): """预测并导出标注结果""" os.makedirs(output_dir, exist_ok=True) image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg'))] for img_file in image_files: img_path = os.path.join(image_dir, img_file) results = self.model(img_path) # 导出标注信息 self.export_annotations(results, img_file, output_dir) def export_annotations(self, results, image_name, output_dir): """导出标注信息为JSON格式""" annotations = { "image": image_name, "annotations": [] } for result in results: boxes = result.boxes if boxes is not None: for box in boxes: # 获取边界框坐标 x1, y1, x2, y2 = box.xyxy[0].tolist() confidence = box.conf[0].item() class_id = int(box.cls[0].item()) class_name = result.names[class_id] annotation = { "label": class_name, "x": x1, "y": y1, "width": x2 - x1, "height": y2 - y1, "confidence": confidence } annotations["annotations"].append(annotation) # 保存标注文件 json_file = os.path.splitext(image_name)[0] + ".json" json_path = os.path.join(output_dir, json_file) with open(json_path, 'w') as f: json.dump(annotations, f, indent=2) # 使用半自动化标注 labeler = SemiAutoLabeling() labeler.predict_and_export("dataset/images")4.3 数据格式转换
将Label Studio的标注结果转换为YOLO格式:
import json import os from PIL import Image def convert_labelstudio_to_yolo(labelstudio_json, image_dir, output_dir): """将Label Studio JSON格式转换为YOLO格式""" os.makedirs(output_dir, exist_ok=True) with open(labelstudio_json, 'r') as f: data = json.load(f) # 创建类别映射 classes = set() for annotation in data: for result in annotation.get('annotations', [])[0].get('result', []): if 'rectanglelabels' in result['value']: classes.update(result['value']['rectanglelabels']) classes = sorted(list(classes)) class_to_id = {cls: idx for idx, cls in enumerate(classes)} # 保存类别文件 with open(os.path.join(output_dir, 'classes.txt'), 'w') as f: for cls in classes: f.write(cls + '\n') # 转换每个标注 for annotation in data: image_file = annotation['data']['image'].split('/')[-1] image_path = os.path.join(image_dir, image_file) # 获取图片尺寸 with Image.open(image_path) as img: img_width, img_height = img.size # 生成YOLO格式标注 yolo_annotations = [] for result in annotation.get('annotations', [])[0].get('result', []): if 'rectanglelabels' in result['value']: x = result['value']['x'] / 100 * img_width y = result['value']['y'] / 100 * img_height width = result['value']['width'] / 100 * img_width height = result['value']['height'] / 100 * img_height # 转换为YOLO格式(归一化坐标) x_center = (x + width / 2) / img_width y_center = (y + height / 2) / img_height norm_width = width / img_width norm_height = height / img_height class_id = class_to_id[result['value']['rectanglelabels'][0]] yolo_annotations.append(f"{class_id} {x_center} {y_center} {norm_width} {norm_height}") # 保存YOLO标注文件 txt_file = os.path.splitext(image_file)[0] + '.txt' txt_path = os.path.join(output_dir, txt_file) with open(txt_path, 'w') as f: f.write('\n'.join(yolo_annotations)) # 转换标注格式 convert_labelstudio_to_yolo("labelstudio_export.json", "dataset/images", "dataset/labels")5. YOLO模型训练实战
5.1 准备YOLO格式数据集
YOLO需要特定的目录结构:
dataset/ ├── images/ │ ├── train/ │ │ ├── image1.jpg │ │ └── image2.jpg │ └── val/ │ ├── image3.jpg │ └── image4.jpg └── labels/ ├── train/ │ ├── image1.txt │ └── image2.txt └── val/ ├── image3.txt └── image4.txt创建数据集配置文件dataset.yaml:
# dataset.yaml path: /path/to/dataset # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 # 类别数量 nc: 4 # 类别名称 names: ['person', 'car', 'dog', 'cat']5.2 模型训练完整代码
使用YOLOv8进行模型训练:
from ultralytics import YOLO import os def train_yolo_model(): """训练YOLO模型""" # 加载预训练模型 model = YOLO('yolov8n.pt') # 可以选择yolov8s.pt, yolov8m.pt等 # 训练参数配置 training_results = model.train( data='dataset.yaml', # 数据集配置文件 epochs=100, # 训练轮数 imgsz=640, # 输入图片尺寸 batch=16, # 批次大小 device='cpu', # 使用CPU训练,如有GPU可改为0或[0,1] workers=4, # 数据加载线程数 patience=10, # 早停耐心值 lr0=0.01, # 初始学习率 lrf=0.01, # 最终学习率 momentum=0.937, # 动量 weight_decay=0.0005, # 权重衰减 warmup_epochs=3.0, # 热身轮数 box=7.5, # 边界框损失权重 cls=0.5, # 分类损失权重 dfl=1.5, # DFL损失权重 save=True, # 保存训练检查点 exist_ok=True, # 覆盖现有输出 pretrained=True, # 使用预训练权重 optimizer='auto', # 优化器自动选择 verbose=True, # 显示训练详情 seed=42 # 随机种子 ) return training_results # 开始训练 if __name__ == "__main__": results = train_yolo_model() print("训练完成!")5.3 训练过程监控
实时监控训练进度和指标:
import matplotlib.pyplot as plt import pandas as pd def plot_training_results(results_dir): """绘制训练结果图表""" # 读取训练结果CSV文件 results_csv = os.path.join(results_dir, 'results.csv') if not os.path.exists(results_csv): print("未找到训练结果文件") return df = pd.read_csv(results_csv) # 创建图表 fig, axes = plt.subplots(2, 2, figsize=(15, 10)) # 损失函数曲线 axes[0, 0].plot(df['epoch'], df['train/box_loss'], label='Box Loss') axes[0, 0].plot(df['epoch'], df['train/cls_loss'], label='Cls Loss') axes[0, 0].plot(df['epoch'], df['train/dfl_loss'], label='DFL Loss') axes[0, 0].set_title('Training Loss') axes[0, 0].set_xlabel('Epoch') axes[0, 0].set_ylabel('Loss') axes[0, 0].legend() axes[0, 0].grid(True) # 验证损失曲线 axes[0, 1].plot(df['epoch'], df['val/box_loss'], label='Val Box Loss') axes[0, 1].plot(df['epoch'], df['val/cls_loss'], label='Val Cls Loss') axes[0, 1].plot(df['epoch'], df['val/dfl_loss'], label='Val DFL Loss') axes[0, 1].set_title('Validation Loss') axes[0, 1].set_xlabel('Epoch') axes[0, 1].set_ylabel('Loss') axes[0, 1].legend() axes[0, 1].grid(True) # 精确率和召回率 axes[1, 0].plot(df['epoch'], df['metrics/precision(B)'], label='Precision') axes[1, 0].plot(df['epoch'], df['metrics/recall(B)'], label='Recall') axes[1, 0].set_title('Precision & Recall') axes[1, 0].set_xlabel('Epoch') axes[1, 0].set_ylabel('Score') axes[1, 0].legend() axes[1, 0].grid(True) # mAP指标 axes[1, 1].plot(df['epoch'], df['metrics/mAP50(B)'], label='mAP@0.5') axes[1, 1].plot(df['epoch'], df['metrics/mAP50-95(B)'], label='mAP@0.5:0.95') axes[1, 1].set_title('mAP Metrics') axes[1, 1].set_xlabel('Epoch') axes[1, 1].set_ylabel('mAP') axes[1, 1].legend() axes[1, 1].grid(True) plt.tight_layout() plt.savefig('training_results.png', dpi=300, bbox_inches='tight') plt.show() # 绘制训练结果 plot_training_results('runs/detect/train')6. 模型评估与优化
6.1 模型性能评估
训练完成后,需要对模型进行全面评估:
from ultralytics import YOLO import matplotlib.pyplot as plt def evaluate_model(model_path, data_yaml): """评估模型性能""" # 加载训练好的模型 model = YOLO(model_path) # 在验证集上评估 metrics = model.val(data=data_yaml, split='val') print("模型评估结果:") print(f"mAP@0.5: {metrics.box.map50:.3f}") print(f"mAP@0.5:0.95: {metrics.box.map:.3f}") print(f"精确率: {metrics.box.mp:.3f}") print(f"召回率: {metrics.box.mr:.3f}") return metrics # 评估模型 metrics = evaluate_model('runs/detect/train/weights/best.pt', 'dataset.yaml')6.2 混淆矩阵分析
def plot_confusion_matrix(model_path, data_yaml): """绘制混淆矩阵""" model = YOLO(model_path) # 生成混淆矩阵 model.val(data=data_yaml, plots=True) # 混淆矩阵图片保存在runs/detect/val目录 print("混淆矩阵已生成") plot_confusion_matrix('runs/detect/train/weights/best.pt', 'dataset.yaml')6.3 模型优化策略
根据评估结果进行模型优化:
def optimize_model_training(): """模型优化训练配置""" model = YOLO('yolov8n.pt') # 优化后的训练参数 results = model.train( data='dataset.yaml', epochs=150, # 增加训练轮数 imgsz=640, batch=8, # 减小批次大小 lr0=0.001, # 降低学习率 weight_decay=0.0001, # 调整权重衰减 augment=True, # 启用数据增强 degrees=10.0, # 旋转增强 translate=0.1, # 平移增强 scale=0.5, # 缩放增强 shear=2.0, # 剪切增强 perspective=0.0001, # 透视变换 flipud=0.0, # 上下翻转 fliplr=0.5, # 左右翻转 mosaic=1.0, # Mosaic数据增强 mixup=0.0, # MixUp增强 copy_paste=0.0 # 复制粘贴增强 ) return results # 如果需要优化,可以重新训练 # optimized_results = optimize_model_training()7. 模型导出与本地部署
7.1 模型格式导出
将训练好的模型导出为不同格式,便于部署:
def export_model(model_path, export_formats=['onnx', 'torchscript']): """导出模型为不同格式""" model = YOLO(model_path) for format in export_formats: try: # 导出模型 exported_path = model.export(format=format, imgsz=640, optimize=True) print(f"模型已导出为 {format.upper()} 格式: {exported_path}") except Exception as e: print(f"导出 {format} 格式失败: {e}") # 导出模型 export_model('runs/detect/train/weights/best.pt')7.2 本地推理部署
创建本地推理应用程序:
from ultralytics import YOLO import cv2 import numpy as np import time class YOLODeployer: def __init__(self, model_path): self.model = YOLO(model_path) self.class_names = self.model.names def predict_image(self, image_path, conf_threshold=0.25, iou_threshold=0.7): """单张图片预测""" results = self.model.predict( source=image_path, conf=conf_threshold, iou=iou_threshold, imgsz=640, save=True ) return results def real_time_detection(self, camera_index=0): """实时摄像头检测""" cap = cv2.VideoCapture(camera_index) if not cap.isOpened(): print("无法打开摄像头") return print("实时检测已启动,按 'q' 键退出") while True: ret, frame = cap.read() if not ret: break # 进行目标检测 results = self.model(frame) # 绘制检测结果 annotated_frame = results[0].plot() # 显示FPS cv2.putText(annotated_frame, f"FPS: {int(1/(time.time() - start_time))}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow('YOLO Real-time Detection', annotated_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() def batch_predict(self, image_dir, output_dir="predictions"): """批量图片预测""" import os os.makedirs(output_dir, exist_ok=True) results = self.model.predict( source=image_dir, save=True, project=output_dir, name="exp", exist_ok=True ) print(f"批量预测完成,结果保存在: {output_dir}") return results # 使用部署类 deployer = YOLODeployer('runs/detect/train/weights/best.pt') # 单张图片测试 results = deployer.predict_image('test_image.jpg') # 实时检测(取消注释以启用) # deployer.real_time_detection()7.3 创建Web API服务
使用FastAPI创建模型推理API:
from fastapi import FastAPI, File, UploadFile from fastapi.responses import JSONResponse import cv2 import numpy as np from ultralytics import YOLO import io from PIL import Image app = FastAPI(title="YOLO目标检测API") # 加载模型 model = YOLO('runs/detect/train/weights/best.pt') @app.post("/predict") async def predict_image(file: UploadFile = File(...)): """图片预测API接口""" # 读取上传的图片 image_data = await file.read() image = Image.open(io.BytesIO(image_data)) image_np = np.array(image) # 进行预测 results = model(image_np) # 解析结果 detections = [] for result in results: boxes = result.boxes if boxes is not None: for box in boxes: detection = { "class": model.names[int(box.cls[0])], "confidence": float(box.conf[0]), "bbox": { "x1": float(box.xyxy[0][0]), "y1": float(box.xyxy[0][1]), "x2": float(box.xyxy[0][2]), "y2": float(box.xyxy[0][3]) } } detections.append(detection) return JSONResponse({ "filename": file.filename, "detections": detections, "detection_count": len(detections) }) @app.get("/") async def root(): return {"message": "YOLO目标检测API服务运行中"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)启动API服务:
python api_server.py8. 常见问题与解决方案
8.1 训练过程中的常见问题
问题1:CUDA内存不足
解决方案: - 减小批次大小(batch size) - 减小输入图片尺寸(imgsz) - 使用梯度累积 - 清理GPU缓存问题2:训练损失不下降
解决方案: - 检查学习率是否合适 - 验证数据标注质量 - 增加数据增强 - 检查模型架构是否适合任务问题3:过拟合
解决方案: - 增加正则化(权重衰减) - 使用早停(early stopping) - 增加数据量 - 简化模型结构8.2 部署时的常见问题
问题1:模型推理速度慢
# 优化推理速度的配置 results = model.predict( source=image, imgsz=320, # 减小输入尺寸 half=True, # 使用半精度推理 device='cpu', # 确保使用正确的设备 verbose=False # 关闭详细输出 )问题2:检测框不准确
# 调整检测参数 results = model.predict( source=image, conf=0.5, # 提高置信度阈值 iou=0.5, # 调整IoU阈值 agnostic_nms=False, # 使用类别感知NMS max_det=100 # 限制最大检测数量 )8.3 数据相关的常见问题
问题:类别不平衡
# 在训练配置中处理类别不平衡 results = model.train( data='dataset.yaml', cls=1.0, # 调整分类损失权重 fl_gamma=1.5, # 使用Focal Loss # 或者使用过采样/欠采样策略 )9. 项目实战:构建完整应用
9.1 创建综合检测应用
下面是一个完整的桌面应用示例,集成训练好的YOLO模型:
import tkinter as tk from tkinter import filedialog, messagebox from PIL import Image, ImageTk import cv2 from ultralytics import YOLO import threading class YOLOApp: def __init__(self, root): self.root = root self.root.title("YOLO目标检测应用") self.root.geometry("800x600") # 加载模型 self.model = YOLO('runs/detect/train/weights/best.pt') self.setup_ui() def setup_ui(self): """设置用户界面""" # 顶部按钮框架 button_frame = tk.Frame(self.root) button_frame.pack(pady=10) # 功能按钮 self.load_btn = tk.Button(button_frame, text="加载图片", command=self.load_image) self.load_btn.pack(side=tk.LEFT, padx=5) self.detect_btn = tk.Button(button_frame, text="开始检测", command=self.detect_objects) self.detect_btn.pack(side=tk.LEFT, padx=5) self.camera_btn = tk.Button(button_frame, text="摄像头检测", command=self.camera_detection) self.camera_btn.pack(side=tk.LEFT, padx=5) # 图片显示区域 self.image_label = tk.Label(self.root) self.image_label.pack(expand=True, fill=tk.BOTH, padx=10, pady=10) # 状态栏 self.status_var = tk.StringVar() self.status_var.set("就绪") status_bar = tk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) def load_image(self): """加载图片""" file_path = filedialog.askopenfilename( filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")] ) if file_path: self.original_image = Image.open(file_path) self.display_image(self.original_image) self.status_var.set(f"已加载: {file_path}") def display_image(self, image): """显示图片""" # 调整图片大小以适应窗口 width, height = image.size max_size = 600 if width > max_size or height > max_size: ratio = min(max_size/width, max_size/height) new_size = (int(width*ratio), int(height*ratio)) image = image.resize(new_size, Image.Resampling.LANCZOS) self.tk_image = ImageTk.PhotoImage(image) self.image_label.config(image=self.tk_image) def detect_objects(self): """目标检测""" if hasattr(self, 'original_image'): # 在新线程中执行检测,避免界面卡顿 threading.Thread(target=self._detect_objects_thread).start() else: messagebox.showwarning("警告", "请先加载图片") def _detect_objects_thread(self): """检测线程""" self.status_var.set("检测中...") self.detect_btn.config(state=tk.DISABLED) # 转换图片格式 image_cv = cv2.cvtColor(np.array(self.original_image), cv2.COLOR_RGB2BGR) # 进行检测 results = self.model(image_cv) # 绘制检测结果 annotated_image = results[0].plot() annotated_image_rgb = cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB) result_image = Image.fromarray(annotated_image_rgb) # 更新界面 self.root.after(0, self._update_detection_result, result_image) def _update_detection > 🚀 30+款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉[点击领海量免费额度](https://taotoken.net/models/detail/chat?modelId=deepseek-v4-pro&utm_source=tt_blog_mr)