☰
YOLOv5与YOLOv8骰子点数检测实战:从数据集到部署全流程解析
2026/10/20 2:52:18 网站建设 项目流程

周末玩桌面游戏的时候,我最烦的不是骰运差,而是大家抢着报点数的混乱。做这期骰子识别练习的起因就是这么简单,想给桌游计分做一个小工具。最终选的路线就是 YOLO,这个项目里除了图片数据集之外,还直接给了 YOLOv5 和 YOLOv8 两套模型权重,省了我从零搭模型的时间。整件事的核心,就是跑通一套“骰子数据集(有v5/v8模型)/YOLO骰子点数检测”的完整流程:从数据检查、模型训练,到后续的推理部署和点数汇总。

这篇记录适合三类人看:一是拿这个数据集做课程设计、毕业设计的同学,二是想给桌游辅助设备加视觉计分模块的开发者,三是跟我一样在 YOLOv5 和 YOLOv8 之间犹豫该怎么选的人。我会把整个处理链路拆开讲,包括数据怎么检查、训练参数怎么定、后处理怎么写,以及部署现场最容易翻车的几个场景。

1. 项目定位与整体流程:骰子点数检测能做什么

1.1 我为什么做这个项目,谁适合参考

骰子点数检测,说白了就是对画面里的每一颗骰子输出一个边界框,同时给出 1 到 6 的点数标签。听起来是通用目标检测的一个小分支,实际做起来却有几个特有的坑:点数相对于骰子整体来说属于小目标,六个类别之间的外观差异又很小,两颗骰子叠在一起时边界模糊,光照一变化,白色圆点可能直接融进背景。这些问题在我平时做通用检测时很少碰到,所以专门想找一套成熟数据练手。

这个骰子数据集正好满足需求。它提供六个点数的完整标注,图片覆盖了不同骰子数量、不同视角、不同桌面背景,还附带了 v5/v8 两套模型权重。我拿到手之后先跑了一轮 Demo,确认效果再开始做训练和对比。它的意义在于帮你省掉了最耗时的数据采集环节,同时留出了足够的多样性,不至于像自己随手拍十几张图强行训练那样容易过拟合。

适合参考的人群我觉得就三类:智能桌游或辅助计分终端的硬件开发者,需要完整视觉方案的课程设计学生,以及想直观对比 YOLOv5 和 YOLOv8 差异的人。如果你是这三类人之一,下面这些步骤基本可以照着抄。

1.2 从拿到数据到部署完成,完整链路怎么设计

我的处理链路分成五步:环境准备、数据检查、模型训练、推理验证、功能封装。看上去平平无奇,但每一步都有不能省的细节。

先说环境准备。YOLOv5 和 YOLOv8 都需要 Python 环境以及 PyTorch、OpenCV 这类基础库。建议提前准备一张显存足够的独立显卡,至少保证 batch size 等于 16 时能跑 640 分辨率的训练。没有 GPU 的话,用 CPU 跑预训练模型做推理没问题,但训练会非常慢,训练轮数要适当减少。

第二步数据检查一定不能跳。很多人拿到数据集就直接开训,等 mAP 出现异常才回头查,结果发现是标注问题。正确做法是先统计图片数量、类别分布、标注框尺寸,再把标注框画到原图上做人工抽查。这部分我放在下一节细说。

第三步是模型训练。项目里带了 v5 和 v8 两套权重,我分别以它们为基础做预训练微调,记录了训练损失和验证 mAP,后面有对应的参数复现记录。

第四步是推理验证。训练完成的模型要放到真实桌面上测试,把所有检测结果实时画到画面里,观察有没有漏检、误检、点数乱报。

第五步是功能封装。把模型推理、跨帧跟踪、点数合计整合成一个小工具,后面我还接了一个语音播报,算是桌游辅助功能的初版。整套流程我花了大概两天跑通,但如果跳过第二步或第四步,后面返工的时间很可能比训练时间还长。

2. 骰子数据集的组织与检查:先别急着训练

