恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLOv10扑克牌识别实战:数据集制作、模型训练与UI系统构建
首页
资讯中心
/
YOLOv10扑克牌识别实战:数据集制作、模型训练与UI系统构建
YOLOv10扑克牌识别实战:数据集制作、模型训练与UI系统构建
发布时间:2026/10/20 20:52:12
1. 项目定位与方案选型扑克牌识别到底在解决什么问题1.1 54类目标检测任务的核心难点最早想碰这个项目是因为朋友组了个局说能不能做个东西拿手机对着牌桌一拍就能自动读出每个人手里是什么牌。听起来像博弈作弊工具但正经的落地场景根本不是作弊而是棋牌教学复盘、牌局记录、魔术表演辅助、甚至桌游直播时的自动解说。把用途定在合法合规的方向这个项目就很有含金量。扑克牌识别从技术上看是一个典型的细粒度目标检测任务。标准扑克牌除去大小王有52张牌每张牌由点数花色组成再加上两张王一共54个类别。和常规分类任务不一样分类任务只要判断这是一张牌检测任务得同时输出牌在图像里的位置和它是哪一张牌这两个信息。难点在于红桃3和红桃8在牌面不够清晰的时候视觉特征非常接近尤其当牌处于倾斜、部分遮挡、强反光状态很多模型会翻车。在实际训练中还有一个隐藏难点——类间不平衡。如果你只是随手拍一堆牌局照片会发现能组成顺子的大牌经常出现某张偏门小牌很少露脸。直接拿原始数据训练模型对常见牌很敏感对冷门牌会漏检。所以数据集的构建策略决定了这个项目的上限后面我会专门讲。再说项目的交付形态。标题里提到的UI界面Python项目源码模型意味着这不是一个跑在命令行里的玩具而是一个带图形界面的完整软件。用户不用懂代码打开界面、选张图片或调起摄像头就能看到每张牌被框出来并且标好名称。这个定位非常像工程落地项目而不是算法demo所以在系统架构上要考虑的东西也会比纯训练更多。1.2 为什么选YOLOv10做模型底座YOLO系列目前已经迭代了很多版本为什么要选YOLOv10而不是大家更熟的YOLOv8或者YOLOv5我的答案很直接YOLOv10把NMS后处理去掉了让推理链路更简洁也更适合工程项目里二次开发。传统YOLO模型在推理时会输出一堆冗余的候选框然后要跑一遍非极大值抑制NMS来去掉重复框最后留下最可信的那个。这个NMS过程不复杂但会引入额外的计算开销而且框的置信度阈值需要人工调调不好就会出现漏框或者一个目标两个框的尴尬情况。YOLOv10提出了一致的双分配策略从结构上绕开了NMS推理时直接输出最终框。实际体验下来不仅速度更快代码里也少了一段容易出问题的后处理逻辑边界框的稳定性明显更好。除此之外YOLOv10还有一些很实用的结构改进。比如它的骨干网络和颈部设计在不同尺度下都做了优化对牌面这种中等大小、目标密集、边缘清晰的目标兼容性很好。扑克牌经常在画面里多张重叠每张牌面积占比不大YOLOv10的多尺度特征融合能力能保证小牌面不被漏掉。它的轻量级版本s/m系列在CPU上也能勉强跑配上独立显卡就是完全实时。还有一点YOLOv10的生态非常成熟。HuggingFace、Ultralytics等社区体系都直接支持训练脚本、数据集格式转换工具、调参样例都很全。一个新手只要能把环境装好把数据准备好跟着官方样例改几行参数就能出第一版模型。从这个角度看选YOLOv10不是因为它最前沿而是因为它在效果、速度、生态、易用性四个维度上对扑克牌识别这个项目来说性价比最高。2. 数据集制作全过程从采集到标注的一条龙细节2.1 数据采集方式与规模规划做目标检测很多人第一个问题都是我得准备多少张图片。这个没有一个标准答案但我自己跑下来的经验是单类目标不低于500个实例全部类别总量控制在3万到5万个标注框效果会比较稳。对于54类扑克牌如果平均每张图片出现4到6张牌那准备6000到8000张图片就差不多。采集方式我推荐三种混合着来。第一种是拍摄真实牌局照片这是最接近实际使用环境的数据。把牌摊在桌上、拿在手里、放在不同背景前用手机多角度拍重点拍局部遮挡和牌面倾斜的画面。第二种是单张牌逐一拍摄把54张牌分别拍正视图保证每一类的样本量都均衡这一步是为了后面做数据均衡用的。第三种是合成数据利用图像处理库把牌面贴图随机旋转、缩放、叠加到各种背景上。合成数据可以大量补充罕见牌出现的频率效果出乎意料地好。在拍摄真实照片时有一个很容易被忽视的点分辨率与牌面大小的比例。如果你用很高像素的手机拍摄牌在画面里占比却很小缩放到模型输入尺寸后牌面细节基本就糊了。尽量让牌面占据图片宽度的20%以上否则标注的时候你会发现框被画得非常大但模型实际看到的有效纹理很少。2.2 标注工具与标签规范标注工具我用的是LabelImg和LabelStudio都支持YOLO格式导出。YOLO格式的标注文件是一个txt文件每行五个数字类别ID、中心点x坐标、中心点y坐标、框宽度、框高度。这些数值全部要归一化到0到1之间而不是用像素坐标。很多人第一次标注会栽在这里导出后发现训练读不到框多半是归一化没做对。扑克牌的类别定义需要仔细设计。54类按点数花色组合来定比如0代表红桃A1代表红桃2一直到黑桃K最后加两个王。这里要注意同一点数不同花色的牌是不同类别不能把所有A归成一类否则系统只能告诉你这是一张A而无法告诉你是红桃A还是黑桃A这在很多应用场景里是不够的。如果业务上只需要识别点数不关心花色那可以不区分花色但经典的扑克牌识别系统都是按54类处理。标注的时候边界框要贴着牌面的四个边尽量不包含背景。扑克牌虽然有圆角但框是矩形稍微收进一点圆角区域即可。不要为了省事把框画得比牌面大一圈那样模型会学偏把背景纹理也当成牌的一部分推理时出现框比牌大的问题。标注文件里还有一个容易踩的坑图片里如果有一张牌被另一张挡住了大部分只露出一条边这时候标不标我的建议是被遮挡面积超过50%的牌不要标。模型会从半个牌面里学到错误的特征反而造成误检。漏标一张牌的结果是它成了负样本的一部分让模型更倾向于在该位置不预测这个代价远比少训练一张图要大。2.3 数据增强的实用组合YOLOv10在训练时默认会做数据增强比如随机翻转、马赛克、色彩抖动等。但扑克牌识别和通用目标检测不一样牌面的字符特征对翻转非常敏感。比如红桃牌的花色方向、点上图标的排列顺序水平翻转之后在视觉上是合理的变化但垂直翻转会让牌变得很奇怪。建议训练时把垂直翻转关掉flipud0水平翻转可以保留因为牌横向翻转后类别的语义基本不受影响。除了内置增强我额外做了两个自定义处理。第一是多尺度缩放。训练时把图片随机缩放到0.5到1.5倍让模型适应牌在画面中不同的大小。这个增强对扑克牌特别重要——场景里牌有时离镜头近有时远如果模型只在某一尺度上见得多测试时换个距离就漏检。第二是HSV颜色扰动。把色调、饱和度、亮度的扰动范围适当加大。黄绿牌桌、夜晚黄光、手机屏幕的冷色光不同场景色温差异很大颜色扰动能让模型更依赖牌面的纹理结构而不是整体色调来识别。但注意饱和度扰动别太狠否则红色和花色会混在一起。数据集的目录结构也按YOLO惯例组织datasets/ cards/ images/ train/ # 训练集图片 val/ # 验证集图片 labels/ train/ # 对应标签txt val/图片和标签要同名同basename后缀不同。划分比例建议train:val9:1不用单独划test因为项目最终会用真实拍摄的图片做外部验证。划分的时候一定要按图片级划分而不是按框级划分避免同一张牌的不同截图同时出现在训练集和验证集中导致验证指标虚高。3. 训练环节完整复盘YOLOv10配置、命令与调参记录3.1 环境搭建与依赖版本注意事项YOLOv10的训练环境我建议用Python 3.10PyTorch 2.0以上的版本。在安装依赖的时候很多人图省事直接pip install ultralytics这会装成最新版而YOLOv10的官方代码库和Ultralytics的接口差异不小。建议使用YOLOv10官方GitHub仓库安装这样才能稳定复现训练结果。显卡显存直接决定batch size。我训练时用的单卡16GB显存选的yolov10s模型batch size设为16加上默认的约640分辨率输入训练大概3到4个小时就能收敛到不错的效果。如果你的显卡只有8GB显存batch size降到8或者4效果依然可以只是训练时间会长一些。扑克牌识别不是特别复杂的任务网络深度太深反而容易在细分类别上产生过拟合所以单卡完全够用。安装完成后先跑一个检测脚本输入一张测试图确认环境能正常推理再开始训练。这一步是为了隔离环境问题和训练问题别一上来就开训练跑了两小时报错才发现是opencv版本冲突。3.2 数据集格式转换与目录结构YOLOv10非常依赖统一的目录结构如果你的数据来自不同工具一定要先做格式转换。我用LabelImg导出的就是YOLO格式相对省事。如果是LabelStudio导出的COCO格式JSON文件需要用脚本把COCO转成YOLO格式遍历每张图片的标注信息读取每个框的坐标计算中心点和宽高再归一化到0~1。转换脚本没什么特殊的但要注意类别的ID顺序必须和训练配置文件保持一致。很多人栽在这里COCO格式里类别ID从1开始YOLO格式类别ID从0开始差一个数字训练出来的模型分类全部错位。转换完成后一定要写个脚本随机抽样几张图片把框画回图片上人眼检查一遍再开始训练。在写YOLOv10的数据配置文件cards.yaml时内容是path: datasets/cards train: images/train val: images/val nc: 54 names: 0: HA 1: H2 2: H3 ... 53: JOKER2names列表顺序就是类别ID顺序严格一致。如果names里写错了顺序前面标注的工作就全白费了。3.3 关键训练参数与调优经验训练命令我一般这样写python train.py --data cards.yaml --weights yolov10s.pt --img 640 --epochs 200 --batch 16 --device 0有几个参数值得细说。epochs设200但不是所有200都有用。我加了一个早停机制当val的mAP连续30个epoch不涨就停止训练。扑克牌本身特征相对简单通常50到80个epoch就已经收敛跑满200会有严重的过拟合风险。如果你发现训练集loss在降、验证集mAP在降那八成是过拟合了早点停下来。输入尺寸img设为640是性价比最高的选项。有人觉得图片分辨率越大牌面细节越多就把img调到1280。实测下来1080p的视频图像缩放到640牌面信息已经足够调到1280之后显存占用翻倍、推理速度明显下降但mAP的提升不到1个百分点。除非你的牌特别小或者密集度特别高否则640完全够用。NMS-free模式带来的额外调参项是conf阈值。因为没有了NMS后处理最终输出框就是模型的直接预测conf阈值对结果的影响更直接。训练时不涉及这个参数但推理时尽量先用conf0.25跑一版查看误检率再逐步提高。扑克牌这种目标纹理清晰类别差异性大最终在0.3到0.4之间通常是最佳区间太高会漏检低亮度的牌。训练完以后评估指标我会同时看mAP50和mAP50-95。扑克牌检测项目里mAP50通常都到0.98以上而mAP50-95更能反映框的精确定位能力。正常情况mAP50-95应该达到0.85以上如果只有0.7左右说明框定位不够准需要检查标注框是否贴边、数据增强是否过度。4. 系统架构与UI交互设计从模型到可操作软件4.1 整体架构与Python技术栈训练出模型只是第一步把模型包装成一个带界面、能被非技术人员使用的软件才是项目完整交付的关键。我采用的架构是单机版Python桌面应用组件包括本地推理引擎、UI界面层、数据输入输出模块、模型管理模块。不依赖网络不用部署服务端开箱即用的体验对这个项目来说最友好。技术栈方面UI我选了PySide6即Qt的Python绑定。其实也可以用Tkinter或者PyQt5但PySide6的许可证更友好控件风格更现代搭配OpenCV做图像显示也方便。推理引擎就是刚才训练好的YOLOv10模型加载方式采用直接Python调用因为都是Python生态耦合度最低。图片的读取和预处理用OpenCV结果框的绘制和展示也交给OpenCV完成。整体流程是这样的用户选择图片识别或摄像头识别图片/视频帧进入预处理模块缩放到640做letterbox填充模型推理输出框坐标、类别ID、置信度显示模块在原始图像上绘制检测框和类别文字结果面板输出检测到N张牌红桃A、黑桃K、方块10...这个流程看起来简单但每一步都有不少工程细节。比如letterbox填充模型输入是正方形640而实际图片一般是16:9或4:3直接拉伸会改变牌面的宽高比例导致检测精度下降。正确做法是等比缩放后在两侧或上下补灰边这就是letterbox。推理解码时框坐标也要按照同样的等比关系映射回原图。很多新手把框画歪多半就是忘了这一步的坐标逆变换。4.2 UI界面设计与交互逻辑界面设计我参考了主流图像标注工具的思路分成三个区域左侧主画布区显示原始图像和检测结果支持鼠标滚轮缩放右侧结果列表区列出每一张被检测到的牌显示类别、置信度、坐标信息顶部工具栏文件选择、摄像头切换、置信度滑块、导出结果按钮置信度滑块是大多数人忽略但非常实用的功能。推理出来的框会随阈值变化实时刷新用户可以根据当前环境光照情况直接调整不需要改代码。这比固定阈值在真实场景中更可靠因为白天阳光下的牌和晚上暖光灯下的牌模型输出置信度分布差异很大。针对摄像头实时识别界面里我额外加了一个FPS显示。这对调优来说非常重要——当你改动了某个模块比如加了预处理、换了推理后端立刻就能从FPS变化看出性能影响。YOLOv10在GPU上跑640输入单帧推理大约10-20毫秒加上界面绘制30FPS甚至更高都没问题。如果用的是CPU帧率会掉到10FPS以下这时建议把输入尺寸降到320或者使用模型的更小版本。结果导出我支持了两种格式CSV表格和JSON。CSV适合直接做统计分析JSON方便对接其他程序。每一条结果包含图片名、牌类别、置信度、框坐标这样一个牌局的复盘数据就能完整留存。这个功能在棋牌教学场景里特别好用一局打完自动生成全部出牌的时序记录。4.3 推理性能的工程优化模型训练完是.pt格式直接用来推理没问题但推理速度还有优化空间。我在优化推理性能时做了三件事。第一半精度推理。PyTorch支持FP16在GPU上把模型权重转为半精度推理速度几乎翻倍精度损失可忽略。扑克牌检测的纹理特征不受影响代码上只需要在加载模型后加一句model.half()。第二批处理与缓存。处理大量图片时不要一张一张循环把图片路径列表一次性传入模型设一个batch size比如32这样GPU利用率会高很多。如果只是处理单张图片或者视频流这个优化就不适用了。第三摄像头帧的异步读取。摄像头读取是I/O操作如果不做异步处理就会阻塞推理线程导致帧率不稳。我用了Python的队列一个线程专门读帧、一个线程专推理、主线程只负责绘制和响应界面这样就算推理偶尔慢了一帧画面也不会卡死。性能优化的原则是先保证输出结果稳定再谈加速。不要一上来就做一堆花哨的优化结果钻进去出不来了。先把基础流程跑对帧率低一点没关系功能完整之后再去优化效率最高。5. 实测排错记录检测效果与常见问题的整改历程5.1 重叠牌与遮挡场景的处理第一批模型训练出来在正经摆拍的牌局上效果不错一到实战就原形毕露。最典型的问题就是重叠牌。几个人在牌桌上牌不会摆得整整齐齐经常是几张贴在一起露出个角。YOLO模型对密集目标的预测本质上依赖于锚点或者查询在空间上区分不同目标。YOLOv10的去掉了NMS理论上更利落但面对密集重叠时它的检测头依然需要高分辨率的特征图来区分相邻目标。我的实测结论是两张牌重叠面积小于30%时检测成功率超过95%重叠面积超过50%误检率急速上升。这个问题无法完全靠模型解决得从两个层面入手。一是数据我在真实牌局里大量采集了露角的牌面照片专门标注那些露出面积够大、能够辨识的牌增强模型对小面积目标的敏感度。二是工程逻辑在UI界面里允许用户框选区域或者设置最小框面积过滤把面积过小的候选框直接剔除。破损框的置信度本来就不高面积过滤能有效减少这类误报。还有一点很有意思牌堆侧面的轮廓也就是牌垛的厚度边缘经常被模型检测成一张牌。这是因为侧面的纹理和牌面真有些相似。我最后用了置信度惩罚策略——所有候选框的长宽比超过正常扑克牌的合理范围比如大于1.8在输出时把置信度打八折。这样虽然不能彻底消除但至少不至于影响主要结果。5.2 花色数字混淆的典型误检第二个大坑是花色和点数识别混淆。这是细粒度分类任务的高频问题典型的错误有红桃Q识别成方块Q、黑桃A识别成黑桃4。做错因分析时我把错误样本拿出来看发现集中在这几种情况。第一牌面反光。牌是塑料或覆膜的灯光一照反光区域刚好落在花色图标上图标的颜色和形状都变了模型分不清红桃和方块。第二低分辨率。牌的像素在图像里太细小网络无法提取到足够的花色细节就只能靠猜测。这时候模型很容易把在训练集里常见的那类花色作为预测结果导致某些花色误检率偏高。针对第一个原因我在数据增强里刻意增加了随机亮斑——在图像上随机画几个高亮的半透明圆斑模拟反光效果。针对第二个原因我增加了一批低分辨率牌面的训练样本把牌面缩到原来一半或者更小让模型看到不清晰的牌也得老实输出。这两项调整下来花色识别准确率从94%提升到了98.5%左右剩下的1.5%集中在强光直射的极端场景我认为这是合理水平再往上靠模型本身难度会非常大。如果业务上有硬性要求可以换更高分辨率相机拍牌或者从不同角度布光。5.3 光照变化带来的泛化问题项目快收尾时我把系统搬到一个比较暗的房间里测试结果发现推理结果明显变差。看日志发现并不是模型崩溃而是大量牌的置信度掉到了0.2以下。原因是训练集里晴天自然光和室内白光比较多暗环境样本占比太少。遇到这种问题常规做法是重新采集暗光数据再微调。但我当时的增强已经比较完备了还是出现了明显的亮度分布偏差说明真实场景数据的采集不能忽视。增强只能拓宽分布不能替代真实分布的采样。我补拍了一批傍晚和灯光混合照明下的牌局照片加入数据集后微调了大概30个epoch效果大幅回升。微调的时候建议把学习率调低比如默认的0.01降到0.001冻结前几层骨干网络只更新后层的检测头这样既能保留原有的识别能力又能快速适配新的光照分布。这个问题的教训是做项目前一定要先问自己最终用户会在什么环境下用。如果用户主要是在牌桌上用那就直接在牌桌上采集训练数据模拟真实的灯光、背景和桌面纹理。技术人员喜欢在整洁的办公室里测试但实际场景从来不会那么整洁。越早把这种差异考虑到数据集中后面的返工就越少。6. 模型导出与后续扩展方向6.1 导出不同格式的必要性与注意点模型训练完以后原始的PyTorch.pt文件在Python环境中可以直接用但如果你想让系统更轻量、启动更快或想让UI界面与推理分离导出成其他格式就很有必要了。我主要导出了两种格式ONNX和TorchScript。ONNX的好处是跨平台、跨语言。导出之后理论上就可以脱离PyTorch环境用ONNX Runtime来推理这在没有GPU的机器上能显著降低依赖复杂度。导出命令也比较简单在YOLOv10的代码仓库里有现成的脚本。但导出有坑。我第一版导出的ONNX在Python里跑发现结果和.pt模型差很多排查了半天发现是导出时没有固定输入尺寸。如果导出时允许动态宽高OpenCV的预处理和ONNX Runtime的输入张量维度会匹配出问题。我的解决办法是导出时固定输入尺寸为640x640并且把归一化、letterbox的预处理逻辑写成独立的函数在导出和部署两端保持一致。TorchScript导出的意义不太一样。它的特点是能在Python环境下完全复现PyTorch模型的行为同时带来一定程度的推理加速和部署便利性。如果你的UI界面是用PyQt开发同时又不想让用户在部署时补装一整套复杂的依赖那么TorchScript是一个体积小、不用额外装太多运行库的选择。不过说实话在纯Python项目里原生的.pt文件已经足够好用导出其他格式的收益在上了摄像头实时推理之后才会体现出来。6.2 项目可延伸的玩法扑克牌识别这个项目的天花板很高目前做出来的只是基础检测后面有几个方向非常值得扩展。一个是牌局时序分析。检测出每一帧的牌面还不够加上一个时序逻辑模块记录牌出现的顺序、位置变化、停留时长就能自动生成一局牌的过程报告。比如3号位在第一轮出的红桃A第二轮改出黑桃K这个数据在棋牌教学和复盘场景会非常有用。实现方式也不复杂对每帧检测结果做目标跟踪即可不需要重新训练模型。另一个是多摄像头场景融合。牌桌很大一个摄像头拍不全可以用两个摄像头分别拍桌子左右两半再把检测结果按空间坐标合并。逻辑上就是两个独立的检测流跑同样的模型在合并阶段按框的空间重叠度做去重。这样既不会丢失牌面信息又能覆盖更大的牌桌面积。还有一个方向是把模型量化到更小的体积。比如用TensorRT在NVIDIA显卡上做推理加速或者把模型压缩后部署到手机上。扑克牌检测对算力要求不算高手机端的YOLOv10n模型跑起来完全可行这样就能做一个随时随地识别牌面的移动端App。不过移动端的UI设计和权限问题又是一套新工程得单独规划。从做完整个项目的经验来看YOLOv10在扑克牌识别上确实是目前综合体验最好的方案之一。它把检测速度、定位精度和工程生态都平衡得比较好最让我满意的是去掉NMS之后代码逻辑真的清爽很多排查问题时少走了很多弯路。如果自己动手做这个项目我建议不要把目标一下子定太高先做一个能跑的版本把数据、训练、UI的主链路走通再逐步迭代优化。牌面识别这种项目往往是数据精细程度带来90%的效果差异模型结构反而不是瓶颈。只要把数据集质量打磨到位系统最终的表现不会让你失望。