
想象这样一个场景:牌局进行中,你打开手机 APP,把摄像头往牌桌上一架,屏幕上就实时标出每张牌的位置与牌面------谁在第几个拿到牌、桌上现在有几张牌、每家手里是什么牌型,全部自动识别并在 UI 界面上整理得清清楚楚。这不是后期剪辑特效,而是一个典型的移动端实时计算机视觉项目:摄像头采集、目标检测、牌面字符识别、多目标时序跟踪、规则引擎判定,最后落到一个流畅的手机界面上。
这件事听起来简单------"不就是认个牌吗",但真正动手做会发现它是一条环环相扣的技术链:斜着拍的桌面要先矫正,运动中的牌要检测,叠在一起的牌要分开,角标上又小又花的点数花色要读准,发牌的先后次序要靠跟踪而不是单帧猜测,最后还要把一堆离散的牌翻译成"对子、顺子、同花、葫芦"这样的牌型。任何一环掉链子,最终 UI 上显示的结果就是错的。
本文完整拆解这样一个"摄像头读牌 + 牌型输出"APP 的技术实现:从需求拆解、总体架构,到桌面透视矫正、牌检测、牌面识别、发牌跟踪、牌型规则引擎,再到移动端NCNN/TFLite 部署和UI 交互,并专门讨论遮挡、反光、叠牌等真实难题的解法。适合移动端算法工程师、计算机视觉学习者和棋牌类产品开发者参考。
一、需求拆解:系统要同时完成三件不同的事
把用户的一句话需求拆开,其实包含三个层次完全不同的视觉与逻辑任务,必须分别设计、再串联起来。
|---------------|----------------------------------------|-----------------------|-----------------------|
| 需求 | 本质任务 | 技术范畴 | 输出 |
| 识别发牌次序(牌的数量) | 逐帧检测牌、跨帧跟踪同一张牌、记录首次出现时刻、归属玩家 | 目标检测 + 多目标跟踪 + 时序状态管理 | 发牌顺序序列、各家牌数、桌面牌数 |
| 识别牌面信息(牌面内容) | 读出每张牌的点数(A、2-10、J、Q、K)与花色(黑桃、红桃、梅花、方块) | 角标区域定位 + 字符/图案分类识别 | 52 张标准牌之一(如红桃 A、黑桃 K) |
| 输出各家牌型(UI 界面) | 按玩法规则把一组牌归类为合法牌型并排序展示 | 规则引擎 + 移动端 UI 渲染 | 各家牌型标签、牌面列表、统计信息 |
这三件事的难度曲线很不一样:牌面识别是"看得清就能认"的感知问题,发牌次序是"必须结合时间维度"的跟踪问题,牌型输出则是纯逻辑的规则问题。把三者混在一个模型里端到端解决既不现实也不经济,正确的做法是分层解耦------视觉层负责"看到了哪些牌",逻辑层负责"这些牌意味着什么",表现层负责"怎么展示给用户"。
还要先界定两个边界。其一,标准扑克牌共 52 张(不含大小王为 52,含则 54),点数 13 种、花色 4 种,类别空间封闭且有限,这是一个"闭集识别"问题,比识别万千真实物体简单,识别准确率可以做得很高,公开方案在测试集上达到 98% 以上并不困难。其二,本文讨论的是"摄像头实拍真实桌面"的方案,它和"截取游戏 APP 屏幕做记牌器"是两条技术路线------后者画面规整、可用模板匹配,前者要处理真实光照、透视、遮挡,难度高得多,也是本文重点。
二、总体架构:一条从光子到 UI 的处理链
整套 APP 可以划分为六个串行、局部并行的处理阶段,每一阶段的输出是下一阶段的输入。

