简介:面向目标检测入门与扑克牌识别场景的标注数据集,适合学习YOLO、SSD等检测模型的初学者,也适用于需要自定义扑克牌识别任务的开发者。数据集中图片均来自真实拍摄画面,标注类别涵盖queen、ten、nine、king、jack、ace六种常见扑克牌,使用LabelImg工具生成VOC格式的XML标签,便于直接接入主流检测框架进行训练与验证。整个压缩包共726个文件,包括363张JPG原图与363个对应的XML标注文件,资源总大小约36.62MB,图片与标签一一对应,目录结构简洁,无需额外整理即可使用。目前已有395人学习下载,适合作为目标识别课程作业、算法实验或扑克牌识别项目的前期数据基础。通过该数据集,读者可以快速完成数据加载、标签解析、模型训练与精度评估等流程,节省自行采集和标注图片的时间成本,也可在此数据上进一步扩展更多牌面类别,开展更丰富的检测实验。
1. 扑克牌目标识别数据集:为什么一张牌面的标注比普通物体更难
做棋牌 AI 或者牌桌自动化应用时,很多人第一次接触扑克牌数据标注会觉得“这不就是框个矩形、贴个名字吗”。真正上手才发现,红桃和方块的牌面差异极小,黑桃 A 在旋转 180 度后和它本身的区别比猫和狗的距离还近,加上反光、遮挡、俯拍角度带来的形变,普通目标识别的标注套路在扑克牌上会频繁失效。
这个标题要解决的就是:为扑克牌目标识别任务准备一套可靠标注数据集的完整路径——从工具选型、类设计、标注规范,到难例处理和标注质量验证。它适合正在做棋牌类视觉项目、牌桌机器人、魔术教学识别或自动发牌验证系统的工程师,也适合刚接触数据标注、想把自己采集的牌面图像变成能训练 YOLO 或 Detectron2 的标准数据集的人。下面按“先定规范、再动手标、最后验质量”的顺序展开。
2. 标注前的准备:工具对比、类设计与扑克牌标注规范
2.1 选对标注工具:CVAT、LabelMe、Roboflow 怎么挑
扑克牌标注属于刚性目标识别,边界清晰但类别多(标准扑克 52 张牌,区分花色和点数),所以工具选择的优先级跟行人检测那种场景完全不同。我一般建议从三个维度来筛选:是否支持多分类标签、导出格式是否覆盖 YOLO/COCO/VOC、是否支持多边形以外的旋转框——因为扑克牌在桌面上经常是倾斜摆放的,水平矩形框在斜置牌面上会框进大量背景干扰。
| 工具 | 适用规模 | 旋转框支持 | 多分类标签 | 部署方式 |
|---|---|---|---|---|
| LabelMe | 千张以内单机标注 | 多边形自由绘制 | 支持 | pip 安装本地运行 |
| CVAT | 中小团队并行标注 | 支持旋转矩形 | 支持,可层级组织 | Docker 部署后浏览器访问 |
| Roboflow | 需要预标注模型协助的团队 | 支持旋转框 | 支持 | 云服务 |
| X-AnyLabeling | 需要半自动 AI 辅助标注 | 支持 | 支持 | 本地运行,带推理模型 |
单机个人项目我一般推荐 LabelMe,因为它的 JSON 结构透明、二次处理脚本简单。团队协作或者数据量超过两千张时,用 CVAT 更合适,它能直接在浏览器里分配任务、多人标同一批次图、标的进度可视化,而且自带自动标注插件,能用预先训练好的模型生成初始框,人工只需要调整和改类别。CVAT 用 Docker 起服务的方式比较标准:
git clone https://github.com/cvat-ai/cvat cd cvat docker compose up -d参数说明:默认账号在docker compose logs cvat里看初始密码;首次启动需要拉取 PostgreSQL、Redis 和 CVAT 三个镜像,占用端口 8080。启动后访问本机 8080 即可打开标注界面。用 CVAT 标注扑克牌时,记得在任务设置里把标注模式改成rect加rotation,这样拉出来的框能够贴合牌面角度。
2.2 类别体系的设计:52 类直接标还是花色+点数分开标
扑克牌目标识别数据集的类别设计,从结果上影响模型的收敛难度。常见有两种做法。
方案 A:直接标 52 个类别。每张牌一个类,例如AH表示红桃 A,AS表示黑桃 A。标签数量多,但每个类在数据集里天然均衡(一副牌每种一张),模型输出直接是“这张牌是谁”,不需要后处理拼装。缺点是如果某张牌的训练图特别少——比如倾斜角度过大的黑桃 5——这个类的 AP 会被拉低。
方案 B:把点数(Rank)和花色(Suit)分开做两个分支。这种在模型结构支持多任务输出时更合理,例如点数 13 类、花色 4 类。推理时联合输出“它是 A 且是红桃”。它的好处是类别总数从 52 降到 17,小样本问题被缓解,但代价是推理链路多了组合步骤,错误会传播。YOLOv8 这类单阶段检测器默认不支持多分支分类输出,强行改结构性价比不高。
实际工程项目里我做的最多的是方案 A 的改良版:52 类全量标注,但把牌面旋转超过 45 度的难样本单独建一个镜像类——即旋转后的黑桃 A 标成AS_rot。这样模型不需要自己学习跨界角度的判定,推理时再做一个几何校正,准确率比单纯加大旋转增强稳很多。
标注 Directory 结构上,原始图像建议按train/ valid/ test/三个目录分好,每张图的可视化标注文件跟在原图旁边。
2.3 标注规范:最小外接框还是紧密贴合
扑克牌牌面本身是直角矩形,但在照片里由于透视变成任意四边形,这引出一个常见的标注争议:到底是拉一个水平的矩形框,还是用四个顶点拉一个贴合牌面边缘的四边形。
我的建议是:如果后续只用 YOLO 系列做训练,标注水平矩形框加旋转增强足够,因为 YOLO 的检测头输出的是轴对齐框,你标注旋转框后 loss 反而不好算。但如果用的是 Faster R-CNN 加 RoI Transformer,或者后续有做牌面矫正、牌面内容识别的需求,那么用四点标注贴合牌面更合适,这样后续拿标注点做透视矫正时就省一轮工作量。
比较稳妥的做法是:扑克牌目标识别用水平框标注,但硬性要求矩形框的四个边贴近牌边缘 2 像素以内,不允许为了省事框大一圈。原因是扑克牌在识别时,边缘的白色边框和圆角本身携带颜色信息,框得太大会吞进桌面纹理,导致模型学到桌面背景特征。同时要求类别名统一用小写,例如ace_of_hearts而不是AceOfHearts,这样导出 YOLO 格式时类别索引与data.yaml的映射不容易对错。
3. 用 LabelMe 实操标注扑克牌数据集的完整流程
3.1 环境搭建:安装 LabelMe 与图片整理
LabelMe 是当前单人标注场景下最顺手的工具。安装就直接走 pip:
pip install labelme labelme --version参数说明:LabelMe 需要 Python 3.8 以上,界面基于 PyQt5 实现,所以安装时如果提示缺PyQt5,直接用 pip 补装即可。运行目录建议做一个images/和labels/的对称结构:
mkdir -p dataset/images dataset/labels先把采集到的扑克牌图片统一转成 JPG 格式,分辨率不要低于 640×640。如果原始照片是 4K 大图,先用批量脚本压缩到 1280 或 1920 再标注,不然标注界面拖动很卡,导出训练时数据加载也慢。这一步容易被忽略:标注前统一图像尺寸,会比训练时做 resize 更可控,因为你在标的时候就能判断小牌面是否清晰可辨。
3.2 标注操作步骤:从打开图片到保存 JSON
打开 LabelMe 后依次操作:
1. 点击 Open Dir,选择 dataset/images 目录 2. 点击 Edit -> Create Rectangle,在牌面上拉框 3. 弹窗中输入类别名,例如 king_of_spades 4. 按下 Ctrl+S 保存,LabelMe 会在图片同名目录下生成 JSON每张图标注完成后,检查一下右侧 File List 里对应的 JSON 是否生成。LabelMe 的 JSON 里除了points和label字段外,还带有imageWidth和imageHeight,这两个参数在转 YOLO 格式时要用于坐标归一化,缺了会导致坐标全部错位。
提示:如果标注过程中发现牌的边缘反光导致看不清花色,把图片亮度调低一档再标,或者在采集时用偏振镜片消除反光。反光边缘的像素用肉眼看是花色的一部分,但模型训练看到的却是白色高光块,标注时尽量贴合实际可见的图案边界而非牌面物理边界。
3.3 把 JSON 转成 YOLO 格式的脚本与参数说明
标注完的 LabelMe JSON 是不能直接丢给 YOLO 训练的,必须转成每张图片对应一个.txt文件的格式。下面这个脚本完成扑克牌数据集从 JSON 到 YOLO 的转换:
import json import os def labelme_to_yolo(json_path, output_dir, class_names): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) img_w = data['imageWidth'] img_h = data['imageHeight'] txt_lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_names: continue class_id = class_names.index(label) points = shape['points'] xs = [p[0] for p in points] ys = [p[1] for p in points] x_min, x_max = min(xs), max(xs) y_min, y_max = min(ys), max(ys) center_x = (x_min + x_max) / 2 / img_w center_y = (y_min + y_max) / 2 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h txt_lines.append(f"{class_id} {center_x:.6f} {center_y:.6f} {width:.6f} {height:.6f}") output_path = os.path.join(output_dir, os.path.basename(json_path).replace('.json', '.txt')) with open(output_path, 'w') as f: f.write('\n'.join(txt_lines)) class_names = ['ace_of_hearts', 'king_of_spades', ...] # 与标注时一致逻辑说明:这个脚本取一个标注框的所有顶点坐标,计算最小外接矩形的中心点、宽高,再分别除以图像宽高完成归一化,最终输出 YOLO 格式的一行class_id cx cy w h。参数class_names的顺序对应最终data.yaml里的类别顺序,想调整类别索引就在这里改顺序,改完后训练配置里要同步改。
转换完成后检查一下输出 txt 里的坐标数值,全部在 0 到 1 之间才是正常的。如果出现 0 或负数,说明原始 JSON 里有的坐标超出了 imageWidth/imageHeight,这是因为标注时不小心把标注框拖出画布边界了,回到 LabelMe 里修正即可。
4. 扑克牌标注里的难例处理与半自动标注
4.1 模型最容易犯的错,标注时最容易忽略的样本
扑克牌目标识别中,以下几类样本直接影响最终模型的精度,而且都是标注阶段决定成败的:
一是近邻花色混淆样本。红桃和方块在不看颜色时非常像,尤其是低分辨率下。标注这类牌面时,颜色通道如果是灰色、灯光偏黄,肉眼看不清,必须放大到 300% 再确认花色。我一般要求标注员在拿不准时直接看牌局部放大图判花色,而不是靠经验猜。
二是旋转角度刁钻的样本。一张黑桃 A 如果旋转 90 度,它和没旋转的样本差很多。按 2.2 节的方案,超过 45 度的旋转样本单独设置类别或做镜像标记,否则模型在推理时会误判点数的上下方向,使得 A 和倒着的 A 变成两个特征。
三是遮挡牌面。叠牌场景里,下面一张牌露出 60% 的情况很常见。标注这类样本时必须坚持“看不见完整牌面的牌不标”,但露出面积超过 50% 且能明确辨别类别时要标——这个比例阈值就是标注规范里最核心的参数,通常定在 50%。低于 50% 的牌标注进数据集,模型会学着猜而不是学着看,导致真实场景下误检率升高。
四是桌面反光和手指遮挡。数据集里没有手指的样本,训练出来的模型在真实场景中有手入镜时容易把手指尖当成牌角。解决方法是标注时把手和牌的交界处统一处理成被遮挡牌不标。大原则是:宁可漏标,不可错标。
| 难例场景 | 标注策略 | 期望的增强效果 |
|---|---|---|
| 红桃 vs 方块 | 放大确认花色 | 降低近邻花色误检 |
| 旋转 90 度 | 独立旋转镜像类 | 避免点数方向误判 |
| 遮挡低于 50% | 不标注该牌 | 减少无法检出的噪声 |
| 手指/物体遮挡 | 只标可见部分,不补全牌面 | 提升遮挡场景鲁棒性 |
| 模糊/运动拖影 | 不标注或移到 hard 集 | 防止模型学到模糊特征 |
4.2 用预标注模型加速扑克牌标注
当待标注图片超过一千张时,纯人工标注一张平均要 30 秒,加上休息和复查,一天最多处理 800 张。这时候做半自动预标注是效率最高的做法。CVAT 里可以直接集成预训练的目标检测模型做自动标注,也可以本地用 YOLOv8 跑一遍推理生成初步框,再导入 LabelMe 人工纠错。
以本地的方式为例,用 YOLOv8 生成预标注结果并转成 LabelMe JSON:
yolo predict model=best_ckpt.pt source=dataset/images/ save_txt=True save_conf=True参数说明:save_txt=True会输出每张图的 YOLO 格式 txt,save_conf=True同时输出置信度。生成的 txt 里每行是class_id cx cy w h conf,我们可以写一个反向转换脚本,把带置信度的 txt 合并成 LabelMe JSON,然后再用 LabelMe 打开逐一纠错。
这个流程能减少 60% 以上的拉框时间,因为预标注模型对常见牌面的定位已经比较准,人工只需要改类别错误和调整边界。注意预标注的置信度阈值不要拉太高,否则漏框很多反而省不了时间;一般设到 0.25 比较合适,宁可多出几个需要删掉的假框,也不要漏掉难检的牌面。
提示:半监督学习方法在扑克牌场景同样可以用。第一次用少量人工标注样本(比如 200 张)训练一个粗模型,然后让它去给未标注的图片做伪标注,人工只修正低置信度的样本,再把新样本加入训练集迭代。这种“从弱到强”的方式在扑克牌固定类别场景下能明显降低标注工作量。
5. 验证标注质量并快速接入 YOLOv8 训练
5.1 标注质量抽检:类别分布与框面积的异常检测
标注完成后,训练之前必须先做一次数据体检。最容易出问题的几个点:某个牌面类别样本数明显少于其他类、标注框面积占比异常(通常不会超过整图的 60%)、坐标数值越界。下面用脚本统计类别分布和框尺寸:
import os label_dirs = ['labels/train', 'labels/valid'] cls_count = {} box_area_ratios = [] for label_dir in label_dirs: for txt_file in os.listdir(label_dir): with open(os.path.join(label_dir, txt_file), 'r') as f: for line in f.readlines(): parts = line.strip().split() cls_id = int(parts[0]) cls_count[cls_id] = cls_count.get(cls_id, 0) + 1 w = float(parts[3]) h = float(parts[4]) box_area_ratios.append(w * h) print("类别分布:", cls_count) print("框面积占比均值:", sum(box_area_ratios) / len(box_area_ratios))逻辑说明:类别分布用字典统计每个class_id出现的次数,如果某类数量低于平均数的三分之一,说明该牌面的训练样本不足,需要针对性补采或增加该类的旋转增强。框面积占比均值在 5% 到 20% 之间比较正常,如果超过 40%,可能是标注时把桌面背景都框进去了,这将导致模型学習到大量背景特征。
5.2 用 YOLOv8 验证数据集的可训练性
数据体检通过后,直接拉起一个最小训练验证数据集质量。用 YOLOv8 训练自己的数据集,命令如下:
yolo detect train data=dataset/data.yaml model=yolov8s.pt epochs=50 imgsz=640 batch=16参数说明:data.yaml里要写清楚train和val的图片路径及类别列表,model=yolov8s.pt表示用 YOLOv8s 的预训练权重做迁移学习。epochs=50对扑克牌数据集来说够用了,因为牌面差异相比自然图像更小,模型收敛快,如果 50 轮后 mAP 还在明显上升再调大。训练完成后查看runs/detect/train/下的results.png,重点看mAP50和mAP50-95两条曲线的增长坡度,如果mAP50在 20 轮内没超过 0.9,大概率是标注数据里有错误标签——最常见的错误是红桃和方块标混。
最终记住一个验证技巧:训练完成后用最佳权重对训练集做一次全量回测,找出所有置信度低于 0.3 的检测结果并打印原始标注与之对比。这些低分样本就是标注质量最差的区域,逐张修正后重新训练,扑克牌识别准确率会有一次明显跃升。
本文还有配套的精品资源,点击获取