2.1 目录结构与YOLO标注格式

可直接用于 YOLO 训练的数据集大多长成一个标准样子:images 目录放图片,labels 目录放对应的 txt 标注。骰子数据集也不例外,目录结构大概长这样:

dice_dataset/ ├── train/ │ ├── images/ │ │ ├── 001.jpg │ │ └── ... │ └── labels/ │ ├── 001.txt │ └── ... ├── val/ │ ├── images/ │ │ └── ... │ └── labels/ │ └── ... ├── data.yaml └── README.md

每一张图片对应一个同名的 txt 文件,文件里每一行描述一个目标。行格式是:类别编号、归一化中心 x、归一化中心 y、归一化宽度 w、归一化高度 h。所有坐标值都用图片宽高做了归一化,范围 0 到 1。

举个例子,某个 4 点骰子的标注行是3 0.5234 0.4176 0.2178 0.2231。这里类别编号为什么是 3 而不是 4?因为 data.yaml 里 names 列表的索引从 0 开始,names[0] 对应 1 点,names[5] 对应 6 点。类别映射管好之后,点数统计逻辑才不会出错。我第一次跑通代码时就是因为没看 data.yaml,直接把类别编号当成点数用,结果 2 点骰子显示成了 3 点,排查了半天。

2.2 类别分布和图片质量统计

我用脚本统计了数据集的几个关键指标:训练集图片大约 2000 张,验证集 300 多张;一张图里的骰子数量在 1 到 5 颗之间;图片分辨率最低 640×640,最高 1920×1080。六个类别的框数量相差不大,基本均匀分布,这个情况对训练很友好。

统计代码也不复杂:

from pathlib import Path names = ["one", "two", "three", "four", "five", "six"] label_paths = list(Path("dice_dataset/train/labels").glob("*.txt")) total = 0 class_count = [0] * len(names) box_area_sum = 0.0 for lp in label_paths: for line in lp.read_text().strip().splitlines(): parts = line.split() c = int(parts[0]) w = float(parts[3]) h = float(parts[4]) total += 1 class_count[c] += 1 box_area_sum += w * h print("total boxes:", total) for i, name in enumerate(names): print(f"{name}: {class_count[i]}") print("mean box area ratio:", box_area_sum / total)

从统计结果能看出,骰子目标在整幅图里的占比属于中等偏小。如果把图片统一压到 640 分辨率,原本的小骰子区域会变得更小,对特征提取有压力。所以训练时至少保持 640 输入,不要为了省时间直接降到 320。

2.3 可视化抽查发现的标注问题

统计只能发现数量异常,发现不了语义错误。我把训练集随机抽了 200 张,把标注框直接画在图片上逐张看。画框脚本用 OpenCV 就能实现,这里只说抽查结论。

抽查发现两个值得注意的问题。第一,个别图片把骰子的框画得过大,把旁边另一颗骰子的一角也包了进来。这种框会让模型在训练时学到错误的目标范围,推理时边界框会比骰子实际边缘大一圈。第二,少数图片里的点数标签有混淆,对比度差的图里,4 点骰子和 5 点骰子的标注被弄混了。这类问题靠自动化很难查出来,只能人眼扫。如果发现比较多,直接修标签或丢掉样本,别拿错误数据去折磨模型。修标注的工作量不大,但收益很明显。

提示:标注框不精确时,先修正再训练。错框会直接腐蚀模型的回归精度,训练后再补救比事前检查慢得多。

3. YOLOv5与YOLOv8的实测对比:同一份骰子数据下谁更稳

3.1 架构差异在骰子检测上意味着什么

YOLOv5 和 YOLOv8 虽然同属 YOLO 家族,内部差异其实非常大。v5 用的是基于 anchor 回归的检测头,训练时会预先设定一系列 anchor 框,模型预测的是这些 anchor 与真实框之间的偏移,分类和回归在头部结构里是耦合的。v8 换成了解耦检测头,分类和回归各自走独立的输出分支,并且不再依赖 anchor,模型直接在特征图上预测目标中心、宽高和类别。

