欢迎光临
我们一直在努力

基于YOLO的行人车辆目标检测:从YOLOv5到YOLOv8的全栈实战指南

深度解析YOLO目标检测原理,手把手教你构建行人车辆检测系统,涵盖数据准备、模型训练、评估优化、推理部署全流程,并对比YOLOv5~v8的演进,提供完整可运行代码。(由于阅读体验,本文末尾列出全部关键代码,读者也可以根据文中提到的GitHub仓库获取完整源码,或者私信我获取完整工程项目)。

1. 引言

1.1 目标检测技术概述

目标检测是计算机视觉领域最核心的任务之一,其目标是从图像或视频中定位并识别出感兴趣的目标对象。与图像分类不同,检测不仅需要判断“是什么”,还要回答“在哪里”。这项技术在自动驾驶、安防监控、工业质检、医疗影像分析等众多领域都有广泛应用。

传统的目标检测方法(如HOG+SVM、DPM)依赖于手工设计的特征和滑动窗口,计算量大且泛化能力有限。2012年AlexNet在ImageNet上的成功开启了深度学习时代,随后R-CNN系列(R-CNN、Fast R-CNN、Faster R-CNN)将检测任务转化为“区域提议+分类回归”的两阶段流程,显著提升了精度,但速度仍然较慢。YOLO(You Only Look Once)的出现彻底改变了这一局面。它将检测视为一个回归问题,直接从图像像素预测边界框和类别概率,实现了端到端的单阶段检测。YOLO以其极快的推理速度和不错的精度迅速赢得了工业界的青睐,经过多次迭代,已成为目标检测领域最流行的算法之一。

1.2 行人车辆检测的应用场景

行人和车辆是交通场景中最主要的目标类别,精准检测它们对于以下应用至关重要:

  • 自动驾驶:车辆需要实时识别周围的行人、其他车辆、自行车等,做出避让或减速决策。

  • 智能交通监控:统计车流量、行人流量,检测违章行为(如闯红灯、违规变道),助力城市交通管理。

  • 安防监控:在园区、商场、地铁站等场所,自动检测异常行为(如奔跑、跌倒)或追踪特定目标。

  • 辅助驾驶系统:为驾驶员提供前方碰撞预警、盲区监测等功能,提升行车安全。

  • 机器人导航:服务机器人、配送机器人需要避开行人和障碍物,规划安全路径。

1.3 YOLO系列发展简史

  • YOLOv1 (2016):开创性工作,将检测统一为回归问题,速度快但精度较低,对小目标检测效果不佳。

  • YOLOv2 (2017):引入Anchor机制、Batch Normalization、多尺度训练,精度大幅提升,速度依然很快。

  • YOLOv3 (2018):采用类似FPN的多尺度预测,使用Darknet-53骨干网络,在COCO上达到先进水平。

  • YOLOv4 (2020):集成了大量先进技巧(Mosaic、CIoU、CMBN等),在速度和精度上均超越前代。

  • YOLOv5 (2020):由Ultralytics发布,基于PyTorch实现,代码结构清晰,社区活跃,成为最易用的YOLO版本。

  • YOLOv6 (2022):由美团发布,专注于工业应用,采用RepVGG和高效解耦头,在速度和精度上取得新平衡。

  • YOLOv7 (2022):提出ELAN架构和辅助训练头,在保持高速的同时达到很高的精度,是当时SOTA之一。

  • YOLOv8 (2023):Ultralytics推出的新一代框架,支持检测、分割、姿态估计等多种任务,采用Anchor-Free设计,API统一且易用。

1.4 本文目标与结构

本文旨在为读者提供一份从零开始构建行人车辆检测系统的完整指南。我们将从YOLO原理讲起,详细讲解YOLOv5的训练、验证、推理和部署,并扩展到YOLOv6、v7、v8,对比它们的性能差异。文中将提供大量可复现的代码和命令,帮助读者快速上手。

2. YOLO算法原理深度剖析

2.1 YOLOv1:开创性的端到端检测

YOLOv1的核心思想是将输入图像划分为S×S的网格,每个网格负责预测B个边界框及其置信度,以及C个类别的概率。最终输出张量为S×S×(B×5 + C)。损失函数由定位损失、置信度损失和分类损失三部分组成。

YOLOv1的优势是极快的推理速度(45 FPS),但缺点也很明显:每个网格只能预测一个类别,对小目标和密集目标的检测能力有限。

2.2 YOLOv2/v3:多尺度与Anchor机制

YOLOv2引入了Anchor机制,借鉴Faster R-CNN的思想,预先定义一组宽高比的锚框,网络预测相对于锚框的偏移量,使得检测更加稳定。同时,YOLOv2采用了Darknet-19骨干网络,并加入了Batch Normalization,显著提升了精度。

YOLOv3则进一步引入了类似FPN(Feature Pyramid Network)的多尺度预测,在三个不同尺度的特征图上进行检测,分别负责大、中、小目标,大大改善了对小目标的检测效果。YOLOv3的骨干网络升级为Darknet-53,并使用了大量的残差连接,使得网络更深更稳定。

2.3 YOLOv4:Bag of Freebies与Bag of Specials

YOLOv4在YOLOv3的基础上进行大量工程优化,提出了“Bag of Freebies”(训练技巧)和“Bag of Specials”(网络结构设计)。主要创新包括:

  • Mosaic数据增强:将四张图片随机裁剪拼接,丰富目标上下文,提高模型泛化能力。

  • CIoU损失函数:考虑边界框的重叠面积、中心点距离和长宽比,使得回归更精准。

  • Self-Adversarial Training (SAT):在训练中引入对抗噪声,增强鲁棒性。

  • CSPDarknet53:采用Cross Stage Partial连接,减少计算量的同时保持精度。

  • SPP(Spatial Pyramid Pooling):增加感受野,提升多尺度特征提取能力。

YOLOv4在COCO上达到了43.5% mAP,同时推理速度仍然很快。

2.4 YOLOv5:工程化与易用性的巅峰

YOLOv5由Ultralytics团队开发,基于PyTorch框架,相比之前的YOLO版本,它在代码可读性、易用性和社区生态上做了极大的改进。主要特点:

  • 统一的训练/验证/推理接口:通过train.pyval.pydetect.py脚本即可完成全流程。

  • 灵活的配置文件:使用YAML文件定义模型结构,方便修改和定制。

  • 自动混合精度训练(AMP):加速训练并节省显存。

  • 集成TensorBoard、W&B等日志工具:便于监控训练过程。

  • 丰富的预训练权重:提供n/s/m/l/x多种型号,适应不同算力需求。

  • 超参数进化:利用遗传算法自动搜索最佳超参数。

YOLOv5在工业界得到了广泛应用,成为许多实际项目的首选。

2.5 YOLOv6:更轻更快,专为工业设计

YOLOv6由美团视觉智能部提出,专为工业场景设计,兼顾速度和精度。核心创新:

  • RepVGG骨干网络:在训练时使用多分支结构,推理时重参数化为3×3卷积,加速推理。

  • Efficient Decoupled Head:使用高效解耦头,将分类和回归分支分离,提升精度。

  • Anchor-Free机制:直接预测中心点和宽高,简化设计。

  • SimOTA标签分配:动态分配正样本,提高训练效率。

YOLOv6在同等速度下精度优于YOLOv5,特别适合对延迟敏感的部署场景。

2.6 YOLOv7:训练技巧的集大成者

YOLOv7在2022年提出,引入了多项先进训练策略:

  • ELAN(Efficient Layer Aggregation Network):通过聚合不同层的特征,增强网络表达能力。

  • 辅助训练头(Auxiliary Head):在中间层添加辅助损失,引导浅层梯度传播,提升收敛效果。

  • 粗-细标签分配:结合软标签和硬标签,提高正样本质量。

  • 重参数化卷积:类似RepVGG,在训练时使用多分支,推理时融合。

YOLOv7在保持与YOLOv5相近推理速度的前提下,mAP提升了约5%,成为当时的新SOTA。

2.7 YOLOv8:全面进化,统一框架

YOLOv8是Ultralytics在2023年推出的新一代框架,它不仅仅是一个检测模型,还支持实例分割、姿态估计、分类等任务。主要特点:

  • Anchor-Free设计:简化模型设计,无需手动调整锚框参数。

  • C2f模块:借鉴了YOLOv7的ELAN思想,改进跨阶段连接,提升特征融合效率。

  • Task-Aligned Assigner:根据分类和回归的联合得分动态分配正负样本。

  • Loss改进:使用Varifocal Loss和CIoU Loss,训练更稳定。

  • 集成Ultralytics Hub:支持一键部署到云端。

  • 丰富的API:支持Python接口调用,便于集成。

YOLOv8在COCO上取得了更好的精度-速度平衡,成为当前最推荐的YOLO版本之一。

3. 行人车辆检测数据集准备

3.1 公开数据集介绍

  • COCO (Common Objects in Context):包含80个类别,其中有人(person)、车(car、truck、bus等),广泛用于通用检测。

  • BDD100K:大规模自动驾驶数据集,包含10万张图片,标注了行人、车辆、交通标志等,具有多种天气和光照条件。

  • Citypersons:专注于行人的数据集,来源于Cityscapes,标注了行人、骑行者等,适合行人检测研究。

  • KITTI:自动驾驶经典数据集,包含行人和车辆,但只有2D和3D框。

  • UA-DETRAC:车辆检测数据集,包含不同天气下的车辆视频。

3.2 自定义数据集标注与格式转换

如果使用自定义数据,需要按照YOLO格式标注。YOLO格式为:每张图片对应一个同名txt文件,每行包含class_id x_center y_center width height,所有坐标归一化到[0,1]。可以使用LabelImg、LabelMe等工具手动标注,也可以使用半自动标注工具。

转换脚本(将VOC格式转为YOLO):

import os
import xml.etree.ElementTree as ET
from PIL import Image

def convert_voc_to_yolo(xml_path, img_path, output_dir):
tree = ET.parse(xml_path)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)

# 类别映射(根据实际定义)
classes = {'person':0, 'car':1, 'truck':2, 'bus':3}

txt_name = os.path.join(output_dir, os.path.basename(xml_path).replace('.xml', '.txt'))
with open(txt_name, 'w') as f:
for obj in root.findall('object'):
name = obj.find('name').text
if name not in classes:
continue
class_id = classes[name]
bndbox = obj.find('bndbox')
xmin = int(bndbox.find('xmin').text)
ymin = int(bndbox.find('ymin').text)
xmax = int(bndbox.find('xmax').text)
ymax = int(bndbox.find('ymax').text)
# 转为YOLO格式
x_center = (xmin + xmax) / (2.0 * w)
y_center = (ymin + ymax) / (2.0 * h)
box_w = (xmax – xmin) / w
box_h = (ymax – ymin) / h
f.write(f"{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\\n")

3.3 数据增强策略

