
简介面向目标检测实战与模型训练场景这份大型扑克牌检测标注资源已按YOLOV5目录规范整理覆盖52种扑克牌类别含四种花色、A至K图片分辨率为720×720 RGB并提供了清晰的训练集与验证集划分方便直接用于YOLO系列模型训练。资源包内共2000个文件主体为1999个txt标签文件负责记录每张图片中目标的类别与边界框坐标另附1个Python脚本show.py可随机读取图像并绘制边界框保存到当前目录便于快速预览标注效果。整套数据打包为7z大小约947.74MB适合目标检测入门练习、模型调参以及卡牌识别类项目开发目前已有133人学习/下载。除标注文件外还附带类别字典txt文件标签文本与图片命名一一对应简化数据加载与类别映射流程帮助使用者直接聚焦网络训练与效果验证。1. 大型扑克牌目标检测数据集的核心难点扑克牌识别是目标检测里一个非常典型的“类多、相似性高、场景复杂”的任务。标题里的“52类别”指的是标准扑克牌去掉大小王后的全部牌面每张三张牌既有花色信息又有点数信息类别数量远高于常见的几类物体检测。真正让这个任务变难的不是类别数量本身而是红心与方块在低分辨率下几乎无法区分K、Q、J的轮廓高度相似加上实际图像中出现的旋转、倒置、反光和牌面重叠导致很多通用目标检测模型在COCO上表现不错一到扑克牌数据集中就频繁出现误检。要解决这个问题第一步不是调模型而是把数据集本身做扎实。YOLOV5目录格式规定了一套严格的图像与标签组织方式训练脚本、验证脚本和推理脚本都依赖这个目录结构来定位数据。本文从目录搭建、类别映射、数据清洗、训练配置到评估排错完整梳理一条适合扑克牌52类检测的落地路径适合正在准备毕业设计、棋牌应用或数据比赛的人参考。2. YOLOV5目录格式与扑克牌52类别映射2.1 标准目录结构images与labels缺一不可YOLOV5对数据集目录结构的要求非常明确所有训练图像放在images/train下对应的标签文件放在labels/train下文件名与图像文件名保持一致扩展名为.txt。验证集和测试集同理。目录结构如下poker_dataset/ ├── images/ │ ├── train/ │ │ ├── img_00001.jpg │ │ ├── img_00002.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ ├── img_00001.txt │ │ ├── img_00002.txt │ │ └── ... │ ├── val/ │ └── test/ └── poker.yamlpoker.yaml是YOLOV5的数据集描述文件内容包含数据集路径、类别数量和类别名称列表YOLOV5在训练时会通过这个文件里的path字段找到训练和验证数据的完整路径。一个常见的写法是path: /data/poker_dataset train: images/train val: images/val test: images/test nc: 52 names: 0: AS 1: AH 2: AD 3: AC 4: KS # ... 其余类别yaml文件里的路径可以直接写成绝对路径也可以写成相对path的路径。我习惯把path写成数据集的根目录train、val、test只写相对于根目录的路径这样数据文件夹迁移到别的机器时只需要改path一行。2.2 52类编号方案与类别命名稳定性52个类别必须有一套固定的编号顺序训练、验证、推理全流程共享同一套映射。常用的方案是按“点数优先”排列先A、K、Q、J、10、9、8、7、6、5、4、3、2每个点数内按黑桃、红心、方块、梅花的顺序排列。编号0对应黑桃A编号51对应梅花2。编号点数花色编号点数花色0A黑桃2610方块1A红心2710梅花2A方块289黑桃3A梅花299红心4K黑桃309方块5K红心319梅花6K方块328黑桃7K梅花338红心8Q黑桃348方块9Q红心358梅花10Q方块367黑桃11Q梅花377红心12J黑桃387方块13J红心397梅花14J方块406黑桃15J梅花416红心1610黑桃426方块1710红心436梅花1810方块445黑桃1910梅花455红心209黑桃465方块219红心475梅花229方块484黑桃239梅花494红心248黑桃504方块258红心514梅花类别编号一旦确定就不要改动如果训练中途改编号顺序之前的标签全部作废。一个很实用的做法是把类别映射单独存成一个JSON文件和数据集放在一起每次训练前用脚本校验一遍标签中的类别号是否都在0到51范围内防止某张图的标签解析出错导致训练时类别索引溢出。2.3 标签格式与归一化坐标校验YOLOV5的标签文件是纯文本每行表示一个检测目标格式为“类别编号 x_center y_center width height”其中四个坐标值全部归一化到0到1之间。比如一张1920×1080的图像中某张牌的中心点位于像素坐标(960, 540)标注框宽400像素、高600像素归一化坐标就是(0.5, 0.5, 0.2083, 0.5556)。标签文件的生成和校验可以用一个简单的Python脚本完成读取一张图像和它的标注文件把坐标反算回像素值并绘制边界框用于人工抽查。import cv2 import numpy as np # 读取标签文件 image_path images/train/img_00001.jpg label_path labels/train/img_00001.txt image cv2.imread(image_path) if image is None: raise ValueError(图像加载失败: image_path) height, width image.shape[:2] with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() if len(parts) ! 5: print(格式错误:, line) continue cls, x_center, y_center, box_w, box_h map(float, parts) # 检查坐标是否越界 if not (0 x_center 1 and 0 y_center 1): print(中心点越界:, line) # 反算像素坐标 x1 int((x_center - box_w / 2) * width) y1 int((y_center - box_h / 2) * height) x2 int((x_center box_w / 2) * width) y2 int((y_center box_h / 2) * height) cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_result.jpg, image)这段代码遍历标签文件的每一行先做格式和越界检查再把归一化坐标乘回图像宽高画出边界框辅助人眼确认。坐标检查这一步骤绝对不能省实际数据集中我发现过标签x_center写成0但框宽写成1的情况这类错误训练时不容易发现推理时却会导致检测框严重偏移。3. 从原始扑克牌图像到目标检测数据集的四步处理流程3.1 图像清洗剔除模糊、重复与过度曝光图像构建大型扑克牌图像数据集的第一步是清洗原始素材。真实场景下拍摄的图像往往包含大量不适合训练的内容对焦失败的模糊图像、同一副牌的同角度重复帧、强闪光造成的过度曝光图像。它们不仅不能提供有效特征还会让训练时的loss曲线出现奇怪波动。常用的清洗策略是感知哈希去重和方差检测。感知哈希可以快速找出内容几乎相同的图像避免训练集和验证集之间出现“连体双胞胎”导致评估结果虚高。下面这段脚本用图像的感知哈希对图片去重import hashlib import os from PIL import Image def perceptual_hash(image_path, hash_size16): image Image.open(image_path).convert(L) image image.resize((hash_size, hash_size), Image.LANCZOS) pixels list(image.getdata()) avg sum(pixels) / len(pixels) diff .join(1 if p avg else 0 for p in pixels) return hex(int(diff, 2)) hash_dict {} for root, _, files in os.walk(raw_images): for name in files: if not name.lower().endswith((.jpg, .jpeg, .png)): continue path os.path.join(root, name) img_hash perceptual_hash(path) if img_hash in hash_dict: print(重复图像:, path, 与, hash_dict[img_hash], 相似) else: hash_dict[img_hash] path感知哈希的hash_size决定敏感度值越大对微小差异越敏感。我一般把hash_size设为16既能过滤几乎相同的帧又不会把同一场景不同角度的照片误判为重复。模糊图像的检测可以用OpenCV的Laplacian方差方差低于某个阈值就判定为模糊具体阈值和图像分辨率有关建议先抽样看一批正常图像的方差分布再定。3.2 从VOC或COCO标注转换为YOLO格式很多扑克牌数据集最初以VOC格式或COCO格式标注需要转换为YOLO格式。VOC格式的XML文件里有object节点每个object包含类别名和bndbox边界框坐标转换的核心工作就是读取这些坐标并做归一化。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, output_dir, class_dict): tree ET.parse(xml_path) root tree.getroot() # 获取图像尺寸 size root.find(size) img_width float(size.find(width).text) img_height float(size.find(height).text) yolo_lines [] for obj in root.findall(object): class_name obj.find(name).text if class_name not in class_dict: continue class_id class_dict[class_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 box_width (x2 - x1) / img_width box_height (y2 - y1) / img_height yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) output_path os.path.join(output_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(output_path, w) as f: f.write(\n.join(yolo_lines))转换脚本里最容易出错的是类别名与编号的映射尤其是扑克牌这种类别名接近的数据集。“heart_A”和“A_heart”这两种命名风格如果同时出现在一批XML文件里类别字典就会漏掉一部分目标。建议转换前先统计所有XML里出现的类别名人工确认命名统一后再执行转换。3.3 数据集划分按牌局分不按单张分数据集划分是影响评估可信度的隐形因素。扑克牌图像数据集的图像之间互相独立但同一个牌局的连续帧之间高度相关如果随机打乱后划分同一局牌的图像会同时出现在训练集和验证集导致验证指标虚高。正确做法是先按牌局编号或拍摄时间段分组再把整组分配到训练集、验证集、测试集。假设文件名以table_01_frame_0001.jpg这种格式命名可以提取前两段作为组标识。划分比例我一般用8比1比1大型数据集可以放宽到9比0.5比0.5但验证集不少于200张否则验证loss波动太大。import random import os from collections import defaultdict image_paths [] for root, _, files in os.walk(images_all): for name in files: if name.endswith(.jpg): image_paths.append(os.path.join(root, name)) groups defaultdict(list) for path in image_paths: # 假设文件名结构为 table_01_frame_0001.jpg basename os.path.basename(path) group_key basename.split(_)[0] _ basename.split(_)[1] groups[group_key].append(path) group_names list(groups.keys()) random.shuffle(group_names) train_groups group_names[:int(len(group_names) * 0.8)] val_groups group_names[int(len(group_names) * 0.8):int(len(group_names) * 0.9)] test_groups group_names[int(len(group_names) * 0.9):]这里的关键是按组分配而非按单张分配。随机种子固定后每次划分的结果可复现方便对比不同超参数组合的效果。划分完成后还可以统计每个集合里各类别的数量分布确保验证集里52类都有样本避免出现某张牌在所有类别的mAP都正常的假象。3.4 数据增强的边界与类别均衡策略YOLOV5训练时默认开启Mosaic增强、水平翻转和HSV色彩扰动这些增强对自然场景目标检测很有效但针对扑克牌需要重新审视。扑克牌是有方向性的物体垂直翻转后牌面上下颠倒红心A变成了倒着的红心A如果数据集里没有这种样本增强产生的标签就是噪声。YOLOV5的flipud参数默认是0即不启用垂直翻转这个默认值对扑克牌数据集是合理的。水平翻转对牌面真实性影响不大可以保留。马赛克增强会自动拼接多张图像生成新样能有效增加每张图像中的目标数量对扑克牌这种目标可能密集出现的场景很有帮助。类别均衡是另一个必须处理的问题。扑克牌共有52类如果原始图像里大部分是黑桃和红心的牌面梅花和方块的样本就会偏少。可以在训练前统计一次标签文件里所有类别的出现次数输出直方图数据然后给样本量少的类别补充对应的图像或该类别目标更多的图像。4. YOLOV5训练扑克牌52类模型的配置与超参数调优4.1 模型尺寸与输入分辨率从yolov5s起步训练扑克牌52类目标检测模型我建议从yolov5s或yolov5m开始而不是一上来就选yolov5l。52类虽然多但每张牌在画面里的特征相对规整s模型的参数量已经能容纳足够多的类别特征。输入分辨率img参数直接决定小尺寸牌面的检测效果如果画面里牌面宽像素小于32像素应该把img调大到960否则特征图上的响应会非常弱。标准训练命令如下cd yolov5 python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data /data/poker_dataset/poker.yaml \ --weights yolov5s.pt \ --cache命令里的cache参数表示把图像缓存到内存数据量大时能显著加快训练速度。batch大小受显存限制我习惯把batch设为显存允许的最大值再用多卡训练batch size太小会导致BN层统计不稳定模型在小目标上的表现波动很大。epochs可以先设100轮配合YOLOV5默认的早停机制连续50轮没有验证集改进就会自动停止。4.2 超参数调整flipud、mosaic与学习率YOLOV5的超参数文件hyp.scratch-low.yaml里包含大量数据增强和控制训练行为的参数其中对扑克牌检测影响最大的是flipud、mosaic和lr0。下面是一份针对扑克牌场景调整过的hyp文件片段lr0: 0.01 lrf: 0.01 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3.0 mosaic: 1.0 mixup: 0.0 fliplr: 0.5 flipud: 0.0 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4参数说明如下表参数建议值说明lr00.01初始学习率数据量大的扑克牌数据集可以从0.01开始观察前5轮loss变化再微调lrf0.01最终学习率与初始学习率的比值控制学习率衰减幅度mosaic1.0马赛克增强概率扑克牌图像里目标密集时非常有帮助flipud0.0垂直翻转概率扑克牌方向敏感必须保持0mixup0.0样本混合增强扑克牌类别相似度高混叠会造成额外混淆hsv_h0.015色相扰动扑克牌花色是关键特征扰动幅度过大会改变红心与方块的区分度mixup参数默认在部分yaml中是开启的但扑克牌这种类别间差异微弱的场景混合两张牌面会生成一张价格不菲的“四不像”所以设置为0。hsv_h控制在0.015左右只做轻微色偏避免红心和方块的色相在增强后相互靠近。训练时如果想禁用自动学习锚框可以加上--noautoanchor参数。扑克牌检测框的特点是有规律的宽高比单张牌的宽高比通常落在0.6到0.8之间自动锚框会在训练前根据标签聚类出最合适的初始锚框尺寸。如果数据集中牌面尺寸分布极度不均匀也可以先用运行一段代码计算数据集中所有标注框的宽高比分布再手工指定锚框。4.3 训练过程中的监控loss曲线与验证集表现训练过程中需要同时关注训练loss和验证集mAP不能只看训练loss下降趋势。扑克牌这类类别数多的数据集训练集loss降到很低不代表模型真的区分开了花色可能是记住了图像的背景特征。我每隔5个epoch在验证集上跑一次val.py把mAP0.5和mAP0.5:0.95记录到同一个CSV里训练结束后画出两条曲线对比。如果训练loss正常下降但验证集mAP徘徊在低水平多半是数据划分出了问题验证集和训练集分布不一致或者某个类别在验证集里样本过少。如果验证集mAP在一轮训练后突然暴跌检查训练过程中的缓存数据是否被其他进程意外改动这类问题在共享数据集目录的多用户环境下经常出现。5. 扑克牌52类目标检测的评估指标与暗坑排查5.1 用val.py跑出按类别的mAP并分析PR曲线YOLOV5训练结束后需要用验证集独立评估模型效果不能直接拿训练集来吹指标。标准验证命令python val.py \ --data /data/poker_dataset/poker.yaml \ --weights runs/train/exp/weights/best.pt \ --task val \ --conf-thres 0.001 \ --iou-thres 0.6 \ --plotsconf-thres设为0.001是为了让验证阶段能输出完整的PR曲线如果按默认的0.25设置低置信度的预测会被过滤导致PR曲线在召回率较低时直接截断无法准确反映模型上限。iou-thres表示预测框和标注框的交并比阈值0.6意味着预测框与真实框中重叠比例达到0.6才算作正确预测。验证结果会生成confusion_matrix.png这是一张52×52的混淆矩阵对角线越亮代表正确率越高。针对扑克牌数据要特别关注几个近邻对红心类与方块类在深色背景下容易混淆K与Q在牌面特写不足时也容易判断错误。如果混淆矩阵里红心8和方块8之间有大量误检说明模型的主要特征来源是点数而不是花色可以增加花色区域的局部增强或者补充更多两种花色同时出现的图像。5.2 按类别统计召回率找到拖后腿的牌面val.py的结果会输出一个每类AP的列表通常保存在runs/val/exp/目录下的labels.csv或results.txt中。52类的AP分布往往不是均匀的个别类别的AP会显著低于平均值。我见过扑克牌数据集里三类问题最典型数字牌10与J在低分辨率下互相误检黑桃A的倒置形态被漏检梅花7和红心7在不同拍摄角度下特征混淆。针对AP低的类别不一定要盲目增加图像数量而是先看这个类别的置信度分布。如果置信度普遍偏低说明模型对该类别的特征把握不足更适合补充清晰的特写样本如果置信度分布正常但和另一个类别混淆严重说明两个类别的特征边界需要更多反例来划清。5.3 常见训练失败表现与修复手段训练扑克牌数据集时有几类问题出现频率极高。loss直接变成NaN最常见的原因是标签文件里某一行坐标写了负数或超界或者在batch里混入了一张完全损坏的图像用上一章的标签校验脚本扫一遍即可定位。训练正常结束但测试结果全为空检查yaml中names列表顺序是否与标签文件中的类别编号一致这类错误在改动过类别映射后最容易产生。验证集mAP很高但实际部署效果差往往是验证图像和训练图像来自同一场景划分数据时没有按牌局分组导致。推理阶段输出的边界框大量重叠在同一张牌上说明NMS阈值过松或置信度阈值过低调整--conf-thres到0.3再测试。这类问题不算模型训练失败但相当影响实际使用体验尤其是牌桌场景里多张牌紧密排列时重叠框会让下游识别逻辑无法拿到干净的牌面位置。6. 用伪标注闭环放大数据集让52类越训越稳数据集的构建不是一次性的而是随着模型能力提升不断扩大的循环。可以用已训练好的模型对新收集的未标注扑克牌图像做自动标注人工只修正错标和漏标这样能以较低成本把数据集从几千张扩到几万张。YOLOV5的detect.py本身就支持输出标签文件伪标注命令如下python detect.py \ --weights runs/train/exp/weights/best.pt \ --source /data/poker_unlabeled/ \ --conf-thres 0.5 \ --save-txt \ --save-confsave-txt会把检测结果写成YOLO格式的标签文件save-conf会在标签文件里追加置信度一列。伪标注生成后需要按置信度分层处理置信度高于0.8的标签可直接当作训练数据0.3到0.8之间的标签保留边界框但标记为待人工复核低于0.3的检测结果作为漏检案例留存。人工复核阶段重点关注自动标注中置信度中等但类别和相邻牌的类别高度接近的情况例如红心6被标注成方块6。这类错误一旦混入训练集会抵消伪标注节省的时间。复核工具不需要太复杂我一般用opencv写一个逐张显示图像和预测框的小脚本按键切换保留或删除标签文件。航班闭环扩完后重新用train.py训练一轮并比较新旧版本在验证集上的按类别AP曲线。如果新版本在之前表现差的类别上有明显提升说明补样方向和数量都对如果某个类别AP反而下降回头检查该类别伪标注的标签是否引入了大量边界框过松的训练样本适当收紧伪标注的置信度阈值后重训一次。这个迭代流程比一次性收集完所有数据再训练要高效得多长时间运行下来52个类别的检测稳定性和边界框质量都会逐步逼近生产可用的水平。本文还有配套的精品资源点击获取