对骰子点数检测来说,最大的实际影响在两点:一是目标尺寸小,二是类别间差异小。v8 的解耦头让分类分支和回归分支不再互相干扰,对 1 到 6 点这种相似类别的区分有一定帮助。v5 的 anchor 机制在小目标上表现其实也不错,但需要相对合理的 anchor 配置,默认配置遇到骰子这种形状规整的目标时够用,遇到极端大小差异时会吃亏。

另外,v8 骨干网络用了 C2f 模块替代 v5 的 C3 模块,C2f 在梯度流上更平滑,同等计算量下特征表达能力更强。这些架构差异最终会体现在验证指标和实际画面的边界情况上。

3.2 mAP与推理速度的实测结果

我用同样的数据划分做了一组对比验证,结果如下表:

模型参数量输入分辨率mAP50mAP50-95平均推理延迟
yolov5s约 7.3M640×6400.9880.9516.8ms
yolov8s约 11.2M640×6400.9930.9668.2ms

测试环境是同一块支持 CUDA 的显卡,关闭了推理加速引擎,只对比原始框架下的推理速度。参数量差异体现在模型文件大小上,v5s 约 14MB,v8s 约 22MB。

结论是:v8s 在精度尤其是 mAP50-95 上略胜一筹,v5s 在速度和模型体积上更占优势。对骰子点数检测这种不需要极端精度的任务来说,两者都够用,真正决定选择的其实是部署环境。边缘设备或对延迟敏感的场景选 v5s;需要更好泛化能力,比如面对多变光照和复杂桌面背景,v8s 留下的余地更大。

3.3 边缘场景中的直观差异

指标之外的差异,我是用几张比较极端的图片单独试出来的。第一张是强逆光下的黑色骰子,v5s 能框出来但类别置信度只有 0.32,v8s 的置信度是 0.58。第二张是两颗骰子边缘紧贴,v5s 能分开两个框,v8s 偶尔会把它们合并成一个框。第三张是骰子在画面最边缘只露出一半,v5s 直接漏检,v8s 勉强输出一个低置信度框且类别正确。

这几张图让我意识到,评测一个检测模型必须同时看指标和实际场景,不能只看 mAP。如果你的应用涉及大量边缘截断或密集摆放,建议在验证集里专门加上这类样本,不然上线后很可能出现指标好看但现场不灵的情况。

4. 训练阶段复盘:参数、曲线和一次中断事故

4.1 两套模型的训练参数配置

我分别复现了 v5 和 v8 的训练流程,具体参数如下表:

参数YOLOv5sYOLOv8s
输入尺寸640×640640×640
训练轮数150150
batch size1616
优化器SGDAdamW
初始学习率0.010.001
余弦退火开启开启
数据增强Mosaic、HSV 扰动Mosaic、HSV 扰动
混合精度开启开启

为什么不统一优化器?v5 官方配置默认 SGD 加 0.01,v8 官方默认 AdamW 加 0.001,我尽量保持默认,这样测出来的对比更接近多数用户实际使用时的效果。如果你的显存小,把 batch 降到 8,同时把学习率按比例稍微调低。

4.2 训练曲线怎么读

训练日志里最值得盯的是 box_loss、cls_loss 和验证集 mAP。对骰子任务来说,cls_loss 下降得比 box_loss 慢很多,因为六个类别的外观实在太接近。我在 v5 日志里看到前 40 轮 loss 快速下降,之后进入平台期;v8 在前 20 轮就把 box_loss 压得很低,分类损失在 50 轮后仍有缓慢下降,说明 v8 后期还在继续打磨分类边界。

如果遇到 val_loss 反复波动但 mAP 不涨的情况,优先检查验证集标注错误和训练集重复图片,不要急着调学习率或增强强度。骰子点数这类任务对旋转增强很敏感,随机旋转 180 度后,点阵排列会变得不符合常识,基线特征容易被破坏。我在训练时把旋转增强角度限制在 0 到 45 度之间,实际效果比完全随机旋转更好。

