从视频帧到车牌文本:车辆检测、关联跟踪与 NCNN 识别复盘
本文目录
本文以公开仓库固定提交 729c487be0d5615beee623d7461acae9b71a8ae5 为审阅对象。仓库元数据将它标记为非 fork;该提交的文件树没有 README 或许可证文件,GitHub 元数据也没有声明许可证。代码结构展示的是一条 C++ 视频分析原型:车辆检测、车牌检测、车牌字符识别和跨帧跟踪。本文是源码级工程复盘,没有编译项目、加载模型、运行视频或验证识别准确率;代码能表达设计意图,不代表当前环境可直接复现。
一条视频流水线
视频帧
├─ 每隔若干帧:车辆检测(YOLO / NCNN)
│ 车牌检测(RFB 风格 anchor / NCNN)
│ 车牌区域字符识别(NCNN)
│ 检测框关联到已有轨迹
└─ 其余帧:灰度模板匹配跟踪车辆框
↓
叠加车辆轨迹、检测框和车牌文本
↓
写出处理后视频
CMakeLists.txt 指定 C++11,并要求能找到 OpenCV 与 ncnn。模型参数和权重放在仓库的 model/ 目录;它们是独立的二进制资产,不应只凭文件存在就推断训练数据、来源或再分发条件。
检测:两个模型承担不同任务
车辆检测器将 BGR 帧缩放到接近目标尺寸的输入,再转换颜色通道并按 255 归一化,创建 ncnn extractor 执行推理。输出框的坐标按原帧宽高还原,代码只保留中心点落在帧内且分数高于 0.5 的候选。每次推理还会把缩放图写到当前工作目录的 resize.jpg,这是固定路径的调试副作用:连续视频会反复覆盖同一个文件,也会产生额外磁盘 I/O。
车牌检测器单独生成多层 anchor,读取位置、分类分数和四个角点相关输出;候选先按分数排序,再做 NMS。构造函数实际把阈值设为 0.8、NMS 阈值设为 0.4,随后还会检查检测区域的颜色均值,只保留 B 通道均值同时高于 G、R 的区域。这个过滤条件偏向蓝底车牌样本,并不等于对各种底色、光照和摄像头都适用的通用车牌检测策略。
两个检测器分开是有价值的:车辆框服务于目标跟踪,车牌框则服务于局部识别与文字显示。但阈值、输入预处理、模型版本和颜色筛选共同决定最终召回率。若只调字符识别模型,不验证上游漏检和错框,端到端结果不会因此自动变好。
识别:裁剪、归一化与贪心解码
车牌区域被裁出后统一缩放到 128×32,再交给 ncnn 字符识别网络。代码逐个时间步对输出类别取最大分数,跳过类别 0,并折叠连续重复类别,最后把类别编号映射成字符。这是简化的贪心序列解码路径;源码没有展示 beam search、字符级置信度、候选纠错或人工复核。
车牌区域的角点被用于几何变换,目标尺寸固定为 128×32。复核时应把角点顺序、变换方式、裁剪边界和识别模型的训练输入约定一起核验;仅有四个坐标字段,并不能证明任意倾斜、遮挡和透视条件都已被稳定矫正。
跟踪:检测帧与模板帧交替
主循环在视频上定位到一个固定起始帧后读取图像;每六帧运行一次检测与关联,间隔帧则在车辆灰度图附近建立约 1.5 倍搜索窗,用 OpenCV 模板匹配寻找最相似位置。检测帧刷新车辆框、局部灰度模板和轨迹点;轨迹被绘制到输出视频中。
这里的检测与跟踪是两种不同的信号:检测帧负责纠偏和重新发现目标,模板帧负责低成本地延续短期位置。代码只对车辆区域调用模板匹配;车牌框没有在间隔帧同步跟随车辆框,因此移动中的车牌矩形和文字位置可能在两次检测之间滞后。若画面要用于实时展示,这个视觉偏差应单独评估,而不能只看车辆轨迹是否连续。
车辆关联函数名为 bboxIOU,实际计算的是交集面积除以两个框中较小的面积,并非通常定义的交并比(IoU)。候选分数排序后采用贪心方式逐个占用轨迹和检测框,不是全局最优匹配;发生遮挡、交叉或相邻车辆框高度重叠时,局部最高分可能抢占另一条轨迹的候选。更稳妥的做法是明确相似度定义,再加入运动预测、外观特征、匹配门限和全局分配,并对低置信关联保留“未匹配”状态。
车牌与车辆的关联则按中心点距离和车辆高度门槛逐个挑选候选,并从候选列表中移除已分配车牌。车辆遍历顺序可能影响后续可用候选。真实场景下还应校验车牌是否位于车辆框的合理区域、同一帧的一对一约束、跨帧车牌身份,以及车牌检测短暂缺失时的保留策略。
静态审阅发现的复现与维护风险
- 模型与视频路径依赖工作目录:主程序从
../a7.avi读取视频,两个检测器从../model/读取模型,而字符识别器从./model/读取。它们都是相对进程当前目录的路径,不是相对源文件或可执行文件;同一二进制从不同目录启动会得到不同结果,且不同模块的模型路径约定不一致。 - 视频输出参数固定且未检查:输出路径由输入路径直接追加扩展名,帧率固定为 30、尺寸固定为 1080×1920;代码没有检查 VideoWriter 是否成功打开,也没有按输入视频属性设置输出参数。不同尺寸或方向的视频需要专门验证。
- 成功流程返回非零状态:主循环正常结束后返回 1。常见 shell、CI 或任务调度器会把非零值视为失败,即使视频已经处理完成。
- 跟踪丢失计数并非连续失败计数:模板匹配分数低于 0.7 时递增
deadnum,高分时更新模板,但模板跟踪路径没有同步清零该计数;达到阈值后会移入死亡列表。检测重新匹配会重置计数,因此它的行为取决于检测是否恢复关联,不完全等同于“连续多少帧都跟丢”。 - 调试输出进入主推理路径:每次车辆推理都覆盖
resize.jpg,循环还持续打印帧数、分数和模板匹配结果。运行长视频时应将调试日志和文件导出改为显式开关,并避免高频同步 I/O。 - 缺少可复核的验证闭环:该提交没有 README、测试目录、固定标注集或准确率报告;仓库虽包含模型文件和一个视频样本,但这不足以说明训练来源、数据授权、类别映射和评测口径。当前结论只覆盖源码可以确认的行为。
如果继续维护,建议按这个顺序
- 先统一配置入口:视频输入/输出、模型目录、阈值、抽帧间隔和输出编码都从命令行或配置文件读取;路径按配置文件或可执行文件位置解析,并在启动时逐项校验。
- 给检测和识别增加可复现的离线评测:固定视频切片、标注框与字符标签,分别统计车辆/车牌检测的 precision、recall、漏检率,以及字符整串准确率;同时保存模型版本和预处理参数。
- 把
bboxIOU改成语义明确的 IoU 或 IoM 函数,补充边界框面积为零、无候选、候选冲突和遮挡交叉的单元测试;多目标关联可采用匈牙利算法并加入运动门控。 - 统一车辆框与车牌框的跟踪生命周期:明确分数恢复是否重置丢失计数,在间隔帧同步更新车牌区域,设置最大暂存时长和重识别规则。
- 检查每次裁剪框非空且完全位于图像边界内;检查模型加载、视频解码和视频写入的返回值,失败时返回非零并给出可定位的错误。
- 在确认模型权重、样本视频和训练数据的来源与许可之前,不要把仓库二进制资产打包进公开发行物或商业交付。
可迁移的工程经验
这个项目适合用来理解传统视觉流水线如何把多个模型拼起来:车辆检测不等于车牌检测,车牌检测也不等于字符识别;跨帧跟踪能减少重复推理,却会引入状态漂移与关联错误。排查结果时应同时看原帧、检测框、裁剪图、模型输出、轨迹关联和最终解码文本,并为每一段保留可回放的中间产物。若没有固定样本和指标,肉眼看到几帧“识别正确”不能替代准确率结论。
源仓库
仓库内容、后续变更以及模型与数据的使用条件应以源项目当前版本为准。本文是原创的工程解读,没有复制源代码,也没有声称已在本地编译或运行:cdyforever/PlateAndCar
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。