读牌 APP 技术链路:视频采集 → 桌面透视矫正 → 牌检测与跟踪 → 牌面识别 → 牌型规则引擎 → UI 渲染输出。
|----------|-------------------|--------------------------|----------|
| 阶段 | 职责 | 关键技术 | 运行频率 |
| 1. 视频采集 | 调起摄像头、取预览帧、自动对焦曝光 | CameraX/AvFoundation | 30FPS |
| 2. 桌面矫正 | 定位桌面、四点透视变换为正俯视 | 边缘/角点检测、Homography | 每帧或低频缓存 |
| 3. 检测跟踪 | 定位每张牌、分配稳定 ID、计数 | 轻量 YOLO + ByteTrack/SORT | 10-30FPS |
| 4. 牌面识别 | 角标 ROI 内识别点数与花色 | 两阶段分类 / 端到端 52 类检测 | 新牌出现时触发 |
| 5. 牌型引擎 | 按玩法归类牌型、归属各家 | 计数排序 + 规则匹配 | 结果变化时 |
| 6. UI 渲染 | 叠加检测框、牌型卡片、统计面板 | Canvas/Compose/SwiftUI | 跟随刷新 |
这里有一个重要的工程优化:牌面识别不必每帧都对每张牌跑一遍。检测跟踪是高频的(保证画面跟手),但一张牌一旦被识别确认,就把结果绑定到它的跟踪ID 上缓存,后续帧只做位置更新,不再重复识别;只有当新ID 出现(新发了一张牌)时才触发识别。这样能把最耗算力的识别调用量降低一个数量级,是移动端实时流畅的关键设计。
三、桌面透视矫正:先把 " 斜着拍 " 变成 " 垂直俯拍 "
人手持手机不可能垂直正对桌面,拍出来的牌桌一定是近大远小的梯形,牌是倾斜的平行四边形。如果直接在这种畸变画面上识别,角标会被压缩变形,识别率明显下降。因此第一步是几何矫正。
3.1 两种矫正策略
第一种是桌面自动检测:通过边缘检测找到绿色桌布区域(桌布颜色单一,可在 HSV 色彩空间用颜色阈值快速分割),提取最大四边形轮廓,得到桌面四个角点;第二种是引导式四点标定:在APP 界面上显示四个可拖拽的角标,让用户首次使用时手动对准桌面四角,简单可靠且对复杂背景更鲁棒。实际产品通常两者结合------自动检测给出初始四角,用户可手动微调。
3.2 透视变换数学原理
拿到原图四个角点和目标俯视图四个角点后,用求解单应性矩阵(Homography,3×3 投影变换矩阵)的方式,把倾斜桌面整体"掰正"为标准矩形俯视图。之后所有检测、识别都在这张矫正图上进行,坐标再反变换回原图用于UI 叠加。透视变换同时归一化了尺度------矫正后一张牌的像素尺寸基本固定,为后续角标 ROI 裁剪提供了稳定基准。
3.3 玩家区域划分
矫正后的俯视图还有一个重要用途:划分各家的牌区。在俯视图上用固定比例或用户标定,画出"上家、对家、下家、自己、公共区/出牌区"等若干感兴趣区域(ROI)。这样检测到一张牌后,看它的中心点落在哪个ROI,就能直接判定它归属哪家,为后续"输出各家牌型"和"发牌次序"提供空间依据。区域划分用一次标定、长期缓存,不必每帧重算。
四、牌的检测与定位:端到端还是两阶段
牌面识别的前提是先找到牌。工程上有两条主流路线,选型取决于对速度、精度和扩展性的权衡。
4.1 路线一:端到端 52 类目标检测
直接训练一个检测模型,把 52 张牌当作 52 个类别,一次前向推理同时输出"每张牌在哪(边界框)+ 是什么牌(类别)"。这是目前公开项目最主流的做法,YOLOv5 到 YOLOv12、YOLO26 都有大量扑克牌检测实践,常用数据集包含 2 万张以上图像、覆盖全部 52 类。其优点是流程短、延迟低、工程简单;缺点是类别多(52 类)、每类都要足够样本,且换一副设计风格差异大的牌(不同背面、不同字体)可能需要补充数据重训。
4.2 路线二:检测 + 角点分类的两阶段方案
第一阶段只做"牌/非牌"的单类检测(或用传统 CV 的轮廓检测定位牌四边形),第二阶段裁剪出牌面角标区域,用一个小分类器分别识别点数(13 类)和花色(4 类)。识别结果 = 点数类别 × 花色类别组合而成。这种方案的分类器极小(13 类、4 类),样本需求少、泛化不同牌背更好,且点数与花色解耦后,某一类识别错误更容易定位和纠正;代价是流程多一步、逻辑稍复杂。
|-------|------------|------------------------|
| 对比维度 | 端到端 52 类检测 | 检测 + 角点两阶段 |
| 推理次数 | 一次 | 检测一次 + 每牌小分类 |
| 类别设计 | 52 类 | 1 类检测 + 13 类点数 + 4 类花色 |
| 样本需求 | 每类都需较多样本 | 分类器小,样本更省 |
| 换牌/泛化 | 风格差异大时需补数据 | 角标通用,泛化更好 |
| 工程复杂度 | 简单直接 | 多阶段,逻辑略复杂 |
| 适用场景 | 固定牌具、追求速度 | 多牌具、要求可维护 |
4.3 传统 CV 定位作为补充
在背景干净、牌与桌面对比强的条件下,不一定需要深度检测:灰度化 + 高斯模糊 + 边缘检测(Canny)+ 找轮廓 + 最小外接矩形/四边形拟合,就能把每张牌框出来,再利用扑克牌标准长宽比(约 5:7,即 63mm×88mm)过滤掉干扰轮廓。传统方法零模型、零推理成本,但对光照不均、牌重叠、复杂背景敏感,通常作为深度模型的兜底或在固定俯拍支架场景使用。
五、牌面信息识别:读出点数与花色
定位到牌之后,核心是读懂左上角(和右下角对称)的角标------扑克牌把点数和花色浓缩印在角上,正是为了扇形持牌时只露角标也能读牌,这个设计也成了机器识别的突破口。