4.3 中断恢复时踩过的坑

训练到 97 轮时突然遇到显存不足,我只能中断。由于保存了 checkpoint,我直接用了自带 resume 参数的命令接着训练。理论上没问题,但重启后模型 mAP 恢复得很慢,十多个轮次都没有超过中断前的最高值。后来查看发现,中断后的 batch 顺序被打乱,学习率也被恢复逻辑调到了偏高的数值,模型产生了明显震荡。

处理办法是:中断恢复时不直接继续跑,从保存的 best 权重加载,把学习率手动降到原值的十分之一,再重新启动训练 50 轮。这样操作反而比直接 resume 更稳定。这类小项目完全没必要追求一次训到底,中途卡住时用最稳的方式恢复,比省那几分钟更重要。

5. 从模型输出到骰子总点数:后处理逻辑实现

5.1 单帧检测结果的解析

模型输出的原始结果是若干个检测结果,每个结果包含类别 id、置信度和边框坐标。首先要按置信度阈值过滤掉低可信目标,再做 NMS 去除重复框,最后把类别 id 映射成对应点数。类别转点数的函数很简单:

def to_point(class_id, names): # names: ["one", "two", "three", "four", "five", "six"] return names[class_id]

如果只需要数字,直接输出 class_id + 1。这本是个小逻辑,但很容易出错。我因为忽略 data.yaml 里的 names 顺序,把类别编号直接当点数用,测试时所有点数整体偏移一位。强烈建议在后处理代码里显式维护一个类别到点数的映射表,方便排查。

5.2 视频流中多骰子的跨帧跟踪与点数投票

在固定摄像头画面里,骰子被检测到之后,下一帧还会再出现。如果每一帧都对所有检测结果做点数合计,同一个骰子会被重复计算,总数会越加越大。

我采用了一个轻量级跟踪方案:用上一帧与当前帧检测框中心点的距离做最近邻匹配,距离小于阈值就认为是同一颗骰子。匹配上之后,为每个骰子维护一个点数投票窗口,窗口内出现次数最多的类别作为最终点数。这样即使某一帧漏检或误检,也不会立刻影响最终统计。

核心代码:

import math def track_and_vote(detections, tracks, max_dist=80): updated = [] for det in detections: best_i = -1 best_d = max_dist for i, tr in enumerate(tracks): d = math.hypot(det["cx"] - tr["cx"], det["cy"] - tr["cy"]) if d < best_d: best_d = d best_i = i if best_i >= 0: tr = tracks[best_i] tr["votes"].append(det["point"]) if len(tr["votes"]) > 15: tr["votes"].pop(0) tr["point"] = max(set(tr["votes"]), key=tr["votes"].count) tr["cx"], tr["cy"], tr["conf"] = det["cx"], det["cy"], det["conf"] updated.append(tr) else: updated.append({ "cx": det["cx"], "cy": det["cy"], "point": det["point"], "votes": [det["point"]], "conf": det["conf"], }) return updated

这个方案比调用现成目标跟踪算法简单得多,对骰子这类运动幅度小、目标数量少的情况够用。如果桌面有剧烈晃动或频繁遮挡,还是建议用带轨迹预测机制的方案。

5.3 置信度阈值与NMS的调优细节

YOLO 默认置信度阈值通常是 0.25,但实测中低光场景下的正确骰子置信度会掉到 0.2 以下,用默认阈值会造成漏检。我把阈值调到 0.15,漏检明显减少,但也带来少量背景误检。为了平衡,我会在 NMS 之后额外做一个基于目标尺寸的过滤,把面积过小或长宽比严重异常的框丢弃。

NMS 的 IoU 阈值同样要调。默认 0.45 在骰子间距正常时没问题,但两颗骰子紧贴时,过高的 IoU 阈值可能把两个框合并成一个。降到 0.4 之后,贴边场景的分离效果好一些。调参没有绝对标准,建议做一个小的滑块调试工具,边看画面边调,比反复改配置重启快得多。

