每年一到开题季,问得最多的一个题目就是“基于深度学习的运动目标识别追踪系统”,配套关键词永远是目标检测、人工智能。这类题看着熟,真动手的人十个里有八个会卡在同一个地方:网上抄来的几行推理代码能跑通单张图,一接上视频就原形毕露——框在抖、ID在跳、帧率掉到个位数,最后连演示都凑不齐。我见过太多人把时间全砸在调模型上,结果系统那部分一片空白,答辩时被追问“你的系统到底能干什么”直接哑火。
这篇东西不打算讲什么宏大叙事,就按一个真正做完这类项目的视角,把“运动目标识别追踪”这件事从选题拆解、检测器选型、数据关联逻辑、评估口径,一路讲到系统封装和答辩追问。适合正在做这类毕设的同学,也适合刚接触多目标追踪、想把一套流程从零跑通的工程同学。基础差一点没关系,关键概念我都会用生活化的说法解释一遍;有基础的可以直接跳到参数和排查链路那几节,那里才是真正耗时间的地方。
1. 把这个题目拆成三件事:检测、追踪、系统
题目里塞了三个技术名词,很多人下意识把它们当成一件事,写代码的时候也搅在一起,一个脚本里既读帧又推理又画框又写文件,跑到一半报错都不知道是哪一层出问题。正确的做法是先在脑子里把它们切开,切得越干净,后面排查越省力。
1.1 检测管“这一帧有没有”,追踪管“和上一帧是不是同一个”,系统管“别人能不能用”
目标检测解决的是空间问题:给一张图,输出若干个框,每个框带类别和置信度。它完全不关心上一帧发生了什么,每一帧都是“失忆”状态。多目标追踪解决的是时间问题:拿到这一帧的框之后,判断它和上一帧的哪条轨迹是同一个人,然后继承那个编号。所以追踪器的输入是“检测框序列”,输出是“带ID的框序列”,它自己不产生框,只负责连线。
系统这一层解决的是可用性问题:视频从哪来、结果往哪去、卡顿了怎么办、用户点了暂停要不要停、导出的文件能不能播放。这三层的输入输出边界一旦定清楚,你就可以单独测检测、单独测追踪、单独测整条链路,而不是每次改一个参数就从头跑一遍完整视频。
我一般建议在项目一开始就画一张很土的数据流图(纸上画就行):帧 → 检测结果列表 → 追踪结果列表 → 渲染图层 → 输出。每一层的数据结构都定死,比如检测结果是[x1, y1, x2, y2, conf, cls]的列表,追踪结果是多了track_id的列表。别小看这个约定,后面你想把检测器从一种换成另一种,只要输出格式不变,追踪那层一行都不用改。
1.2 工作量分配:别在检测精度上死磕,那是投入产出比最低的地方
我带过几届做这个题的同学,最常见的失衡是把 70% 的时间花在“把我的模型 mAP 从 0.72 提到 0.75”上,剩下 30% 时间做系统,最后演示效果很差。实际情况是,对于一个固定场景的追踪系统,mAP 提高两三个点,肉眼几乎看不出区别;但帧率从 8 提到 25、ID 跳变从每秒一次降到整段视频两次,观众立刻能感觉到“这东西能用”。
下面这张表是我自己带项目时的粗略分配,具体数字按你的基础调整:
| 任务块 | 典型技术方案 | 建议时间占比 | 最容易卡住的地方 |
|---|---|---|---|
| 数据采集与标注 | 固定机位录像 + 逐帧标注 | 20% | 标注规范不统一,同一人类别反复改 |
| 检测模型训练 | 一阶段检测器 + 预训练权重微调 | 25% | 显存不够、增强过猛、验证集泄漏 |
| 追踪器接入与调参 | 卡尔曼滤波 + 数据关联 | 25% | ID 跳变、遮挡后轨迹断裂 |
| 系统封装与优化 | 多线程流水线 + 推理加速 | 20% | 帧率、内存增长、视频写出失败 |
| 文档与演示材料 | 论文 + 演示视频 + 答辩稿 | 10% | 数据没留档,事后补不出来 |
这张表最想传达的一点是:数据那 20% 千万别省。我见过太多人拿着公开数据集的预训练权重直接跑自己的宿舍楼道视频,效果当然一般,然后回头怀疑模型不行,其实是场景差异太大——俯视角、低照度、目标占画面比例很小,这几种情况任何一个都能让通用模型掉一大截。
1.3 答辩前你手上必须有的东西
不是“代码能跑”就行。一个完整的交付至少包括:一份可复现的训练配置(含数据划分、超参、随机种子)、一份追踪器参数配置、一段三分钟以内的演示视频(最好包含遮挡、交叉、进出画面三种情况)、一张端到端速度测试表、一份失败案例分析。
失败案例分析这一项特别容易被忽略,但它是答辩时的加分项。你主动说出“在密集人群场景下我的 IDF1 掉到 0.4,原因是外观特征被相互遮挡污染”,比装作系统完美无缺要可信得多,也说明你真的理解这套东西的边界在哪。
2. 检测器选型的取舍:先跑得动,再谈精度
选检测器这件事,很多人一上来就去翻最新论文榜单,挑排名最高的那个,然后发现权重下不下来、依赖装不上、显存爆掉。毕设是有时间盒的,选型的第一原则是“生态成熟、能导出、资料多”,精度排第二。
2.1 一阶段检测器为什么几乎成了这类题目的默认解
追踪系统对检测器的要求和一个纯离线检测任务完全不同:它要的是每一帧都快、且框的位置抖动小。两阶段检测器精度通常更好,但推理链条长,帧间延迟不稳定,框在相邻帧之间轻微跳动就会被追踪器误判成“新目标”。一阶段检测器一次前向就出结果,延迟稳定,框的抖动也相对可控,这是它成为默认选择的根本原因。
另一个现实因素是导出链路。你要做加速,就得把模型转成中间表示再交给推理引擎,这条路上社区踩过的坑越多,你越省事。选一个大家都在用、导出脚本维护良好的模型族,比选一个精度高零点几但导出报错的新模型划算得多。我在选型时通常只问三个问题:有没有官方预训练权重?能不能一键导出?导出后的推理结果和原框架对齐吗(这个必须自己验)?
顺便提醒一句,模型大小要和你的硬件匹配。用笔记本核显或者入门级独显做开发,就别碰大模型,训练阶段一个 batch 都放不下,你连调参的机会都没有。先用小模型把整条链路跑通,等确认流程没问题,再考虑换大一号的模型重训。
2.2 训练参数里真正影响结果的那几个
超参列表很长,但对这类项目真正起作用的就那么几个。下面这组是我常用的起手配置,输入尺寸 640、批大小按显存调到不 OOM 的最大值:
python train.py \ --img 640 \ --batch 16 \ --epochs 150 \ --data data/mydata.yaml \ --weights pretrained_s.pt \ --cos-lr \ --device 0逐项说明一下为什么要这么设。输入尺寸 640 是速度和精度的常见平衡点,如果你的目标在画面里很小(比如远景监控),可以考虑 960,但帧率会明显下降,需要权衡。批大小不是越大越好,小数据集上大批次会让收敛变慢,显存允许的话 16 是个稳妥起点。轮次方面,几千张图的数据集 100 到 150 轮基本收敛,再多就是过拟合。
学习率是最容易出问题的一项。用 SGD 优化器时初始学习率可以到 0.01,用 Adam 系优化器就要降到 0.001 量级,两者差一个数量级,搞错了就是损失震荡或者根本不下降。余弦退火(--cos-lr)在小数据集上通常比阶梯衰减更平滑,我基本默认开着。
数据增强这块要克制。马赛克增强(把四张图拼成一张)对提升小目标检测很有效,但在你的场景是固定机位、目标尺度变化不大的情况下,开太强反而会让模型学到不存在的上下文。我通常的做法是先开着默认增强训一版,看验证集曲线,如果训练损失降得很快而验证损失早早抬头,说明增强不够或者数据太少;如果两者都降得很慢,说明增强过猛或者学习率不对。
2.3 检测阈值不是拍脑袋定的,要迁就追踪器
这是新手最容易忽略的一点。做纯检测任务时,大家习惯把置信度阈值设到 0.5,只留高置信框。但在追踪系统里,这个思路是错的。追踪器的职责之一就是“用运动信息把漏检补上、把误检滤掉”,所以你应该给追踪器喂更多的候选框,让它自己去判断。
我的经验值是这样的:
| 参数 | 纯检测任务常用值 | 追踪任务建议值 | 原因 |
|---|---|---|---|
| 置信度阈值 conf | 0.5 | 0.25 至 0.3 | 提高召回,漏检比误检更难补救 |
| NMS 的 IoU 阈值 | 0.45 | 0.5 至 0.6 | 密集场景下避免把相邻目标抑制掉 |
| 最小框面积 | 不限 | 按分辨率设下限 | 过滤掉大量噪点小框,减轻追踪器负担 |
阈值调低之后误检会变多,这是预期的,追踪器的确认机制(一条轨迹要连续命中若干帧才被“激活”)会把大部分一闪而过的误检干掉。反过来,如果你把阈值设得很高,导致目标被遮挡两帧就彻底消失,追踪器再厉害也接不回来。
2.4 数据集这一关过不去,后面全是白干
训练集的问题通常出在两处:类别定义和数据划分。
类别定义上,别贪多。一个追踪系统里检测 80 类没有意义,你只需要关心场景里真正会动的目标。类别越少,模型收敛越快,误检越少。我一般建议先只做 1 到 3 类,把流程跑顺,需要扩展再加。
数据划分上,有一个非常隐蔽的坑:同一个视频序列的相邻帧不能同时出现在训练集和验证集里。相邻帧几乎一模一样,模型见过前一帧就等于见过后一帧,验证指标会虚高十几个点,等你换一段新视频立刻原形毕露。正确做法是按视频片段切分,训练用一部分片段,验证用完全没参与训练的另一部分片段,最好拍摄时间、光照条件也有差异。这一点在做毕设论文时一定要写清楚,是能体现方法论严谨性的地方。
3. 多目标追踪的核心:数据关联怎么把框连成轨迹
检测器给出的是无序的框集合,追踪器要做的是给它排顺序、编身份。这部分算法看着唬人,说穿了就是“先预测每个老目标这一帧应该在哪,再拿预测位置和新检测框做配对,配不上的开新号或者销号”。
3.1 卡尔曼滤波加匈牙利匹配,这套组合到底在算什么
卡尔曼滤波在这里的用途是预测。它维护每个目标的运动状态,通常用一个八维向量表示:中心点坐标、宽高比、高度,以及这四个量各自的变化率。有了这个状态,即使这一帧检测器没给出框(遮挡、模糊),追踪器也能根据上一帧的速度推测出“它大概在这个位置”,然后继续往下走。
预测出来之后要和新检测框配对,这就是指派问题。假设现在有 5 条已有轨迹、4 个新检测框,要找出一个最优的一一对应关系使总代价最小,经典解法是匈牙利算法。代价怎么定义?最直接的是框之间的交并比(IoU),重叠越多越可能是同一个。但只靠位置在拥挤场景会失效——两个人肩并肩走,位置挨得近,光看 IoU 分不清谁是谁。
所以现代追踪器会再加一路“外观代价”:给每个框抠出来的图像区域过一个特征提取网络,得到一个特征向量,然后比较新旧特征向量的余弦距离。位置代价和外观代价加权求和,得到最终的代价矩阵。这个加权系数就是你需要调的关键参数之一。
3.2 交叉之后 ID 就乱,问题往往出在特征质量和匹配顺序
两个人交叉走过的那几帧,是所有追踪器最难受的时刻。位置信息在这个窗口内基本失效(两个人几乎重叠),唯一能依靠的就是外观特征。这时候如果特征提取用的是整张图而不是抠出来的目标区域,或者抠出来的区域因为框本身不准而包含了大量背景,特征就不可靠,ID 就会交换。
除了特征质量,匹配顺序也很关键。有一类改进思路是“级联匹配”:先让最近几帧都持续被检测到的轨迹优先匹配,因为它们的可信度最高;那些已经丢失了一段时间的轨迹放到最后匹配,避免它们“抢走”本该属于活跃目标的检测框。这个顺序上的小小调整,在实际视频里对减少 ID 跳变的效果非常明显。
我建议你在接入追踪器时,把下面这几个参数当成必须理解的项,而不是照抄默认值:
| 参数 | 含义 | 调大的后果 | 我常用的初值 |
|---|---|---|---|
| 外观距离阈值 | 特征余弦距离超过它就不匹配 | 更容易认错人,ID 交换增加 | 0.2 |
| IoU 距离阈值 | 位置代价的上限 | 允许位置跳动更大的匹配 | 0.7 |
| 轨迹最大存活帧数 | 连续多少帧没匹配上才删除轨迹 | 长期保留幽灵轨迹,误匹配 | 30 至 40 |
| 确认所需命中帧数 | 连续命中几帧才正式分配 ID | 出号变慢,检测到出号有延迟 | 3 |
这几个参数之间是联动的。比如你把最大存活帧数从 30 调到 100,遮挡后恢复的能力变强了,但同时也意味着一条早已离开画面的轨迹还在参与匹配,很可能把一个新出现的人“认领”成旧目标。我的建议是先按上表初值跑通,再针对具体视频逐项调,一次只改一个。
3.3 低分框也是信息,看看两阶段关联的思路
有一类追踪方案值得单独说,因为它对遮挡场景的改善很直观。传统做法是检测阈值以下的框直接扔掉,但阈值附近那些 0.1 到 0.3 置信度的框,很多时候恰恰是被遮挡的目标或者运动模糊的人。把它们全丢掉,等于主动放弃了一部分真实目标。
两阶段关联的思路是:先用高分框和现有轨迹做第一次匹配(这时候只信高置信度,保证准确);剩下没匹配上的轨迹,再和低分框做第二次匹配(这时候放宽标准,主要靠位置,因为低分框的外观特征不可靠);最后还没匹配上的高分框才开新轨迹。这个设计在人群密集、遮挡频繁的场景提升很明显,代码实现也不复杂,多加一轮匹配循环而已。
不过它有个前提:你的检测器在低阈值下输出的框位置要足够准。如果低分框的位置飘得厉害,二次匹配反而会引入错误。所以用这条路子之前,最好先看一眼你模型在 0.1 到 0.3 置信度区间的框质量,画几张图肉眼确认一下。
4. 一次 ID 频繁跳变的完整排查链路
这一节我想把排查过程完整走一遍,因为这类问题没法靠猜,只能一层层往回倒。假设你遇到的现象是:同一个人在画面里待了二十秒,ID 换了五次。
4.1 先确认现象,把问题量化出来
别上来就改参数。第一步是把追踪结果落盘成标准格式(每帧每目标的 ID 和框坐标),然后写个小脚本统计:整段视频里 ID 总数是多少、真实目标数是多少、每个 ID 的平均存活帧数是多少、切换发生在哪些帧。
这一步的意义在于把“感觉 ID 在跳”变成具体数字。如果 ID 总数是目标数的三五倍,说明是短命轨迹太多,问题大概率在检测不稳或者确认阈值太高;如果 ID 总数只比目标数多一点,但切换都集中在某几个时间点,那就要去看那几个时间点画面里发生了什么(遮挡、交叉、出画)。
我通常还会做一个可视化检查:把每个 ID 用不同颜色画框,导出一段视频。颜色频繁变化的地方一眼就能看出来,比看表格快得多。
4.2 从检测端往回倒:框本身稳不稳
追踪的上游是检测。如果检测框在相邻帧之间左右横跳几个像素,或者置信度忽高忽低导致中途断帧,追踪器再怎么调也救不回来。检查方法很简单:关掉追踪,只看检测结果导出一段视频,观察同一个目标的框。
我在排查中遇到过三种典型的检测端问题。第一种是框的尺寸抖动,目标的脚部或头部被切掉一部分,框高变化剧烈,这会直接破坏卡尔曼滤波对高度变化率的估计,导致预测位置偏移。第二种是连续几帧检测不到,原因是置信度恰好卡在阈值附近,这种最坑,因为它不是完全消失,而是断断续续。第三种是密集场景下的框合并,两个挨得近的人被一个框覆盖,追踪器会认为其中一个消失了。
对应的处理办法:框抖动可以通过降低 NMS 的 IoU 阈值或者提高训练数据中相似姿态的样本量缓解;断帧问题最直接的办法是把检测阈值往下调,给追踪器更多候选;框合并则要考虑换用更好的后处理或者干脆提高输入分辨率。
4.3 再看追踪端:特征是不是被污染了
如果检测端确认没问题,接下来看外观特征。最有效的检查方法是把追踪器内部保存的特征向量取出来,算一下同一个目标在不同帧之间的余弦相似度,以及不同目标之间的相似度。如果同类相似度只有 0.5 而不同类相似度有 0.4,说明特征根本没有区分能力,这时候调匹配阈值是徒劳的,得换特征提取方式。
特征被污染的常见原因有三个:一是抠图区域不准,框本身包含了大量背景,尤其是框偏大的时候;二是输入特征的图像分辨率太低,抠出来只有 30 像素宽,细节全丢了;三是特征更新策略有问题,某个目标被遮挡时抠到了一个错误的区域,这个错误特征又被更新进特征库,之后就越错越远。
我的做法是给特征库设一个容量上限(比如每个轨迹保留最近 100 个特征向量),匹配时取最小距离,这样单次错误不会污染整条轨迹。另外,对于未确认的临时轨迹,不更新特征库,等它确认之后再说。
4.4 修复后的验证方式:别只看一段视频
改完参数一定要用多段没参与调参的视频验证,而不是在出问题的那段上反复试。在问题视频上调到完美是很容易的,但那叫过拟合,换一段立刻打回原形。
我会准备三段视频做验证:一段和调参视频条件接近、一段光照或有差异、一段目标更密集。三段上的 ID 切换次数都下降,才算真的改好了。如果只有第一段变好,说明参数是贴着那段视频拧出来的,得退回去重调。
5. 数据集、标注与评估:别拿一个准确率糊弄自己
到这一节,我们得聊聊“怎么证明你的系统是好的”这个事。毕设论文里最忌讳的就是写一句“系统识别准确率达到 95%”然后没有任何说明。这个 95% 是怎么算的?按帧算还是按目标算?漏检算不算错?这些不说清楚,答辩老师一定会追问,而且大概率追问到你答不上来。
5.1 自建数据的采集和标注规范
采集环节只有一条铁律:尽量贴近你的实际演示场景。如果你打算在实验室走廊演示,就去走廊录;打算在操场演示,就去操场录。别用网上下载的交通监控视频训练,然后跑去室内演示,那是自找麻烦。
录制的时候注意几个细节:机位固定不要手持(手持带来的全局抖动会让所有轨迹都跟着漂,追踪器会崩溃)、尽量多录几种光照(白天、傍晚、开灯)、每个场景至少录三到五分钟(太短不够产生足够的轨迹切换)。
标注环节最耗时间,也最容易出不一致。我的做法是先定一份简短的规范写在文档里,比如:被遮挡超过一半的目标不标、目标只露出头部时不标、框的边界贴着目标可见部分的边缘、同类目标之间的框允许重叠。然后标前一百张,回头统一检查一遍,确认标准统一了再往下标。不然后面你会发现前两百张和后两百张的标准完全不一样,模型学出来的东西是矛盾的。
5.2 按帧随机切分是最常见的数据泄漏
前面提过一次,这里单独强调。很多人图省事,把标注好的所有帧打乱,随机取 80% 训练、20% 验证。这个做法在视频任务里是错的,因为相邻帧之间差异极小,模型在训练时见过目标在第 100 帧的样子,验证时遇到第 101 帧几乎是送分。
正确做法是以视频为单位划分:视频 A、B 用于训练,视频 C 用于验证,视频 D 用于最终测试,三个集合互不重叠。如果视频数量太少,至少也要保证同一段视频不跨集合。这个细节写进论文的“实验设置”一节,是方法论严谨性的体现。
5.3 MOTA、IDF1、MOTP 分别在说什么
多目标追踪有一套专门的评估指标体系,别用检测的 mAP 去评价追踪。常用的几个:
| 指标 | 它在衡量什么 | 高值意味着 | 主要盲区 |
|---|---|---|---|
| MOTA | 综合漏检、误检、ID 切换的错误率 | 整体错误少 | ID 切换占比小,对跳变不敏感 |
| IDF1 | ID 层面的 F1,看身份保持得多好 | 身份维护准确 | 对框的位置精度不敏感 |
| MOTP | 匹配上的框与真值的平均位置误差 | 框定位准 | 不反映漏检误检 |
| 每帧 ID 切换数 | 身份切换的频次 | 越少越好 | 需要单独统计 |
实际写论文时,我的建议是至少报 MOTA 和 IDF1 两个,再加一个帧率。只报 MOTA 容易被质疑“你对身份维护到底做得怎么样”,只报 IDF1 又显得框的位置精度没交代。两个一起报,配合一段失败案例的定性分析,说服力就够了。
5.4 速度指标必须测端到端
很多人在论文里写“推理速度 60 FPS”,一问才发现那是纯模型前向的时间,不含预处理、后处理、追踪、渲染、写文件。而实际系统里,预处理(缩放、归一化)和后处理(NMS)加起来可能比前向还耗时。
端到端的测法很朴素:记录开始处理第一帧的时间和处理完最后一帧的时间,用总帧数除以总耗时。同时记录一下峰值显存或内存占用。这两个数字才是别人复现时关心的。我在测试时会跑三遍取平均,因为第一遍往往包含模型加载和缓存预热,会明显偏慢。
6. 从能跑的脚本到能演示的系统
代码能跑和能演示之间隔着一段距离,这段距离里全是琐碎问题。这一节讲的就是怎么把脚本收拾成一个看起来像个系统的东西。
6.1 流水线结构:读、推、追、画、写
一个能用的结构是五段式流水线:读帧线程、推理线程、追踪模块、渲染模块、写出线程。读帧和推理分属不同线程,中间用一个容量很小的队列连接(容量 1 到 2 就够)。
为什么要用这么小的队列?因为视频处理是实时性任务,队列积压意味着延迟越来越大——你处理的是十秒前的画面,用户看到的画面和现实完全脱节。小队列配合“满了就丢最旧的帧”的策略,能保证处理端永远跟得上采集端。这个策略叫保新弃旧,实现起来就几行代码,但对观感的改善是巨大的。
from queue import Queue, Full import threading frame_q = Queue(maxsize=2) def put_frame(q, frame): try: q.put_nowait(frame) except Full: q.get_nowait() # 丢掉最旧的一帧 q.put_nowait(frame)写出这一步容易踩的坑:编码器和输入帧的分辨率必须一致,帧率要显式指定,输出路径不要带中文和特殊字符。用 OpenCV 写文件时,VideoWriter初始化失败是静默的,不报错但写出来是空文件,所以初始化后一定要检查isOpened()。
6.2 跳帧和加速:把个位数帧率拉到可用
如果你的系统在 640 分辨率下只有 8 FPS,别急着换硬件,有两个立刻见效的办法。
第一个是跳帧检测:每两帧做一次检测,中间那帧不检测,直接用追踪器的预测位置画框。因为追踪器本身就有预测能力,视觉上几乎看不出差别,帧率直接翻倍。跳帧的代价是快速运动的目标框会有轻微滞后,如果你的场景里目标移动不快,这个代价完全可以接受。
第二个是模型导出加速。把训练好的权重导出成中间表示,再用推理引擎加载,开启半精度,通常能有 1.5 到 2.5 倍的提升。导出命令大致长这样:
python export.py --weights best.pt --include onnx engine --half --imgsz 640注意:导出后必须做一次对齐验证。用同一批图片分别跑原模型和导出模型,对比输出的框坐标,差异应该在很小范围内(比如 1 到 2 个像素)。如果差异很大,通常是预处理方式不一致导致的,比如归一化参数或图像通道顺序搞错了。这个坑我踩过不止一次。
还有一个容易被忽略的优化点:把渲染和编码也挪出主循环。画框本身不慢,但如果每一帧都调用putText写很多文字、又做了字母叠加,累加起来会吃掉不少时间。
6.3 那些只有演示当天才会暴露的问题
中文乱码是最经典的一个。OpenCV 的putText不支持中文字符,直接写会显示成一串问号。解决办法是把画面转成图像对象,用支持中文的字体库绘制后再转回来。这个问题在开发时用英文标签完全不会暴露,等到你换成中文界面就全乱了。
还有一个是长时运行的资源增长。如果你的代码在每帧都新建一个追踪器实例,内存会一路飙升,跑十分钟就卡死。追踪器应该全局只创建一次,跨帧保持状态。
第三是视频源中断。用摄像头输入时,偶尔会出现读不到帧的情况,如果不做判空处理,程序会拿着空数组往下走,报一个莫名其妙的形状错误。读帧后先判断是否为空,连续失败若干次就退出或者重连,这个兜底逻辑一定要写。
6.4 交互层别做太重,但要让人一眼看懂
毕设的系统界面不需要花哨,但必须让人在十秒内明白你在干什么。我推荐的布局是:左边原始视频,右边带框和轨迹的视频,下面一排控制项(开始、暂停、切换视频源、导出结果),再加一个实时信息栏显示当前帧号、目标数、处理帧率。
技术上,桌面端可以用界面库做,网页端可以用轻量框架配视频流推送。后者部署方便,演示时用浏览器打开就行,不用担心环境。但如果时间紧,我建议直接做桌面端,省掉前端调试的时间。
有个小细节值得做:给每个轨迹画一条短的历史轨迹线(保留最近二三十帧的中心点)。这条线在视觉上非常直观,观众一眼就能看出"这个框是在跟同一个目标移动",比单纯换颜色有说服力得多。
7. 时间表和答辩现场:把活干在点上
最后聊点务实的。这类项目最大的敌人不是技术难度,是时间管理。
7.1 一份八周的排期
第 1 到 2 周:定场景、录数据、标数据,同时把检测模型用公开数据跑通,确认环境没问题。这两周结束你应该有一个能对单张图出框的脚本。
第 3 到 4 周:训练自己的数据,看验证曲线,调增强和阈值。目标是检测在你的场景下肉眼可用。同时开始接追踪器,先跑通最简单的关联。
第 5 周:专项处理遮挡和交叉场景,调追踪参数,做 ID 切换的统计和优化。这周最痛苦,但也最能出成果。
第 6 周:系统封装,多线程、界面、导出、性能优化。目标是把帧率做到可接受范围。
第 7 周:补实验数据,跑多段视频的评估,整理表格和失败案例。
第 8 周:写文档、录演示视频、准备答辩。
这个排期里,第 5 周和第 6 周最容易超时,建议提前把这两块的时间留出缓冲。另外,第 2 周结束时如果环境还没搞定,一定要果断换更简单的方案,别死磕。
7.2 答辩最容易被追问的六个问题
我把被问过的、以及同学反馈被问过的问题整理了一下,提前准备好答案,现场会从容很多:
| 追问 | 想考察什么 | 回答的着力点 |
|---|---|---|
| 为什么选这个检测器,比较过吗 | 选型有没有依据 | 讲帧间延迟稳定性、导出链路、硬件匹配 |
| 目标被长时间遮挡后再出现,怎么办 | 是否理解追踪的边界 | 承认局限,说明最大存活帧数和特征库策略 |
| 你的准确率是怎么算的 | 评估是否规范 | 说清指标定义、数据集划分方式 |
| ID 号是随机分配的吗 | 是否理解轨迹管理 | 讲确认机制、编号分配时机 |
| 实时性怎么保证的 | 工程能力 | 讲跳帧、异步流水线、模型导出加速 |
| 换个场景还能用吗 | 泛化边界 | 讲训练数据覆盖范围,坦诚说明需要适配 |
其中"换个场景还能不能跑"这个问题,我建议不要硬答"能"。更好的回答是说清楚在什么条件下能、什么条件下需要重新标数据微调,并给出你实测过的差异数据。老师更认可有边界意识的回答,而不是打包票。
7.3 演示翻车的预案
演示当天最怕的不是效果一般,是视频放不出来。所以一定要准备离线录屏,把完整流程走一遍录下来,存在本地。现场如果实时演示卡住,直接切录屏,并说明"这是相同配置下录制的完整流程",没人会为难你。
再准备一个 30 秒的精华片段,包含遮挡恢复和交叉不换号这两个最能体现工作量的场景。答辩时间紧张的时候,直接放这一段,效果比放五分钟平淡视频好得多。
最后说点我自己的体会。这类题目做下来,真正拉开差距的从来不是你用了多新的模型,而是你有没有把"数据—训练—追踪—系统—评估"这条链路走完整,并且能说清楚每一环为什么这么选。我见过用很朴素的检测器加基础追踪方案、但整条链路扎实、评估规范、演示流畅的项目,得分远高于那些堆了新模型却连帧率都测不出来的。把功夫下在能演示、能解释、能复现上,这个方向不会错。
如果你准备在自己的场景上再往前走一步,可以考虑两个扩展:一是把单摄像头扩展到多摄像头,做跨镜头的目标关联,这是同一个技术栈的自然延伸;二是引入轻量的行为分析,比如停留时长统计、越界判定,这些不需要重训模型,把轨迹数据拿来做后处理就能出结果,性价比很高。