倾斜牌面经透视变换矫正后,裁剪左上角角标 ROI,分别识别点数与花色,是最稳的识别路径。
5.1 角标 ROI 裁剪
矫正后的牌面尺寸固定,角标位置也随之固定:取牌面左上角约 15%-20% 宽高的小区域作为 ROI,里面上方是点数字符(A、2-10、J、Q、K),下方是小花色图案。只识别这一小块而非整张牌,能排除牌面中央大图案的干扰,输入分辨率可以集中在角标上,小字符也能看得很清。为了保险,可以同时裁左上角和右下角,两路识别结果交叉验证,不一致时取置信度高者。
5.2 花色识别:红黑分离 + 形状分类
花色识别有一个非常稳定的物理先验:红桃、方块是红色,黑桃、梅花是黑色。先在 HSV 色彩空间做红黑二分,把 4 类问题降为两个 2 类问题------红色组只需区分红桃与方块,黑色组只需区分黑桃与梅花,难度骤降。红黑组内再用形状区分:红桃♥顶部有凹陷、底部尖;方块♦是菱形;黑桃♠像倒心形带柄;梅花♣是三圆簇。用一个 4 类(或两组 2 类)微型 CNN 分类即可,模型只有几十 KB。黑白/灰度摄像头下无法用颜色先验,则直接 4 类形状分类。
5.3 点数识别:CNN 分类为主,模板匹配为辅
点数共 13 类(A、2-10、J、Q、K),识别方式有三种:
|-----------|---------------------------------------------|-------------------------|
| 方法 | 原理 | 优势/局限 |
| 微型 CNN 分类 | 角标二值化后送入 13 类小网络 | 鲁棒、抗字体差异,推荐主用 |
| 模板匹配 | 预置 13 个点数模板,归一化相关系数(TM_CCOEFF_NORMED)比对取最高分 | 零训练、快,但对字体/尺度敏感 |
| OCR | Tesseract 等识别字符 | 通用但对 A/J/Q/K 艺术字体易错,非首选 |
工程上推荐 CNN 为主、模板匹配做兜底:CNN 处理大多数情况,当 CNN 置信度低于阈值时用模板匹配二次确认。注意数字 10 是两位字符、其余是单字符,二值化连通域分析时要把"1"和"0"合并为一个点数,这是一个常见的小坑。
5.4 提升识别鲁棒性的图像预处理
角标送入分类器前做一组标准预处理:灰度化、自适应阈值二值化(应对光照不均)、形态学开运算去除噪点、按红黑特性设计加权灰度化强化对比、尺寸归一化到分类器输入。运动模糊(发牌瞬间牌在动)则通过"等牌静止再识别"来规避------检测框连续两帧位移小于阈值、说明牌已落定,此时才触发识别,从源头避免识别花掉的帧。
5.5 结果置信度与多帧投票
单帧识别可能抖动(这一帧读成红桃 8、下一帧读成红桃 6)。对同一跟踪 ID,在其落定后的连续若干帧里多次识别、做多数投票(或置信度加权融合),得到稳定结果后锁定。这与人类"多看两眼确认"是同一个道理,能把偶发误识别几乎完全滤掉。
六、发牌次序与数量:靠跟踪而不是靠猜
"识别发牌时的次序和牌的数量"是这个项目区别于静态认牌的关键,也是最容易被低估的难点。单帧画面只能告诉你"此刻桌上有什么",只有引入跨帧跟踪和时序状态,才能回答"谁先拿到、发了第几张、现在一共几张"。
6.1 多目标跟踪:给每张牌一个稳定 ID
在检测器之上叠加轻量多目标跟踪算法(SORT/ByteTrack 一类):检测器逐帧输出牌框,跟踪器根据相邻帧框的交并比(IoU)和运动预测,把同一物理牌在不同帧的检测框关联起来,分配一个全局唯一的track ID。于是画面里每一张牌从进入视野到离开,都有稳定身份,不会因为检测框抖动而被反复当成"新牌"。ByteTrack 对低置信度检测框也能二次匹配,在牌部分遮挡、运动模糊时比单纯IoU 匹配更稳。
6.2 发牌次序的记录逻辑
有了稳定 ID,发牌次序就是一个时序事件流:
|---------|----------------------------|-------------------------|
| 事件 | 判定条件 | 系统动作 |
| 新牌出现 | 产生新 track ID 且连续存活 N 帧(去抖) | 记录发牌序号、时间戳、初始位置,加入待识别队列 |
| 牌面落定 | 该 ID 框位移小于阈值、清晰度达标 | 触发牌面识别,结果绑定 ID |
| 归属玩家 | 框中心落入某玩家 ROI | 把该牌计入对应玩家的牌集合 |
| 牌被收回/离开 | ID 连续丢失超过 M 帧 | 标记离场,更新计数(或归档到上一局) |
按"新牌 ID 首次确认"的时间先后排序,就得到完整发牌次序序列(例如:第 1 张→下家黑桃 3,第 2 张→对家红桃 Q......)。连续存活 N 帧才确认新牌的"去抖"设计很重要------单帧误检会凭空制造一张不存在的牌,要求目标稳定出现多帧才能登记,能过滤绝大多数虚警。
6.3 数量统计与一致性校验
牌数统计分三个口径同时维护:桌面总牌数(当前存活 ID 数)、各家牌数(按 ROI 归属计数)、本局累计发牌数(历史 ID 去重计数)。扑克牌是封闭集合,可以用"牌库一致性"做强校验:一副牌 52 张、同一点数花色组合全局唯一,一旦识别出两张完全相同的牌(比如两个黑桃 A),说明其中至少一张识别错了,触发重新识别或提示用户,这是扑克牌识别独有的、非常有效的纠错机制。
6.4 叠牌与扇形持牌的计数难题
真实牌局中牌常常叠在一起:手牌呈扇形叠放、出牌区多张牌互相压边。此时检测框会重叠、角标可能只露出一部分。对策包括:一是专门用重叠/遮挡样本训练检测模型,让模型能检出被部分遮挡的牌;二是利用扇形排列规律------手牌角标等间距露出,沿弧线检测露出的角标数量来计数,比检测整张牌更可靠;三是结合运动信息,发牌是逐张进入的,跟踪"入场事件"比在最终叠放静态图里数牌更准。
七、牌型判定:把离散的牌翻译成规则
识别出一张张独立的牌之后,要由规则引擎把它们组合成"牌型"并按家输出。这部分不涉及视觉,是纯粹的数据结构与算法问题,但它直接决定APP 输出对用户有没有用。不同玩法的牌型体系完全不同,引擎要按玩法插拔。
7.1 统一的牌数据结构
先把识别结果归一化为内部结构:每张牌含点数秩(rank,2-14,A=14)、花色(suit,4 枚举)、检测置信度、归属玩家、ID。牌型引擎只消费这个标准结构,与上层视觉完全解耦,方便同一引擎复用于斗地主、德州、桥牌等不同玩法。
7.2 斗地主牌型判定
斗地主牌型种类多、组合约束复杂,判定思路是"先按张数和点数频次分组,再匹配模式"。先统计每个点数出现几次,得到频次分布,再按下表逐层匹配。
|------------|--------|-------------------|
| 牌型 | 张数 | 组合约束 |
| 单牌/对子/三张 | 1/2/3 | 同一点数分别出现 1/2/3 次 |
| 三带一/三带对 | 4/5 | 一个三张 + 一张单牌/一个对子 |
| 顺子 | ≥5 | 连续单牌,不含 2 和王 |
| 连对 | ≥6 且偶数 | 三个以上连续对子 |
| 飞机(带/不带翅膀) | ≥6 | 两个以上连续三张,可带等量单/对 |
| 四带二 | 6 | 一个四张 + 两张单牌(或两对子) |
| 炸弹/王炸 | 4/2 | 四张同点 / 大小王各一,王炸最大 |
7.3 德州扑克牌型判定
德州扑克从 7 张牌(2 张手牌 + 5 张公共牌)中选出最优 5 张,牌型按强度严格分 10 级。判定算法先枚举 7 选 5 的全部组合(仅 21 种,计算量极小),对每个 5 张组合按"同花标志 + 连续性 + 点数频次"判级,再取强度最高者。
|----|---------|-------------------------------|
| 强度 | 牌型 | 判定条件 |
| 9 | 皇家同花顺 | 10-J-Q-K-A 同花 |
| 8 | 同花顺 | 5 张连续且同花 |
| 7 | 四条 | 4 张同点 + 1 张踢脚 |
| 6 | 葫芦(满堂红) | 3 张同点 + 1 个对子 |
| 5 | 同花 | 5 张同花、不连续 |
| 4 | 顺子 | 5 张连续、不同花(A 可作 1 组 A-2-3-4-5) |
| 3 | 三条 | 3 张同点 + 两张单牌 |
| 2 | 两对 | 两个对子 + 一张踢脚 |
| 1 | 一对 | 一个对子 + 三张单牌 |
| 0 | 高牌 | 不满足以上任何一种 |
7.4 牌型引擎的工程价值
规则引擎要用确定性代码实现(查表、枚举、排序),不要用模型去"猜牌型"------规则是封闭、确定、可穷举的,写死的代码 100% 正确且零算力开销,模型反而会引入不必要的错误。引擎输出结构化结果(牌型名、关键牌、强度排序、各家对比),直接供UI 层渲染。如果产品要进一步做胜率估算、出牌建议,可以在牌型引擎之上叠加博弈AI(如斗地主的DouZero 一类自我对弈强化学习模型),但这属于增值能力,与"识别并展示牌型"的基础需求分层实现。
八、移动端落地:让模型在手机上离线实时跑
算法在服务器上跑通只是开始,真正的挑战是塞进手机、不联网、低延迟、不发烫。这一层决定产品体验。
8.1 推理框架选型
|-----------------|------------------------|-------------------------------|
| 框架 | 平台 | 特点与适用 |
| NCNN | Android/iOS 全平台,无第三方依赖 | 腾讯开源、移动端生态最成熟、YOLO 官方支持导出,首选 |
| TensorFlow Lite | Android 为主,跨平台 | 支持 INT8 量化、GPU/NNAPI 委托,安卓生态顺 |
| MNN | 阿里开源,全平台 | 轻量高性能,国内应用多 |
| Core ML | iOS 专属 | 调用苹果 ANE/GPU,iPhone 上能效最优 |
标准部署链路是:PyTorch 训练 → 导出 ONNX → 转换为 NCNN(.param+.bin)或 TFLite 模型 → 放进 APP 的 assets 目录 → 端侧加载推理,全程离线、无需联网,既省流量又保护牌局隐私。
8.2 模型轻量化三板斧
第一,选小模型。检测主干用 YOLOv8n/nano 级别的超轻量网络,牌面分类用只有几层卷积的微型 CNN,类别少、目标简单,不需要大模型。第二,INT8 量化。把浮点权重量化为 8 位整数,模型体积缩小约 4 倍、推理显著加速,扑克牌这种纹理清晰、类间差异大的目标,量化精度损失很小。第三,输入分辨率适配。检测输入不必追求640×640,桌面矫正后牌占比大,320-416输入往往足够,分辨率降低对移动端帧率提升立竿见影。
8.3 多线程流水线
手机端同样要做异步流水线,避免相机预览被推理卡住:采集线程持续取帧放进有界队列(只保留最新帧、丢弃积压旧帧,保证低延迟);推理线程取最新帧做检测识别;UI线程只负责绘制结果。检测高频、识别按需触发(新ID 才识别)、牌型引擎结果变化时才算,三层频率解耦后,中端手机也能做到预览流畅、结果实时。
8.4 性能与功耗平衡
不必让推理全程满载。牌局大部分时间桌面是静止的,可以用画面变化检测(帧差)判断"有没有新动作"------无变化时降低推理频率甚至休眠,检测到发牌动作再恢复高频推理。这种动态调频能显著降低发热和耗电,是移动端持续运行的关键细节。
九、 UI 界面:把识别结果变成用户看得懂的信息
用户不关心算法,只关心界面上呈现的结果是否清晰、可信、跟手。UI 设计要围绕"实时反馈 + 结构化结果 + 可纠错"三个目标。
9.1 三层信息呈现
|--------|------------------------|-----------------------|
| UI 层 | 内容 | 呈现方式 |
| 实时叠加层 | 每张牌的检测框、牌面标签、跟踪 ID、置信度 | 相机预览上半透明框 + 角标文字,跟手移动 |
| 各家牌型面板 | 按玩家分区列出已识别手牌与判定牌型 | 屏幕侧边卡片,随发牌实时增长 |
| 全局统计栏 | 发牌次序、桌面牌数、剩余牌、牌型强度排序 | 顶部/底部固定信息条 |
9.2 发牌次序的可视化
发牌先后可以用带序号的小角标(①②③)标在每张牌上,同时在侧栏按时间线列出"第 N 张→某家→某牌"。牌数用计数器实时显示,并在通过"52 张全局唯一"一致性校验时给绿色对勾、检测到重复牌时给黄色警示,让用户对系统的判断有信心、也能及时发现识别异常。
9.3 人工纠错闭环
再高的准确率也要给用户兜底:点击任意识别结果可弹出 52 张牌选择器手动改正;被纠正的样本可选择保存,作为后续模型迭代的数据。低置信度结果用虚线框或问号标注,提示用户重点核对。识别系统的可信度,很大程度来自"它知道自己什么时候不确定"。
十、真实难题与鲁棒性对策
实验室 Demo 和好用的产品之间,差的正是对以下真实难题的处理。
|---------|----------------|------------------------|
| 难题 | 成因 | 对策 |
| 运动模糊 | 发牌瞬间牌在快速移动 | 跟踪到位、牌静止后再识别;提高快门/曝光 |
| 反光眩光 | 塑封牌面、灯光在牌面形成高光 | 多角度双角标互验、偏振建议、多帧投票 |
| 牌重叠遮挡 | 扇形手牌、出牌压边 | 遮挡样本训练、沿弧线数露出角标、时序入场计数 |
| 光照变化/色偏 | 暖光、暗光、混合光源 | HSV 自适应阈值、白平衡、数据增强覆盖色温 |
| 手抖/视角变化 | 手持不稳 | 每帧重算或跟踪桌面四角、视觉惯性平滑 |
| 背景干扰 | 桌面杂物、相似色块 | ROI 限定桌面区、长宽比约束过滤 |
| 识别跳变 | 单帧误识别 | 多帧投票 + 52 张唯一性校验 |
这些对策背后是一条通用原则:**尽量用时序、先验和约束去弥补单帧视觉的不确定**。扑克牌场景有大量可利用的先验------封闭的 52 张集合、固定的角标位置、稳定的长宽比、红黑二分的颜色规律、发牌的时间顺序------把这些先验全部用足,系统的鲁棒性会远超"一个检测模型打天下"的朴素方案。
十一、数据集、训练与效果评估
11.1 数据集怎么构建
扑克牌识别的数据集构建有"合成 + 实拍"两条腿。合成数据成本极低:用牌面素材图通过随机旋转、透视变换、缩放、光照变化、背景替换、运动模糊、相互遮挡等增强,可以批量生成几万张带精确标注的训练图,公开扑克牌数据集多在2.4 万张量级、覆盖52 类。但合成图与真实摄像头成像存在域差异(真实噪点、塑封反光、镜头畸变、桌面纹理),必须补充多角度实拍数据,尤其是叠牌、暗光、反光这些难例。数据增强要刻意模拟真实难题:随机透视对应手持斜拍、色彩抖动对应不同光源、随机遮挡对应扇形叠牌。
11.2 训练策略
端到端方案直接训练 52 类检测模型,注意类别均衡(每类牌样本量相当)和小目标增强(角标小,可用更高分辨率检测头)。两阶段方案分别训练单类牌检测器、13类点数分类器、4 类花色分类器,分类器结构简单、收敛快。训练完成后必须做量化感知评估:对比FP32 与INT8 量化后的精度,确认量化没有把易混牌(如红桃 A与方块 A、数字 6 与 9)混淆率拉高。
11.3 评估指标体系
|-----|---------------|---------------------------|
| 层级 | 指标 | 含义与目标 |
| 检测层 | mAP、召回率 | 牌有没有被找到、框得准不准,重点看遮挡召回 |
| 识别层 | 牌面分类准确率 | 点数花色是否读对,公开方案测试集可达 98% 以上 |
| 跟踪层 | ID 切换次数、计数准确率 | 同一张牌不被反复换 ID、牌数统计正确 |
| 时序层 | 发牌次序准确率 | 发牌顺序序列与真实顺序一致的比例 |
| 系统层 | 端到端帧率、单局牌型正确率 | 中端机实时性、最终牌型判定整体正确率 |
评估不能只看单帧分类准确率,更要看"完整一局"的端到端正确率------因为跟踪丢 ID、计数错误、牌型组合错误都不会体现在单帧指标里,却直接决定产品可不可用。建议录制多局完整牌局视频作为回归测试集,每次模型或逻辑更新后跑一遍,统计从发牌到牌型输出的全链路正确率。
十二、工程落地路线建议
对想做出这类产品的团队,推荐分四阶段递进,每阶段都能独立验证,避免一步到位踩大坑。
|------------|--------------------------|---------------------|
| 阶段 | 目标 | 关键交付 |
| 阶段一:静态识别 | 拍一张静止牌桌照片,正确框出并读出所有牌 | 检测+识别模型跑通,验证基础准确率 |
| 阶段二:视频跟踪 | 实时视频中稳定跟踪、计数、记录发牌次序 | 叠加多目标跟踪与时序状态机 |
| 阶段三:牌型与 UI | 按玩法输出各家牌型,完成手机界面 | 规则引擎 + 三层 UI + 人工纠错 |
| 阶段四:移动端优化 | NCNN/TFLite 离线部署、量化、动态调频 | 中端机实时流畅、低功耗稳定运行 |
总结
回到最初的需求------"打开 APP 对准桌面,识别发牌次序和牌面,输出各家牌型",拆解到底是一条清晰的分层技术链:先用四点透视变换把斜拍桌面矫正为正俯视并划分玩家区域;再用轻量检测模型(端到端52 类或检测+角点两阶段)定位每张牌,靠 HSV 红黑分离和微型 CNN 从角标 ROI 读出花色与点数;然后用SORT/ByteTrack 给每张牌稳定ID,以"新 ID 首次确认"的时序记录发牌次序、以 ROI 归属和 ID 计数统计各家牌数,并用"52 张全局唯一"先验做强纠错;接着用确定性规则引擎把离散牌组合成斗地主或德州牌型;最后经NCNN/TFLite 量化部署到手机离线实时运行,通过三层UI 把结果清晰呈现。
这个项目最值得借鉴的方法论是:**视觉负责感知,时序负责稳定,规则负责理解,三者分层解耦、各用其长**。单帧深度学习解决"看清",多帧跟踪与封闭集合先验解决"看稳",确定性规则引擎解决"看懂",移动端工程化解决"流畅可用"。扑克牌因为类别封闭、先验丰富,是练习完整 CV 系统的绝佳场景------把这套"检测---识别---跟踪---规则---部署---交互"的链路走通,迁移到工业零件计数、票据识别、桌面机器人等更广阔的场景,底层方法是相通的。