YOLO v11扑克牌识别实战:从数据集制作到部署
发布时间:2026/9/15 2:02:24来源:尧图网络
简介扑克牌识别数据集专注于A-K全字母牌面检测面向计算机视觉入门及目标检测实战人群可直接用于YOLOv11模型训练与验证。资源共2000个文件包括1850个txt格式的标注文件、149张jpg原始图像及1个yaml配置文件压缩包大小109.76MB。txt文件对应每张图片的目标框与类别标签yaml文件定义了数据集路径与类别名称整体结构规范能够帮助使用者跳过数据清洗与格式转换环节快速开展扑克牌识别模型的训练与验证。该数据集包含多角度、多样环境下的真实照片标注精度较高在测试条件下正确识别率可达98.7%适合构建棋牌识别、发牌检测、牌局自动化记录等应用可用于课程设计、毕业设计或实际项目开发省去大量数据准备时间。目前已有241人学习浏览对有类似识别需求的研究者或开发者来说是一份可直接上手的实用资源。1. 扑克牌识别数据集在YOLO v11里怎么用做棋牌类AR应用或扑克牌自动计分系统时“识别A-K所有牌字母”是常见的第一步。这个标题的数据集给出1850张原始图标注文件是YOLO v11可直接读取的txt格式训练难度不高正确识别率做到98.7%并不意外13个类别、牌面图形相对固定、背景干扰可控。适合刚接触yolo训练自己数据集的工程师也适合需要一个干净基线来验证数据增强和部署方案的熟手。相比满网保存的COCO 2017或车牌数据集这种小而专属于桌面场景的数据集反而能让你把一套“数据→训练→评估→部署”的流程完整走通。下面我按自己训练牌类识别模型时的做法从标注格式、训练闭环、精度边界到验证手段逐层展开。2. YOLO v11格式的扑克牌数据集图片、txt与类别映射2.1 一张扑克牌图对应的标注长什么样YOLO格式不保存像素坐标保存的是归一化坐标。对每个目标标注文件的一行是“类别ID 中心点x 中心点y 宽度 高度”全部是相对图片宽高的比例取值范围在0到1之间。拿一张640x480的红桃A举例牌面右上角字母A的像素框是x_min120, y_min170, x_max210, y_max260则中心点x (120210)/2 / 640 0.2578中心点y (170260)/2 / 480 0.4479宽度 (210-120)/640 0.1406高度 (260-170)/480 0.1875最终txt文件里对应行就是0 0.2578 0.4479 0.1406 0.1875。这个数据集把A到K映射成连续类别ID花色不参与分类所以类别数固定为13。牌面字母类别ID备注A0Ace21Two32Three43Four54Five65Six76Seven87Eight98Nine109TenJ10JackQ11QueenK12King2.1.1 数据集目录结构与data.yaml拿到这类数据集后第一步是把它整理成ultralytics默认识别的目录树。我一般会按下面的结构摆放cards_dataset/ ├── data.yaml ├── train/ │ ├── images/ │ └── labels/ └── val/ ├── images/ └── labels/data.yaml是这个数据集和YOLO v11之间的接口文件内容如下train: ./train/images val: ./val/images nc: 13 names: [A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K]nc必须与names列表长度一致顺序一旦确定就不要在训练中途改。如果原始数据集的标注文件里出现了“红桃A”“黑桃K”这类带花色的标签名需要先过滤或统一映射否则LabelError会在训练启动阶段直接报出来错误信息会告诉你“Label class X exceeds nc13”。2.1.2 批量转换标注时我用的Python脚本很多公开数据集不会直接给你YOLO txt而是给JSON或XML。以下是我把JSON转成YOLO格式的常用脚本可以直接套用到手头数据集import json from pathlib import Path from PIL import Image def convert_annotation(json_path: Path, output_dir: Path): 把JSON标注像素坐标转成YOLO txt。 假设JSON结构为: { image: img_0001.jpg, boxes: [ {label: A, x1: 120, y1: 170, x2: 210, y2: 260} ] } label_to_id {A: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, 10: 9, J: 10, Q: 11, K: 12} with open(json_path, r, encodingutf-8) as f: data json.load(f) img Image.open(json_path.parent / data[image]) w, h img.size lines [] for b in data[boxes]: cls_id label_to_id.get(b[label]) if cls_id is None: continue # 跳过非A-K的花色标记 x_center (b[x1] b[x2]) / 2 / w y_center (b[y1] b[y2]) / 2 / h box_w (b[x2] - b[x1]) / w box_h (b[y2] - b[y1]) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) output_dir.mkdir(parentsTrue, exist_okTrue) (output_dir / (data[image][:-4] .txt)).write_text(\n.join(lines), encodingutf-8)这个脚本先读原图尺寸再把像素框换算成归一化坐标。关键点在于cls_id的映射表如果你手里的数据集把“10”放在“9”和“J”之间ID顺序要保持一致。另外输出的小数位保留6位就够YOLO读取时对第7位之后的多余精度不敏感但少于4位会明显掉边框精度。2.1.3 标注质量检查把框画回图上数据集标注“看着能用”和“真的能用”是两件事。我拿到1850张牌的标注后不会直接开训而是先把txt画回图片上做一遍人工抽查这一步能迅速发现坐标归一化错误、文件错位、框体偏移到牌面外等问题。import cv2 def check_label(img_path: str, label_path: str): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: 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, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imwrite(check.jpg, img)对扑克牌这类高对比度目标如果框中心落在牌面纹理而非字母上模型的分类头会学到错误特征。边框最好框住整个牌面或单独框住角落字母两者不能混用否则同类别的正样本外观差异过大训练时要花更多epoch才能收敛。2.2 小数据集下如何划分train/val1850张图不算多我一般按85%训练、15%验证来切也就是大约1572张训练、278张验证。不要按整副牌来分要按图片切防止同一张图里的多张牌同时出现在训练集和验证集。具体切分可以直接用ultralytics的val参数指定目录不需要额外写脚本。如果验证集里出现某一张牌面在训练时已经见过验证指标会被明显高估最终上线时正确识别率会掉5个百分点以上。3. 用YOLO v11把扑克牌数据集跑成可识别模型3.1 训练命令与参数选择目录就绪后训练只需要一条命令yolo train datacards_dataset/data.yaml modelyolo11n.pt epochs100 imgsz640 batch16 device0这里modelyolo11n.pt用的是YOLO v11的nano预训练权重COCO预训练权重能加速收敛比随机初始化更容易在小数据集上稳定。imgsz640与数据集原始分辨率匹配多数牌面图本身就在640附近不需要强行放大到1280。参数推荐值说明modelyolo11n.pt13类小任务nano足够imgsz640与图片分辨率一致epochs100~1501850张图为基准100轮后可看曲线batch16或32显存不够就降为8workers8数据加载线程数按CPU核数调cacheTrue小数据集直接预加载到内存显著降训练时间我一般会加cacheTrue1850张图完整缓存到内存也就一两GB读取速度提升明显。batch在单卡16G显存下设为16比较稳。device0指定单GPU训练没有GPU时改为devicecpu但12类小任务也会慢好几倍建议先用Google Colab或租卡跑通。3.1.1 为什么从yolo11n.pt而不是yolo11s.pt开始扑克牌识别不属于细粒度分类不需要大模型容量来区分相似物种。A到K之间虽然有易混淆对但整体特征差异足够大nano模型已经能把mAP50推到0.98以上。大模型在这个数据量下更容易过拟合训练日志里会出现train loss继续降、val loss抬头的典型曲线。如果你手里的显卡有余量可以先跑一版nano作baseline再用modelyolo11m.pt对比但不要指望精度有质变。3.2 训练过程中的Loss与指标怎么看训练启动后ultralytics会在runs/detect/train/下生成results.csv和results.png。你需要盯着四个指标train/box_loss、train/cls_loss、metrics/mAP50和metrics/mAP50-95。对扑克牌识别box_loss会在前20个epoch快速下降30轮后进入平台期。cls_loss是重点A和4、J和1这类易混淆字母会让它降得比其他任务慢。mAP50如果稳定在0.98以上说明检测头已经能准确定位牌面mAP50-95在0.9附近就算健康因为牌面框和真实框重叠度很高框精度对识别率影响不大。训练结束后验证集评测命令是yolo val modelruns/detect/train/weights/best.pt datacards_dataset/data.yaml splitval输出结果里重点看Class列精度。真正决定“正确识别率98.7%”这个数字的不是总体mAP而是每个字母类别的加权准确率所以必须看到A、K、Q这些单类的precision值。3.2.1 训练500轮不如先检查数据有人看到数据集小就盲目加epochs从100加到300甚至500。实际上扑克牌识别跑到150轮后val指标基本不再变化继续训练只会增加过拟合风险。我遇到这类数据集更倾向于在训练前把数据增强参数调好再决定epoch数。数据增强对牌面识别的影响下一章详细展开。4. 把98.7%正确识别率做实牌面字母的边界与增强4.1 “牌字母识别”和“纸牌检测”是两回事标题里写的是“可识别A-K所有的牌字母”不是“识别整副牌的类别”。两者的标注粒度不同如果是检测整张牌一个图里一张牌只有一个框如果是识别字母一张牌上通常有多个字母左上角和右下角各一个且字母框比牌面框小得多。如果数据集提供的是整牌框标注模型学到的是“整张牌长什么样”换个背景或牌面角度就掉点。我一般处理扑克牌数据集会优先采用字母级标注至少也要让训练框覆盖到牌面中央的大字母。标题里这个数据集标注的是牌字母说明原始设计者已经帮你把粒度控住了。4.2 A-K字母识别的三个典型坑4.2.1 A与4在反光下的混淆红桃A和红桃4都有明显的三角和竖线结构当牌面有反光、阴影或拍摄角度倾斜时A的内部横线会被高光吃掉模型就会给出4的预测。解法不是换更大模型而是在训练时加入轻微旋转和亮度扰动。# 推荐的增强参数组合 yolo train datacards_dataset/data.yaml modelyolo11n.pt epochs100 imgsz640 \ hsv_h0.02 hsv_s0.5 hsv_v0.3 fliplr0.5 flipud0.5 scale0.3hsv_v0.3模拟光照变化scale0.3让模型适应牌在不同距离下的大小变化。flipud0.5很关键扑克牌上下旋转180度后仍然是有效的牌面如果训练时不允许上下翻转推理时遇到倒置牌就会漏检或错检。4.2.2 J、Q、K这类带人像的牌占用更多像素J、Q、K在牌面中央有人像角落字母只占全图很小比例。如果标注目标是“牌字母”边框过小会导致下采样后语义信息丢失模型经常把J检出但把角落的J字母漏掉。我常用的做法是让标注框适度外扩比如在原始字母框基础上扩展20%像素保留一点背景上下文分类准确率反而更高。4.2.3 倒置牌与flip增强的关系这里有个容易踩的坑YOLO的fliplr和flipud会同时翻转标注框的坐标ultralytics会自动处理不需要你手动镜像数据。但如果你用了真值增强比如自己写离线翻转脚本就必须同步翻转坐标否则训练时会报“box size out of bounds”或更隐蔽的错位框。4.3 训练集只有1850张过拟合怎么压1850张图对13类识别不算富裕尤其是Q和K这类样本数量天然偏少的牌。我一般用两种手段压制过拟合一是mixup0.2让模型看到牌面之间的混合图像二是把close_mosaic设为10让mosaic增强在最后10个epoch关掉避免增强分布和真实分布差距过大导致val波动。yolo train ... mosaic1.0 mixup0.2 close_mosaic10在小数据集上这两个参数的收益比调任何网络结构都明显。训练完成后用yolo val输出混淆矩阵runs/detect/train/confusion_matrix.png能直接告诉你谁和谁互相打结。5. 验证98.7%识别率的两个硬指标与一个部署技巧5.1 用每个字母的准确率而不是只看mAPmAP是检测任务的标准指标但它混合了定位和分类的误差。拿标题里的98.7%来说用户想知道的是“图里出现一张牌模型有没有读对上面的字母”这更接近classification accuracy。我在验证阶段会把val集的预测结果和人工标注做逐框匹配IoU0.5且类别一致才算一个TP然后单独统计每个字母的precision、recall和F1。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(sourcecards_dataset/val/images, save_txtTrue, conf0.25) # 解析results按类别统计 from collections import defaultdict stats defaultdict(lambda: {tp: 0, fp: 0, fn: 0}) for r in results: for box in r.boxes: cls int(box.cls[0]) conf float(box.conf[0]) # 假设计数逻辑这里需要结合GT做匹配完整脚本略 if conf 0.25: stats[cls][tp] 1 # 实际需与GT比对实际统计时我会把每个预测框和真值框做IoU匹配避免光靠置信度自欺欺人。脚本不长但能输出一张表格每个字母的precision、recall、F1再把13个类别的准确率按样本数加权平均得到和标题里的98.7%可对比的数字。5.2 导出ONNX后在部署端设双阈值验证通过后把best.pt导出为部署格式yolo export modelruns/detect/train/weights/best.pt formatonnx opset12ONNX在部署端的推理速度比PyTorch快不少而且可以脱离训练框架运行。导出后建议在推理代码里做双阈值判断置信度大于0.9的预测直接采用0.5到0.9之间的预测进入二次确认。二次确认可以是对同一张牌裁出区域再做一次字母分类也可以是简单的“连续两帧结果一致才生效”。这个技巧尤其适合实时视频流场景能明显压制A与4、J与1这类偶尔闪烁的误判。5.3 牌面角度修正把78.6%的旋转牌拉回到正确轴线最后说一个实拍场景里最影响识别率的点牌不会端端正正放在桌上。桌面摄像头拍到的牌经常带45度、90度甚至任意角度旋转模型虽然对旋转有一定鲁棒性但超出30度后字母形变严重漏检率上升。我的做法是在检测结果上加一个角度修正步骤先用YOLO定位整张牌的粗略位置拿到四个角点用最小外接矩形求旋转角以牌中心为轴做仿射变换把牌转正转正后的小图再送入模型做字母分类。这一步代码量不多但能直接把摄像头俯拍场景下的正确识别率推高两到三个点。配合上面第5.2节的双阈值整体识别流程已经接近标题所描述的98.7%水平。如果试完发现某些特定字体或特殊牌背仍然误判优先去检查对应类别的数据增强参数而不是立刻换更大的模型。本文还有配套的精品资源点击获取
网站建设高端定制企业官网