简介这是一份面向目标检测实战的扑克牌图像数据集采用YOLOV5标准目录格式覆盖黑桃、红桃、梅花、方块四种花色的1~K共52个类别。所有图像统一为720×720 RGB训练集与验证集分别包含16000张和4000张图片及对应txt标签原始数据总量约987MB每类牌型样本充足可直接接入YOLOV5训练、验证与推理流程。资源包共2000个文件其中1999个为txt标签文件1个为Python可视化脚本压缩后体积约947.74MB并附带类别字典txt文件便于标签映射与类别管理。目前已有133人浏览学习。压缩包内另提供即用型py脚本随机传入一张图片即可自动绘制边界框并保存结果无需任何配置既适合快速预览标注质量也可作为自制目标检测数据集时YOLOV5目录结构的参考模板。对于需要规范数据集格式、快速开展目标检测实验的开发者这份资源能显著减少数据整理时间。1. 52类别扑克牌检测数据集YOLOv5目录格式拿过来就能直接训练做目标检测的都知道准备数据集往往比调模型更耗时间。特别是拿到一批原始图片后要转格式、分训练验证集、写类别文件一套流程下来至少浪费半天。这个大型扑克牌图像检测数据集数据量接近1GB包含52个类别的扑克牌图像训练集16000张图片验证集4000张而且图像和标签一一对应全部按照YOLOv5标准目录结构整理好。更关键的是每张图片都配了对应txt标签文件类别索引从0到51直接就能被YOLOv5、YOLOv8这类模型读入并开始训练不需要再写额外的格式转换脚本。个人做毕设、做工程预研或者想在细粒度分类场景下验证检测模型这套数据都够用。2. 数据集拆解目录结构与YOLO标签的读取方式2.1 datasets目录下images与labels的对应关系拿到资源解压之后第一件事不是急着复制到训练环境而是先把目录结构看清楚确认图片与标签是否一一对应。这个数据集的目录设计是YOLOv5训练时最常见的组织方式也就是datasets目录下分别建images和labels两个主目录每个主目录内再按train和val拆分。用tree命令能看到类似下面的结构datasets/ ├── images/ │ ├── train/ │ │ ├── 318686725.jpg │ │ ├── 612173347.jpg │ │ └── ... │ └── val/ │ ├── 989583012.jpg │ ├── 293501472.jpg │ └── ... └── labels/ ├── train/ │ ├── 318686725.txt │ ├── 612173347.txt │ └── ... └── val/ ├── 989583012.txt ├── 293501472.txt └── ...命令里的tree只是查看用的在Linux和macOS上默认可用Windows系统如果没有tree命令可以用dir /s来查看同样的递归目录结构。这里有一个很关键的细节images/train里的图片文件名和labels/train里的txt文件名是一一对应的后缀不同但文件名主体完全相同这是YOLO格式能够正确匹配图片与标签的前提。如果文件名对不上训练时会出现“找不到对应标签文件”的报错后续训练就会直接中断。在把这套数据集接入自己的项目之前先做一个数量核验这一步值得做因为数据集拷贝过程中偶尔会丢文件。核验方法很简单分别统计train和val下图片与标签的数量两边应该严格相等再抽查几个文件名是否配对。如果是16000张训练图片那么labels/train下也应该是16000个txt文件多一个空文件可以容忍少一个就说明有图片缺标签训练时那条数据会被跳过但不会报错问题比较隐蔽。2.2 YOLO标签格式解析归一化坐标与行内容含义YOLOv5的标签文件和VOC的xml格式完全不同它不存左上角坐标和右下角坐标而是存归一化后的中心点坐标与宽高。打开任意一个txt文件每一行都代表图片中的一个目标格式为类别索引、中心点x坐标、中心点y坐标、目标宽度、目标高度五个数值用空格分隔坐标全部归一化到0到1之间。比如下面这行标签17 0.486328 0.542969 0.166406 0.226562从左往右读第一个数字17表示这张扑克牌的类别索引对应红心9索引从0开始0-12对应黑桃A到K13-25对应红心A到K后面两个0.486328和0.542969分别代表目标中心点在图片中的相对位置最后两个0.166406和0.226562表示目标框的宽度和高度比例。也就是说实际边框在720×720的图片上中心点像素位置大约是横坐标350、纵坐标391框宽约120像素高约163像素。在写数据校验脚本时我一般会检查最后一行的坐标值范围是否在0到1之间。如果出现大于1的值说明标签文件在标注时没有归一化训练时YOLOv5内部会尝试解析这些坐标结果发现候选框跑到图片外面去了直接影响loss计算和mAP评估。反过来如果坐标全为0代表该txt文件是一个空标签文件即这张图片里没有目标。空标签文件在训练中不参与正样本计算但不会导致训练崩溃属于可容忍的异常情况。2.3 52类别的编码映射花色与牌面如何对应索引位置这个数据集的类别定义是扑克牌中常见的一到K加四种花色组合起来正好52类外加可能出现的“无牌”背景情况。类别索引的排列顺序直接决定了标签文件中第一个数字的含义。根据资源附带的类别字典txt文件索引0到51依次对应着固定的花色与牌面组合例如黑桃A是索引0红心A是索引13梅花A是索引26方块A是索引39。每张图片只包含一张扑克牌时标签文件只有一行但有些图片会同时出现多张牌标签文件就有多行。在YOLOv5的训练配置中data yaml文件里的names列表顺序必须和类别字典保持严格一致否则训练出来的模型即使准确率看起来很高实际推理时输出的类别名称其实是错位的。举个例子如果names列表里把索引17的位置错放成黑桃Q那么网络在预测时输出的17这个索引会被错误解析为黑桃Q但真实牌面是红心K或红心9这就造成预测结果全部错位。为避免这个问题最稳妥的办法是直接用资源附带的类别字典txt文件来生成data yaml里的names字段不要把复制代码过程中偶尔看到的顺序当作真实顺序。3. show.py可视化脚本一张图验证边界框画得对不对3.1 脚本做了什么从图片路径到画框保存的核心逻辑资源附带的show.py是一个可视化验证脚本目的很简单随机传入一张图片读取它对应的标签txt文件把边界框和类别名称绘制到图片上然后保存到当前目录。这个脚本特别适合用来快速验证数据集标签是否正常。在训练之前先随机挑几十张图片跑一遍如果画出来的框位置和牌面对得上说明标签坐标和类别索引没问题可以放心训练。核心逻辑大致可以分为五步读取图片路径、匹配对应的标签文件、逐行解析归一化坐标、将归一化坐标换算成像素坐标并绘制矩形框、最后保存绘制结果。用一个简化版本的代码来说明这个流程import cv2 import numpy as np def draw_yolo_boxes(image_path, label_path, class_names): # 读取图片OpenCV默认使用BGR通道顺序 img cv2.imread(image_path) height, width img.shape[:2] with open(label_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: # 每一行格式class_id x_center y_center box_width box_height parts line.strip().split() class_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) box_width float(parts[3]) box_height float(parts[4]) # 归一化坐标换算为像素坐标 x1 int((x_center - box_width / 2) * width) y1 int((y_center - box_height / 2) * height) x2 int((x_center box_width / 2) * width) y2 int((y_center box_height / 2) * height) # 绘制矩形框和类别名称 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) label class_names[class_id] cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) output_path visualized_ image_path.split(/)[-1] cv2.imwrite(output_path, img) print(saved:, output_path)代码逻辑并不复杂但有一个值得注意的点归一化坐标换算成像素坐标时x_center必须减去box_width的一半得到左上角横坐标y_center减去box_height的一半得到左上角纵坐标否则画出来的框会偏移。很多同学在这里手动换算时容易出现符号错误明明坐标值都是对的画出来的框却偏到一边。脚本里cv2.rectangle绘制的颜色是BGR顺序绿色边框配合红色类别名称实际调试时如果想区分不同花色可以修改成每种花色一种颜色。3.2 手动运行与批量抽检随机图片验证标签正确性运行show.py的方式很简单在命令行里传入图片路径即可。命令大致是这类风格python show.py --image datasets/images/val/989583012.jpg注意YOLOv5目录结构决定了show.py的工作方式它通过图片路径找到同级labels目录下的txt文件。比如图片路径是datasets/images/val/989583012.jpg对应的标签路径就是datasets/labels/val/989583012.txt。如果运行后报错提示找不到标签文件大概率是图片和标签的目录层级不匹配。日常使用时不需要逐张运行我一般会把单图脚本改成一个批量抽检循环从val集合中随机抽取30张图片逐个绘制并保存然后快速浏览一遍就可以掌握整个数据集的标注质量。批量抽检的常见套路是先用glob.glob拿到val下所有图片路径再用random.sample随机抽取最后循环调用绘制函数。这样比单张单张运行要高效得多而且覆盖的样本量更大能发现更多潜在的标签问题。抽样画框时重点看三个方面框是否完全贴合扑克牌边缘、类别索引是否和牌面一致、同一张图有多张牌时框与框之间是否互相重叠或有遗漏。如果发现框和牌面对不上先怀疑类别字典顺序出了问题再检查标签文件本身按照先前说的索引0到51的映射关系逐个排查。3.3 图片尺寸与坐标系换算720×720下所有参数如何设置这个数据集图片分辨率固定为720×720的RGB图片短边和长边都是720像素。YOLOv5训练时默认会对输入图片做letterbox缩放如果训练参数里设了imgsz640那么720×720的图片会被等比缩放到640×640多余部分填充灰边标签坐标因为已经归一化到0到1缩放过程中不需要任何额外调整。验证可视化时如果直接把原图画框再缩放显示需要注意坐标换算的分母是原图尺寸而不是缩放后的尺寸。show.py内部使用的分母来自img.shape读取到的真实尺寸因此无论原图是多少分辨率都能正确还原坐标。如果自己写脚本时写死了分母为640而实际图片是720×720画框位置就会整体偏移。我见过有人把这类问题当成数据集标签标注错误花了一整天排查最后发现是自己的脚本算错了分母。4. 训练接入从数据配置到YOLOv5/v8模型适配4.1 生成data yamlnc、names与训练验证路径的写法把数据集接入YOLOv5训练流程最核心的文件就是data yaml里面定义了类别数量、类别名称和训练验证目录路径。基于这套扑克牌数据集我一般这样写train: datasets/images/train val: datasets/images/val nc: 52 names: [ SA, S2, S3, S4, S5, S6, S7, S8, S9, S10, SJ, SQ, SK, HA, H2, H3, H4, H5, H6, H7, H8, H9, H10, HJ, HQ, HK, CA, C2, C3, C4, C5, C6, C7, C8, C9, C10, CJ, CQ, CK, DA, D2, D3, D4, D5, D6, D7, D8, D9, D10, DJ, DQ, DK ]里面train和val的路径建议写相对路径这样整个项目文件夹移动位置后不需要重新修改配置。如果写成绝对路径换一台机器训练就要改一遍路径比较麻烦。还有一种写法是把路径写在yaml文件之外通过命令行的--data参数传入这样yaml文件本身可以完全不变。nc参数必须是52这是类别数量和names列表长度严格对应。names列表的顺序则必须和标签txt里的索引值一一映射第0个名字对应索引0的类别。列表里我用SA到SK表示黑桃A到黑桃KHA到HK表示红心CA到CK表示梅花DA到DK表示方块这种命名方式简洁且直观训练日志里也能快速读懂。4.2 命令行训练以YOLOv5s为例的完整参数与含义数据配置写好后训练命令参考YOLOv5官方仓库的标准写法即可常见训练指令是python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 720 \ --batch 16 \ --epochs 100 \ --device 0命令行里的--weights参数指定预训练权重yolov5s.pt是轻量版模型在COCO数据集上预训练过迁移到这个扑克牌数据集上收敛速度会快很多。如果显卡显存有限可以换成yolov5n.pt模型更小batch也能调大一些。--img 720表示训练时把输入图片调整为720×720正好匹配数据集原本的分辨率不需要额外缩放。--batch 16代表每个批次处理16张图片--epochs 100表示训练100轮。训练过程中还可以开启--cache参数将图片加载到内存中避免每次迭代都从磁盘读取图片。对于16000张训练图片来说cache开启后训练速度会有明显提升但也要看机器内存够不够。全部加载大约需要几个GB内存如果内存不够反而容易导致进程被系统杀掉建议先不cache跑一轮观察内存占用后决定是否开启。YOLOv8和YOLOv5的数据接口基本兼容同一个数据集和yaml文件可以直接用于YOLOv8训练只是训练命令换成了yolo detect train data... modelyolov8s.pt。YOLOv8的yaml读取逻辑支持5和8两种版本扑克牌数据集的目录布置方式不用变省去不少适配时间。4.3 标签从VOC转成YOLO格式这套数据集帮你省掉的转换步骤很多目标检测数据集发布时用的是VOC格式即是把每个目标写在一个xml文件里存左上角和右下角的像素坐标。想用于YOLO训练必须先把xml解析出来再计算中心点坐标和宽高最后除以图片宽高得到归一化坐标。这套数据集省掉了这个过程因为发布方已经把标签转换成YOLO格式并按照训练、验证集划分好。如果自己手里还有其他扑克牌数据集想合并进来需要做一次VOC转YOLO的转换这里给一个常见转换脚本的套路import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, class_names, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() yolo_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 像素坐标转归一化中心点与宽高 x_center ((x1 x2) / 2) / img_width y_center ((y1 y2) / 2) / img_height width (x2 - x1) / img_width height (y2 - y1) / img_height yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return yolo_lines脚本的核心是把VOC的像素坐标先求中心点再除以图片宽高做归一化。做这一步转换时最容易出错的地方有两点一是xmin和ymin这类值如果读取出来是字符串必须转换成float后才能参与运算二是class_names列表的顺序必须与目标数据集的索引顺序完全一致不能把类别名字放在set集合里去重否则索引会乱掉。我在转换多个来源的数据时会先固定一个主类别字典然后让所有其他数据集都按这个字典重新映射类别名。5. 避坑指南标签索引、类别字典与数据划分的典型陷阱5.1 类别索引从0开始1到13的排布顺序最容易记错扑克牌数据集的52个类别在标签文件里用0到51表示但很多初学者习惯从1开始数导致画框验标签时总会错位。具体的坑是这样出现的查看第一张图的标签txt第一行写着“0 0.45 0.52 0.18 0.24”于是去查类别字典发现索引0对应黑桃A但原图显示的牌面却是黑桃K于是误以为样本标签标错了。实际上大概率是标签里的索引0对应的就是黑桃A自己看错了牌面或者类别字典顺序本身和标签生成时的顺序不一致。应对的办法很简单先挑一张自己认识的牌比如红心5打开它的标签文件看类别索引是多少再去类别字典里确认该索引对应的名称。如果两者对不上说明类别字典顺序和标签顺序不一致这种数据集文件本身内在不一致的情况偶尔会出现此时以标签文件的具体坐标为准手动修正类别字典即可。5.2 类别字典txt与data.yaml里names顺序不一致资源里除了标签文件之外还附带了一个类别字典txt文件。有人会直接把这个txt文件里的内容原封不动拷贝到data.yaml的names字段里结果训练出来的模型推理时预测名称全部错乱。玄学之处在于明明类别数量没问题、准确率也没问题但输出的类别名对不上。这个问题通常出在txt文件里的行顺序和标签文件里的索引顺序不是一回事。我一般会自己写一段映射检查逻辑把字典txt的每一行读取出来按行号对应到索引然后与数据的实际标签比对确认每一个索引对应的名称是黑桃A、黑桃2还是其他牌面。尤其是在多人协作的项目里字典txt可能被手动编辑过增加或删除了某一行整个索引体系就全部错位。5.3 标签文件大小不一致导致的数量核对失败数据量一大拷贝过程中偶尔会出现文件缺失训练时会提示有些图片找不到标签但实际上training过程并不会中断只是会打印warning然后跳过那条样本。问题在于如果你没有提前检查模型的最终准确率会下降而且很难定位是哪部分数据缺失了。另一种常见翻车场景是标签文件存在但内容为空也就是0字节的txt文件这种情况模型不会报错但该图片等于没有参与训练。规避方法是写一段核对脚本遍历每一个标签txt统计行数同时检查图片是否存在以及能否被OpenCV正常读取。凭经验来说这种校验应该在训练前做而不是等模型训练完发现mAP异常后再回头排查那时已经浪费十几个小时的训练时间了。5.4 图片和标签目录层级跨越不同系统时的路径分隔符问题数据集在Windows上解压后如果直接在Linux服务器上挂载使用路径分隔符和目录命名可能会有差异。尤其在把数据从Windows拷贝到Linux时偶尔会出现文件名大小写不一致或者路径里带有空格的问题。YOLOv5的dataset加载逻辑虽然能处理大部分情况但路径中含空格和特殊字符时会读不到文件。我更习惯的做法是拷贝完成后第一时间执行一条find命令检查所有jpg和txt文件的权限与大小排除损坏文件然后再跑一遍数据集加载脚本看能否正确统计出训练集和验证集的数量。注意图片格式必须是.jpg或.png尽量避免使用.bmp这类体积大且解码慢的格式。这套数据集的图片本来就是.jpg压缩格式这样一个隐患天然不存在。5.5 val集合只有4000张模型评估时置信度阈值的设置技巧验证集有4000张图片相对训练集虽然只有四分之一但样本量已经足够支撑可靠的模型精度评估。推理或验证时如果直接把置信度阈值设成0.5有些扑克牌表面有反光或者轻微遮挡的样本可能被过滤掉导致mAP看起来偏低。我会在val评估时把conf_thres设置成0.001让模型尽量把可能的候选框都输出再通过计算mAP来综合评判性能。推理阶段把conf_thres设得低一点比如0.1也能帮助检查模型对困难样本的预测能力但实际部署时还是要回归到0.3到0.5避免误检过多。这套数据里牌面纹理差异比较大同类牌在不同角度和光线下的特征变化也会影响模型表现所以在调阈值时要在val集上多跑几轮找到适合自己场景的平衡点。6. 进阶玩法扩充数据集、跨框架迁移与置信度验证6.1 用show.py做数据抽检后如何把新采集的牌面合并进现有数据集假设你已经在这个数据集上训练出一个基础模型接下来想提高泛化能力最直接的办法就是自己采集新的扑克牌图片并合并进来。新图片要遵循相同的目录命名规则放在datasets/images/train下同时把对应txt标签放到datasets/labels/train下文件名主体保持一样。新图片标注我一般建议用labelImg这类工具人工标注保存成VOC格式然后用前面给的voc_to_yolo转换函数批量处理再把生成的文件并进数据集的labels目录。合并完成后不要急着训练先跑一遍show.py可视化脚本抽样看新数据的标签是否和原数据集风格一致如果新旧数据的标注框风格差异过大比如旧的框贴得很紧、新的框留白过多模型训练时会出现标签噪声。6.2 把YOLOv5格式迁移到YOLOv8或MMDetection变更点与验证方法迁移到YOLOv8时数据集目录结构和标签格式不需要改YOLOv8会直接读取images和labels目录。变更点是yaml配置文件里的路径写法YOLOv8支持相对路径对train和val的文件夹要求与YOLOv5一致。MMDetection则需要将YOLO标签转成COCO格式的json文件这时候需要把txt中归一化的坐标转换回像素坐标并重组为COCO的segmentation或bbox字段。跨框架迁移完成后有一个轻量的验证技巧随机抽十张图片用源框架跑一次检测再用目标框架跑一次检测对比输出框的重合度和类别结果。如果两者结果一致说明数据转换正确可以放心使用。如果倾向使用在线标注工具重新整理数据务必确认导出格式是否为YOLO格式不然会带回一堆VOC或其他格式的混合数据又得重新走一遍转换流程。6.3 置信度验证与部署前检查用验证集找出模型的短板最后一步是在部署前用验证集做一次全面的置信度验证。我会统计各个类别的AP值找出识别效果最差的几种牌面通常是黑色牌面的黑桃和梅花在暗光环境下容易被混淆。针对这些短板类别可以做针对性数据增强比如调节亮度、对比度、增加模糊把生成的新图片添加到训练集。YOLOv5自带的超参数进化功能也可以尝试通过修改hyp.scratch.yaml里的参数来提升整体精度但调参前先保证数据本身没问题否则调参只会让过拟合更严重。每当我拿到一个新的检测数据集总会强制自己先完整跑一遍可视化抽检再开始训练。这个习惯帮我躲过了不少标签错位和数据损坏的暗坑。说到底模型质量的上限由数据决定动手训练前多花二十分钟做数据体检比事后调参或者清洗数据要划算得多。希望这套流程能帮你顺利跑通自己的扑克牌检测项目。本文还有配套的精品资源点击获取