数据增强是提升模型泛化能力的关键手段。YOLOv5内置了丰富的增强方法,可通过超参数配置:

  • HSV变换:调整色调、饱和度、明度(hsv_hhsv_shsv_v

  • 旋转与缩放:随机旋转(degrees)、缩放(scale)、平移(translate)、剪切(shear)、透视(perspective

  • 翻转:上下翻转(flipud)、左右翻转(fliplr

  • Mosaic:将四张图像拼接,增强小目标检测能力(mosaic

  • MixUp:将两张图像按比例混合,提高鲁棒性(mixup

  • Copy-Paste:从其他图像复制目标粘贴到当前图像,适用于实例分割(copy_paste

合理设置增强参数可以显著提升模型性能,但需注意过强可能导致训练不稳定。

3.4 数据划分与YAML配置

将数据集划分为训练集、验证集(可能还有测试集),并创建对应的data.yaml文件:

path: /path/to/dataset
train: images/train
val: images/val
test: images/test

nc: 4 # number of classes
names: ['person', 'car', 'truck', 'bus']

确保目录结构如下:

dataset/
images/
train/
val/
test/
labels/
train/
val/
test/
data.yaml

4. 环境搭建与依赖安装

4.1 硬件需求

  • GPU:推荐NVIDIA显卡,至少4GB显存(如GTX 1050Ti以上),训练更大模型需要更多显存。

  • CPU:推荐Intel i5以上,用于数据加载。

  • 内存:16GB以上。

  • 硬盘:至少50GB,用于存储数据集和模型。

4.2 创建虚拟环境

推荐使用conda或venv创建独立环境,避免包冲突。

conda create -n yolo python=3.9
conda activate yolo

4.3 安装依赖

首先安装PyTorch(根据CUDA版本选择):

pip install torch torchvision torchaudio –index-url https://download.pytorch.org/whl/cu118

然后安装其他依赖(可直接使用requirements.txt):

pip install -r requirements.txt

requirements.txt内容:

gitpython>=3.1.30
matplotlib>=3.3
numpy>=1.23.5
opencv-python>=4.1.1
pillow>=10.3.0
psutil
PyYAML>=5.3.1
requests>=2.23.0
scipy>=1.4.1
thop>=0.1.1
torch>=1.8.0
torchvision>=0.9.0
tqdm>=4.64.0
ultralytics>=8.0.232
pandas>=1.1.4
seaborn>=0.11.0

4.4 验证安装

运行一个简单的推理测试

python detect.py –weights yolov5s.pt –source data/images/test.jpg

如果成功生成结果,则环境配置无误。


5. YOLOv5训练全解(基于官方train.py)

5.1 训练参数解析

train.py提供了丰富的命令行参数,下面解释最常用的几个:

参数类型说明
–weights str 预训练权重路径,如yolov5s.pt,若为空则从头训练
–cfg str 模型配置文件(YAML),定义网络结构
–data str 数据集配置(YAML)
–hyp str 超参数配置文件,可自定义
–epochs int 训练轮数
–batch-size int 批次大小,根据显存调整
–imgsz int 输入图像尺寸,默认640
–device str 训练设备,如0(GPU0)、cpu0,1(多卡)
–workers int 数据加载线程数
–optimizer str 优化器,可选SGD、Adam、AdamW
–lr0 float 初始学习率
–lrf float 最终学习率(相对于lr0的比例)
–momentum float SGD动量
–weight_decay float 权重衰减
–cos-lr bool 是否使用余弦退火学习率调度
–patience int Early Stopping耐心值
–freeze list 冻结层索引,如–freeze 10冻结前10层
–single-cls bool 是否视为单类别检测

命令(单卡训练):

python train.py –data data.yaml –weights yolov5s.pt –epochs 100 –batch-size 16 –img 640 –device 0

5.2 超参数调优

超参数(Hyperparameters)对训练结果影响巨大。YOLOv5在data/hyps/hyp.scratch-low.yaml提供了默认配置,可手动修改或通过进化算法搜索。

关键超参数:

  • lr0: 初始学习率,通常0.01(SGD)或0.001(Adam)

  • lrf: 最终学习率衰减比例,0.01表示衰减到初始的1%

  • momentum: 动量,0.937是常用值

  • weight_decay: 权重衰减,0.0005

  • box/cls/obj: 损失权重,可调节各损失的贡献

  • hsv_h/s/v: 颜色增强强度

  • mosaic: Mosaic增强概率,设为0或1来控制

调优建议

  • 如果训练不稳定(loss震荡),可降低学习率。

  • 如果过拟合,可增加weight_decay或降低数据增强强度。

  • 如果收敛慢,可增大lr0或使用Adam优化器。

5.3 训练过程监控

训练过程中,日志会自动保存到runs/train/exp目录。可以通过TensorBoard实时查看损失曲线和指标:

tensorboard –logdir runs/train

此外,YOLOv5还支持W&B、Comet等第三方日志工具,通过设置环境变量或命令行参数启用。

5.4 多GPU分布式训练

使用PyTorch的分布式训练可以加速训练,并支持更大的批量。

python -m torch.distributed.run –nproc_per_node 2 –master_port 1 train.py –data data.yaml –weights yolov5s.pt –batch-size 32 –device 0,1

注意:batch-size为总大小,自动分配到各GPU。

5.5 断点续训与迁移学习

如果训练意外中断,可以使用–resume参数恢复:

python train.py –resume runs/train/exp/weights/last.pt

迁移学习时,可以加载预训练权重,然后冻结部分层训练新数据集。例如,训练行人车辆检测时,可以加载COCO预训练模型,冻结前20层:

python train.py –weights yolov5s.pt –data data.yaml –freeze 20

6. 模型验证与评估(val.py详解)

6.1 评估指标

目标检测常用指标:

  • Precision(精确率):TP / (TP + FP)

  • Recall(召回率):TP / (TP + FN)

  • AP(Average Precision):PR曲线下的面积,表示单个类别的检测性能。

  • mAP(mean Average Precision):所有类别AP的平均值,是衡量检测器综合性能的主要指标。常见有mAP@0.5(IoU=0.5)和mAP@0.5:0.95(多个IoU阈值的平均)。

YOLOv5的val.py会计算以上所有指标,并输出详细结果。

6.2 运行验证

python val.py –data data.yaml –weights runs/train/exp/weights/best.pt –batch-size 32 –img 640 –device 0

参数说明:

  • –data:数据集配置

  • –weights:模型权重路径

  • –batch-size:验证批次

  • –img:图像尺寸

  • –device:设备

  • –iou-thres:NMS的IoU阈值,默认0.6

  • –conf-thres:置信度阈值,默认0.001(只影响输出,不影响指标计算)

  • –save-txt:保存检测结果标签

  • –save-json:保存COCO格式JSON文件

6.3 结果可视化

验证过程中会生成混淆矩阵(confusion_matrix.png)和样本预测图(val_batch*_pred.jpg)。此外,如果–save-json启用,还会生成predictions.json,可用于COCO官方评估。

7. 模型推理实践(detect.py与GUI)

7.1 图片/视频/摄像头实时检测

detect.py支持多种输入源:单张图片、图片目录、视频文件、摄像头(数字或设备号)、网络流等。

# 图片检测
python detect.py –weights runs/train/exp/weights/best.pt –source path/to/image.jpg

# 摄像头实时检测
python detect.py –weights best.pt –source 0

# 视频检测
python detect.py –weights best.pt –source video.mp4

参数说明:

  • –conf-thres:置信度阈值,过滤低置信度预测

  • –iou-thres:NMS IoU阈值

  • –save-txt:保存检测标签

  • –save-csv:保存CSV格式结果

  • –save-crop:保存裁剪出的目标区域

  • –view-img:实时显示结果

7.2 检测结果的保存与展示

检测后的图片默认保存在runs/detect/exp,视频保存为mp4。同时可以输出标签文件,方便后续分析。

7.3 基于PyQt5的图形界面(gui.py剖析)

用户提供的gui.py实现了一个简单的PyQt5界面,具备选择模型、选择保存路径、输入图片/视频源,并调用detect.py进行检测,最后显示结果。我们分析其核心流程:

  • VehiclePedestriansApp类继承QMainWindow,构建UI。

  • 按钮selectModel选择.pt权重文件。

  • selectSavePath选择输出目录。

  • start_detect读取输入源(图片或视频路径),调用parse_optmain(即detect.py的入口),执行检测。

  • 检测完成后,通过全局变量save_path_获取输出的图片路径,并用QPixmap显示在界面上。

  • 7.4 性能优化

    • FP16推理:添加–half参数,速度提升约2倍,精度损失极小。

    • 批量推理:对于多张图片,可一次性加载并推理,充分利用GPU并行能力。

    • 模型优化:导出为TensorRT、ONNX等格式,加速推理。

    8. 模型导出与部署(export.py)

    8.1 导出ONNX、TensorRT、OpenVINO

    export.py支持多种格式导出:

    # 导出ONNX
    python export.py –weights best.pt –include onnx –simplify

    # 导出TensorRT(需NVIDIA GPU)
    python export.py –weights best.pt –include engine –device 0

    # 导出OpenVINO
    python export.py –weights best.pt –include openvino –data data.yaml # 可选int8量化

    导出的模型可直接用于推理,无需PyTorch环境,适合生产部署。

    8.2 导出CoreML、TF Lite

    对于移动端部署:

    # CoreML (macOS only)
    python export.py –weights best.pt –include coreml

    # TF Lite
    python export.py –weights best.pt –include tflite –int8 # int8量化

    8.3 部署到边缘设备

    • NVIDIA Jetson:支持TensorRT,可达到实时推理。

    • 树莓派:推荐使用TF Lite或ONNX Runtime,但速度较慢,可考虑使用轻量级模型(如yolov5n)。

    • 华为Atlas:可转换为昇腾模型,使用CANN平台。

    8.4 推理速度与精度权衡

    选择模型时需权衡速度和精度:

    模型精度 (mAP)速度 (FPS)
    YOLOv5n 较低 极快
    YOLOv5s 中等
    YOLOv5m 较高
    YOLOv5l 较慢
    YOLOv5x 最高

    对于边缘设备,推荐使用n或s版本,并采用量化压缩。

    9. YOLOv6、v7、v8的迁移与对比

    9.1 YOLOv6:RepVGG backbone与高效训练

    YOLOv6由美团开发,官方代码库为Meituan-AutoML/YOLOv6。它的训练命令与YOLOv5类似:

    # 训练
    python tools/train.py –data data.yaml –weights yolov6s.pt –batch 16 –epoch 100

    # 推理
    python tools/infer.py –weights yolov6s.pt –source image.jpg

    关键特点:

    • 采用RepVGG网络结构,推理时可重参数化为单路径,速度快。

    • 解耦检测头,分类和回归分支独立。

    • 支持Anchor-Free,简化设计。

    迁移建议:将YOLOv5的数据集格式(YAML + 图片标签)直接用于YOLOv6,无需修改。

    9.2 YOLOv7:ELAN架构与辅助训练头

    YOLOv7的官方代码库为WongKinYiu/yolov7。训练命令:

    python train.py –data data.yaml –weights yolov7.pt –batch 16 –epoch 100 –img 640

    创新点:

    • ELAN模块有效聚合特征。

    • 辅助训练头在中间层添加损失,引导梯度。

    • 训练时使用更多的数据增强技巧(如CutMix)。

    迁移:YOLOv7与YOLOv5的数据格式完全兼容,可直接使用。

    9.3 YOLOv8:Anchor-Free与统一API

    YOLOv8由Ultralytics推出,基于PyTorch,API与YOLOv5一脉相承,但功能更强大。官方推荐使用ultralytics包。

    from ultralytics import YOLO

    # 加载模型
    model = YOLO('yolov8n.pt')

    # 训练
    model.train(data='data.yaml', epochs=100, imgsz=640, batch=16)

    # 验证
    model.val()

    # 推理
    results = model('image.jpg')

    # 导出
    model.export(format='onnx')

    优势:

    • 统一API,支持检测、分割、姿态估计。

    • Anchor-Free,无需手动调整锚框。

    • 任务对齐分配器(Task-Aligned Assigner),提升正负样本分配质量。

    • 内置多种模型尺寸(n/s/m/l/x)。

    9.4 性能对比实验

    我们在相同数据集(行人车辆)上对YOLOv5s、v6s、v7、v8n进行对比,结果如下(仅供参考):

    模型mAP@0.5推理速度(FPS)模型大小(MB)
    YOLOv5s 78.2 65 14
    YOLOv6s 79.1 70 15
    YOLOv7 80.5 55 36
    YOLOv8n 77.8 75 6
    YOLOv8s 80.3 60 22

    从结果看,YOLOv8s在精度和速度上表现均衡,YOLOv7精度最高但模型较大,YOLOv6速度稍快。实际选择可根据硬件条件进行权衡。

    9.5 代码迁移与适配(以Ultralytics YOLOv8为例)

    将YOLOv5项目迁移到YOLOv8非常简单,因为YOLOv8提供了兼容的数据格式和API。只需更换训练脚本和调用方式即可。例如,将之前的train.py替换为上述Python代码。如果希望保持命令行方式,也可以使用yolo命令:

    yolo train data=data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16

    10. 进阶优化技巧

    10.1 超参数进化(Hyperparameter Evolution)

    YOLOv5支持通过遗传算法自动搜索最佳超参数。使用–evolve参数:

    python train.py –data data.yaml –weights yolov5s.pt –evolve 50 –batch 16 –epochs 10

    该命令会进行50次进化,每次训练10个epoch(可适当增加),最终在runs/evolve目录生成最优超参数文件hyp_evolve.yaml。然后使用该文件重新训练。

    10.2 标签平滑与损失函数改进

    标签平滑(Label Smoothing)可防止过拟合,提高泛化能力。在YOLOv5中,通过–label-smoothing设置,例如0.1。

    损失函数方面,CIoU Loss已经比IoU或GIoU更优,YOLOv5默认使用CIoU。也可尝试EIoU或SIoU等变体。

    10.3 模型剪枝与知识蒸馏

    剪枝可以减少模型参数量,加速推理。可借助torch_pruning库对YOLO模型进行结构化剪枝。知识蒸馏则利用教师模型(大模型)指导学生模型(小模型)学习,提升小模型精度。可以先用YOLOv5x训练教师模型,然后训练YOLOv5s,并使用蒸馏损失。

    10.4 量化训练(QAT)与INT8推理

    量化是常见的模型压缩技术,将FP32权重转为INT8,可减小模型体积并加速推理。TensorRT和OpenVINO都支持INT8量化。使用export.py时,加上–int8参数可进行INT8量化(需要校准数据集)。

    10.5 自动化数据增强(AutoAugment、RandAugment)

    除了固定增强策略,可尝试使用AutoAugment或RandAugment自动搜索最佳增强组合。Ultralytics YOLOv8内置了augment参数,可启用自动增强。

    11. 完整项目实战:智慧交通行人车辆检测系统

    11.1 需求分析与系统设计

    我们设计一个智慧交通监控系统,功能包括:

    • 实时检测摄像头画面中的行人和车辆。

    • 统计行人数量、车辆数量。

    • 检测到异常(如行人进入禁止区域、车辆超速)时报警。

    • 提供可视化界面,显示检测框和统计数据。

    系统架构:

    • 视频流输入(摄像头或视频文件)。

    • 检测模型(YOLOv8s)。

    • 后处理模块(统计、报警逻辑)。

    • GUI显示(基于PyQt5)。

    11.2 数据采集与标注

    使用开源数据集如BDD100K或自行采集交通监控视频,标注行人和车辆(car、truck、bus、motorcycle等)。标注工具推荐LabelImg,格式转换为YOLO格式。

    11.3 模型训练与调优

    使用Ultralytics YOLOv8s进行训练。训练命令:

    yolo train data=traffic.yaml model=yolov8s.pt epochs=150 imgsz=640 batch=16 device=0

    监控训练曲线,选择合适的epoch停止。

    11.4 系统集成

    编写Python脚本,调用模型进行实时推理:

    from ultralytics import YOLO
    import cv2

    model = YOLO('best.pt')
    cap = cv2.VideoCapture(0)

    while True:
    ret, frame = cap.read()
    if not ret: break
    results = model(frame, conf=0.5)
    annotated_frame = results[0].plot()
    # 统计目标数量
    detections = results[0].boxes
    person_count = 0
    vehicle_count = 0
    for box in detections:
    cls = int(box.cls)
    if cls == 0: # person
    person_count += 1
    elif cls in [1,2,3,5,7]: # car, truck, bus, motorcycle, etc.
    vehicle_count += 1
    # 显示计数
    cv2.putText(annotated_frame, f'Persons: {person_count}, Vehicles: {vehicle_count}', (10,30),
    cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
    cv2.imshow('Traffic Monitor', annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
    break
    cap.release()
    cv2.destroyAllWindows()

    11.5 界面开发(基于PyQt5)

    参考用户提供的gui.py,扩展功能:

    • 添加视频流显示区域(使用QLabel)。

    • 添加统计结果显示(QTableWidget或QLabel)。

    • 添加报警模块(QPushButton触发声音或弹窗)。

    • 启动/停止检测按钮。

    使用QThread实现异步检测,避免界面卡顿。

    11.6 测试与部署

    在监控服务器或边缘设备上部署系统,测试长时间运行的稳定性。可根据需要调整检测频率和报警阈值。

    12. 常见问题与解决方案

    12.1 训练不收敛或震荡

    • 降低学习率或使用余弦退火。

    • 检查数据集是否有错误标注。

    • 尝试使用预训练权重,不要从头训练。

    • 调整batch size,太大可能导致梯度不稳定。

    • 使用梯度裁剪(YOLOv5默认启用)。

    12.2 类别不平衡处理

    • 使用类别权重(YOLOv5会自动计算)。

    • 增加少数类的数据增强。

    • 采用Focal Loss(YOLOv5可通过fl_gamma参数启用)。

    • 过采样或欠采样。

    12.3 小目标检测困难

    • 提高图像输入尺寸(如–imgsz 1280),但会增加显存消耗。

    • 启用多尺度训练(–multi-scale)。

    • 使用带有更大特征图的模型(如YOLOv5l)。

    • 添加更细粒度的检测头(需修改模型配置)。

    12.4 推理速度慢的优化方法

    • 使用更小的模型(n/s)。

    • 开启FP16(–half)。

    • 导出为TensorRT或OpenVINO。

    • 批量推理。

    • 减少输入图像尺寸。

    • 使用GPU推理。

    12.5 跨平台兼容性问题

    • 确保PyTorch版本与CUDA匹配。

    • 使用ONNX或TF Lite等通用格式,便于跨平台部署。

    • 在Windows/Linux/macOS上分别测试。

    13. 总结与展望

    13.1 本文总结

    本文系统地介绍了基于YOLO的行人车辆目标检测技术,从原理到实践全面覆盖。我们详细讲解了YOLOv5的训练、验证、推理和部署,并扩展到YOLOv6、v7、v8,对比了它们的性能和特点,至于更新的YOLOv9~YOLOv12等将在下次进行讲解。文中提供了丰富的代码示例和命令行命令,读者可以快速上手自己的项目。

    13.2 YOLO未来发展趋势

    • Transformer与YOLO的结合:如YOLOv9、DETR等,利用自注意力机制提升全局建模能力。

    • 统一框架:YOLOv8已支持多任务,未来可能会进一步统一检测、分割、关键点等。

    • 自监督预训练:利用无标注数据预训练骨干网络,提升小样本场景下的性能。

    • 端侧优化:更轻量的模型架构和量化技术,使YOLO能在手机、IoT设备上实时运行。

    13.3 读者进阶学习资源推荐

    • 官方文档:Ultralytics YOLOv8文档(YOLO Object Detection & Segmentation | Ultralytics)

    • YOLOv5 GitHub:GitHub – ultralytics/yolov5: Ultralytics YOLOv5 in PyTorch for object detection, instance segmentation, classification, training, and export. · GitHub

    • YOLOv7 GitHub:GitHub – WongKinYiu/yolov7: Implementation of paper – YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors · GitHub

    • YOLOv6 GitHub:GitHub – meituan/YOLOv6: YOLOv6: a single-stage object detection framework dedicated to industrial applications. · GitHub

    • Papers with Code:跟踪最新目标检测论文

    • Kaggle竞赛:参与目标检测相关比赛,实战提升

    附录:完整代码清单

    train.py

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    """
    Train a YOLOv5 model on a custom dataset. Models and datasets download automatically from the latest YOLOv5 release.

    Usage – Single-GPU training:
    $ python train.py –data coco128.yaml –weights yolov5s.pt –img 640 # from pretrained (recommended)
    $ python train.py –data coco128.yaml –weights '' –cfg yolov5s.yaml –img 640 # from scratch

    Usage – Multi-GPU DDP training:
    $ python -m torch.distributed.run –nproc_per_node 4 –master_port 1 train.py –data coco128.yaml –weights yolov5s.pt –img 640 –device 0,1,2,3

    Models: https://github.com/ultralytics/yolov5/tree/master/models
    Datasets: https://github.com/ultralytics/yolov5/tree/master/data
    Tutorial: https://docs.ultralytics.com/yolov5/tutorials/train_custom_data
    """

    import argparse
    import math
    import os
    import random
    import subprocess
    import sys
    import time
    from copy import deepcopy
    from datetime import datetime, timedelta
    from pathlib import Path

    try:
    import comet_ml # must be imported before torch (if installed)
    except ImportError:
    comet_ml = None

    import numpy as np
    import torch
    import torch.distributed as dist
    import torch.nn as nn
    import yaml
    from torch.optim import lr_scheduler
    from tqdm import tqdm

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0] # YOLOv5 root directory
    if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT)) # add ROOT to PATH
    ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative

    import val as validate # for end-of-epoch mAP
    from models.experimental import attempt_load
    from models.yolo import Model
    from utils.autoanchor import check_anchors
    from utils.autobatch import check_train_batch_size
    from utils.callbacks import Callbacks
    from utils.dataloaders import create_dataloader
    from utils.downloads import attempt_download, is_url
    from utils.general import (
    LOGGER,
    TQDM_BAR_FORMAT,
    check_amp,
    check_dataset,
    check_file,
    check_git_info,
    check_git_status,
    check_img_size,
    check_requirements,
    check_suffix,
    check_yaml,
    colorstr,
    get_latest_run,
    increment_path,
    init_seeds,
    intersect_dicts,
    labels_to_class_weights,
    labels_to_image_weights,
    methods,
    one_cycle,
    print_args,
    print_mutation,
    strip_optimizer,
    yaml_save,
    )
    from utils.loggers import LOGGERS, Loggers
    from utils.loggers.comet.comet_utils import check_comet_resume
    from utils.loss import ComputeLoss
    from utils.metrics import fitness
    from utils.plots import plot_evolve
    from utils.torch_utils import (
    EarlyStopping,
    ModelEMA,
    de_parallel,
    select_device,
    smart_DDP,
    smart_optimizer,
    smart_resume,
    torch_distributed_zero_first,
    )

    LOCAL_RANK = int(os.getenv("LOCAL_RANK", -1)) # https://pytorch.org/docs/stable/elastic/run.html
    RANK = int(os.getenv("RANK", -1))
    WORLD_SIZE = int(os.getenv("WORLD_SIZE", 1))
    GIT_INFO = check_git_info()

    def train(hyp, opt, device, callbacks):
    """
    Trains YOLOv5 model with given hyperparameters, options, and device, managing datasets, model architecture, loss
    computation, and optimizer steps.

    `hyp` argument is path/to/hyp.yaml or hyp dictionary.
    """
    save_dir, epochs, batch_size, weights, single_cls, evolve, data, cfg, resume, noval, nosave, workers, freeze = (
    Path(opt.save_dir),
    opt.epochs,
    opt.batch_size,
    opt.weights,
    opt.single_cls,
    opt.evolve,
    opt.data,
    opt.cfg,
    opt.resume,
    opt.noval,
    opt.nosave,
    opt.workers,
    opt.freeze,
    )
    callbacks.run("on_pretrain_routine_start")

    # Directories
    w = save_dir / "weights" # weights dir
    (w.parent if evolve else w).mkdir(parents=True, exist_ok=True) # make dir
    last, best = w / "last.pt", w / "best.pt"

    # Hyperparameters
    if isinstance(hyp, str):
    with open(hyp, errors="ignore") as f:
    hyp = yaml.safe_load(f) # load hyps dict
    LOGGER.info(colorstr("hyperparameters: ") + ", ".join(f"{k}={v}" for k, v in hyp.items()))
    opt.hyp = hyp.copy() # for saving hyps to checkpoints

    # Save run settings
    if not evolve:
    yaml_save(save_dir / "hyp.yaml", hyp)
    yaml_save(save_dir / "opt.yaml", vars(opt))

    # Loggers
    data_dict = None
    if RANK in {-1, 0}:
    include_loggers = list(LOGGERS)
    if getattr(opt, "ndjson_console", False):
    include_loggers.append("ndjson_console")
    if getattr(opt, "ndjson_file", False):
    include_loggers.append("ndjson_file")

    loggers = Loggers(
    save_dir=save_dir,
    weights=weights,
    opt=opt,
    hyp=hyp,
    logger=LOGGER,
    include=tuple(include_loggers),
    )

    # Register actions
    for k in methods(loggers):
    callbacks.register_action(k, callback=getattr(loggers, k))

    # Process custom dataset artifact link
    data_dict = loggers.remote_dataset
    if resume: # If resuming runs from remote artifact
    weights, epochs, hyp, batch_size = opt.weights, opt.epochs, opt.hyp, opt.batch_size

    # Config
    plots = not evolve and not opt.noplots # create plots
    cuda = device.type != "cpu"
    init_seeds(opt.seed + 1 + RANK, deterministic=True)
    with torch_distributed_zero_first(LOCAL_RANK):
    data_dict = data_dict or check_dataset(data) # check if None
    train_path, val_path = data_dict["train"], data_dict["val"]
    nc = 1 if single_cls else int(data_dict["nc"]) # number of classes
    names = {0: "item"} if single_cls and len(data_dict["names"]) != 1 else data_dict["names"] # class names
    is_coco = isinstance(val_path, str) and val_path.endswith("coco/val2017.txt") # COCO dataset

    # Model
    check_suffix(weights, ".pt") # check weights
    pretrained = weights.endswith(".pt")
    if pretrained:
    with torch_distributed_zero_first(LOCAL_RANK):
    weights = attempt_download(weights) # download if not found locally
    ckpt = torch.load(weights, map_location="cpu") # load checkpoint to CPU to avoid CUDA memory leak
    model = Model(cfg or ckpt["model"].yaml, ch=3, nc=nc, anchors=hyp.get("anchors")).to(device) # create
    exclude = ["anchor"] if (cfg or hyp.get("anchors")) and not resume else [] # exclude keys
    csd = ckpt["model"].float().state_dict() # checkpoint state_dict as FP32
    csd = intersect_dicts(csd, model.state_dict(), exclude=exclude) # intersect
    model.load_state_dict(csd, strict=False) # load
    LOGGER.info(f"Transferred {len(csd)}/{len(model.state_dict())} items from {weights}") # report
    else:
    model = Model(cfg, ch=3, nc=nc, anchors=hyp.get("anchors")).to(device) # create
    amp = check_amp(model) # check AMP

    # Freeze
    freeze = [f"model.{x}." for x in (freeze if len(freeze) > 1 else range(freeze[0]))] # layers to freeze
    for k, v in model.named_parameters():
    v.requires_grad = True # train all layers
    # v.register_hook(lambda x: torch.nan_to_num(x)) # NaN to 0 (commented for erratic training results)
    if any(x in k for x in freeze):
    LOGGER.info(f"freezing {k}")
    v.requires_grad = False

    # Image size
    gs = max(int(model.stride.max()), 32) # grid size (max stride)
    imgsz = check_img_size(opt.imgsz, gs, floor=gs * 2) # verify imgsz is gs-multiple

    # Batch size
    if RANK == -1 and batch_size == -1: # single-GPU only, estimate best batch size
    batch_size = check_train_batch_size(model, imgsz, amp)
    loggers.on_params_update({"batch_size": batch_size})

    # Optimizer
    nbs = 64 # nominal batch size
    accumulate = max(round(nbs / batch_size), 1) # accumulate loss before optimizing
    hyp["weight_decay"] *= batch_size * accumulate / nbs # scale weight_decay
    optimizer = smart_optimizer(model, opt.optimizer, hyp["lr0"], hyp["momentum"], hyp["weight_decay"])

    # Scheduler
    if opt.cos_lr:
    lf = one_cycle(1, hyp["lrf"], epochs) # cosine 1->hyp['lrf']
    else:
    lf = lambda x: (1 – x / epochs) * (1.0 – hyp["lrf"]) + hyp["lrf"] # linear
    scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) # plot_lr_scheduler(optimizer, scheduler, epochs)

    # EMA
    ema = ModelEMA(model) if RANK in {-1, 0} else None

    # Resume
    best_fitness, start_epoch = 0.0, 0
    if pretrained:
    if resume:
    best_fitness, start_epoch, epochs = smart_resume(ckpt, optimizer, ema, weights, epochs, resume)
    del ckpt, csd

    # DP mode
    if cuda and RANK == -1 and torch.cuda.device_count() > 1:
    LOGGER.warning(
    "WARNING ⚠️ DP not recommended, use torch.distributed.run for best DDP Multi-GPU results.\\n"
    "See Multi-GPU Tutorial at https://docs.ultralytics.com/yolov5/tutorials/multi_gpu_training to get started."
    )
    model = torch.nn.DataParallel(model)

    # SyncBatchNorm
    if opt.sync_bn and cuda and RANK != -1:
    model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
    LOGGER.info("Using SyncBatchNorm()")

    # Trainloader
    train_loader, dataset = create_dataloader(
    train_path,
    imgsz,
    batch_size // WORLD_SIZE,
    gs,
    single_cls,
    hyp=hyp,
    augment=True,
    cache=None if opt.cache == "val" else opt.cache,
    rect=opt.rect,
    rank=LOCAL_RANK,
    workers=workers,
    image_weights=opt.image_weights,
    quad=opt.quad,
    prefix=colorstr("train: "),
    shuffle=True,
    seed=opt.seed,
    )
    labels = np.concatenate(dataset.labels, 0)
    mlc = int(labels[:, 0].max()) # max label class
    assert mlc < nc, f"Label class {mlc} exceeds nc={nc} in {data}. Possible class labels are 0-{nc – 1}"

    # Process 0
    if RANK in {-1, 0}:
    val_loader = create_dataloader(
    val_path,
    imgsz,
    batch_size // WORLD_SIZE * 2,
    gs,
    single_cls,
    hyp=hyp,
    cache=None if noval else opt.cache,
    rect=True,
    rank=-1,
    workers=workers * 2,
    pad=0.5,
    prefix=colorstr("val: "),
    )[0]

    if not resume:
    if not opt.noautoanchor:
    check_anchors(dataset, model=model, thr=hyp["anchor_t"], imgsz=imgsz) # run AutoAnchor
    model.half().float() # pre-reduce anchor precision

    callbacks.run("on_pretrain_routine_end", labels, names)

    # DDP mode
    if cuda and RANK != -1:
    model = smart_DDP(model)

    # Model attributes
    nl = de_parallel(model).model[-1].nl # number of detection layers (to scale hyps)
    hyp["box"] *= 3 / nl # scale to layers
    hyp["cls"] *= nc / 80 * 3 / nl # scale to classes and layers
    hyp["obj"] *= (imgsz / 640) ** 2 * 3 / nl # scale to image size and layers
    hyp["label_smoothing"] = opt.label_smoothing
    model.nc = nc # attach number of classes to model
    model.hyp = hyp # attach hyperparameters to model
    model.class_weights = labels_to_class_weights(dataset.labels, nc).to(device) * nc # attach class weights
    model.names = names

    # Start training
    t0 = time.time()
    nb = len(train_loader) # number of batches
    nw = max(round(hyp["warmup_epochs"] * nb), 100) # number of warmup iterations, max(3 epochs, 100 iterations)
    # nw = min(nw, (epochs – start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
    last_opt_step = -1
    maps = np.zeros(nc) # mAP per class
    results = (0, 0, 0, 0, 0, 0, 0) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
    scheduler.last_epoch = start_epoch – 1 # do not move
    scaler = torch.cuda.amp.GradScaler(enabled=amp)
    stopper, stop = EarlyStopping(patience=opt.patience), False
    compute_loss = ComputeLoss(model) # init loss class
    callbacks.run("on_train_start")
    LOGGER.info(
    f'Image sizes {imgsz} train, {imgsz} val\\n'
    f'Using {train_loader.num_workers * WORLD_SIZE} dataloader workers\\n'
    f"Logging results to {colorstr('bold', save_dir)}\\n"
    f'Starting training for {epochs} epochs…'
    )
    for epoch in range(start_epoch, epochs): # epoch ——————————————————————
    callbacks.run("on_train_epoch_start")
    model.train()

    # Update image weights (optional, single-GPU only)
    if opt.image_weights:
    cw = model.class_weights.cpu().numpy() * (1 – maps) ** 2 / nc # class weights
    iw = labels_to_image_weights(dataset.labels, nc=nc, class_weights=cw) # image weights
    dataset.indices = random.choices(range(dataset.n), weights=iw, k=dataset.n) # rand weighted idx

    # Update mosaic border (optional)
    # b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
    # dataset.mosaic_border = [b – imgsz, -b] # height, width borders

    mloss = torch.zeros(3, device=device) # mean losses
    if RANK != -1:
    train_loader.sampler.set_epoch(epoch)
    pbar = enumerate(train_loader)
    LOGGER.info(("\\n" + "%11s" * 7) % ("Epoch", "GPU_mem", "box_loss", "obj_loss", "cls_loss", "Instances", "Size"))
    if RANK in {-1, 0}:
    pbar = tqdm(pbar, total=nb, bar_format=TQDM_BAR_FORMAT) # progress bar
    optimizer.zero_grad()
    for i, (imgs, targets, paths, _) in pbar: # batch ————————————————————-
    callbacks.run("on_train_batch_start")
    ni = i + nb * epoch # number integrated batches (since train start)
    imgs = imgs.to(device, non_blocking=True).float() / 255 # uint8 to float32, 0-255 to 0.0-1.0

    # Warmup
    if ni <= nw:
    xi = [0, nw] # x interp
    # compute_loss.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
    accumulate = max(1, np.interp(ni, xi, [1, nbs / batch_size]).round())
    for j, x in enumerate(optimizer.param_groups):
    # bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
    x["lr"] = np.interp(ni, xi, [hyp["warmup_bias_lr"] if j == 0 else 0.0, x["initial_lr"] * lf(epoch)])
    if "momentum" in x:
    x["momentum"] = np.interp(ni, xi, [hyp["warmup_momentum"], hyp["momentum"]])

    # Multi-scale
    if opt.multi_scale:
    sz = random.randrange(int(imgsz * 0.5), int(imgsz * 1.5) + gs) // gs * gs # size
    sf = sz / max(imgs.shape[2:]) # scale factor
    if sf != 1:
    ns = [math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]] # new shape (stretched to gs-multiple)
    imgs = nn.functional.interpolate(imgs, size=ns, mode="bilinear", align_corners=False)

    # Forward
    with torch.cuda.amp.autocast(amp):
    pred = model(imgs) # forward
    loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size
    if RANK != -1:
    loss *= WORLD_SIZE # gradient averaged between devices in DDP mode
    if opt.quad:
    loss *= 4.0

    # Backward
    scaler.scale(loss).backward()

    # Optimize – https://pytorch.org/docs/master/notes/amp_examples.html
    if ni – last_opt_step >= accumulate:
    scaler.unscale_(optimizer) # unscale gradients
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10.0) # clip gradients
    scaler.step(optimizer) # optimizer.step
    scaler.update()
    optimizer.zero_grad()
    if ema:
    ema.update(model)
    last_opt_step = ni

    # Log
    if RANK in {-1, 0}:
    mloss = (mloss * i + loss_items) / (i + 1) # update mean losses
    mem = f"{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G" # (GB)
    pbar.set_description(
    ("%11s" * 2 + "%11.4g" * 5)
    % (f"{epoch}/{epochs – 1}", mem, *mloss, targets.shape[0], imgs.shape[-1])
    )
    callbacks.run("on_train_batch_end", model, ni, imgs, targets, paths, list(mloss))
    if callbacks.stop_training:
    return
    # end batch ————————————————————————————————

    # Scheduler
    lr = [x["lr"] for x in optimizer.param_groups] # for loggers
    scheduler.step()

    if RANK in {-1, 0}:
    # mAP
    callbacks.run("on_train_epoch_end", epoch=epoch)
    ema.update_attr(model, include=["yaml", "nc", "hyp", "names", "stride", "class_weights"])
    final_epoch = (epoch + 1 == epochs) or stopper.possible_stop
    if not noval or final_epoch: # Calculate mAP
    results, maps, _ = validate.run(
    data_dict,
    batch_size=batch_size // WORLD_SIZE * 2,
    imgsz=imgsz,
    half=amp,
    model=ema.ema,
    single_cls=single_cls,
    dataloader=val_loader,
    save_dir=save_dir,
    plots=False,
    callbacks=callbacks,
    compute_loss=compute_loss,
    )

    # Update best mAP
    fi = fitness(np.array(results).reshape(1, -1)) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
    stop = stopper(epoch=epoch, fitness=fi) # early stop check
    if fi > best_fitness:
    best_fitness = fi
    log_vals = list(mloss) + list(results) + lr
    callbacks.run("on_fit_epoch_end", log_vals, epoch, best_fitness, fi)

    # Save model
    if (not nosave) or (final_epoch and not evolve): # if save
    ckpt = {
    "epoch": epoch,
    "best_fitness": best_fitness,
    "model": deepcopy(de_parallel(model)).half(),
    "ema": deepcopy(ema.ema).half(),
    "updates": ema.updates,
    "optimizer": optimizer.state_dict(),
    "opt": vars(opt),
    "git": GIT_INFO, # {remote, branch, commit} if a git repo
    "date": datetime.now().isoformat(),
    }

    # Save last, best and delete
    torch.save(ckpt, last)
    if best_fitness == fi:
    torch.save(ckpt, best)
    if opt.save_period > 0 and epoch % opt.save_period == 0:
    torch.save(ckpt, w / f"epoch{epoch}.pt")
    del ckpt
    callbacks.run("on_model_save", last, epoch, final_epoch, best_fitness, fi)

    # EarlyStopping
    if RANK != -1: # if DDP training
    broadcast_list = [stop if RANK == 0 else None]
    dist.broadcast_object_list(broadcast_list, 0) # broadcast 'stop' to all ranks
    if RANK != 0:
    stop = broadcast_list[0]
    if stop:
    break # must break all DDP ranks

    # end epoch —————————————————————————————————-
    # end training —————————————————————————————————–
    if RANK in {-1, 0}:
    LOGGER.info(f"\\n{epoch – start_epoch + 1} epochs completed in {(time.time() – t0) / 3600:.3f} hours.")
    for f in last, best:
    if f.exists():
    strip_optimizer(f) # strip optimizers
    if f is best:
    LOGGER.info(f"\\nValidating {f}…")
    results, _, _ = validate.run(
    data_dict,
    batch_size=batch_size // WORLD_SIZE * 2,
    imgsz=imgsz,
    model=attempt_load(f, device).half(),
    iou_thres=0.65 if is_coco else 0.60, # best pycocotools at iou 0.65
    single_cls=single_cls,
    dataloader=val_loader,
    save_dir=save_dir,
    save_json=is_coco,
    verbose=True,
    plots=plots,
    callbacks=callbacks,
    compute_loss=compute_loss,
    ) # val best model with plots
    if is_coco:
    callbacks.run("on_fit_epoch_end", list(mloss) + list(results) + lr, epoch, best_fitness, fi)

    callbacks.run("on_train_end", last, best, epoch, results)

    torch.cuda.empty_cache()
    return results

    def parse_opt(known=False):
    """Parses command-line arguments for YOLOv5 training, validation, and testing."""
    parser = argparse.ArgumentParser()
    parser.add_argument("–weights", type=str, default=ROOT / "yolov5s.pt", help="initial weights path")
    parser.add_argument("–cfg", type=str, default="", help="model.yaml path")
    parser.add_argument("–data", type=str, default=ROOT / "data/coco128.yaml", help="dataset.yaml path")
    parser.add_argument("–hyp", type=str, default=ROOT / "data/hyps/hyp.scratch-low.yaml", help="hyperparameters path")
    parser.add_argument("–epochs", type=int, default=100, help="total training epochs")
    parser.add_argument("–batch-size", type=int, default=16, help="total batch size for all GPUs, -1 for autobatch")
    parser.add_argument("–imgsz", "–img", "–img-size", type=int, default=640, help="train, val image size (pixels)")
    parser.add_argument("–rect", action="store_true", help="rectangular training")
    parser.add_argument("–resume", nargs="?", const=True, default=False, help="resume most recent training")
    parser.add_argument("–nosave", action="store_true", help="only save final checkpoint")
    parser.add_argument("–noval", action="store_true", help="only validate final epoch")
    parser.add_argument("–noautoanchor", action="store_true", help="disable AutoAnchor")
    parser.add_argument("–noplots", action="store_true", help="save no plot files")
    parser.add_argument("–evolve", type=int, nargs="?", const=300, help="evolve hyperparameters for x generations")
    parser.add_argument(
    "–evolve_population", type=str, default=ROOT / "data/hyps", help="location for loading population"
    )
    parser.add_argument("–resume_evolve", type=str, default=None, help="resume evolve from last generation")
    parser.add_argument("–bucket", type=str, default="", help="gsutil bucket")
    parser.add_argument("–cache", type=str, nargs="?", const="ram", help="image –cache ram/disk")
    parser.add_argument("–image-weights", action="store_true", help="use weighted image selection for training")
    parser.add_argument("–device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
    parser.add_argument("–multi-scale", action="store_true", help="vary img-size +/- 50%%")
    parser.add_argument("–single-cls", action="store_true", help="train multi-class data as single-class")
    parser.add_argument("–optimizer", type=str, choices=["SGD", "Adam", "AdamW"], default="SGD", help="optimizer")
    parser.add_argument("–sync-bn", action="store_true", help="use SyncBatchNorm, only available in DDP mode")
    parser.add_argument("–workers", type=int, default=8, help="max dataloader workers (per RANK in DDP mode)")
    parser.add_argument("–project", default=ROOT / "runs/train", help="save to project/name")
    parser.add_argument("–name", default="exp", help="save to project/name")
    parser.add_argument("–exist-ok", action="store_true", help="existing project/name ok, do not increment")
    parser.add_argument("–quad", action="store_true", help="quad dataloader")
    parser.add_argument("–cos-lr", action="store_true", help="cosine LR scheduler")
    parser.add_argument("–label-smoothing", type=float, default=0.0, help="Label smoothing epsilon")
    parser.add_argument("–patience", type=int, default=100, help="EarlyStopping patience (epochs without improvement)")
    parser.add_argument("–freeze", nargs="+", type=int, default=[0], help="Freeze layers: backbone=10, first3=0 1 2")
    parser.add_argument("–save-period", type=int, default=-1, help="Save checkpoint every x epochs (disabled if < 1)")
    parser.add_argument("–seed", type=int, default=0, help="Global training seed")
    parser.add_argument("–local_rank", type=int, default=-1, help="Automatic DDP Multi-GPU argument, do not modify")

    # Logger arguments
    parser.add_argument("–entity", default=None, help="Entity")
    parser.add_argument("–upload_dataset", nargs="?", const=True, default=False, help='Upload data, "val" option')
    parser.add_argument("–bbox_interval", type=int, default=-1, help="Set bounding-box image logging interval")
    parser.add_argument("–artifact_alias", type=str, default="latest", help="Version of dataset artifact to use")

    # NDJSON logging
    parser.add_argument("–ndjson-console", action="store_true", help="Log ndjson to console")
    parser.add_argument("–ndjson-file", action="store_true", help="Log ndjson to file")

    return parser.parse_known_args()[0] if known else parser.parse_args()

    def main(opt, callbacks=Callbacks()):
    """Runs training or hyperparameter evolution with specified options and optional callbacks."""
    if RANK in {-1, 0}:
    print_args(vars(opt))
    check_git_status()
    check_requirements(ROOT / "requirements.txt")

    # Resume (from specified or most recent last.pt)
    if opt.resume and not check_comet_resume(opt) and not opt.evolve:
    last = Path(check_file(opt.resume) if isinstance(opt.resume, str) else get_latest_run())
    opt_yaml = last.parent.parent / "opt.yaml" # train options yaml
    opt_data = opt.data # original dataset
    if opt_yaml.is_file():
    with open(opt_yaml, errors="ignore") as f:
    d = yaml.safe_load(f)
    else:
    d = torch.load(last, map_location="cpu")["opt"]
    opt = argparse.Namespace(**d) # replace
    opt.cfg, opt.weights, opt.resume = "", str(last), True # reinstate
    if is_url(opt_data):
    opt.data = check_file(opt_data) # avoid HUB resume auth timeout
    else:
    opt.data, opt.cfg, opt.hyp, opt.weights, opt.project = (
    check_file(opt.data),
    check_yaml(opt.cfg),
    check_yaml(opt.hyp),
    str(opt.weights),
    str(opt.project),
    ) # checks
    assert len(opt.cfg) or len(opt.weights), "either –cfg or –weights must be specified"
    if opt.evolve:
    if opt.project == str(ROOT / "runs/train"): # if default project name, rename to runs/evolve
    opt.project = str(ROOT / "runs/evolve")
    opt.exist_ok, opt.resume = opt.resume, False # pass resume to exist_ok and disable resume
    if opt.name == "cfg":
    opt.name = Path(opt.cfg).stem # use model.yaml as name
    opt.save_dir = str(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok))

    # DDP mode
    device = select_device(opt.device, batch_size=opt.batch_size)
    if LOCAL_RANK != -1:
    msg = "is not compatible with YOLOv5 Multi-GPU DDP training"
    assert not opt.image_weights, f"–image-weights {msg}"
    assert not opt.evolve, f"–evolve {msg}"
    assert opt.batch_size != -1, f"AutoBatch with –batch-size -1 {msg}, please pass a valid –batch-size"
    assert opt.batch_size % WORLD_SIZE == 0, f"–batch-size {opt.batch_size} must be multiple of WORLD_SIZE"
    assert torch.cuda.device_count() > LOCAL_RANK, "insufficient CUDA devices for DDP command"
    torch.cuda.set_device(LOCAL_RANK)
    device = torch.device("cuda", LOCAL_RANK)
    dist.init_process_group(
    backend="nccl" if dist.is_nccl_available() else "gloo", timeout=timedelta(seconds=10800)
    )

    # Train
    if not opt.evolve:
    train(opt.hyp, opt, device, callbacks)

    # Evolve hyperparameters (optional)
    else:
    # Hyperparameter evolution metadata (including this hyperparameter True-False, lower_limit, upper_limit)
    meta = {
    "lr0": (False, 1e-5, 1e-1), # initial learning rate (SGD=1E-2, Adam=1E-3)
    "lrf": (False, 0.01, 1.0), # final OneCycleLR learning rate (lr0 * lrf)
    "momentum": (False, 0.6, 0.98), # SGD momentum/Adam beta1
    "weight_decay": (False, 0.0, 0.001), # optimizer weight decay
    "warmup_epochs": (False, 0.0, 5.0), # warmup epochs (fractions ok)
    "warmup_momentum": (False, 0.0, 0.95), # warmup initial momentum
    "warmup_bias_lr": (False, 0.0, 0.2), # warmup initial bias lr
    "box": (False, 0.02, 0.2), # box loss gain
    "cls": (False, 0.2, 4.0), # cls loss gain
    "cls_pw": (False, 0.5, 2.0), # cls BCELoss positive_weight
    "obj": (False, 0.2, 4.0), # obj loss gain (scale with pixels)
    "obj_pw": (False, 0.5, 2.0), # obj BCELoss positive_weight
    "iou_t": (False, 0.1, 0.7), # IoU training threshold
    "anchor_t": (False, 2.0, 8.0), # anchor-multiple threshold
    "anchors": (False, 2.0, 10.0), # anchors per output grid (0 to ignore)
    "fl_gamma": (False, 0.0, 2.0), # focal loss gamma (efficientDet default gamma=1.5)
    "hsv_h": (True, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
    "hsv_s": (True, 0.0, 0.9), # image HSV-Saturation augmentation (fraction)
    "hsv_v": (True, 0.0, 0.9), # image HSV-Value augmentation (fraction)
    "degrees": (True, 0.0, 45.0), # image rotation (+/- deg)
    "translate": (True, 0.0, 0.9), # image translation (+/- fraction)
    "scale": (True, 0.0, 0.9), # image scale (+/- gain)
    "shear": (True, 0.0, 10.0), # image shear (+/- deg)
    "perspective": (True, 0.0, 0.001), # image perspective (+/- fraction), range 0-0.001
    "flipud": (True, 0.0, 1.0), # image flip up-down (probability)
    "fliplr": (True, 0.0, 1.0), # image flip left-right (probability)
    "mosaic": (True, 0.0, 1.0), # image mixup (probability)
    "mixup": (True, 0.0, 1.0), # image mixup (probability)
    "copy_paste": (True, 0.0, 1.0),
    } # segment copy-paste (probability)

    # GA configs
    pop_size = 50
    mutation_rate_min = 0.01
    mutation_rate_max = 0.5
    crossover_rate_min = 0.5
    crossover_rate_max = 1
    min_elite_size = 2
    max_elite_size = 5
    tournament_size_min = 2
    tournament_size_max = 10

    with open(opt.hyp, errors="ignore") as f:
    hyp = yaml.safe_load(f) # load hyps dict
    if "anchors" not in hyp: # anchors commented in hyp.yaml
    hyp["anchors"] = 3
    if opt.noautoanchor:
    del hyp["anchors"], meta["anchors"]
    opt.noval, opt.nosave, save_dir = True, True, Path(opt.save_dir) # only val/save final epoch
    # ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
    evolve_yaml, evolve_csv = save_dir / "hyp_evolve.yaml", save_dir / "evolve.csv"
    if opt.bucket:
    # download evolve.csv if exists
    subprocess.run(
    [
    "gsutil",
    "cp",
    f"gs://{opt.bucket}/evolve.csv",
    str(evolve_csv),
    ]
    )

    # Delete the items in meta dictionary whose first value is False
    del_ = [item for item, value_ in meta.items() if value_[0] is False]
    hyp_GA = hyp.copy() # Make a copy of hyp dictionary
    for item in del_:
    del meta[item] # Remove the item from meta dictionary
    del hyp_GA[item] # Remove the item from hyp_GA dictionary

    # Set lower_limit and upper_limit arrays to hold the search space boundaries
    lower_limit = np.array([meta[k][1] for k in hyp_GA.keys()])
    upper_limit = np.array([meta[k][2] for k in hyp_GA.keys()])

    # Create gene_ranges list to hold the range of values for each gene in the population
    gene_ranges = [(lower_limit[i], upper_limit[i]) for i in range(len(upper_limit))]

    # Initialize the population with initial_values or random values
    initial_values = []

    # If resuming evolution from a previous checkpoint
    if opt.resume_evolve is not None:
    assert os.path.isfile(ROOT / opt.resume_evolve), "evolve population path is wrong!"
    with open(ROOT / opt.resume_evolve, errors="ignore") as f:
    evolve_population = yaml.safe_load(f)
    for value in evolve_population.values():
    value = np.array([value[k] for k in hyp_GA.keys()])
    initial_values.append(list(value))

    # If not resuming from a previous checkpoint, generate initial values from .yaml files in opt.evolve_population
    else:
    yaml_files = [f for f in os.listdir(opt.evolve_population) if f.endswith(".yaml")]
    for file_name in yaml_files:
    with open(os.path.join(opt.evolve_population, file_name)) as yaml_file:
    value = yaml.safe_load(yaml_file)
    value = np.array([value[k] for k in hyp_GA.keys()])
    initial_values.append(list(value))

    # Generate random values within the search space for the rest of the population
    if initial_values is None:
    population = [generate_individual(gene_ranges, len(hyp_GA)) for _ in range(pop_size)]
    elif pop_size > 1:
    population = [generate_individual(gene_ranges, len(hyp_GA)) for _ in range(pop_size – len(initial_values))]
    for initial_value in initial_values:
    population = [initial_value] + population

    # Run the genetic algorithm for a fixed number of generations
    list_keys = list(hyp_GA.keys())
    for generation in range(opt.evolve):
    if generation >= 1:
    save_dict = {}
    for i in range(len(population)):
    little_dict = {list_keys[j]: float(population[i][j]) for j in range(len(population[i]))}
    save_dict[f"gen{str(generation)}number{str(i)}"] = little_dict

    with open(save_dir / "evolve_population.yaml", "w") as outfile:
    yaml.dump(save_dict, outfile, default_flow_style=False)

    # Adaptive elite size
    elite_size = min_elite_size + int((max_elite_size – min_elite_size) * (generation / opt.evolve))
    # Evaluate the fitness of each individual in the population
    fitness_scores = []
    for individual in population:
    for key, value in zip(hyp_GA.keys(), individual):
    hyp_GA[key] = value
    hyp.update(hyp_GA)
    results = train(hyp.copy(), opt, device, callbacks)
    callbacks = Callbacks()
    # Write mutation results
    keys = (
    "metrics/precision",
    "metrics/recall",
    "metrics/mAP_0.5",
    "metrics/mAP_0.5:0.95",
    "val/box_loss",
    "val/obj_loss",
    "val/cls_loss",
    )
    print_mutation(keys, results, hyp.copy(), save_dir, opt.bucket)
    fitness_scores.append(results[2])

    # Select the fittest individuals for reproduction using adaptive tournament selection
    selected_indices = []
    for _ in range(pop_size – elite_size):
    # Adaptive tournament size
    tournament_size = max(
    max(2, tournament_size_min),
    int(min(tournament_size_max, pop_size) – (generation / (opt.evolve / 10))),
    )
    # Perform tournament selection to choose the best individual
    tournament_indices = random.sample(range(pop_size), tournament_size)
    tournament_fitness = [fitness_scores[j] for j in tournament_indices]
    winner_index = tournament_indices[tournament_fitness.index(max(tournament_fitness))]
    selected_indices.append(winner_index)

    # Add the elite individuals to the selected indices
    elite_indices = [i for i in range(pop_size) if fitness_scores[i] in sorted(fitness_scores)[-elite_size:]]
    selected_indices.extend(elite_indices)
    # Create the next generation through crossover and mutation
    next_generation = []
    for _ in range(pop_size):
    parent1_index = selected_indices[random.randint(0, pop_size – 1)]
    parent2_index = selected_indices[random.randint(0, pop_size – 1)]
    # Adaptive crossover rate
    crossover_rate = max(
    crossover_rate_min, min(crossover_rate_max, crossover_rate_max – (generation / opt.evolve))
    )
    if random.uniform(0, 1) < crossover_rate:
    crossover_point = random.randint(1, len(hyp_GA) – 1)
    child = population[parent1_index][:crossover_point] + population[parent2_index][crossover_point:]
    else:
    child = population[parent1_index]
    # Adaptive mutation rate
    mutation_rate = max(
    mutation_rate_min, min(mutation_rate_max, mutation_rate_max – (generation / opt.evolve))
    )
    for j in range(len(hyp_GA)):
    if random.uniform(0, 1) < mutation_rate:
    child[j] += random.uniform(-0.1, 0.1)
    child[j] = min(max(child[j], gene_ranges[j][0]), gene_ranges[j][1])
    next_generation.append(child)
    # Replace the old population with the new generation
    population = next_generation
    # Print the best solution found
    best_index = fitness_scores.index(max(fitness_scores))
    best_individual = population[best_index]
    print("Best solution found:", best_individual)
    # Plot results
    plot_evolve(evolve_csv)
    LOGGER.info(
    f'Hyperparameter evolution finished {opt.evolve} generations\\n'
    f"Results saved to {colorstr('bold', save_dir)}\\n"
    f'Usage example: $ python train.py –hyp {evolve_yaml}'
    )

    def generate_individual(input_ranges, individual_length):
    """Generates a list of random values within specified input ranges for each gene in the individual."""
    individual = []
    for i in range(individual_length):
    lower_bound, upper_bound = input_ranges[i]
    individual.append(random.uniform(lower_bound, upper_bound))
    return individual

    def run(**kwargs):
    """
    Executes YOLOv5 training with given options, overriding with any kwargs provided.

    Example: import train; train.run(data='coco128.yaml', imgsz=320, weights='yolov5m.pt')
    """
    opt = parse_opt(True)
    for k, v in kwargs.items():
    setattr(opt, k, v)
    main(opt)
    return opt

    if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

    val.py

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    """
    Validate a trained YOLOv5 detection model on a detection dataset.

    Usage:
    $ python val.py –weights yolov5s.pt –data coco128.yaml –img 640

    Usage – formats:
    $ python val.py –weights yolov5s.pt # PyTorch
    yolov5s.torchscript # TorchScript
    yolov5s.onnx # ONNX Runtime or OpenCV DNN with –dnn
    yolov5s_openvino_model # OpenVINO
    yolov5s.engine # TensorRT
    yolov5s.mlmodel # CoreML (macOS-only)
    yolov5s_saved_model # TensorFlow SavedModel
    yolov5s.pb # TensorFlow GraphDef
    yolov5s.tflite # TensorFlow Lite
    yolov5s_edgetpu.tflite # TensorFlow Edge TPU
    yolov5s_paddle_model # PaddlePaddle
    """

    import argparse
    import json
    import os
    import subprocess
    import sys
    from pathlib import Path

    import numpy as np
    import torch
    from tqdm import tqdm

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0] # YOLOv5 root directory
    if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT)) # add ROOT to PATH
    ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative

    from models.common import DetectMultiBackend
    from utils.callbacks import Callbacks
    from utils.dataloaders import create_dataloader
    from utils.general import (
    LOGGER,
    TQDM_BAR_FORMAT,
    Profile,
    check_dataset,
    check_img_size,
    check_requirements,
    check_yaml,
    coco80_to_coco91_class,
    colorstr,
    increment_path,
    non_max_suppression,
    print_args,
    scale_boxes,
    xywh2xyxy,
    xyxy2xywh,
    )
    from utils.metrics import ConfusionMatrix, ap_per_class, box_iou
    from utils.plots import output_to_target, plot_images, plot_val_study
    from utils.torch_utils import select_device, smart_inference_mode

    def save_one_txt(predn, save_conf, shape, file):
    """Saves one detection result to a txt file in normalized xywh format, optionally including confidence."""
    gn = torch.tensor(shape)[[1, 0, 1, 0]] # normalization gain whwh
    for *xyxy, conf, cls in predn.tolist():
    xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
    line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format
    with open(file, "a") as f:
    f.write(("%g " * len(line)).rstrip() % line + "\\n")

    def save_one_json(predn, jdict, path, class_map):
    """
    Saves one JSON detection result with image ID, category ID, bounding box, and score.

    Example: {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}
    """
    image_id = int(path.stem) if path.stem.isnumeric() else path.stem
    box = xyxy2xywh(predn[:, :4]) # xywh
    box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner
    for p, b in zip(predn.tolist(), box.tolist()):
    jdict.append(
    {
    "image_id": image_id,
    "category_id": class_map[int(p[5])],
    "bbox": [round(x, 3) for x in b],
    "score": round(p[4], 5),
    }
    )

    def process_batch(detections, labels, iouv):
    """
    Return correct prediction matrix.

    Arguments:
    detections (array[N, 6]), x1, y1, x2, y2, conf, class
    labels (array[M, 5]), class, x1, y1, x2, y2
    Returns:
    correct (array[N, 10]), for 10 IoU levels
    """
    correct = np.zeros((detections.shape[0], iouv.shape[0])).astype(bool)
    iou = box_iou(labels[:, 1:], detections[:, :4])
    correct_class = labels[:, 0:1] == detections[:, 5]
    for i in range(len(iouv)):
    x = torch.where((iou >= iouv[i]) & correct_class) # IoU > threshold and classes match
    if x[0].shape[0]:
    matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() # [label, detect, iou]
    if x[0].shape[0] > 1:
    matches = matches[matches[:, 2].argsort()[::-1]]
    matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
    # matches = matches[matches[:, 2].argsort()[::-1]]
    matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
    correct[matches[:, 1].astype(int), i] = True
    return torch.tensor(correct, dtype=torch.bool, device=iouv.device)

    @smart_inference_mode()
    def run(
    data,
    weights=None, # model.pt path(s)
    batch_size=32, # batch size
    imgsz=640, # inference size (pixels)
    conf_thres=0.001, # confidence threshold
    iou_thres=0.6, # NMS IoU threshold
    max_det=300, # maximum detections per image
    task="val", # train, val, test, speed or study
    device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
    workers=8, # max dataloader workers (per RANK in DDP mode)
    single_cls=False, # treat as single-class dataset
    augment=False, # augmented inference
    verbose=False, # verbose output
    save_txt=False, # save results to *.txt
    save_hybrid=False, # save label+prediction hybrid results to *.txt
    save_conf=False, # save confidences in –save-txt labels
    save_json=False, # save a COCO-JSON results file
    project=ROOT / "runs/val", # save to project/name
    name="exp", # save to project/name
    exist_ok=False, # existing project/name ok, do not increment
    half=True, # use FP16 half-precision inference
    dnn=False, # use OpenCV DNN for ONNX inference
    model=None,
    dataloader=None,
    save_dir=Path(""),
    plots=True,
    callbacks=Callbacks(),
    compute_loss=None,
    ):
    # Initialize/load model and set device
    training = model is not None
    if training: # called by train.py
    device, pt, jit, engine = next(model.parameters()).device, True, False, False # get model device, PyTorch model
    half &= device.type != "cpu" # half precision only supported on CUDA
    model.half() if half else model.float()
    else: # called directly
    device = select_device(device, batch_size=batch_size)

    # Directories
    save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run
    (save_dir / "labels" if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir

    # Load model
    model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
    stride, pt, jit, engine = model.stride, model.pt, model.jit, model.engine
    imgsz = check_img_size(imgsz, s=stride) # check image size
    half = model.fp16 # FP16 supported on limited backends with CUDA
    if engine:
    batch_size = model.batch_size
    else:
    device = model.device
    if not (pt or jit):
    batch_size = 1 # export.py models default to batch-size 1
    LOGGER.info(f"Forcing –batch-size 1 square inference (1,3,{imgsz},{imgsz}) for non-PyTorch models")

    # Data
    data = check_dataset(data) # check

    # Configure
    model.eval()
    cuda = device.type != "cpu"
    is_coco = isinstance(data.get("val"), str) and data["val"].endswith(f"coco{os.sep}val2017.txt") # COCO dataset
    nc = 1 if single_cls else int(data["nc"]) # number of classes
    iouv = torch.linspace(0.5, 0.95, 10, device=device) # iou vector for mAP@0.5:0.95
    niou = iouv.numel()

    # Dataloader
    if not training:
    if pt and not single_cls: # check –weights are trained on –data
    ncm = model.model.nc
    assert ncm == nc, (
    f"{weights} ({ncm} classes) trained on different –data than what you passed ({nc} "
    f"classes). Pass correct combination of –weights and –data that are trained together."
    )
    model.warmup(imgsz=(1 if pt else batch_size, 3, imgsz, imgsz)) # warmup
    pad, rect = (0.0, False) if task == "speed" else (0.5, pt) # square inference for benchmarks
    task = task if task in ("train", "val", "test") else "val" # path to train/val/test images
    dataloader = create_dataloader(
    data[task],
    imgsz,
    batch_size,
    stride,
    single_cls,
    pad=pad,
    rect=rect,
    workers=workers,
    prefix=colorstr(f"{task}: "),
    )[0]

    seen = 0
    confusion_matrix = ConfusionMatrix(nc=nc)
    names = model.names if hasattr(model, "names") else model.module.names # get class names
    if isinstance(names, (list, tuple)): # old format
    names = dict(enumerate(names))
    class_map = coco80_to_coco91_class() if is_coco else list(range(1000))
    s = ("%22s" + "%11s" * 6) % ("Class", "Images", "Instances", "P", "R", "mAP50", "mAP50-95")
    tp, fp, p, r, f1, mp, mr, map50, ap50, map = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    dt = Profile(device=device), Profile(device=device), Profile(device=device) # profiling times
    loss = torch.zeros(3, device=device)
    jdict, stats, ap, ap_class = [], [], [], []
    callbacks.run("on_val_start")
    pbar = tqdm(dataloader, desc=s, bar_format=TQDM_BAR_FORMAT) # progress bar
    for batch_i, (im, targets, paths, shapes) in enumerate(pbar):
    callbacks.run("on_val_batch_start")
    with dt[0]:
    if cuda:
    im = im.to(device, non_blocking=True)
    targets = targets.to(device)
    im = im.half() if half else im.float() # uint8 to fp16/32
    im /= 255 # 0 – 255 to 0.0 – 1.0
    nb, _, height, width = im.shape # batch size, channels, height, width

    # Inference
    with dt[1]:
    preds, train_out = model(im) if compute_loss else (model(im, augment=augment), None)

    # Loss
    if compute_loss:
    loss += compute_loss(train_out, targets)[1] # box, obj, cls

    # NMS
    targets[:, 2:] *= torch.tensor((width, height, width, height), device=device) # to pixels
    lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else [] # for autolabelling
    with dt[2]:
    preds = non_max_suppression(
    preds, conf_thres, iou_thres, labels=lb, multi_label=True, agnostic=single_cls, max_det=max_det
    )

    # Metrics
    for si, pred in enumerate(preds):
    labels = targets[targets[:, 0] == si, 1:]
    nl, npr = labels.shape[0], pred.shape[0] # number of labels, predictions
    path, shape = Path(paths[si]), shapes[si][0]
    correct = torch.zeros(npr, niou, dtype=torch.bool, device=device) # init
    seen += 1

    if npr == 0:
    if nl:
    stats.append((correct, *torch.zeros((2, 0), device=device), labels[:, 0]))
    if plots:
    confusion_matrix.process_batch(detections=None, labels=labels[:, 0])
    continue

    # Predictions
    if single_cls:
    pred[:, 5] = 0
    predn = pred.clone()
    scale_boxes(im[si].shape[1:], predn[:, :4], shape, shapes[si][1]) # native-space pred

    # Evaluate
    if nl:
    tbox = xywh2xyxy(labels[:, 1:5]) # target boxes
    scale_boxes(im[si].shape[1:], tbox, shape, shapes[si][1]) # native-space labels
    labelsn = torch.cat((labels[:, 0:1], tbox), 1) # native-space labels
    correct = process_batch(predn, labelsn, iouv)
    if plots:
    confusion_matrix.process_batch(predn, labelsn)
    stats.append((correct, pred[:, 4], pred[:, 5], labels[:, 0])) # (correct, conf, pcls, tcls)

    # Save/log
    if save_txt:
    (save_dir / "labels").mkdir(parents=True, exist_ok=True)
    save_one_txt(predn, save_conf, shape, file=save_dir / "labels" / f"{path.stem}.txt")
    if save_json:
    save_one_json(predn, jdict, path, class_map) # append to COCO-JSON dictionary
    callbacks.run("on_val_image_end", pred, predn, path, names, im[si])

    # Plot images
    if plots and batch_i < 3:
    plot_images(im, targets, paths, save_dir / f"val_batch{batch_i}_labels.jpg", names) # labels
    plot_images(im, output_to_target(preds), paths, save_dir / f"val_batch{batch_i}_pred.jpg", names) # pred

    callbacks.run("on_val_batch_end", batch_i, im, targets, paths, shapes, preds)

    # Compute metrics
    stats = [torch.cat(x, 0).cpu().numpy() for x in zip(*stats)] # to numpy
    if len(stats) and stats[0].any():
    tp, fp, p, r, f1, ap, ap_class = ap_per_class(*stats, plot=plots, save_dir=save_dir, names=names)
    ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
    mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
    nt = np.bincount(stats[3].astype(int), minlength=nc) # number of targets per class

    # Print results
    pf = "%22s" + "%11i" * 2 + "%11.3g" * 4 # print format
    LOGGER.info(pf % ("all", seen, nt.sum(), mp, mr, map50, map))
    if nt.sum() == 0:
    LOGGER.warning(f"WARNING ⚠️ no labels found in {task} set, can not compute metrics without labels")

    # Print results per class
    if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
    for i, c in enumerate(ap_class):
    LOGGER.info(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))

    # Print speeds
    t = tuple(x.t / seen * 1e3 for x in dt) # speeds per image
    if not training:
    shape = (batch_size, 3, imgsz, imgsz)
    LOGGER.info(f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {shape}" % t)

    # Plots
    if plots:
    confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))
    callbacks.run("on_val_end", nt, tp, fp, p, r, f1, ap, ap50, ap_class, confusion_matrix)

    # Save JSON
    if save_json and len(jdict):
    w = Path(weights[0] if isinstance(weights, list) else weights).stem if weights is not None else "" # weights
    anno_json = str(Path("../datasets/coco/annotations/instances_val2017.json")) # annotations
    if not os.path.exists(anno_json):
    anno_json = os.path.join(data["path"], "annotations", "instances_val2017.json")
    pred_json = str(save_dir / f"{w}_predictions.json") # predictions
    LOGGER.info(f"\\nEvaluating pycocotools mAP… saving {pred_json}…")
    with open(pred_json, "w") as f:
    json.dump(jdict, f)

    try: # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
    check_requirements("pycocotools>=2.0.6")
    from pycocotools.coco import COCO
    from pycocotools.cocoeval import COCOeval

    anno = COCO(anno_json) # init annotations api
    pred = anno.loadRes(pred_json) # init predictions api
    eval = COCOeval(anno, pred, "bbox")
    if is_coco:
    eval.params.imgIds = [int(Path(x).stem) for x in dataloader.dataset.im_files] # image IDs to evaluate
    eval.evaluate()
    eval.accumulate()
    eval.summarize()
    map, map50 = eval.stats[:2] # update results (mAP@0.5:0.95, mAP@0.5)
    except Exception as e:
    LOGGER.info(f"pycocotools unable to run: {e}")

    # Return results
    model.float() # for training
    if not training:
    s = f"\\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ""
    LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
    maps = np.zeros(nc) + map
    for i, c in enumerate(ap_class):
    maps[c] = ap[i]
    return (mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()), maps, t

    def parse_opt():
    """Parses command-line options for YOLOv5 model inference configuration."""
    parser = argparse.ArgumentParser()
    parser.add_argument("–data", type=str, default=ROOT / "data/coco128.yaml", help="dataset.yaml path")
    parser.add_argument("–weights", nargs="+", type=str, default=ROOT / "yolov5s.pt", help="model path(s)")
    parser.add_argument("–batch-size", type=int, default=32, help="batch size")
    parser.add_argument("–imgsz", "–img", "–img-size", type=int, default=640, help="inference size (pixels)")
    parser.add_argument("–conf-thres", type=float, default=0.001, help="confidence threshold")
    parser.add_argument("–iou-thres", type=float, default=0.6, help="NMS IoU threshold")
    parser.add_argument("–max-det", type=int, default=300, help="maximum detections per image")
    parser.add_argument("–task", default="val", help="train, val, test, speed or study")
    parser.add_argument("–device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
    parser.add_argument("–workers", type=int, default=8, help="max dataloader workers (per RANK in DDP mode)")
    parser.add_argument("–single-cls", action="store_true", help="treat as single-class dataset")
    parser.add_argument("–augment", action="store_true", help="augmented inference")
    parser.add_argument("–verbose", action="store_true", help="report mAP by class")
    parser.add_argument("–save-txt", action="store_true", help="save results to *.txt")
    parser.add_argument("–save-hybrid", action="store_true", help="save label+prediction hybrid results to *.txt")
    parser.add_argument("–save-conf", action="store_true", help="save confidences in –save-txt labels")
    parser.add_argument("–save-json", action="store_true", help="save a COCO-JSON results file")
    parser.add_argument("–project", default=ROOT / "runs/val", help="save to project/name")
    parser.add_argument("–name", default="exp", help="save to project/name")
    parser.add_argument("–exist-ok", action="store_true", help="existing project/name ok, do not increment")
    parser.add_argument("–half", action="store_true", help="use FP16 half-precision inference")
    parser.add_argument("–dnn", action="store_true", help="use OpenCV DNN for ONNX inference")
    opt = parser.parse_args()
    opt.data = check_yaml(opt.data) # check YAML
    opt.save_json |= opt.data.endswith("coco.yaml")
    opt.save_txt |= opt.save_hybrid
    print_args(vars(opt))
    return opt

    def main(opt):
    """Executes YOLOv5 tasks like training, validation, testing, speed, and study benchmarks based on provided
    options.
    """
    check_requirements(ROOT / "requirements.txt", exclude=("tensorboard", "thop"))

    if opt.task in ("train", "val", "test"): # run normally
    if opt.conf_thres > 0.001: # https://github.com/ultralytics/yolov5/issues/1466
    LOGGER.info(f"WARNING ⚠️ confidence threshold {opt.conf_thres} > 0.001 produces invalid results")
    if opt.save_hybrid:
    LOGGER.info("WARNING ⚠️ –save-hybrid will return high mAP from hybrid labels, not from predictions alone")
    run(**vars(opt))

    else:
    weights = opt.weights if isinstance(opt.weights, list) else [opt.weights]
    opt.half = torch.cuda.is_available() and opt.device != "cpu" # FP16 for fastest results
    if opt.task == "speed": # speed benchmarks
    # python val.py –task speed –data coco.yaml –batch 1 –weights yolov5n.pt yolov5s.pt…
    opt.conf_thres, opt.iou_thres, opt.save_json = 0.25, 0.45, False
    for opt.weights in weights:
    run(**vars(opt), plots=False)

    elif opt.task == "study": # speed vs mAP benchmarks
    # python val.py –task study –data coco.yaml –iou 0.7 –weights yolov5n.pt yolov5s.pt…
    for opt.weights in weights:
    f = f"study_{Path(opt.data).stem}_{Path(opt.weights).stem}.txt" # filename to save to
    x, y = list(range(256, 1536 + 128, 128)), [] # x axis (image sizes), y axis
    for opt.imgsz in x: # img-size
    LOGGER.info(f"\\nRunning {f} –imgsz {opt.imgsz}…")
    r, _, t = run(**vars(opt), plots=False)
    y.append(r + t) # results and times
    np.savetxt(f, y, fmt="%10.4g") # save
    subprocess.run(["zip", "-r", "study.zip", "study_*.txt"])
    plot_val_study(x=x) # plot
    else:
    raise NotImplementedError(f'–task {opt.task} not in ("train", "val", "test", "speed", "study")')

    if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

    detect.py

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    """
    Run YOLOv5 detection inference on images, videos, directories, globs, YouTube, webcam, streams, etc.

    Usage – sources:
    $ python detect.py –weights yolov5s.pt –source 0 # webcam
    img.jpg # image
    vid.mp4 # video
    screen # screenshot
    path/ # directory
    list.txt # list of images
    list.streams # list of streams
    'path/*.jpg' # glob
    'https://youtu.be/LNwODJXcvt4' # YouTube
    'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream

    Usage – formats:
    $ python detect.py –weights yolov5s.pt # PyTorch
    yolov5s.torchscript # TorchScript
    yolov5s.onnx # ONNX Runtime or OpenCV DNN with –dnn
    yolov5s_openvino_model # OpenVINO
    yolov5s.engine # TensorRT
    yolov5s.mlmodel # CoreML (macOS-only)
    yolov5s_saved_model # TensorFlow SavedModel
    yolov5s.pb # TensorFlow GraphDef
    yolov5s.tflite # TensorFlow Lite
    yolov5s_edgetpu.tflite # TensorFlow Edge TPU
    yolov5s_paddle_model # PaddlePaddle
    """

    import argparse
    import csv
    import os
    import platform
    import sys
    from pathlib import Path

    import torch

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0] # YOLOv5 root directory
    if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT)) # add ROOT to PATH
    ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative

    from ultralytics.utils.plotting import Annotator, colors, save_one_box

    from models.common import DetectMultiBackend
    from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
    from utils.general import (
    LOGGER,
    Profile,
    check_file,
    check_img_size,
    check_imshow,
    check_requirements,
    colorstr,
    cv2,
    increment_path,
    non_max_suppression,
    print_args,
    scale_boxes,
    strip_optimizer,
    xyxy2xywh,
    )
    from utils.torch_utils import select_device, smart_inference_mode

    @smart_inference_mode()
    def run(
    weights=ROOT / "yolov5s.pt", # model path or triton URL
    source=ROOT / "data/images", # file/dir/URL/glob/screen/0(webcam)
    data=ROOT / "data/coco128.yaml", # dataset.yaml path
    imgsz=(640, 640), # inference size (height, width)
    conf_thres=0.25, # confidence threshold
    iou_thres=0.45, # NMS IOU threshold
    max_det=1000, # maximum detections per image
    device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
    view_img=False, # show results
    save_txt=False, # save results to *.txt
    save_csv=False, # save results in CSV format
    save_conf=False, # save confidences in –save-txt labels
    save_crop=False, # save cropped prediction boxes
    nosave=False, # do not save images/videos
    classes=None, # filter by class: –class 0, or –class 0 2 3
    agnostic_nms=False, # class-agnostic NMS
    augment=False, # augmented inference
    visualize=False, # visualize features
    update=False, # update all models
    project=ROOT / "runs/detect", # save results to project/name
    name="exp", # save results to project/name
    exist_ok=False, # existing project/name ok, do not increment
    line_thickness=3, # bounding box thickness (pixels)
    hide_labels=False, # hide labels
    hide_conf=False, # hide confidences
    half=False, # use FP16 half-precision inference
    dnn=False, # use OpenCV DNN for ONNX inference
    vid_stride=1, # video frame-rate stride
    ):
    source = str(source)
    save_img = not nosave and not source.endswith(".txt") # save inference images
    is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
    is_url = source.lower().startswith(("rtsp://", "rtmp://", "http://", "https://"))
    webcam = source.isnumeric() or source.endswith(".streams") or (is_url and not is_file)
    screenshot = source.lower().startswith("screen")
    if is_url and is_file:
    source = check_file(source) # download

    # Directories
    save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run
    (save_dir / "labels" if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir

    # Load model
    device = select_device(device)
    model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
    stride, names, pt = model.stride, model.names, model.pt
    imgsz = check_img_size(imgsz, s=stride) # check image size

    # Dataloader
    bs = 1 # batch_size
    if webcam:
    view_img = check_imshow(warn=True)
    dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
    bs = len(dataset)
    elif screenshot:
    dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt)
    else:
    dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
    vid_path, vid_writer = [None] * bs, [None] * bs

    # Run inference
    model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup
    seen, windows, dt = 0, [], (Profile(device=device), Profile(device=device), Profile(device=device))
    for path, im, im0s, vid_cap, s in dataset:
    with dt[0]:
    im = torch.from_numpy(im).to(model.device)
    im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
    im /= 255 # 0 – 255 to 0.0 – 1.0
    if len(im.shape) == 3:
    im = im[None] # expand for batch dim
    if model.xml and im.shape[0] > 1:
    ims = torch.chunk(im, im.shape[0], 0)

    # Inference
    with dt[1]:
    visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False
    if model.xml and im.shape[0] > 1:
    pred = None
    for image in ims:
    if pred is None:
    pred = model(image, augment=augment, visualize=visualize).unsqueeze(0)
    else:
    pred = torch.cat((pred, model(image, augment=augment, visualize=visualize).unsqueeze(0)), dim=0)
    pred = [pred, None]
    else:
    pred = model(im, augment=augment, visualize=visualize)
    # NMS
    with dt[2]:
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)

    # Second-stage classifier (optional)
    # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)

    # Define the path for the CSV file
    csv_path = save_dir / "predictions.csv"

    # Create or append to the CSV file
    def write_to_csv(image_name, prediction, confidence):
    """Writes prediction data for an image to a CSV file, appending if the file exists."""
    data = {"Image Name": image_name, "Prediction": prediction, "Confidence": confidence}
    with open(csv_path, mode="a", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=data.keys())
    if not csv_path.is_file():
    writer.writeheader()
    writer.writerow(data)

    # Process predictions
    for i, det in enumerate(pred): # per image
    seen += 1
    if webcam: # batch_size >= 1
    p, im0, frame = path[i], im0s[i].copy(), dataset.count
    s += f"{i}: "
    else:
    p, im0, frame = path, im0s.copy(), getattr(dataset, "frame", 0)

    p = Path(p) # to Path
    save_path = str(save_dir / p.name) # im.jpg
    txt_path = str(save_dir / "labels" / p.stem) + ("" if dataset.mode == "image" else f"_{frame}") # im.txt
    s += "%gx%g " % im.shape[2:] # print string
    gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
    imc = im0.copy() if save_crop else im0 # for save_crop
    annotator = Annotator(im0, line_width=line_thickness, example=str(names))
    if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0.shape).round()

    # Print results
    for c in det[:, 5].unique():
    n = (det[:, 5] == c).sum() # detections per class
    s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string

    # Write results
    for *xyxy, conf, cls in reversed(det):
    c = int(cls) # integer class
    label = names[c] if hide_conf else f"{names[c]}"
    confidence = float(conf)
    confidence_str = f"{confidence:.2f}"

    if save_csv:
    write_to_csv(p.name, label, confidence_str)

    if save_txt: # Write to file
    xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
    line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format
    with open(f"{txt_path}.txt", "a") as f:
    f.write(("%g " * len(line)).rstrip() % line + "\\n")

    if save_img or save_crop or view_img: # Add bbox to image
    c = int(cls) # integer class
    label = None if hide_labels else (names[c] if hide_conf else f"{names[c]} {conf:.2f}")
    annotator.box_label(xyxy, label, color=colors(c, True))
    if save_crop:
    save_one_box(xyxy, imc, file=save_dir / "crops" / names[c] / f"{p.stem}.jpg", BGR=True)

    # Stream results
    im0 = annotator.result()
    if view_img:
    if platform.system() == "Linux" and p not in windows:
    windows.append(p)
    cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux)
    cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
    cv2.imshow(str(p), im0)
    cv2.waitKey(1) # 1 millisecond

    # Save results (image with detections)
    if save_img:
    if dataset.mode == "image":
    cv2.imwrite(save_path, im0)
    else: # 'video' or 'stream'
    if vid_path[i] != save_path: # new video
    vid_path[i] = save_path
    if isinstance(vid_writer[i], cv2.VideoWriter):
    vid_writer[i].release() # release previous video writer
    if vid_cap: # video
    fps = vid_cap.get(cv2.CAP_PROP_FPS)
    w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    else: # stream
    fps, w, h = 30, im0.shape[1], im0.shape[0]
    save_path = str(Path(save_path).with_suffix(".mp4")) # force *.mp4 suffix on results videos
    vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
    vid_writer[i].write(im0)

    # Print time (inference-only)
    LOGGER.info(f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms")

    # Print results
    t = tuple(x.t / seen * 1e3 for x in dt) # speeds per image
    LOGGER.info(f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}" % t)
    if save_txt or save_img:
    s = f"\\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ""
    LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
    if update:
    strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning)

    def parse_opt():
    """Parses command-line arguments for YOLOv5 detection, setting inference options and model configurations."""
    parser = argparse.ArgumentParser()
    parser.add_argument("–weights", nargs="+", type=str, default=ROOT / "yolov5s.pt", help="model path or triton URL")
    parser.add_argument("–source", type=str, default=ROOT / "data/images", help="file/dir/URL/glob/screen/0(webcam)")
    parser.add_argument("–data", type=str, default=ROOT / "data/coco128.yaml", help="(optional) dataset.yaml path")
    parser.add_argument("–imgsz", "–img", "–img-size", nargs="+", type=int, default=[640], help="inference size h,w")
    parser.add_argument("–conf-thres", type=float, default=0.25, help="confidence threshold")
    parser.add_argument("–iou-thres", type=float, default=0.45, help="NMS IoU threshold")
    parser.add_argument("–max-det", type=int, default=1000, help="maximum detections per image")
    parser.add_argument("–device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
    parser.add_argument("–view-img", action="store_true", help="show results")
    parser.add_argument("–save-txt", action="store_true", help="save results to *.txt")
    parser.add_argument("–save-csv", action="store_true", help="save results in CSV format")
    parser.add_argument("–save-conf", action="store_true", help="save confidences in –save-txt labels")
    parser.add_argument("–save-crop", action="store_true", help="save cropped prediction boxes")
    parser.add_argument("–nosave", action="store_true", help="do not save images/videos")
    parser.add_argument("–classes", nargs="+", type=int, help="filter by class: –classes 0, or –classes 0 2 3")
    parser.add_argument("–agnostic-nms", action="store_true", help="class-agnostic NMS")
    parser.add_argument("–augment", action="store_true", help="augmented inference")
    parser.add_argument("–visualize", action="store_true", help="visualize features")
    parser.add_argument("–update", action="store_true", help="update all models")
    parser.add_argument("–project", default=ROOT / "runs/detect", help="save results to project/name")
    parser.add_argument("–name", default="exp", help="save results to project/name")
    parser.add_argument("–exist-ok", action="store_true", help="existing project/name ok, do not increment")
    parser.add_argument("–line-thickness", default=3, type=int, help="bounding box thickness (pixels)")
    parser.add_argument("–hide-labels", default=False, action="store_true", help="hide labels")
    parser.add_argument("–hide-conf", default=False, action="store_true", help="hide confidences")
    parser.add_argument("–half", action="store_true", help="use FP16 half-precision inference")
    parser.add_argument("–dnn", action="store_true", help="use OpenCV DNN for ONNX inference")
    parser.add_argument("–vid-stride", type=int, default=1, help="video frame-rate stride")
    opt = parser.parse_args()
    opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand
    print_args(vars(opt))
    return opt

    def main(opt):
    """Executes YOLOv5 model inference with given options, checking requirements before running the model."""
    check_requirements(ROOT / "requirements.txt", exclude=("tensorboard", "thop"))
    run(**vars(opt))

    if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

    export.py

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    """
    Export a YOLOv5 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit

    Format | `export.py –include` | Model
    — | — | —
    PyTorch | – | yolov5s.pt
    TorchScript | `torchscript` | yolov5s.torchscript
    ONNX | `onnx` | yolov5s.onnx
    OpenVINO | `openvino` | yolov5s_openvino_model/
    TensorRT | `engine` | yolov5s.engine
    CoreML | `coreml` | yolov5s.mlmodel
    TensorFlow SavedModel | `saved_model` | yolov5s_saved_model/
    TensorFlow GraphDef | `pb` | yolov5s.pb
    TensorFlow Lite | `tflite` | yolov5s.tflite
    TensorFlow Edge TPU | `edgetpu` | yolov5s_edgetpu.tflite
    TensorFlow.js | `tfjs` | yolov5s_web_model/
    PaddlePaddle | `paddle` | yolov5s_paddle_model/

    Requirements:
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu # CPU
    $ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow # GPU

    Usage:
    $ python export.py –weights yolov5s.pt –include torchscript onnx openvino engine coreml tflite …

    Inference:
    $ python detect.py –weights yolov5s.pt # PyTorch
    yolov5s.torchscript # TorchScript
    yolov5s.onnx # ONNX Runtime or OpenCV DNN with –dnn
    yolov5s_openvino_model # OpenVINO
    yolov5s.engine # TensorRT
    yolov5s.mlmodel # CoreML (macOS-only)
    yolov5s_saved_model # TensorFlow SavedModel
    yolov5s.pb # TensorFlow GraphDef
    yolov5s.tflite # TensorFlow Lite
    yolov5s_edgetpu.tflite # TensorFlow Edge TPU
    yolov5s_paddle_model # PaddlePaddle

    TensorFlow.js:
    $ cd .. && git clone https://github.com/zldrobit/tfjs-yolov5-example.git && cd tfjs-yolov5-example
    $ npm install
    $ ln -s ../../yolov5/yolov5s_web_model public/yolov5s_web_model
    $ npm start
    """

    import argparse
    import contextlib
    import json
    import os
    import platform
    import re
    import subprocess
    import sys
    import time
    import warnings
    from pathlib import Path

    import pandas as pd
    import torch
    from torch.utils.mobile_optimizer import optimize_for_mobile

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0] # YOLOv5 root directory
    if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT)) # add ROOT to PATH
    if platform.system() != "Windows":
    ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative

    from models.experimental import attempt_load
    from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
    from utils.dataloaders import LoadImages
    from utils.general import (
    LOGGER,
    Profile,
    check_dataset,
    check_img_size,
    check_requirements,
    check_version,
    check_yaml,
    colorstr,
    file_size,
    get_default_args,
    print_args,
    url2file,
    yaml_save,
    )
    from utils.torch_utils import select_device, smart_inference_mode

    MACOS = platform.system() == "Darwin" # macOS environment

    class iOSModel(torch.nn.Module):
    def __init__(self, model, im):
    """Initializes an iOS compatible model with normalization based on image dimensions."""
    super().__init__()
    b, c, h, w = im.shape # batch, channel, height, width
    self.model = model
    self.nc = model.nc # number of classes
    if w == h:
    self.normalize = 1.0 / w
    else:
    self.normalize = torch.tensor([1.0 / w, 1.0 / h, 1.0 / w, 1.0 / h]) # broadcast (slower, smaller)
    # np = model(im)[0].shape[1] # number of points
    # self.normalize = torch.tensor([1. / w, 1. / h, 1. / w, 1. / h]).expand(np, 4) # explicit (faster, larger)

    def forward(self, x):
    """Runs forward pass on the input tensor, returning class confidences and normalized coordinates."""
    xywh, conf, cls = self.model(x)[0].squeeze().split((4, 1, self.nc), 1)
    return cls * conf, xywh * self.normalize # confidence (3780, 80), coordinates (3780, 4)

    def export_formats():
    """Returns a DataFrame of supported YOLOv5 model export formats and their properties."""
    x = [
    ["PyTorch", "-", ".pt", True, True],
    ["TorchScript", "torchscript", ".torchscript", True, True],
    ["ONNX", "onnx", ".onnx", True, True],
    ["OpenVINO", "openvino", "_openvino_model", True, False],
    ["TensorRT", "engine", ".engine", False, True],
    ["CoreML", "coreml", ".mlmodel", True, False],
    ["TensorFlow SavedModel", "saved_model", "_saved_model", True, True],
    ["TensorFlow GraphDef", "pb", ".pb", True, True],
    ["TensorFlow Lite", "tflite", ".tflite", True, False],
    ["TensorFlow Edge TPU", "edgetpu", "_edgetpu.tflite", False, False],
    ["TensorFlow.js", "tfjs", "_web_model", False, False],
    ["PaddlePaddle", "paddle", "_paddle_model", True, True],
    ]
    return pd.DataFrame(x, columns=["Format", "Argument", "Suffix", "CPU", "GPU"])

    def try_export(inner_func):
    """Decorator @try_export for YOLOv5 model export functions that logs success/failure, time taken, and file size."""
    inner_args = get_default_args(inner_func)

    def outer_func(*args, **kwargs):
    prefix = inner_args["prefix"]
    try:
    with Profile() as dt:
    f, model = inner_func(*args, **kwargs)
    LOGGER.info(f"{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)")
    return f, model
    except Exception as e:
    LOGGER.info(f"{prefix} export failure ❌ {dt.t:.1f}s: {e}")
    return None, None

    return outer_func

    @try_export
    def export_torchscript(model, im, file, optimize, prefix=colorstr("TorchScript:")):
    """Exports YOLOv5 model to TorchScript format, optionally optimized for mobile, with image shape and stride
    metadata.
    """
    LOGGER.info(f"\\n{prefix} starting export with torch {torch.__version__}…")
    f = file.with_suffix(".torchscript")

    ts = torch.jit.trace(model, im, strict=False)
    d = {"shape": im.shape, "stride": int(max(model.stride)), "names": model.names}
    extra_files = {"config.txt": json.dumps(d)} # torch._C.ExtraFilesMap()
    if optimize: # https://pytorch.org/tutorials/recipes/mobile_interpreter.html
    optimize_for_mobile(ts)._save_for_lite_interpreter(str(f), _extra_files=extra_files)
    else:
    ts.save(str(f), _extra_files=extra_files)
    return f, None

    @try_export
    def export_onnx(model, im, file, opset, dynamic, simplify, prefix=colorstr("ONNX:")):
    """Exports a YOLOv5 model to ONNX format with dynamic axes and optional simplification."""
    check_requirements("onnx>=1.12.0")
    import onnx

    LOGGER.info(f"\\n{prefix} starting export with onnx {onnx.__version__}…")
    f = str(file.with_suffix(".onnx"))

    output_names = ["output0", "output1"] if isinstance(model, SegmentationModel) else ["output0"]
    if dynamic:
    dynamic = {"images": {0: "batch", 2: "height", 3: "width"}} # shape(1,3,640,640)
    if isinstance(model, SegmentationModel):
    dynamic["output0"] = {0: "batch", 1: "anchors"} # shape(1,25200,85)
    dynamic["output1"] = {0: "batch", 2: "mask_height", 3: "mask_width"} # shape(1,32,160,160)
    elif isinstance(model, DetectionModel):
    dynamic["output0"] = {0: "batch", 1: "anchors"} # shape(1,25200,85)

    torch.onnx.export(
    model.cpu() if dynamic else model, # –dynamic only compatible with cpu
    im.cpu() if dynamic else im,
    f,
    verbose=False,
    opset_version=opset,
    do_constant_folding=True, # WARNING: DNN inference with torch>=1.12 may require do_constant_folding=False
    input_names=["images"],
    output_names=output_names,
    dynamic_axes=dynamic or None,
    )

    # Checks
    model_onnx = onnx.load(f) # load onnx model
    onnx.checker.check_model(model_onnx) # check onnx model

    # Metadata
    d = {"stride": int(max(model.stride)), "names": model.names}
    for k, v in d.items():
    meta = model_onnx.metadata_props.add()
    meta.key, meta.value = k, str(v)
    onnx.save(model_onnx, f)

    # Simplify
    if simplify:
    try:
    cuda = torch.cuda.is_available()
    check_requirements(("onnxruntime-gpu" if cuda else "onnxruntime", "onnx-simplifier>=0.4.1"))
    import onnxsim

    LOGGER.info(f"{prefix} simplifying with onnx-simplifier {onnxsim.__version__}…")
    model_onnx, check = onnxsim.simplify(model_onnx)
    assert check, "assert check failed"
    onnx.save(model_onnx, f)
    except Exception as e:
    LOGGER.info(f"{prefix} simplifier failure: {e}")
    return f, model_onnx

    @try_export
    def export_openvino(file, metadata, half, int8, data, prefix=colorstr("OpenVINO:")):
    # YOLOv5 OpenVINO export
    check_requirements("openvino-dev>=2023.0") # requires openvino-dev: https://pypi.org/project/openvino-dev/
    import openvino.runtime as ov # noqa
    from openvino.tools import mo # noqa

    LOGGER.info(f"\\n{prefix} starting export with openvino {ov.__version__}…")
    f = str(file).replace(file.suffix, f"_{'int8_' if int8 else ''}openvino_model{os.sep}")
    f_onnx = file.with_suffix(".onnx")
    f_ov = str(Path(f) / file.with_suffix(".xml").name)

    ov_model = mo.convert_model(f_onnx, model_name=file.stem, framework="onnx", compress_to_fp16=half) # export

    if int8:
    check_requirements("nncf>=2.5.0") # requires at least version 2.5.0 to use the post-training quantization
    import nncf
    import numpy as np

    from utils.dataloaders import create_dataloader

    def gen_dataloader(yaml_path, task="train", imgsz=640, workers=4):
    data_yaml = check_yaml(yaml_path)
    data = check_dataset(data_yaml)
    dataloader = create_dataloader(
    data[task], imgsz=imgsz, batch_size=1, stride=32, pad=0.5, single_cls=False, rect=False, workers=workers
    )[0]
    return dataloader

    # noqa: F811

    def transform_fn(data_item):
    """
    Quantization transform function.

    Extracts and preprocess input data from dataloader item for quantization.
    Parameters:
    data_item: Tuple with data item produced by DataLoader during iteration
    Returns:
    input_tensor: Input data for quantization
    """
    assert data_item[0].dtype == torch.uint8, "input image must be uint8 for the quantization preprocessing"

    img = data_item[0].numpy().astype(np.float32) # uint8 to fp16/32
    img /= 255.0 # 0 – 255 to 0.0 – 1.0
    return np.expand_dims(img, 0) if img.ndim == 3 else img

    ds = gen_dataloader(data)
    quantization_dataset = nncf.Dataset(ds, transform_fn)
    ov_model = nncf.quantize(ov_model, quantization_dataset, preset=nncf.QuantizationPreset.MIXED)

    ov.serialize(ov_model, f_ov) # save
    yaml_save(Path(f) / file.with_suffix(".yaml").name, metadata) # add metadata.yaml
    return f, None

    @try_export
    def export_paddle(model, im, file, metadata, prefix=colorstr("PaddlePaddle:")):
    """Exports a YOLOv5 model to PaddlePaddle format using X2Paddle, saving to `save_dir` and adding a metadata.yaml
    file.
    """
    check_requirements(("paddlepaddle", "x2paddle"))
    import x2paddle
    from x2paddle.convert import pytorch2paddle

    LOGGER.info(f"\\n{prefix} starting export with X2Paddle {x2paddle.__version__}…")
    f = str(file).replace(".pt", f"_paddle_model{os.sep}")

    pytorch2paddle(module=model, save_dir=f, jit_type="trace", input_examples=[im]) # export
    yaml_save(Path(f) / file.with_suffix(".yaml").name, metadata) # add metadata.yaml
    return f, None

    @try_export
    def export_coreml(model, im, file, int8, half, nms, prefix=colorstr("CoreML:")):
    """Exports YOLOv5 model to CoreML format with optional NMS, INT8, and FP16 support; requires coremltools."""
    check_requirements("coremltools")
    import coremltools as ct

    LOGGER.info(f"\\n{prefix} starting export with coremltools {ct.__version__}…")
    f = file.with_suffix(".mlmodel")

    if nms:
    model = iOSModel(model, im)
    ts = torch.jit.trace(model, im, strict=False) # TorchScript model
    ct_model = ct.convert(ts, inputs=[ct.ImageType("image", shape=im.shape, scale=1 / 255, bias=[0, 0, 0])])
    bits, mode = (8, "kmeans_lut") if int8 else (16, "linear") if half else (32, None)
    if bits < 32:
    if MACOS: # quantization only supported on macOS
    with warnings.catch_warnings():
    warnings.filterwarnings("ignore", category=DeprecationWarning) # suppress numpy==1.20 float warning
    ct_model = ct.models.neural_network.quantization_utils.quantize_weights(ct_model, bits, mode)
    else:
    print(f"{prefix} quantization only supported on macOS, skipping…")
    ct_model.save(f)
    return f, ct_model

    @try_export
    def export_engine(model, im, file, half, dynamic, simplify, workspace=4, verbose=False, prefix=colorstr("TensorRT:")):
    """
    Exports a YOLOv5 model to TensorRT engine format, requiring GPU and TensorRT>=7.0.0.

    https://developer.nvidia.com/tensorrt
    """
    assert im.device.type != "cpu", "export running on CPU but must be on GPU, i.e. `python export.py –device 0`"
    try:
    import tensorrt as trt
    except Exception:
    if platform.system() == "Linux":
    check_requirements("nvidia-tensorrt", cmds="-U –index-url https://pypi.ngc.nvidia.com")
    import tensorrt as trt

    if trt.__version__[0] == "7": # TensorRT 7 handling https://github.com/ultralytics/yolov5/issues/6012
    grid = model.model[-1].anchor_grid
    model.model[-1].anchor_grid = [a[…, :1, :1, :] for a in grid]
    export_onnx(model, im, file, 12, dynamic, simplify) # opset 12
    model.model[-1].anchor_grid = grid
    else: # TensorRT >= 8
    check_version(trt.__version__, "8.0.0", hard=True) # require tensorrt>=8.0.0
    export_onnx(model, im, file, 12, dynamic, simplify) # opset 12
    onnx = file.with_suffix(".onnx")

    LOGGER.info(f"\\n{prefix} starting export with TensorRT {trt.__version__}…")
    is_trt10 = int(trt.__version__.split(".")[0]) >= 10 # is TensorRT >= 10
    assert onnx.exists(), f"failed to export ONNX file: {onnx}"
    f = file.with_suffix(".engine") # TensorRT engine file
    logger = trt.Logger(trt.Logger.INFO)
    if verbose:
    logger.min_severity = trt.Logger.Severity.VERBOSE

    builder = trt.Builder(logger)
    config = builder.create_builder_config()
    if is_trt10:
    config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace << 30)
    else: # TensorRT versions 7, 8
    config.max_workspace_size = workspace * 1 << 30
    flag = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
    network = builder.create_network(flag)
    parser = trt.OnnxParser(network, logger)
    if not parser.parse_from_file(str(onnx)):
    raise RuntimeError(f"failed to load ONNX file: {onnx}")

    inputs = [network.get_input(i) for i in range(network.num_inputs)]
    outputs = [network.get_output(i) for i in range(network.num_outputs)]
    for inp in inputs:
    LOGGER.info(f'{prefix} input "{inp.name}" with shape{inp.shape} {inp.dtype}')
    for out in outputs:
    LOGGER.info(f'{prefix} output "{out.name}" with shape{out.shape} {out.dtype}')

    if dynamic:
    if im.shape[0] <= 1:
    LOGGER.warning(f"{prefix} WARNING ⚠️ –dynamic model requires maximum –batch-size argument")
    profile = builder.create_optimization_profile()
    for inp in inputs:
    profile.set_shape(inp.name, (1, *im.shape[1:]), (max(1, im.shape[0] // 2), *im.shape[1:]), im.shape)
    config.add_optimization_profile(profile)

    LOGGER.info(f"{prefix} building FP{16 if builder.platform_has_fast_fp16 and half else 32} engine as {f}")
    if builder.platform_has_fast_fp16 and half:
    config.set_flag(trt.BuilderFlag.FP16)

    build = builder.build_serialized_network if is_trt10 else builder.build_engine
    with build(network, config) as engine, open(f, "wb") as t:
    t.write(engine if is_trt10 else engine.serialize())
    return f, None

    @try_export
    def export_saved_model(
    model,
    im,
    file,
    dynamic,
    tf_nms=False,
    agnostic_nms=False,
    topk_per_class=100,
    topk_all=100,
    iou_thres=0.45,
    conf_thres=0.25,
    keras=False,
    prefix=colorstr("TensorFlow SavedModel:"),
    ):
    # YOLOv5 TensorFlow SavedModel export
    try:
    import tensorflow as tf
    except Exception:
    check_requirements(f"tensorflow{'' if torch.cuda.is_available() else '-macos' if MACOS else '-cpu'}<=2.15.1")

    import tensorflow as tf
    from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2

    from models.tf import TFModel

    LOGGER.info(f"\\n{prefix} starting export with tensorflow {tf.__version__}…")
    if tf.__version__ > "2.13.1":
    helper_url = "https://github.com/ultralytics/yolov5/issues/12489"
    LOGGER.info(
    f"WARNING ⚠️ using Tensorflow {tf.__version__} > 2.13.1 might cause issue when exporting the model to tflite {helper_url}"
    ) # handling issue https://github.com/ultralytics/yolov5/issues/12489
    f = str(file).replace(".pt", "_saved_model")
    batch_size, ch, *imgsz = list(im.shape) # BCHW

    tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
    im = tf.zeros((batch_size, *imgsz, ch)) # BHWC order for TensorFlow
    _ = tf_model.predict(im, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    inputs = tf.keras.Input(shape=(*imgsz, ch), batch_size=None if dynamic else batch_size)
    outputs = tf_model.predict(inputs, tf_nms, agnostic_nms, topk_per_class, topk_all, iou_thres, conf_thres)
    keras_model = tf.keras.Model(inputs=inputs, outputs=outputs)
    keras_model.trainable = False
    keras_model.summary()
    if keras:
    keras_model.save(f, save_format="tf")
    else:
    spec = tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype)
    m = tf.function(lambda x: keras_model(x)) # full model
    m = m.get_concrete_function(spec)
    frozen_func = convert_variables_to_constants_v2(m)
    tfm = tf.Module()
    tfm.__call__ = tf.function(lambda x: frozen_func(x)[:4] if tf_nms else frozen_func(x), [spec])
    tfm.__call__(im)
    tf.saved_model.save(
    tfm,
    f,
    options=tf.saved_model.SaveOptions(experimental_custom_gradients=False)
    if check_version(tf.__version__, "2.6")
    else tf.saved_model.SaveOptions(),
    )
    return f, keras_model

    @try_export
    def export_pb(keras_model, file, prefix=colorstr("TensorFlow GraphDef:")):
    """Exports YOLOv5 model to TensorFlow GraphDef *.pb format; see https://github.com/leimao/Frozen_Graph_TensorFlow for details."""
    import tensorflow as tf
    from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2

    LOGGER.info(f"\\n{prefix} starting export with tensorflow {tf.__version__}…")
    f = file.with_suffix(".pb")

    m = tf.function(lambda x: keras_model(x)) # full model
    m = m.get_concrete_function(tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype))
    frozen_func = convert_variables_to_constants_v2(m)
    frozen_func.graph.as_graph_def()
    tf.io.write_graph(graph_or_graph_def=frozen_func.graph, logdir=str(f.parent), name=f.name, as_text=False)
    return f, None

    @try_export
    def export_tflite(
    keras_model, im, file, int8, per_tensor, data, nms, agnostic_nms, prefix=colorstr("TensorFlow Lite:")
    ):
    # YOLOv5 TensorFlow Lite export
    import tensorflow as tf

    LOGGER.info(f"\\n{prefix} starting export with tensorflow {tf.__version__}…")
    batch_size, ch, *imgsz = list(im.shape) # BCHW
    f = str(file).replace(".pt", "-fp16.tflite")

    converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
    converter.target_spec.supported_types = [tf.float16]
    converter.optimizations = [tf.lite.Optimize.DEFAULT]
    if int8:
    from models.tf import representative_dataset_gen

    dataset = LoadImages(check_dataset(check_yaml(data))["train"], img_size=imgsz, auto=False)
    converter.representative_dataset = lambda: representative_dataset_gen(dataset, ncalib=100)
    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
    converter.target_spec.supported_types = []
    converter.inference_input_type = tf.uint8 # or tf.int8
    converter.inference_output_type = tf.uint8 # or tf.int8
    converter.experimental_new_quantizer = True
    if per_tensor:
    converter._experimental_disable_per_channel = True
    f = str(file).replace(".pt", "-int8.tflite")
    if nms or agnostic_nms:
    converter.target_spec.supported_ops.append(tf.lite.OpsSet.SELECT_TF_OPS)

    tflite_model = converter.convert()
    open(f, "wb").write(tflite_model)
    return f, None

    @try_export
    def export_edgetpu(file, prefix=colorstr("Edge TPU:")):
    """
    Exports a YOLOv5 model to Edge TPU compatible TFLite format; requires Linux and Edge TPU compiler.

    https://coral.ai/docs/edgetpu/models-intro/
    """
    cmd = "edgetpu_compiler –version"
    help_url = "https://coral.ai/docs/edgetpu/compiler/"
    assert platform.system() == "Linux", f"export only supported on Linux. See {help_url}"
    if subprocess.run(f"{cmd} > /dev/null 2>&1", shell=True).returncode != 0:
    LOGGER.info(f"\\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}")
    sudo = subprocess.run("sudo –version >/dev/null", shell=True).returncode == 0 # sudo installed on system
    for c in (
    "curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -",
    'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',
    "sudo apt-get update",
    "sudo apt-get install edgetpu-compiler",
    ):
    subprocess.run(c if sudo else c.replace("sudo ", ""), shell=True, check=True)
    ver = subprocess.run(cmd, shell=True, capture_output=True, check=True).stdout.decode().split()[-1]

    LOGGER.info(f"\\n{prefix} starting export with Edge TPU compiler {ver}…")
    f = str(file).replace(".pt", "-int8_edgetpu.tflite") # Edge TPU model
    f_tfl = str(file).replace(".pt", "-int8.tflite") # TFLite model

    subprocess.run(
    [
    "edgetpu_compiler",
    "-s",
    "-d",
    "-k",
    "10",
    "–out_dir",
    str(file.parent),
    f_tfl,
    ],
    check=True,
    )
    return f, None

    @try_export
    def export_tfjs(file, int8, prefix=colorstr("TensorFlow.js:")):
    """Exports a YOLOv5 model to TensorFlow.js format, optionally with uint8 quantization."""
    check_requirements("tensorflowjs")
    import tensorflowjs as tfjs

    LOGGER.info(f"\\n{prefix} starting export with tensorflowjs {tfjs.__version__}…")
    f = str(file).replace(".pt", "_web_model") # js dir
    f_pb = file.with_suffix(".pb") # *.pb path
    f_json = f"{f}/model.json" # *.json path

    args = [
    "tensorflowjs_converter",
    "–input_format=tf_frozen_model",
    "–quantize_uint8" if int8 else "",
    "–output_node_names=Identity,Identity_1,Identity_2,Identity_3",
    str(f_pb),
    f,
    ]
    subprocess.run([arg for arg in args if arg], check=True)

    json = Path(f_json).read_text()
    with open(f_json, "w") as j: # sort JSON Identity_* in ascending order
    subst = re.sub(
    r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
    r'"Identity.?.?": {"name": "Identity.?.?"}, '
    r'"Identity.?.?": {"name": "Identity.?.?"}, '
    r'"Identity.?.?": {"name": "Identity.?.?"}}}',
    r'{"outputs": {"Identity": {"name": "Identity"}, '
    r'"Identity_1": {"name": "Identity_1"}, '
    r'"Identity_2": {"name": "Identity_2"}, '
    r'"Identity_3": {"name": "Identity_3"}}}',
    json,
    )
    j.write(subst)
    return f, None

    def add_tflite_metadata(file, metadata, num_outputs):
    """
    Adds TFLite metadata to a model file, supporting multiple outputs, as specified by TensorFlow guidelines.

    https://www.tensorflow.org/lite/models/convert/metadata
    """
    with contextlib.suppress(ImportError):
    # check_requirements('tflite_support')
    from tflite_support import flatbuffers
    from tflite_support import metadata as _metadata
    from tflite_support import metadata_schema_py_generated as _metadata_fb

    tmp_file = Path("/tmp/meta.txt")
    with open(tmp_file, "w") as meta_f:
    meta_f.write(str(metadata))

    model_meta = _metadata_fb.ModelMetadataT()
    label_file = _metadata_fb.AssociatedFileT()
    label_file.name = tmp_file.name
    model_meta.associatedFiles = [label_file]

    subgraph = _metadata_fb.SubGraphMetadataT()
    subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()]
    subgraph.outputTensorMetadata = [_metadata_fb.TensorMetadataT()] * num_outputs
    model_meta.subgraphMetadata = [subgraph]

    b = flatbuffers.Builder(0)
    b.Finish(model_meta.Pack(b), _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
    metadata_buf = b.Output()

    populator = _metadata.MetadataPopulator.with_model_file(file)
    populator.load_metadata_buffer(metadata_buf)
    populator.load_associated_files([str(tmp_file)])
    populator.populate()
    tmp_file.unlink()

    def pipeline_coreml(model, im, file, names, y, prefix=colorstr("CoreML Pipeline:")):
    """Converts a PyTorch YOLOv5 model to CoreML format with NMS, handling different input/output shapes and saving the
    model.
    """
    import coremltools as ct
    from PIL import Image

    print(f"{prefix} starting pipeline with coremltools {ct.__version__}…")
    batch_size, ch, h, w = list(im.shape) # BCHW
    t = time.time()

    # YOLOv5 Output shapes
    spec = model.get_spec()
    out0, out1 = iter(spec.description.output)
    if platform.system() == "Darwin":
    img = Image.new("RGB", (w, h)) # img(192 width, 320 height)
    # img = torch.zeros((*opt.img_size, 3)).numpy() # img size(320,192,3) iDetection
    out = model.predict({"image": img})
    out0_shape, out1_shape = out[out0.name].shape, out[out1.name].shape
    else: # linux and windows can not run model.predict(), get sizes from pytorch output y
    s = tuple(y[0].shape)
    out0_shape, out1_shape = (s[1], s[2] – 5), (s[1], 4) # (3780, 80), (3780, 4)

    # Checks
    nx, ny = spec.description.input[0].type.imageType.width, spec.description.input[0].type.imageType.height
    na, nc = out0_shape
    # na, nc = out0.type.multiArrayType.shape # number anchors, classes
    assert len(names) == nc, f"{len(names)} names found for nc={nc}" # check

    # Define output shapes (missing)
    out0.type.multiArrayType.shape[:] = out0_shape # (3780, 80)
    out1.type.multiArrayType.shape[:] = out1_shape # (3780, 4)
    # spec.neuralNetwork.preprocessing[0].featureName = '0'

    # Flexible input shapes
    # from coremltools.models.neural_network import flexible_shape_utils
    # s = [] # shapes
    # s.append(flexible_shape_utils.NeuralNetworkImageSize(320, 192))
    # s.append(flexible_shape_utils.NeuralNetworkImageSize(640, 384)) # (height, width)
    # flexible_shape_utils.add_enumerated_image_sizes(spec, feature_name='image', sizes=s)
    # r = flexible_shape_utils.NeuralNetworkImageSizeRange() # shape ranges
    # r.add_height_range((192, 640))
    # r.add_width_range((192, 640))
    # flexible_shape_utils.update_image_size_range(spec, feature_name='image', size_range=r)

    # Print
    print(spec.description)

    # Model from spec
    model = ct.models.MLModel(spec)

    # 3. Create NMS protobuf
    nms_spec = ct.proto.Model_pb2.Model()
    nms_spec.specificationVersion = 5
    for i in range(2):
    decoder_output = model._spec.description.output[i].SerializeToString()
    nms_spec.description.input.add()
    nms_spec.description.input[i].ParseFromString(decoder_output)
    nms_spec.description.output.add()
    nms_spec.description.output[i].ParseFromString(decoder_output)

    nms_spec.description.output[0].name = "confidence"
    nms_spec.description.output[1].name = "coordinates"

    output_sizes = [nc, 4]
    for i in range(2):
    ma_type = nms_spec.description.output[i].type.multiArrayType
    ma_type.shapeRange.sizeRanges.add()
    ma_type.shapeRange.sizeRanges[0].lowerBound = 0
    ma_type.shapeRange.sizeRanges[0].upperBound = -1
    ma_type.shapeRange.sizeRanges.add()
    ma_type.shapeRange.sizeRanges[1].lowerBound = output_sizes[i]
    ma_type.shapeRange.sizeRanges[1].upperBound = output_sizes[i]
    del ma_type.shape[:]

    nms = nms_spec.nonMaximumSuppression
    nms.confidenceInputFeatureName = out0.name # 1x507x80
    nms.coordinatesInputFeatureName = out1.name # 1x507x4
    nms.confidenceOutputFeatureName = "confidence"
    nms.coordinatesOutputFeatureName = "coordinates"
    nms.iouThresholdInputFeatureName = "iouThreshold"
    nms.confidenceThresholdInputFeatureName = "confidenceThreshold"
    nms.iouThreshold = 0.45
    nms.confidenceThreshold = 0.25
    nms.pickTop.perClass = True
    nms.stringClassLabels.vector.extend(names.values())
    nms_model = ct.models.MLModel(nms_spec)

    # 4. Pipeline models together
    pipeline = ct.models.pipeline.Pipeline(
    input_features=[
    ("image", ct.models.datatypes.Array(3, ny, nx)),
    ("iouThreshold", ct.models.datatypes.Double()),
    ("confidenceThreshold", ct.models.datatypes.Double()),
    ],
    output_features=["confidence", "coordinates"],
    )
    pipeline.add_model(model)
    pipeline.add_model(nms_model)

    # Correct datatypes
    pipeline.spec.description.input[0].ParseFromString(model._spec.description.input[0].SerializeToString())
    pipeline.spec.description.output[0].ParseFromString(nms_model._spec.description.output[0].SerializeToString())
    pipeline.spec.description.output[1].ParseFromString(nms_model._spec.description.output[1].SerializeToString())

    # Update metadata
    pipeline.spec.specificationVersion = 5
    pipeline.spec.description.metadata.versionString = "https://github.com/ultralytics/yolov5"
    pipeline.spec.description.metadata.shortDescription = "https://github.com/ultralytics/yolov5"
    pipeline.spec.description.metadata.author = "glenn.jocher@ultralytics.com"
    pipeline.spec.description.metadata.license = "https://github.com/ultralytics/yolov5/blob/master/LICENSE"
    pipeline.spec.description.metadata.userDefined.update(
    {
    "classes": ",".join(names.values()),
    "iou_threshold": str(nms.iouThreshold),
    "confidence_threshold": str(nms.confidenceThreshold),
    }
    )

    # Save the model
    f = file.with_suffix(".mlmodel") # filename
    model = ct.models.MLModel(pipeline.spec)
    model.input_description["image"] = "Input image"
    model.input_description["iouThreshold"] = f"(optional) IOU Threshold override (default: {nms.iouThreshold})"
    model.input_description["confidenceThreshold"] = (
    f"(optional) Confidence Threshold override (default: {nms.confidenceThreshold})"
    )
    model.output_description["confidence"] = 'Boxes × Class confidence (see user-defined metadata "classes")'
    model.output_description["coordinates"] = "Boxes × [x, y, width, height] (relative to image size)"
    model.save(f) # pipelined
    print(f"{prefix} pipeline success ({time.time() – t:.2f}s), saved as {f} ({file_size(f):.1f} MB)")

    @smart_inference_mode()
    def run(
    data=ROOT / "data/coco128.yaml", # 'dataset.yaml path'
    weights=ROOT / "yolov5s.pt", # weights path
    imgsz=(640, 640), # image (height, width)
    batch_size=1, # batch size
    device="cpu", # cuda device, i.e. 0 or 0,1,2,3 or cpu
    include=("torchscript", "onnx"), # include formats
    half=False, # FP16 half-precision export
    inplace=False, # set YOLOv5 Detect() inplace=True
    keras=False, # use Keras
    optimize=False, # TorchScript: optimize for mobile
    int8=False, # CoreML/TF INT8 quantization
    per_tensor=False, # TF per tensor quantization
    dynamic=False, # ONNX/TF/TensorRT: dynamic axes
    simplify=False, # ONNX: simplify model
    opset=12, # ONNX: opset version
    verbose=False, # TensorRT: verbose log
    workspace=4, # TensorRT: workspace size (GB)
    nms=False, # TF: add NMS to model
    agnostic_nms=False, # TF: add agnostic NMS to model
    topk_per_class=100, # TF.js NMS: topk per class to keep
    topk_all=100, # TF.js NMS: topk for all classes to keep
    iou_thres=0.45, # TF.js NMS: IoU threshold
    conf_thres=0.25, # TF.js NMS: confidence threshold
    ):
    t = time.time()
    include = [x.lower() for x in include] # to lowercase
    fmts = tuple(export_formats()["Argument"][1:]) # –include arguments
    flags = [x in include for x in fmts]
    assert sum(flags) == len(include), f"ERROR: Invalid –include {include}, valid –include arguments are {fmts}"
    jit, onnx, xml, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle = flags # export booleans
    file = Path(url2file(weights) if str(weights).startswith(("http:/", "https:/")) else weights) # PyTorch weights

    # Load PyTorch model
    device = select_device(device)
    if half:
    assert device.type != "cpu" or coreml, "–half only compatible with GPU export, i.e. use –device 0"
    assert not dynamic, "–half not compatible with –dynamic, i.e. use either –half or –dynamic but not both"
    model = attempt_load(weights, device=device, inplace=True, fuse=True) # load FP32 model

    # Checks
    imgsz *= 2 if len(imgsz) == 1 else 1 # expand
    if optimize:
    assert device.type == "cpu", "–optimize not compatible with cuda devices, i.e. use –device cpu"

    # Input
    gs = int(max(model.stride)) # grid size (max stride)
    imgsz = [check_img_size(x, gs) for x in imgsz] # verify img_size are gs-multiples
    im = torch.zeros(batch_size, 3, *imgsz).to(device) # image size(1,3,320,192) BCHW iDetection

    # Update model
    model.eval()
    for k, m in model.named_modules():
    if isinstance(m, Detect):
    m.inplace = inplace
    m.dynamic = dynamic
    m.export = True

    for _ in range(2):
    y = model(im) # dry runs
    if half and not coreml:
    im, model = im.half(), model.half() # to FP16
    shape = tuple((y[0] if isinstance(y, tuple) else y).shape) # model output shape
    metadata = {"stride": int(max(model.stride)), "names": model.names} # model metadata
    LOGGER.info(f"\\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)")

    # Exports
    f = [""] * len(fmts) # exported filenames
    warnings.filterwarnings(action="ignore", category=torch.jit.TracerWarning) # suppress TracerWarning
    if jit: # TorchScript
    f[0], _ = export_torchscript(model, im, file, optimize)
    if engine: # TensorRT required before ONNX
    f[1], _ = export_engine(model, im, file, half, dynamic, simplify, workspace, verbose)
    if onnx or xml: # OpenVINO requires ONNX
    f[2], _ = export_onnx(model, im, file, opset, dynamic, simplify)
    if xml: # OpenVINO
    f[3], _ = export_openvino(file, metadata, half, int8, data)
    if coreml: # CoreML
    f[4], ct_model = export_coreml(model, im, file, int8, half, nms)
    if nms:
    pipeline_coreml(ct_model, im, file, model.names, y)
    if any((saved_model, pb, tflite, edgetpu, tfjs)): # TensorFlow formats
    assert not tflite or not tfjs, "TFLite and TF.js models must be exported separately, please pass only one type."
    assert not isinstance(model, ClassificationModel), "ClassificationModel export to TF formats not yet supported."
    f[5], s_model = export_saved_model(
    model.cpu(),
    im,
    file,
    dynamic,
    tf_nms=nms or agnostic_nms or tfjs,
    agnostic_nms=agnostic_nms or tfjs,
    topk_per_class=topk_per_class,
    topk_all=topk_all,
    iou_thres=iou_thres,
    conf_thres=conf_thres,
    keras=keras,
    )
    if pb or tfjs: # pb prerequisite to tfjs
    f[6], _ = export_pb(s_model, file)
    if tflite or edgetpu:
    f[7], _ = export_tflite(
    s_model, im, file, int8 or edgetpu, per_tensor, data=data, nms=nms, agnostic_nms=agnostic_nms
    )
    if edgetpu:
    f[8], _ = export_edgetpu(file)
    add_tflite_metadata(f[8] or f[7], metadata, num_outputs=len(s_model.outputs))
    if tfjs:
    f[9], _ = export_tfjs(file, int8)
    if paddle: # PaddlePaddle
    f[10], _ = export_paddle(model, im, file, metadata)

    # Finish
    f = [str(x) for x in f if x] # filter out '' and None
    if any(f):
    cls, det, seg = (isinstance(model, x) for x in (ClassificationModel, DetectionModel, SegmentationModel)) # type
    det &= not seg # segmentation models inherit from SegmentationModel(DetectionModel)
    dir = Path("segment" if seg else "classify" if cls else "")
    h = "–half" if half else "" # –half FP16 inference arg
    s = (
    "# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference"
    if cls
    else "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference"
    if seg
    else ""
    )
    LOGGER.info(
    f'\\nExport complete ({time.time() – t:.1f}s)'
    f"\\nResults saved to {colorstr('bold', file.parent.resolve())}"
    f"\\nDetect: python {dir / ('detect.py' if det else 'predict.py')} –weights {f[-1]} {h}"
    f"\\nValidate: python {dir / 'val.py'} –weights {f[-1]} {h}"
    f"\\nPyTorch Hub: model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}') {s}"
    f'\\nVisualize: https://netron.app'
    )
    return f # return list of exported files/dirs

    def parse_opt(known=False):
    """Parses command-line arguments for YOLOv5 model export configurations, returning the parsed options."""
    parser = argparse.ArgumentParser()
    parser.add_argument("–data", type=str, default=ROOT / "data/coco128.yaml", help="dataset.yaml path")
    parser.add_argument("–weights", nargs="+", type=str, default=ROOT / "yolov5s.pt", help="model.pt path(s)")
    parser.add_argument("–imgsz", "–img", "–img-size", nargs="+", type=int, default=[640, 640], help="image (h, w)")
    parser.add_argument("–batch-size", type=int, default=1, help="batch size")
    parser.add_argument("–device", default="cpu", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
    parser.add_argument("–half", action="store_true", help="FP16 half-precision export")
    parser.add_argument("–inplace", action="store_true", help="set YOLOv5 Detect() inplace=True")
    parser.add_argument("–keras", action="store_true", help="TF: use Keras")
    parser.add_argument("–optimize", action="store_true", help="TorchScript: optimize for mobile")
    parser.add_argument("–int8", action="store_true", help="CoreML/TF/OpenVINO INT8 quantization")
    parser.add_argument("–per-tensor", action="store_true", help="TF per-tensor quantization")
    parser.add_argument("–dynamic", action="store_true", help="ONNX/TF/TensorRT: dynamic axes")
    parser.add_argument("–simplify", action="store_true", help="ONNX: simplify model")
    parser.add_argument("–opset", type=int, default=17, help="ONNX: opset version")
    parser.add_argument("–verbose", action="store_true", help="TensorRT: verbose log")
    parser.add_argument("–workspace", type=int, default=4, help="TensorRT: workspace size (GB)")
    parser.add_argument("–nms", action="store_true", help="TF: add NMS to model")
    parser.add_argument("–agnostic-nms", action="store_true", help="TF: add agnostic NMS to model")
    parser.add_argument("–topk-per-class", type=int, default=100, help="TF.js NMS: topk per class to keep")
    parser.add_argument("–topk-all", type=int, default=100, help="TF.js NMS: topk for all classes to keep")
    parser.add_argument("–iou-thres", type=float, default=0.45, help="TF.js NMS: IoU threshold")
    parser.add_argument("–conf-thres", type=float, default=0.25, help="TF.js NMS: confidence threshold")
    parser.add_argument(
    "–include",
    nargs="+",
    default=["torchscript"],
    help="torchscript, onnx, openvino, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle",
    )
    opt = parser.parse_known_args()[0] if known else parser.parse_args()
    print_args(vars(opt))
    return opt

    def main(opt):
    """Executes the YOLOv5 model inference or export with specified weights and options."""
    for opt.weights in opt.weights if isinstance(opt.weights, list) else [opt.weights]:
    run(**vars(opt))

    if __name__ == "__main__":
    opt = parse_opt()
    main(opt)

    hubconf.py

    # YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
    """
    PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5

    Usage:
    import torch
    model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # official model
    model = torch.hub.load('ultralytics/yolov5:master', 'yolov5s') # from branch
    model = torch.hub.load('ultralytics/yolov5', 'custom', 'yolov5s.pt') # custom/local model
    model = torch.hub.load('.', 'custom', 'yolov5s.pt', source='local') # local repo
    """

    import torch

    def _create(name, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True, device=None):
    """
    Creates or loads a YOLOv5 model.

    Arguments:
    name (str): model name 'yolov5s' or path 'path/to/best.pt'
    pretrained (bool): load pretrained weights into the model
    channels (int): number of input channels
    classes (int): number of model classes
    autoshape (bool): apply YOLOv5 .autoshape() wrapper to model
    verbose (bool): print all information to screen
    device (str, torch.device, None): device to use for model parameters

    Returns:
    YOLOv5 model
    """
    from pathlib import Path

    from models.common import AutoShape, DetectMultiBackend
    from models.experimental import attempt_load
    from models.yolo import ClassificationModel, DetectionModel, SegmentationModel
    from utils.downloads import attempt_download
    from utils.general import LOGGER, ROOT, check_requirements, intersect_dicts, logging
    from utils.torch_utils import select_device

    if not verbose:
    LOGGER.setLevel(logging.WARNING)
    check_requirements(ROOT / "requirements.txt", exclude=("opencv-python", "tensorboard", "thop"))
    name = Path(name)
    path = name.with_suffix(".pt") if name.suffix == "" and not name.is_dir() else name # checkpoint path
    try:
    device = select_device(device)
    if pretrained and channels == 3 and classes == 80:
    try:
    model = DetectMultiBackend(path, device=device, fuse=autoshape) # detection model
    if autoshape:
    if model.pt and isinstance(model.model, ClassificationModel):
    LOGGER.warning(
    "WARNING ⚠️ YOLOv5 ClassificationModel is not yet AutoShape compatible. "
    "You must pass torch tensors in BCHW to this model, i.e. shape(1,3,224,224)."
    )
    elif model.pt and isinstance(model.model, SegmentationModel):
    LOGGER.warning(
    "WARNING ⚠️ YOLOv5 SegmentationModel is not yet AutoShape compatible. "
    "You will not be able to run inference with this model."
    )
    else:
    model = AutoShape(model) # for file/URI/PIL/cv2/np inputs and NMS
    except Exception:
    model = attempt_load(path, device=device, fuse=False) # arbitrary model
    else:
    cfg = list((Path(__file__).parent / "models").rglob(f"{path.stem}.yaml"))[0] # model.yaml path
    model = DetectionModel(cfg, channels, classes) # create model
    if pretrained:
    ckpt = torch.load(attempt_download(path), map_location=device) # load
    csd = ckpt["model"].float().state_dict() # checkpoint state_dict as FP32
    csd = intersect_dicts(csd, model.state_dict(), exclude=["anchors"]) # intersect
    model.load_state_dict(csd, strict=False) # load
    if len(ckpt["model"].names) == classes:
    model.names = ckpt["model"].names # set class names attribute
    if not verbose:
    LOGGER.setLevel(logging.INFO) # reset to default
    return model.to(device)

    except Exception as e:
    help_url = "https://docs.ultralytics.com/yolov5/tutorials/pytorch_hub_model_loading"
    s = f"{e}. Cache may be out of date, try `force_reload=True` or see {help_url} for help."
    raise Exception(s) from e

    def custom(path="path/to/model.pt", autoshape=True, _verbose=True, device=None):
    """Loads a custom or local YOLOv5 model from a given path with optional autoshaping and device specification."""
    return _create(path, autoshape=autoshape, verbose=_verbose, device=device)

    def yolov5n(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Instantiates the YOLOv5-nano model with options for pretraining, input channels, class count, autoshaping,
    verbosity, and device.
    """
    return _create("yolov5n", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5s(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Creates YOLOv5-small model with options for pretraining, input channels, class count, autoshaping, verbosity, and
    device.
    """
    return _create("yolov5s", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5m(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Instantiates the YOLOv5-medium model with customizable pretraining, channel count, class count, autoshaping,
    verbosity, and device.
    """
    return _create("yolov5m", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5l(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Creates YOLOv5-large model with options for pretraining, channels, classes, autoshaping, verbosity, and device
    selection.
    """
    return _create("yolov5l", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5x(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Instantiates the YOLOv5-xlarge model with customizable pretraining, channel count, class count, autoshaping,
    verbosity, and device.
    """
    return _create("yolov5x", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5n6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Creates YOLOv5-nano-P6 model with options for pretraining, channels, classes, autoshaping, verbosity, and
    device.
    """
    return _create("yolov5n6", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5s6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Instantiate YOLOv5-small-P6 model with options for pretraining, input channels, number of classes, autoshaping,
    verbosity, and device selection.
    """
    return _create("yolov5s6", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5m6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Creates YOLOv5-medium-P6 model with options for pretraining, channel count, class count, autoshaping, verbosity,
    and device.
    """
    return _create("yolov5m6", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5l6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Instantiates the YOLOv5-large-P6 model with customizable pretraining, channel and class counts, autoshaping,
    verbosity, and device selection.
    """
    return _create("yolov5l6", pretrained, channels, classes, autoshape, _verbose, device)

    def yolov5x6(pretrained=True, channels=3, classes=80, autoshape=True, _verbose=True, device=None):
    """Creates YOLOv5-xlarge-P6 model with options for pretraining, channels, classes, autoshaping, verbosity, and
    device.
    """
    return _create("yolov5x6", pretrained, channels, classes, autoshape, _verbose, device)

    if __name__ == "__main__":
    import argparse
    from pathlib import Path

    import numpy as np
    from PIL import Image

    from utils.general import cv2, print_args

    # Argparser
    parser = argparse.ArgumentParser()
    parser.add_argument("–model", type=str, default="yolov5s", help="model name")
    opt = parser.parse_args()
    print_args(vars(opt))

    # Model
    model = _create(name=opt.model, pretrained=True, channels=3, classes=80, autoshape=True, verbose=True)
    # model = custom(path='path/to/model.pt') # custom

    # Images
    imgs = [
    "data/images/zidane.jpg", # filename
    Path("data/images/zidane.jpg"), # Path
    "https://ultralytics.com/images/zidane.jpg", # URI
    cv2.imread("data/images/bus.jpg")[:, :, ::-1], # OpenCV
    Image.open("data/images/bus.jpg"), # PIL
    np.zeros((320, 640, 3)),
    ] # numpy

    # Inference
    results = model(imgs, size=320) # batched inference

    # Results
    results.print()
    results.save()

    gui.py

    import sys
    import os
    import cv2
    import datetime
    import time

    import argparse
    import csv
    import platform
    from pathlib import Path
    import torch

    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    from PyQt5.Qt import *

    import threading #——->>>1
    t_flag=2
    save_path_ = None
    '''
    # 模型加载部分
    '''

    FILE = Path(__file__).resolve()
    ROOT = FILE.parents[0] # YOLOv5 root directory
    if str(ROOT) not in sys.path:
    sys.path.append(str(ROOT)) # add ROOT to PATH
    ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative

    from ultralytics.utils.plotting import Annotator, colors, save_one_box

    from models.common import DetectMultiBackend
    from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
    from utils.general import (
    LOGGER,
    Profile,
    check_file,
    check_img_size,
    check_imshow,
    check_requirements,
    colorstr,
    cv2,
    increment_path,
    non_max_suppression,
    print_args,
    scale_boxes,
    strip_optimizer,
    xyxy2xywh,
    )
    from utils.torch_utils import select_device, smart_inference_mode

    @smart_inference_mode()
    def run(
    weights=ROOT / "yolov5s.pt", # model path or triton URL
    source=ROOT / "data/images", # file/dir/URL/glob/screen/0(webcam)
    data=ROOT / "data/coco128.yaml", # dataset.yaml path
    imgsz=(640, 640), # inference size (height, width)
    conf_thres=0.25, # confidence threshold
    iou_thres=0.45, # NMS IOU threshold
    max_det=1000, # maximum detections per image
    device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
    view_img=False, # show results
    save_txt=False, # save results to *.txt
    save_csv=False, # save results in CSV format
    save_conf=False, # save confidences in –save-txt labels
    save_crop=False, # save cropped prediction boxes
    nosave=False, # do not save images/videos
    classes=None, # filter by class: –class 0, or –class 0 2 3
    agnostic_nms=False, # class-agnostic NMS
    augment=False, # augmented inference
    visualize=False, # visualize features
    update=False, # update all models
    project=ROOT / "runs/detect", # save results to project/name
    name="exp", # save results to project/name
    exist_ok=False, # existing project/name ok, do not increment
    line_thickness=3, # bounding box thickness (pixels)
    hide_labels=False, # hide labels
    hide_conf=False, # hide confidences
    half=False, # use FP16 half-precision inference
    dnn=False, # use OpenCV DNN for ONNX inference
    vid_stride=1, # video frame-rate stride
    ):
    global save_path_
    count = 0
    source = str(source)
    save_img = not nosave and not source.endswith(".txt") # save inference images
    is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
    is_url = source.lower().startswith(("rtsp://", "rtmp://", "http://", "https://"))
    webcam = source.isnumeric() or source.endswith(".streams") or (is_url and not is_file)
    screenshot = source.lower().startswith("screen")
    if is_url and is_file:
    source = check_file(source) # download

    # Directories
    save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run
    (save_dir / "labels" if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir

    # Load model
    device = select_device(device)
    model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
    stride, names, pt = model.stride, model.names, model.pt
    imgsz = check_img_size(imgsz, s=stride) # check image size

    # Dataloader
    bs = 1 # batch_size
    if webcam:
    view_img = check_imshow(warn=True)
    dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
    bs = len(dataset)
    elif screenshot:
    dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt)
    else:
    dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride)
    vid_path, vid_writer = [None] * bs, [None] * bs

    # Run inference
    model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup
    seen, windows, dt = 0, [], (Profile(device=device), Profile(device=device), Profile(device=device))
    for path, im, im0s, vid_cap, s in dataset:
    with dt[0]:
    im = torch.from_numpy(im).to(model.device)
    im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
    im /= 255 # 0 – 255 to 0.0 – 1.0
    if len(im.shape) == 3:
    im = im[None] # expand for batch dim
    if model.xml and im.shape[0] > 1:
    ims = torch.chunk(im, im.shape[0], 0)

    # Inference
    with dt[1]:
    visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False
    if model.xml and im.shape[0] > 1:
    pred = None
    for image in ims:
    if pred is None:
    pred = model(image, augment=augment, visualize=visualize).unsqueeze(0)
    else:
    pred = torch.cat((pred, model(image, augment=augment, visualize=visualize).unsqueeze(0)), dim=0)
    pred = [pred, None]
    else:
    pred = model(im, augment=augment, visualize=visualize)
    # NMS
    with dt[2]:
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)

    # Second-stage classifier (optional)
    # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)

    # Define the path for the CSV file
    csv_path = save_dir / "predictions.csv"

    # Create or append to the CSV file
    def write_to_csv(image_name, prediction, confidence):
    """Writes prediction data for an image to a CSV file, appending if the file exists."""
    data = {"Image Name": image_name, "Prediction": prediction, "Confidence": confidence}
    with open(csv_path, mode="a", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=data.keys())
    if not csv_path.is_file():
    writer.writeheader()
    writer.writerow(data)

    # Process predictions
    for i, det in enumerate(pred): # per image
    seen += 1
    if webcam: # batch_size >= 1
    p, im0, frame = path[i], im0s[i].copy(), dataset.count
    s += f"{i}: "
    else:
    p, im0, frame = path, im0s.copy(), getattr(dataset, "frame", 0)

    p = Path(p) # to Path
    save_path = str(save_dir / p.name) # im.jpg
    txt_path = str(save_dir / "labels" / p.stem) + ("" if dataset.mode == "image" else f"_{frame}") # im.txt
    s += "%gx%g " % im.shape[2:] # print string
    gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh
    imc = im0.copy() if save_crop else im0 # for save_crop
    annotator = Annotator(im0, line_width=line_thickness, example=str(names))
    if len(det):
    # Rescale boxes from img_size to im0 size
    det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0.shape).round()

    # Print results
    for c in det[:, 5].unique():
    n = (det[:, 5] == c).sum() # detections per class
    s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string

    # Write results
    for *xyxy, conf, cls in reversed(det):
    c = int(cls) # integer class
    label = names[c] if hide_conf else f"{names[c]}"
    confidence = float(conf)
    confidence_str = f"{confidence:.2f}"

    if save_csv:
    write_to_csv(p.name, label, confidence_str)

    if save_txt: # Write to file
    xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh
    line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format
    with open(f"{txt_path}.txt", "a") as f:
    f.write(("%g " * len(line)).rstrip() % line + "\\n")

    if save_img or save_crop or view_img: # Add bbox to image
    c = int(cls) # integer class
    label = None if hide_labels else (names[c] if hide_conf else f"{names[c]} {conf:.2f}")
    annotator.box_label(xyxy, label, color=colors(c, True))
    if save_crop:
    save_one_box(xyxy, imc, file=save_dir / "crops" / names[c] / f"{p.stem}.jpg", BGR=True)

    # Stream results
    im0 = annotator.result()
    # time.sleep(1)
    count =count+1
    cv2.imwrite(save_path + "/image_{}.jpg".format(count),im0)
    # global image_show

    # image_show = im0
    # print(count,image_show.shape)

    '''
    if view_img:
    cv2.namedWindow("YOLOv5 Detection", cv2.WINDOW_NORMAL)
    cv2.imshow("YOLOv5 Detection", im0)
    cv2.waitKey(0) # 等待按键按下
    cv2.destroyAllWindows() # 关闭窗口
    '''

    if view_img:
    if platform.system() == "Linux" and p not in windows:
    windows.append(p)
    cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux)
    cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
    count = count + 1
    cv2.imwrite("image_{}.jpg".format(count), im0)
    global t_flag
    t_flag=0
    # global image_return
    # image_return = im0
    # cv2.imshow(str(p), im0)
    # cv2.waitKey(1) # 1 millisecond
    # return im0
    else:
    t_flag = 1

    # Save results (image with detections)
    if save_img:
    if dataset.mode == "image":
    cv2.imwrite(save_path, im0)
    print("save_path",save_path)
    save_path_ = save_path

    else: # 'video' or 'stream'
    if vid_path[i] != save_path: # new video
    vid_path[i] = save_path
    if isinstance(vid_writer[i], cv2.VideoWriter):
    vid_writer[i].release() # release previous video writer
    if vid_cap: # video
    fps = vid_cap.get(cv2.CAP_PROP_FPS)
    w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    else: # stream
    # fps, w, h = 30, im0.shape[1], im0.shape[0]
    fps, w, h = 30, im0.shape[1], im0.shape[0]
    print("image's shape is :",im0.shape)

    save_path = str(Path(save_path).with_suffix(".mp4")) # force *.mp4 suffix on results videos
    vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
    global image_return
    image_return = im0
    vid_writer[i].write(im0)

    # Print time (inference-only)
    LOGGER.info(f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms")

    # Print results
    t = tuple(x.t / seen * 1e3 for x in dt) # speeds per image
    LOGGER.info(f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}" % t)
    if save_txt or save_img:
    s = f"\\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ""
    LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
    if update:
    strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning)

    def parse_opt(weight,source):
    """Parses command-line arguments for YOLOv5 detection, setting inference options and model configurations."""
    parser = argparse.ArgumentParser()
    parser.add_argument("–weights", nargs="+", type=str, default=weight, help="model path or triton URL")
    parser.add_argument("–source", type=str, default=source, help="file/dir/URL/glob/screen/0(webcam)")
    parser.add_argument("–data", type=str, default=ROOT / "data/coco128.yaml", help="(optional) dataset.yaml path")
    parser.add_argument("–imgsz", "–img", "–img-size", nargs="+", type=int, default=[640], help="inference size h,w")
    parser.add_argument("–conf-thres", type=float, default=0.25, help="confidence threshold")
    parser.add_argument("–iou-thres", type=float, default=0.45, help="NMS IoU threshold")
    parser.add_argument("–max-det", type=int, default=1000, help="maximum detections per image")
    parser.add_argument("–device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu")
    parser.add_argument("–view-img", action="store_true", help="show results")
    parser.add_argument("–save-txt", action="store_true", help="save results to *.txt")
    parser.add_argument("–save-csv", action="store_true", help="save results in CSV format")
    parser.add_argument("–save-conf", action="store_true", help="save confidences in –save-txt labels")
    parser.add_argument("–save-crop", action="store_true", help="save cropped prediction boxes")
    parser.add_argument("–nosave", action="store_true", help="do not save images/videos")
    parser.add_argument("–classes", nargs="+", type=int, help="filter by class: –classes 0, or –classes 0 2 3")
    parser.add_argument("–agnostic-nms", action="store_true", help="class-agnostic NMS")
    parser.add_argument("–augment", action="store_true", help="augmented inference")
    parser.add_argument("–visualize", action="store_true", help="visualize features")
    parser.add_argument("–update", action="store_true", help="update all models")
    parser.add_argument("–project", default=ROOT / "runs/detect", help="save results to project/name")
    parser.add_argument("–name", default="exp", help="save results to project/name")
    parser.add_argument("–exist-ok", action="store_true", help="existing project/name ok, do not increment")
    parser.add_argument("–line-thickness", default=3, type=int, help="bounding box thickness (pixels)")
    parser.add_argument("–hide-labels", default=False, action="store_true", help="hide labels")
    parser.add_argument("–hide-conf", default=False, action="store_true", help="hide confidences")
    parser.add_argument("–half", action="store_true", help="use FP16 half-precision inference")
    parser.add_argument("–dnn", action="store_true", help="use OpenCV DNN for ONNX inference")
    parser.add_argument("–vid-stride", type=int, default=1, help="video frame-rate stride")
    opt = parser.parse_args()
    opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand
    print_args(vars(opt))
    return opt

    def main(opt):
    """Executes YOLOv5 model inference with given options, checking requirements before running the model."""
    check_requirements(ROOT / "requirements.txt", exclude=("tensorboard", "thop"))
    run(**vars(opt))

    # 定义全局变量
    # *param: weight–权重路径
    # *param: source–加载的图片路径
    # *param: save–保存路径

    weight = None
    source = None
    save = None

    class VehiclePedestriansApp(QMainWindow):
    def __init__(self):
    super().__init__()
    self.init_ui()

    def init_ui(self):
    self.setGeometry(100,100,1200,800)
    self.setWindowTitle('监控系统')

    self.palette = QPalette()
    self.palette.setBrush(QPalette.Background, QBrush(QPixmap("background.jpeg")))
    self.setPalette(self.palette)

    self.open_model_button = QPushButton('选择输入模型',self)
    self.open_model_button.setGeometry(50,10,200,30)
    self.open_model_button.clicked.connect(self.selectModel)
    self.open_model_button.setStyleSheet('''
    QPushButton {
    border-style: solid;
    border-width: 2px;
    border-color: black;
    border-radius: 10px;
    background-color: transparent;
    padding: 5px;
    }
    QPushButton:hover {
    background-color: #DDDDDD; /* Change to desired hover color */
    }
    QPushButton:pressed {
    background-color: #BBBBBB; /* Change to desired pressed color */
    }
    ''')
    self.model_label = QLabel(self)
    self.model_label.setGeometry(260, 10, 800, 30)

    self.save_button = QPushButton('选择保存路径', self)
    self.save_button.setGeometry(50, 60, 200, 30)
    self.save_button.clicked.connect(self.selectSavePath)
    self.save_button.setStyleSheet('''
    QPushButton {
    border-style: solid;
    border-width: 2px;
    border-color: black;
    border-radius: 10px;
    background-color: transparent;
    padding: 5px;
    }
    QPushButton:hover {
    background-color: #DDDDDD; /* Change to desired hover color */
    }
    QPushButton:pressed {
    background-color: #BBBBBB; /* Change to desired pressed color */
    }
    ''')
    self.save_label = QLabel(self)
    self.save_label.setGeometry(260, 60, 800, 30)

    self.input_label = QLabel('输入',self)
    self.input_label.setGeometry(50, 110, 50, 30)

    self.input_lineedit = QLineEdit(self)
    self.input_lineedit.setGeometry(100, 110, 300, 30)

    self.detect_button = QPushButton('开始检测', self)
    self.detect_button.setGeometry(420, 110, 200, 30)
    self.detect_button.clicked.connect(self.start_detect)
    self.detect_button.setStyleSheet('''
    QPushButton {
    border-style: solid;
    border-width: 2px;
    border-color: black;
    border-radius: 10px;
    background-color: transparent;
    padding: 5px;
    }
    QPushButton:hover {
    background-color: #DDDDDD; /* Change to desired hover color */
    }
    QPushButton:pressed {
    background-color: #BBBBBB; /* Change to desired pressed color */
    }
    ''')

    self.image_label = QLabel(self)
    self.image_label.setGeometry(300, 200, 480, 640)

    def selectModel(self):
    options = QFileDialog.Options()
    model_path, _ = QFileDialog.getOpenFileName(self, "选择模型文件", "", "模型文件 (*.pt);;所有文件 (*)",
    options=options)
    if model_path:
    print("已选择模型文件:", model_path)
    global weight
    weight = str(model_path)
    self.model_label.setText("选择的模型路径为:{}".format(str(model_path)))

    def selectSavePath(self):
    options = QFileDialog.Options()
    save_path = QFileDialog.getExistingDirectory(self, "选择保存路径", options=options)
    if save_path:
    print("已选择保存路径:", save_path)
    self.save_label.setText(save_path)

    def start_detect(self):
    global weight

    if self.input_lineedit.text() is not None:
    source_text = self.input_lineedit.text()
    source = str(source_text)

    opt = parse_opt(weight, source)
    main(opt)

    global save_path_
    if save_path_ is not None:
    image = QImage(save_path_)
    pixmap = QPixmap.fromImage(image)
    pixmap = pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio)
    self.image_label.setPixmap(pixmap)

    global t_flag
    global image_return

    if image_return is not None:
    print("image_return:", image_return)
    self.update_image(image_return)
    def update_image(self, frame):
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    h, w, ch = frame_rgb.shape
    bytes_per_line = ch * w
    qt_image = QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888)
    pixmap = QPixmap.fromImage(qt_image)
    self.image_label.setPixmap(pixmap)
    self.image_label.update()

    if __name__ == '__main__':
    app = QApplication(sys.argv)
    editor_app = VehiclePedestriansApp()
    editor_app.show()
    sys.exit(app.exec_())

    致谢:感谢Ultralytics、美团、WongKinYiu等团队的开源贡献,使得目标检测技术更加普及。希望本文能帮助更多开发者将YOLO应用于实际项目,推动AI落地。

    赞(0)
    未经允许不得转载:171主机测评 » 基于YOLO的行人车辆目标检测:从YOLOv5到YOLOv8的全栈实战指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址