YOLOv5扑克牌检测数据集:52类图像标注与训练全攻略

发布时间:2026/9/24 19:39:25
YOLOv5扑克牌检测数据集:52类图像标注与训练全攻略 简介面向目标检测研究与工程应用的大型扑克牌图像数据集按YOLOv5标准目录组织覆盖扑克牌全部五十二种牌型。图片为七百二十乘七百二十像素的RGB彩色图像训练集与验证集划分明确训练集含一万六千张图片及一一对应标签验证集含四千张图片及对应标签可直接纳入YOLO系列模型训练流程省去格式转换与手工整理环节。资源包内共两千个文件除存放标注信息的txt标签与类别字典外还附带一个预览脚本只需传入任意图片即可自动绘制边界框并输出到当前目录便于快速校验标注效果。压缩包整体大小约九百四十八兆字节目前已有一百三十三人学习使用对需要大规模、标准化检测数据的算法研究者与开发者来说可有效缩减数据准备时间支撑扑克牌识别相关项目快速启动。1. 大型扑克牌图像检测数据集YOLOv5目录格式开箱即用做目标检测的人应该都有过这种体验模型反倒不是瓶颈数据整理才是。拿YOLOv5来说训练前把图片和标签按目录分好、把类别文件和yaml写对、再把标签转成txt这套流程走下来半天就没了。如果有一个数据集直接按YOLOv5的目录结构组织好下载完改个yaml就能训省下来的时间足够先跑通一个完整的baseline。这个扑克牌图像检测数据集正是这种拿来即用的类型。52个类别覆盖1到K的四种花色图像统一为720x720的RGB图片训练集16000张图配16000个标签文件验证集4000张图配4000个标签文件总量约987MB。同时附带类别字典txt和可视化脚本show.py用来检查标签和框的对应关系。适合想快速验证YOLOv5训练流程的从业者也适合做扑克牌识别应用或多类别细粒度检测实验的学生与开发者。下面从目录结构开始把这份资源从数据格式到训练配置完整拆解最后给出训练中容易踩的坑和可视化检查技巧。2. 数据集结构与标注细节从目录树到txt标签逐层拆解拿到一份数据集第一件事不是急着训练而是把目录结构和标注格式看清楚。这一步没做透后面训练出现任何诡异问题都很难定位是数据的问题还是模型的问题。这一章按目录树、标签格式、类别映射、可视化脚本四个层面拆解。2.1 目录结构与YOLOv5标准布局YOLOv5官方推荐的训练数据目录结构是images和labels两个平级目录各自下面按train/val划分。这个数据集的目录组织方式如下datasets/ ├── images/ │ ├── train/ │ │ ├── 318686725.jpg │ │ ├── 612173347.jpg │ │ ├── 989583012.jpg │ │ └── ... │ └── val/ │ ├── 293501472.jpg │ ├── 958982519.jpg │ └── ... └── labels/ ├── train/ │ ├── 318686725.txt │ ├── 612173347.txt │ ├── 989583012.txt │ └── ... └── val/ ├── 293501472.txt ├── 958982519.txt └── ...这套布局下图片文件名和标签文件名严格同名只是扩展名不同.jpg对应.txt。YOLOv5训练脚本按images目录下的图片路径去同名labels目录查找对应标签。你不需要额外写数据划分脚本16000张训练图、4000张验证图已经按目录分好。项目正文里出现的长串数字文件名如318686725.txt、612173347.txt是每个样本的唯一ID。用数字ID做文件名在大型数据集中是常见做法好处是文件名短、避免中文或特殊字符带来的路径兼容问题批量shell脚本处理时也省去转义麻烦。下载后目录结构和文件命名保持原样即可直接使用不需要重命名。这里额外说明为什么目录结构对齐YOLOv5这么重要。很多公开数据集提供的是VOC格式xml文件或COCO格式json文件使用前必须用脚本做格式转换还得处理类别映射、标签归一化等一堆细节。这个数据集直接用txt标签且目录结构照搬YOLOv5约定下载解压后唯一需要改的是第3章的yaml配置这是它最大的省心之处。即使是第一次用YOLOv5的新手也可以跳过格式转换直接进入训练流程。2.2 标签txt的格式解读YOLOv5的标签txt是一个纯文本文件每一行代表一个目标框一行包含五个字段class_id x_center y_center width heightclass_id是整数索引从0开始x_center、y_center、width、height都是归一化到0~1区间的浮点数即除以图片宽高后的比例值。以这个扑克牌数据集为例某张图的标签txt内容可能是0 0.512345 0.387654 0.181234 0.153421 1 0.823456 0.654321 0.174532 0.148763 17 0.334567 0.712345 0.165678 0.142345第一行表示类别0的一张牌其中心点位于图像横向51.23%、纵向38.77%处框宽度占全图18.12%高度占15.34%。如果一张图里有多张牌就多写几行顺序不影响训练。解读标签时有几个关键点要记住。第一txt里不允许出现多余的空行或制表符混用不然YOLOv5在数据加载阶段会报格式错误。第二所有坐标必须是归一化浮点数若误把像素坐标写进去框的位置会严重偏移。第三width和height必须大于0否则会被当作无效标签丢弃。第四class_id的取值范围必须是0到51对应52个类别越界会导致训练时数组越界报错。从扑克牌检测的场景看每张图可能包含一张或多张牌标签txt的行数等于牌的数量。YOLOv5原生支持一张图多个目标框的训练多行标签不需要额外处理。你唯一要做的就是确认txt文件编码是UTF-8且无BOM头Windows下用记事本编辑过的文件偶尔会带BOM读取时会影响首行解析。2.3 类别字典txt与索引映射规则数据集附带一个类别字典txt按顺序列出52个类别的名称。这个文件是训练配置的核心参照因为它决定了标签txt里class_id 0到51分别代表什么牌。字典文件的每一行对应一个类别行号就是类别索引ace_of_spades two_of_spades three_of_spades ... king_of_hearts具体命名方式可能随数据集版本不同而变有些用缩写如SA、S2、S3表示黑桃A、黑桃2、黑桃3有些用全称。不管命名风格如何规则只有一条字典txt的行索引必须与标签txt的class_id一一对应0就是第一行51就是最后一行。这条映射关系直接决定了训练yaml文件里的names列表。如果你把names的顺序写错训练过程不会报任何错误但最终推理结果会张冠李戴——黑桃A预测成方块3而且loss看起来一切正常。这是目标检测训练中最隐蔽的错误之一排查成本极高。我的习惯是训练前写一个小脚本读取字典txt自动生成yaml文件里的names列表杜绝手抄出错。具体脚本在第3章给出。同时建议随机挑三五个标签txt手动解析class_id对照字典txt验证一行再配上可视化脚本看图核对。这套流程加起来不过十分钟但能省下后面数小时的排查时间。类别字典txt除了用于生成yaml还有一个用途在计算混淆矩阵时提供可读的类别名。YOLOv5自带的混淆矩阵图默认显示class_id数字如果你遇到某个具体类别误判率异常通过字典txt把数字映射回牌名才能快速理解模型到底混淆了哪两张牌。这里花一分钟对照字典后面分析结果时能省不少时间。2.4 show.py可视化脚本解析show.py是项目提供的可视化辅助脚本作用是随机读取一张图片解析对应的标签txt把每个目标框和类别画在图上并保存到当前目录。这个脚本对检查标注质量非常实用不借助任何标注工具就能快速查看标签是否符合预期。import cv2 import random import os image_dir datasets/images/train label_dir datasets/labels/train img_name random.choice(os.listdir(image_dir)) img_path os.path.join(image_dir, img_name) label_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(visualize_result.jpg, img)代码逻辑不复杂随机挑一张训练图片读同名txt把归一化坐标换算回像素坐标后画绿色矩形框左上角标注class_id数字最后保存成visualize_result.jpg。脚本默认图片和标签目录都是相对路径如果数据集解压路径不是当前目录的datasets记得先改这两个变量。使用上有几个注意点。一是脚本每次运行只输出一张图想批量检查多张要自己加循环第6章会给出批量版本。二是OpenCV读取中文路径图片会失败数据集目录和文件名尽量别带中文。三是脚本里只画了class_id没有把类别名画上去验证第5章说到的类别映射问题时建议把类别字典读进来、把id换成名称显示。3. 用YOLOv5训练52类扑克牌检测模型yaml配置与训练命令数据检查完毕进入正式训练阶段。这一章以YOLOv5官方仓库ultralytics/yolov5为基准从数据集yaml配置、训练命令、验证指标三个维度把整个流程讲透。这一章的方法对YOLOv8也基本适用只是命令行参数略有差异。3.1 数据集yaml文件的写法与校验YOLOv5训练前需要一个数据集配置文件data yaml告诉训练脚本数据根目录在哪里、训练集验证集怎么找、类别数是多少、类别名称是什么。写法如下path: /home/user/poker-datasets # 数据集根目录 train: images/train # 训练集图片目录相对path val: images/val # 验证集图片目录相对path nc: 52 # 类别总数 names: [ # 类别名称与标签class_id顺序一致 ace_of_spades, two_of_spades, three_of_spades, # ... 共52个与类别字典txt完全一致 ]配置时最容易出错的是path和train/val的层级关系。path写数据集根目录train和val写相对路径。部分新手会把train写成绝对路径导致YOLOv5拼接出重复路径训练时直接报文件不存在。另外nc必须填52这个数字跟names列表长度必须一致否则训练脚本在检查配置阶段就会拒绝执行。names列表是另一个高频出错点。最稳妥的写法是从类别字典txt程序化生成避免手抄with open(classes.txt, r) as f: names [line.strip() for line in f.readlines() if line.strip()] assert len(names) 52, f类别数异常: {len(names)} with open(poker52.yaml, w, encodingutf-8) as f: f.write(path: /home/user/poker-datasets\n) f.write(train: images/train\n) f.write(val: images/val\n) f.write(fnc: {len(names)}\n) f.write(names: [\n) for i, name in enumerate(names): suffix , if i len(names) - 1 else f.write(f {name}{suffix}\n) f.write(]\n) print(yaml文件已生成共, len(names), 个类别)生成后执行一条快速自检命令验证yaml能被YOLOv5正确解析python -c import yaml; dyaml.safe_load(open(poker52.yaml)); print(d[nc], len(d[names]))输出应该显示52和52。这一步花不了一分钟但能拦截掉最基础的配置错误。3.2 训练命令与关键超参数说明yaml准备好后直接启动训练。以YOLOv5s为例cd yolov5 pip install -r requirements.txt # 首次运行前安装依赖 python train.py \ --data /path/to/poker52.yaml \ --weights yolov5s.pt \ --img 736 \ --batch 32 \ --epochs 100 \ --device 0各个参数的含义逐一说明。--data指定yaml文件路径--weights填yolov5s.pt表示加载官方COCO预训练权重做迁移学习相比随机初始化收敛更快--img是训练输入尺寸这里填736而不是720是因为YOLOv5内部会把输入缩放到能被32整除的尺寸736是离720最近的32倍数细节在5.5坑里展开--batch控制批大小以720分辨率计算YOLOv5s结构下batch 32大约需要12GB显存显存小就降到16或8--epochs设100轮对扑克牌这种目标单一、背景干净的数据集足够--device 0指定第一块GPUCPU训练则写--device cpu且batch建议降到4。如果不确定从哪个预训练权重开始我一般推荐yolov5s.pt做第一版baseline。m模型和l模型精度更高但训练时间成倍增加对扑克牌检测任务来说s往往已经够用。启动后观察前几个batch是否正常打印loss如果loss输出nan或训练直接中断优先检查标签txt是否存在空文件或越界类别索引。需要补充的是上述命令是单卡训练。如果机器有多张GPU把--device 0改成--device 0,1,2,3即可使用分布式训练。但多卡训练时batch参数是per-GPU的batch实际总batch是batch乘以卡数。比如--batch 32加4卡总batch就是128学习率也需要相应调大否则收敛速度会异常慢。第一次跑实验建议先用单卡、小batch验证流程再考虑多卡扩展。3.3 验证集指标解读与效果评估每轮训练结束后YOLOv5会在验证集上计算检测指标并打印。核心关注四项Precision查准率、Recall查全率、mAP0.5和mAP0.5:0.95。对扑克牌检测场景参考期望如下指标含义扑克牌场景参考值Precision预测框中真正含目标的占比衡量误检0.92以上Recall真实目标被检出的比例衡量漏检0.90以上mAP0.5IoU阈值0.5下的平均精度0.93以上mAP0.5:0.95IoU从0.5到0.95多档平均0.78以上如果mAP0.5达到0.9以上但mAP0.5:0.95偏低说明模型虽然大致能找到牌的位置但预测框与真实框的IoU不够高框的边界不够紧。扑克牌外接矩形包含圆角区域的背景天然限制IoU上限这是数据本身的特性不必过分苛求。训练完成后可以用训练好的权重跑推理脚本python detect.py --weights runs/train/exp/weights/best.pt \ --source test_images/ --img 736 --conf 0.5--conf调置信度阈值扑克牌检测建议0.4到0.5之间设太高会漏检设太低会增加误检。detect.py会自动把带框的结果图保存到runs/detect目录翻看这些图比单看数字更能判断训练效果。4. 52类扑克牌检测的训练难点与数据增强对策52个类别的扑克牌检测表面上是普通的目标检测任务实际训练中会碰到几个特有难点。这一章讲三个重点相近类别的细粒度区分、样本均衡性问题、以及720分辨率下的锚框适配。这些问题是这个数据集的性格不针对性处理最终精度会打折扣。4.1 相近类别混淆红桃与方块的细粒度区分扑克牌52个类别里红桃Hearts和方块Diamonds在视觉上非常接近——都是红色系形状轮廓相似区别主要在内部纹样的走向和断点。梅花Clubs和黑桃Spades同理都是黑色系。模型如果只靠颜色加大致形状做分类很容易混淆这几组牌。这个难点体现在训练曲线上就是loss正常下降但mAP上不去或者验证集上出现典型的系统性误判比如红桃K被识别成方块K。针对这个问题常见做法是调整数据增强的超参数让颜色信息在训练中保持稳定# data/hyps/hyp.scratch-low.yaml 中的关键超参数 mosaic: 1.0 # mosaic增强保持默认 mixup: 0.0 # 细粒度分类建议关闭mixup hsv_h: 0.01 # 色相扰动幅度默认0.015调低以保护颜色语义 hsv_s: 0.7 # 饱和度扰动 hsv_v: 0.4 # 明度扰动这里的关键逻辑是红桃和方块依赖颜色和内部纹样区分过强的HSV色相扰动会把红色的色相推向相邻色调增加模型区分难度。mixup混合增强会让两个不同类别的目标重叠对细粒度分类也不友好建议先关闭跑一版baseline看效果。我用类似细粒度数据集时hsv_h通常压到0.01以下。另一个有效手段是提高输入分辨率。720x720原始图如果用736训练花色纹样有足够像素支持细粒度判别如果降到640牌面上的点数可能缩到十几个像素宽误判率明显上升。这就是第3章里建议--img 736的核心原因。4.2 类别分布检查与样本不均衡处理虽然数据集声明训练集16000张、验证集4000张但52个类别的样本是否完全均衡需要自行验证。真实采集的数据往往存在隐性偏差——某些花色出现频率高某些点数只在特定场景出现。先跑一个类别分布统计脚本import os from collections import Counter label_dir datasets/labels/train cnt Counter() for fname in os.listdir(label_dir): path os.path.join(label_dir, fname) with open(path, r) as f: for line in f.readlines(): parts line.split() if len(parts) ! 5: continue cls int(parts[0]) cnt[cls] 1 for i in range(52): print(fclass {i:2d}: {cnt[i]:5d})统计完把结果做归一化如果发现某些类别的样本量只有平均值的一半以下就需要针对性处理。轻微不均衡最大/最小类别样本比小于2倍可以直接靠mosaic增强和足够长的训练轮数消化严重不均衡则考虑复制粘贴增强把低频类别的目标区域从原图抠出来随机粘贴到其他训练图上生成额外样本。我在做细粒度检测时的一个经验是与其花大量精力做复杂的类别均衡不如先用原始数据跑一版baseline查看混淆矩阵里哪些类别互相误判、哪些类别recall明显偏低再针对性地补充数据或增强。盲目做均衡可能把模型在多数类上的精度拉低。4.3 锚框设计与自动聚类的取舍YOLOv5默认锚框来自COCO数据集聚类适配的是自然场景中各种尺寸和长宽比的目标。扑克牌的目标框长宽比相对固定尺寸跨度也不大直接用默认锚框能训但可能不是最优。YOLOv5提供自动锚框聚类功能默认开启训练启动阶段会自动对训练集标签做K-Means聚类重新计算锚框。命令里的对应参数是python train.py --data poker52.yaml --weights yolov5s.pt \ --img 736 --batch 32 --epochs 100 --noautoanchor注意--noautoanchor这个参数是禁用自动聚类。不加它YOLOv5会自动计算加了它才使用默认锚框。对扑克牌这类目标尺寸集中的数据我通常建议保持默认即不加--noautoanchor让脚本自动聚类。启动日志里会出现AutoAnchor相关提示等它算完会自动进入训练这个过程通常在几十秒内完成不影响整体训练节奏。如果自动聚类的结果看起来不合理比如锚框长宽比全部接近1:1可能是目标框本身接近正方形扑克牌牌面特写时可能出现这时可以手动指定一组适配的锚框。常见做法是从聚类得到的宽高数据出发按从小到大排列并补足到9组。但说实话对多数场景自动聚类已经够用手动改锚框属于进阶调优不是首版训练的必要动作。5. 避坑与常见问题排查训练扑克牌检测模型的五个典型坑这一章记录训练这个扑克牌数据集时最常见的五个坑每一条按现象、原因、解决三步展开。这些是我实际踩过或帮同行排查过的血泪经验看懂之后能省下不少排查时间。5.1 坑一类别索引从0开始导致名实错位现象训练正常、loss持续下降验证集mAP也不低但打开推理图发现结果全部张冠李戴——黑桃A被识别成方块3类别对应完全混乱。原因这是典型的names列表顺序与标签class_id不一致。52个类别对应class_id 0~51如果你在yaml里写names时少了某一项或者把第0行误当成索引1开头那么从某个类别开始整体错位模型学到的映射全部偏移。这个偏移不会触发任何异常loss照常下降评估指标也可能正常。解决训练前用脚本从类别字典txt生成names不要手抄。如果已经训完才发现通常需要修正yaml后重新训练。有个小技巧是打开一张带目标的推理图对照原图看模型输出的类别名是什么再配合字典txt反查就能快速定位是从第几个类别开始错位的。这个坑最麻烦的地方在于没有任何报错提示只能靠可视化推理结果发现。5.2 坑二train与val文件名重复造成数据泄漏现象训练时验证集mAP异常高甚至接近训练集指标明显不合理。原因train和val目录下存在大量同名文件说明数据划分时有重叠验证集包含了训练图评估结果虚高。常见于复制数据时把整个目录拷到两处或者划分脚本按文件名前缀随机抽样时种子没固定。解决训练前跑一遍查重脚本。看到重复数量超过零就手动排查把重复文件从验证集里移除或者用脚本重新按9:1比例划分。这个数据集本身已按16000加4000切好一般无此问题但如果你自己二次整理数据这一步不能跳过。import os train_imgs set(os.listdir(datasets/images/train)) val_imgs set(os.listdir(datasets/images/val)) overlap train_imgs val_imgs print(f重复文件数: {len(overlap)}) if overlap: print(list(overlap)[:5])5.3 坑三show.py路径硬编码导致运行报错现象下载数据集后直接运行show.py报FileNotFoundError提示找不到datasets/images/train目录。原因show.py里默认图片和标签目录是相对当前工作目录的硬编码。如果你的终端当前目录不是数据集根目录或数据集解压到了其他位置路径就对不上。解决打开show.py把image_dir和label_dir改成绝对路径。注意Windows和Linux路径斜杠方向不同建议统一用正斜杠。另外如果目录包含中文名OpenCV的imread会静默返回None导致后续shape取不到值所以数据集路径全程保持英文最省事。5.4 坑四nc参数与标签类别数不一致现象训练刚启动就报IndexError: index 51 is out of bounds或训练不报错但某个类别从未被预测出来。原因yaml里的nc设错。52类数据的class_id范围0~51nc填51时加载最后一类标签会索引越界nc填53时多出的类别没有样本模型学不到。解决检查yaml中nc是否等于52且names长度也是52。用第3章的自检命令一条验证即可。还有一个习惯可以养成改完yaml后先跑一次数据加载测试YOLOv5在启动时会打印数据集统计信息看到52 classes字样再开始训练。5.5 坑五分辨率不为32的倍数导致隐式padding现象--img设720训练时显存占用比预期高训练日志里输入尺寸显示为736而非720。原因YOLOv5网络的下采样倍率是32输入尺寸需能被32整除。传入720时脚本自动letterbox到736填充边也参与计算显存占用随之上升这个行为在日志中不会特别标注容易被忽略。解决显存不足时优先降batch而非降分辨率降到640会损失点数等小目标细节。使用--amp混合精度能省约30%显存是性价比最高的办法。如果想精确按720计算显存预算需要手动把输入裁到能被32整除的尺寸但通常不值得为此改数据。以上五个坑按出现频率排列。前两个坑最隐蔽因为它们不报错、只有结果不对排查成本最高。后三个坑会在训练早期暴露反而好处理。如果训练没跑通优先检查5.3和5.4如果训练跑通了但指标或结果不正常优先检查5.1和5.2。把这条检查顺序记住遇到问题能少走弯路。6. 用show.py做批量可视化检查数据质量的一次快速体检训练前花十分钟做可视化检查远比训练完发现mAP上不去再排查高效。把show.py从随机单张改成批量遍历它就成了数据体检工具import cv2 import os image_dir datasets/images/train label_dir datasets/labels/train save_dir visualize_check os.makedirs(save_dir, exist_okTrue) with open(classes.txt, r) as f: names [line.strip() for line in f.readlines()] count 0 for fname in os.listdir(image_dir)[:200]: img_path os.path.join(image_dir, fname) label_path os.path.join(label_dir, fname.replace(.jpg, .txt)) img cv2.imread(img_path) if img is None: print(f图片读取失败: {img_path}) continue h, w img.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.split() if len(parts) ! 5: print(f标签行格式异常: {label_path}) continue cls, xc, yc, bw, bh map(float, parts) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, names[int(cls)] if 0 int(cls) 52 else str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imwrite(os.path.join(save_dir, fname), img) count 1 print(f完成共生成{count}张可视化图片)这个脚本相比原始show.py多了三件事批量处理而非随机单张、检测标签行字段长度、把类别名直接画在框上。跑完后抽看结果图重点看三方面框是否贴住牌面边缘、类别名是否正确、有没有明显漏标。我印象最深的一次排查用类似脚本抽查训练数据发现约一成图片的标签框比牌面大一圈框进了圆角外的背景。原因是标注时用外接矩形贴牌身但圆角区域被算进框里直接压低了IoU上限。不对照可视化光看mAP很难发现这种系统性偏差。从那以后我拿到新数据集都会把批量可视化当作训练前的固定动作抽200张图扫五分钟比返工省太多。希望这个方法能帮到你。本文还有配套的精品资源点击获取