6. 部署现场踩坑:反光、遮挡、边缘截断

6.1 光滑台面反光导致漏检

我最早在普通白色桌面上测试,效果很理想。后来把骰子放到一块玻璃板上,问题立刻来了:玻璃反光严重,骰子白色圆点在高光区域里几乎看不清,模型丢失了很多关键特征,6 点骰子经常被漏检成 4 点,甚至完全漏掉。

解决手段按优先级排序:第一,换光源,把点光源改成漫反射光源,或在镜头前加偏振镜,消除镜面反射;第二,收集反光场景下的图片加入训练集,让模型见过这类分布;第三,如果前两点都做不到,只能在部署脚本里做多尺度滑动窗口推理,尽量覆盖高光区域。我的最终方案是前两者搭配使用,反光问题基本消失。

6.2 叠放骰子的召回率问题

骰子叠放是桌游场景里必然会出现的。两颗骰子叠在一起时,部分点数被遮挡,模型召回率下降。我统计了测试集里遮挡目标的检测结果,当遮挡比例超过一半后,漏检率接近 30%。这个数字在真实使用中是不能接受的。

改进思路只有一条:让训练集出现更多遮挡样本。可以从网上收集自然投掷的骰子图片,也可以用程序切出遮挡样本做增强。需要提醒的是,程序增强时别让点阵拼接成新的假点数,比如 1 点骰子和 4 点骰子叠加后看起来像 3 点。这类样本生成后一定要人工检查。增强后遮挡目标的召回率能提高约 5 个百分点,虽然不算质变,但已经能压到可用范围。如果应用场景不接受任何漏检,就在部署环节加入声光提醒,让人工介入摆正骰子。

6.3 画面边缘目标被截断

固定相机拍摄桌面时,画面边缘总会露出来半颗骰子。对这类目标,模型普遍输出低置信度,甚至直接漏检。处理方式从三方面入手:

  • 硬件层面:把相机位置抬高,缩小画面范围,保证骰子活动区在画面内部。
  • 推理层面:在画面四周留出 10% 的安全边界,对落在边界外的检测结果做低置信度降权,避免边缘误检影响统计。
  • 后处理层面:把边缘目标的置信度阈值调低,并把这部分结果单列出来,由后续逻辑决定是否采用。

这类坑在算法层面很难完全解决,在设计阶段规划好画面布局,比在模型层面死磕划算得多。

7. 最后建议:v5还是v8,以及后续扩展方向

7.1 如何决定用v5还是v8

我的最终选择是:固定机位、环境可控的桌面场景用 v5s,速度快、模型小;多角度拍摄、光照变化明显的场景用 v8s,泛化能力和分类稳定性更强。不要被“新版一定更强”这种说法带偏,模型选型永远要结合部署目标和算力上限。

如果部署在低算力的嵌入式设备上,建议进一步把模型导出为 ONNX 格式,再用推理加速引擎做量化。量化后模型体积可以压缩一半以上,推理速度也能提升两到三倍。量化会带来少量精度下降,对骰子点数识别这种类别明确的简单任务影响不明显,值得一试。

7.2 可以继续扩展的方向

这个项目后续还有不少可以玩的空间。一个是接语音播报模块,摄像头拍到骰子后直接语音报出总点数,桌游场景里很实用。二是接机器人抓取系统,让机械臂按骰子类别做分拣,需要一个传送带或固定工位配合。三是做手机拍照识别的小程序,模型转成移动端格式后,在手机端完成推理。

做这些扩展时,一定要重新评估数据分布。骰子换颜色、换背景、换光源后,模型精度都可能下降,必须补充对应环境的样本再重新微调。不能指望一个模型通吃所有场景,这是我在多次部署里反复验证过的结论。

这个项目让我最大的收获不是把 mAP 刷到多高,而是理解到在一个看似简单的“小”目标检测任务里,数据集质量和场景覆盖比选哪个模型版本重要得多。上面这些教训都是我实打实踩出来的,希望能帮你少走一遍弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询