1. 从“晃动的镜头”说起为什么传统多目标跟踪在相机运动时会“跟丢”大家好我是老张在AI和智能硬件这个行当里摸爬滚打了十几年做过不少跟摄像头打交道的项目。不知道你们有没有过这样的体验用手机拍一段奔跑的宠物或者快速移动的车流然后想用某个AI工具去追踪画面里的每一个目标结果发现跟踪框要么乱跳要么干脆就跟丢了。尤其是在一些行车记录仪或者无人机航拍的视频里画面本身就在快速移动跟踪效果更是惨不忍睹。这背后的核心难题就是相机运动。传统的多目标跟踪算法比如大家熟知的DeepSORT、ByteTrack这些它们在做目标关联也就是判断下一帧里哪个框是上一帧哪个框时很大程度上依赖于一个叫做交并比IoU的度量。简单来说就是看两个框重叠的面积有多大。这个办法在相机固定不动的监控场景下很好用因为目标移动是平滑的上一帧的框和下一帧的框位置变化不大重叠面积自然就大。但是一旦相机本身开始快速移动或者晃动整个画面背景都在“漂移”目标在图像中的位置会发生剧烈、非线性的跳跃。这时候你上一帧预测的目标位置和这一帧检测到的真实位置它们的IoU可能直接变成0。算法一看“哦没有重叠这肯定不是同一个目标”于是就把一条好好的轨迹给切断了造成了ID切换ID Switch或者轨迹断裂Fragmentation。这就好比你在一个匀速前进的火车上看窗外匀速移动的树很容易判断树的移动轨迹但如果火车突然加速、减速或者转弯你想判断同一棵树的位置就变得极其困难。为了解决相机运动的问题前辈们想出了一个办法叫做相机运动补偿Camera Motion Compensation, CMC。主流的思路比如BoT-SORT是在每一帧图像到来时都去计算当前帧和上一帧之间的像素级运动变换比如一个仿射变换矩阵然后用这个矩阵把上一帧的预测框“掰正”到当前帧的视角下再去计算IoU。这个想法很直观但实测下来有个大坑计算开销太大了。为了估计这个变换矩阵通常需要提取并匹配两帧图像的特征点比如用ORB、SIFT算法这个过程非常耗时。我早年实测过在普通CPU上加入这个步骤后整个跟踪流程的帧率可能会从30FPS暴跌到5-10FPS这在很多实时应用场景里是完全不可接受的。所以我们面临一个两难的局面不用CMC跟踪在动态场景下不准用了CMC速度又跟不上。难道就没有一个既准又快的办法吗这就是我今天想跟大家深入聊的这篇AAAI 2024的工作——UCMCTrack。它提出了两个非常巧妙的思路来破局第一把目标从“图像平面”搬到“真实地面”第二用一套“均匀”的参数搞定整个视频的补偿告别逐帧计算。下面我就带大家一层层剥开它的技术内核。2. 核心思想一从“屏幕坐标”到“世界坐标”的降维打击UCMCTrack最让我拍案叫绝的一点是它看待问题的视角。我们包括大多数传统算法一直是在图像平面Image Plane这个二维世界里打转纠结于像素坐标u, v的变化。但UCMCTrack的作者换了个思路我们真正关心的是目标在真实三维世界里的运动状态。对于一个在路面上行走的行人或者行驶的车辆它在世界坐标系中的运动远比它在相机镜头这个“小窗口”里的投影要稳定和规律得多。2.1 找到地面的“锚点”那么如何把二维的检测框和三维的世界坐标联系起来呢这里需要一个关键的假设也是符合绝大多数地面监控场景的常识被跟踪的目标人、车是站立/行驶在地面上的。因此目标检测框的底边中点可以近似认为是目标与地面的接触点。这个点在图像上的坐标是u, v它对应着真实世界地面我们通常设为X-Y平面上的一个点x, y。这样一来我们的状态向量就从图像平面的框中心u, v, 宽, 高变成了地面坐标点及其速度x [x, y, x˙, y˙]。跟踪问题就变成了在三维世界的地面上预测并关联这些点的运动轨迹。这个转变是根本性的因为它剥离了相机运动带来的干扰。相机再怎么晃只是改变了“观察窗口”的角度和位置目标在地面上的实际位置和移动速度在短时间内是相对稳定、符合匀速运动模型的这正是卡尔曼滤波所擅长的。2.2 坐标转换的“数学桥梁”当然从u, v到x, y不是凭空想象的需要数学建模。这里用到了相机成像的几何模型。一个三维世界点x, y, z投影到二维图像点u, v的过程可以用一个包含相机内参焦距、主点和外参旋转、平移的投影矩阵来描述。UCMCTrack做了一个重要的简化既然我们只关心地面z0或者一个固定高度z0上的点那么就可以把复杂的投影关系简化为一个单应性变换Homography。简单理解就是找到了一个3x3的矩阵A使得下面的等式成立[ u, v, 1 ]^T A * (1/γ) * [ x, y, 1 ]^T这里的γ是一个缩放因子。这个矩阵A蕴含了相机的所有参数和地面的空间关系。一旦我们通过相机标定或者像论文里提到的用一些自动估计方法得到了矩阵A我们就可以通过求逆轻松地将图像点u, v反投影到地面点x, y[ x, y, 1 ]^T γ * A^{-1} * [ u, v, 1 ]^T在实际代码实现中这个计算是一次性的或者只需要在序列开始时计算一次开销极小。2.3 不确定性也跟着“投影”了这里有一个容易被忽略但至关重要的细节我们的检测器给出的边界框位置u, v本身是有噪声的比如一个行人框的底边中点可能因为遮挡或检测误差而上下浮动几个像素。这个噪声在图像平面上我们可以用一个协方差矩阵R_k^{uv}来表示通常它和检测框的宽高成正比框越大定位可能越不准。当我们通过矩阵A把这个点投影到地面时这个定位噪声也被“放大”或“扭曲”了。想象一下你拿一个手电筒照地面手稍微抖一下图像平面的噪声远处光斑的移动幅度地面平面的噪声会更大。UCMCTrack通过误差传播理论严谨地推导出了地面坐标点x, y的噪声协方差矩阵R。推导过程涉及一些矩阵求导核心思想是地面坐标x, y是图像坐标u, v的函数。已知u, v的协方差通过函数关系的雅可比矩阵就是函数在各个方向的变化率就能计算出x, y的协方差。最终公式是R F * R_k^{uv} * F^T其中矩阵F就是那个包含了投影参数和当前坐标x, y的雅可比矩阵。这一步确保了我们的卡尔曼滤波器在“地面坐标系”中运作时所使用的观测噪声是准确无误的这是滤波器能否稳定工作的基石。很多简化实现会忽略这一点直接使用一个固定的噪声值在视角变化大的场景下就容易失准。3. 核心思想二用“马氏距离”替代IoU实现精准关联当我们把目标和轨迹的状态都映射到地面坐标系后如何衡量当前一个检测框和已有的一条轨迹是否属于同一个目标呢传统方法用的IoU在这里彻底失效了——因为坐标系都换了。UCMCTrack请回了跟踪领域的一位“老将”马氏距离Mahalanobis Distance并给它做了一次“地面增强”。3.1 什么是马氏距离它比欧氏距离强在哪简单打个比方。假设我们要判断一个新来的同学是否属于“篮球特长生”这个群体。如果只看身高比如2米他很可能被归入。但如果同时考虑身高和体重发现他体重只有50公斤这就不太像篮球运动员的身材了。马氏距离的妙处就在于它不仅仅测量一个点到群体中心的“直线距离”欧氏距离还会考虑各个特征维度之间的相关性比如身高和体重通常是正相关的。它计算的是点与一个分布由均值和协方差矩阵描述之间的距离自动考虑了数据的“形状”。在跟踪中我们的卡尔曼滤波器会为每一条轨迹预测它在当前帧地面上的位置这个预测值有一个不确定性范围由协方差矩阵P描述。同时当前检测框投影到地面后也有一个观测噪声协方差矩阵R就是上一节算出来的。马氏距离就是计算观测值与轨迹的预测分布之间的距离。距离越小说明这个观测值越有可能来自这条轨迹。3.2 UCMCTrack的改进版马氏距离UCMCTrack计算的距离公式如下D ε^T * S^{-1} * ε ln(det(S))其中ε是观测值与预测值之间的残差向量。S是残差的协方差矩阵S H*P*H^T R它综合了预测的不确定性和观测的不确定性。第一项ε^T * S^{-1} * ε就是标准的马氏距离。第二项ln(det(S))是一个正则化项这是UCMCTrack的一个小创新。这个ln(det(S))项有什么作用呢行列式det(S)在几何上可以理解为由协方差矩阵S所形成的不确定性椭球的体积。当预测和观测都非常不确定时比如目标刚出现或者发生了剧烈遮挡S的行列式值会很大ln(det(S))也会变大从而增加距离D的值。这相当于给那些“模棱两可”的关联施加了一个惩罚让算法更倾向于选择那些确定性更高的匹配。在实际调试中我发现这个项对于减少在复杂场景下的错误关联很有帮助它像一个内置的“信心度”调节器。3.3 效果对比一眼看清优势我们可以用一个极端的例子来感受其威力。假设相机突然剧烈平移导致同一个目标在两帧之间的图像位置完全没有重叠IoU 0。对于依赖IoU的方法这两帧的目标根本无法关联轨迹必然断裂。但对于UCMCTrack情况就不同了。虽然图像上不重叠但通过投影到地面算法发现上一帧预测的目标在地面上的位置x1, y1和当前帧检测框投影到地面的位置x2, y2在考虑了运动模型和不确定性之后它们的马氏距离仍然很小。算法会自信地说“虽然镜头晃得厉害但从真实世界运动来看你们就是同一个目标” 这就实现了对相机运动的强鲁棒性。4. 均匀运动补偿如何用一套参数应对整个视频解决了“在哪里算”和“怎么算”的问题我们再来啃最硬的骨头补偿模型本身。传统的逐帧CMC计算量大根本原因在于它假设相机运动是频繁且不可预测的所以需要每两帧都重新估计一次。但UCMCTrack的作者提出了一个非常实用的观察在很多实际视频序列中相机的运动模式在短时间内往往是连续、均匀的。比如车载摄像头在一段直道行驶中其运动主要是前进和可能的轻微偏航是近似匀速的安防摄像头被风吹动其晃动也可能具有周期性。我们真的需要为每一帧都计算一个不同的补偿参数吗未必。4.1 将运动噪声模型化UCMCTrack的做法是不再显式地估计帧间的变换矩阵而是将相机运动对目标状态的影响建模为一种额外的过程噪声并融入到卡尔曼滤波的预测步骤中。在标准的匀速运动卡尔曼滤波模型中我们假设目标在相邻帧间是匀速直线运动。这个假设在相机运动时会被严重破坏。UCMCTrack引入了一个加速度噪声项 σ。它认为由于相机运动带来的“扰动”可以等效为目标在短时间内受到了一个未知的加速度。这个加速度会导致目标的位置和速度产生额外的变化。根据运动学公式在时间间隔Δt内由加速度引起的额外位置变化Δx 1/2 * σ * Δt^2由加速度引起的额外速度变化Δv σ * Δt4.2 更新过程噪声协方差矩阵在卡尔曼滤波中过程噪声的强度由矩阵Q来描述。在标准模型中Q通常是一个固定的对角阵。UCMCTrack则根据上述模型动态地构建一个与时间间隔Δt相关的噪声更新矩阵GG [ [0.5*Δt^2, 0], [Δt, 0], [0, 0.5*Δt^2], [0, Δt] ].T然后过程噪声协方差矩阵被更新为Q_k G * diag(σ_x^2, σ_y^2) * G^T这里的σ_x和σ_y是分别在X和Y方向上的加速度噪声强度它们是可学习的参数并且在整个视频序列中保持不变。这就是“均匀Uniform”一词的由来。算法在训练或初始化阶段会根据一部分序列数据学习出最优的σ_x和σ_y然后在整个跟踪过程中都使用这组参数。这样做的好处是巨大的它完全避免了每帧进行耗时的特征匹配和矩阵计算只需要在预测步骤中按照上面的公式更新一下Q矩阵即可计算开销几乎可以忽略不计。从“动态补偿”变成了“静态增强”用固定的参数来覆盖一段视频内相机运动的统计特性。5. 实战指南如何上手UCMCTrack理论说了这么多到底怎么用起来呢UCMCTrack的作者已经开源了代码基于PyTorch实现并且集成在了流行的跟踪框架BoxMOT中。下面我结合自己的使用经验给大家梳理一下关键步骤和踩坑点。5.1 环境搭建与依赖安装首先克隆官方仓库git clone https://github.com/corfyi/UCMCTrack.git cd UCMCTrack它的依赖比较干净主要是PyTorch、OpenCV、numpy等科学计算库。建议使用Python 3.8以上的环境并用conda创建一个独立环境来管理。conda create -n ucmctrack python3.8 conda activate ucmctrack pip install -r requirements.txt这里有个小坑原仓库的requirements.txt可能不会列出所有隐式依赖。如果你运行时遇到lap或者scipy等模块缺失手动pip install一下即可。我建议直接安装boxmot包它里面集成了UCMCTrackpip install boxmot5.2 核心参数解析与调优UCMCTrack的配置相对清晰主要需要关注以下几个参数它们通常在args.py或类似的配置文件中投影矩阵homography_matrix 这是整个算法的“钥匙”。你需要一个3x3的单应性矩阵将图像坐标映射到地面坐标。如何获取最准确的方法是进行相机标定。使用棋盘格标定板通过OpenCV的cv2.findHomography()函数可以计算出从图像平面到地平面的单应性矩阵。这需要你拍摄一张包含清晰地面参考物比如有规则网格的地面的图片。简易估计法如果无法进行精细标定论文也提到可以使用自动估计方法例如利用检测到的目标底边点假设它们在真实世界中是等间距的来近似估算。但这种方法误差较大适用于对绝对坐标精度要求不高的场景。格式在配置中它通常是一个9个元素的列表或一个numpy数组。噪声参数sigma_l和sigma_h 这对应着上一节提到的过程噪声强度σ_x和σ_y。它们控制了模型对相机运动扰动的容忍度。默认值代码中通常有默认值如[10, 10]。调优建议如果相机运动非常剧烈如手持剧烈晃动可以适当增大这两个值如[15, 20]让滤波器更“信任”观测值而不是预测值。如果场景相对静止可以减小它们如[5, 5]让轨迹更平滑。我的经验是从默认值开始在验证集上观察ID切换次数微调这两个参数直到效果最佳。马氏距离阈值maha_threshold 这是判断检测与轨迹是否关联的门槛。马氏距离超过这个阈值的关联将被拒绝。默认值通常在9.0左右对应卡方分布的95%置信区间。调优建议如果发现很多该关联的没关联上漏跟可以适当提高阈值如12.0。如果发现很多错误关联ID切换增多则应降低阈值如7.0。这个参数和噪声参数是联动的。最小轨迹存活帧数min_hits和最大丢失帧数max_age 这是多目标跟踪器的通用参数用于管理轨迹的生命周期。min_hits一个目标被检测到多少次后才输出为有效轨迹。设为1会立即输出但可能包含大量噪声设为3可以更稳定但会有初始延迟。max_age一条轨迹在丢失检测后还能保留多少帧。设置越长应对短暂遮挡的能力越强但也越容易产生漂移。5.3 运行你的第一个跟踪demo假设你已经准备好了一个视频文件test_video.mp4一个YOLOv8检测器的权重文件yolov8n.ptUCMCTrack通常与高性能检测器结合计算好的单应性矩阵H这里用一个单位矩阵示例实际必须替换一个最简单的调用boxmot中UCMCTrack的脚本如下import cv2 from boxmot import UCMCTrack from ultralytics import YOLO # 初始化检测器 detector YOLO(yolov8n.pt) # 初始化UCMCTrack跟踪器 # 注意这里的 homography_matrix 需要替换为你实际计算出的3x3矩阵 homography_matrix [[1, 0, 0], [0, 1, 0], [0, 0, 1]] # 示例无效矩阵 tracker UCMCTrack( model_weightsUCMCTrack.ultralytics, homography_matrixhomography_matrix, sigma_l[10, 10], # 过程噪声强度 maha_threshold9.0, min_hits3, max_age30 ) # 打开视频 cap cv2.VideoCapture(test_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break # 使用YOLO检测 results detector(frame, verboseFalse)[0] detections results.boxes.data.cpu().numpy() # 格式: [x1, y1, x2, y2, conf, cls] # 更新跟踪器 tracks tracker.update(detections, frame) # tracks: [x1, y1, x2, y2, id, conf, cls] # 在帧上绘制结果 for track in tracks: x1, y1, x2, y2, track_id, conf, cls_id map(int, track[:7]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, fID:{track_id}, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2) cv2.imshow(UCMCTrack Demo, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()重要提醒上面的homography_matrix是单位矩阵这只是为了代码能跑通。在实际使用中你必须替换为自己场景下计算出的真实单应性矩阵否则地面投影将完全错误跟踪效果可能还不如传统方法。5.4 在不同场景下的表现与调参心得在我自己的测试中UCMCTrack在以下几类场景下提升尤为明显车载前视摄像头车辆自身的加减速、转弯会导致画面全局运动。传统跟踪器ID切换频繁UCMCTrack由于映射到地面轨迹非常稳定。无人机航拍无人机飞行带来的画面旋转和平移非常复杂。UCMCTrack的均匀补偿机制在这里表现突出无需为剧烈的帧间运动支付大量计算成本。手持移动拍摄比如用手机跟踪街上的行人。相机抖动不规则但UCMCTrack学习到的固定噪声参数能够很好地平滑这种扰动。调参的核心心法是观察失败案例。如果跟踪框在目标上“抖动”厉害可能是过程噪声sigma设小了或者马氏距离阈值maha_threshold设高了导致滤波器过于相信预测而忽略观测。如果频繁出现ID切换尤其是在相机运动时首先检查单应性矩阵H是否准确这是最关键的其次可以尝试增大sigma和max_age。6. 横向对比UCMCTrack vs. 其他主流跟踪器为了让大家更直观地了解UCMCTrack的优劣我将其与几个经典和前沿的跟踪器在相机运动场景下做了一个简单的对比分析。特性/跟踪器DeepSORTBoT-SORTOC-SORTUCMCTrack核心关联度量马氏距离 外观特征IoU 外观特征 CMCIoU 运动平滑约束投影马氏距离处理相机运动弱依赖外观特征重识别强但逐帧计算CMC耗时中等通过轨迹插值缓解强地面投影 均匀噪声补偿计算效率高低(CMC是瓶颈)高高(离线/一次性计算H矩阵)需要额外信息无需要计算帧间变换无需要单应性矩阵H在动态场景下的IDF1较低高但速度慢中等高且实时性好适用场景静态或低速相机对精度要求极高算力充足的场景相机运动适中遮挡复杂的场景相机中高速运动且可获取地面信息的场景深度分析DeepSORT它是很多项目的起点但在纯动态场景下其马氏距离在图像平面计算很容易失效严重依赖外观特征库计算量也大。BoT-SORT它认识到了CMC的重要性是UCMCTrack重要的对比对象。但其“逐帧计算”的模式是性能瓶颈。在实际工程部署特别是边缘设备上这个开销很难承受。OC-SORT它另辟蹊径不显式补偿相机运动而是通过设计更聪明的轨迹管理逻辑如轨迹间关联、平滑性约束来对抗相机运动带来的关联错误。它在许多基准测试上表现很好且无需额外信息。但在相机运动极其剧烈、目标密度高的场景其性能可能不如显式进行几何建模的方法。UCMCTrack它找到了一个巧妙的平衡点。通过引入先验的几何信息地面将问题转化到一个更稳定的坐标系一劳永逸地解决了视角变化问题。同时用“均匀补偿”的轻量级方式替代了重量的逐帧计算。它的优势在于原理清晰、效率高、在适配场景下鲁棒性极强。其代价是需要一个单应性矩阵这在一定程度上限制了其泛化性比如没有明确地面的水上或空中场景。7. 总结与展望UCMCTrack给我们的启示通篇看下来UCMCTrack与其说是一个颠覆性的新算法不如说是一次极其出色的工程思想整合。它没有提出全新的神经网络结构而是将计算机视觉中的多视角几何单应性变换、经典的状态估计理论卡尔曼滤波和实际工程中的效率约束均匀补偿巧妙地结合在一起解决了一个非常具体的痛点——动态相机下的鲁棒跟踪。从我个人的项目经验来看这类工作的价值巨大。在AI落地过程中我们常常沉迷于堆叠更深的网络、更大的数据集却忽略了那些经典、轻量但有效的先验知识和物理约束。UCMCTrack的成功告诉我们有时候换一个更本质的坐标系看问题比在原有坐标系里用更复杂的模型去拟合要有效得多。当然它也不是银弹。对单应性矩阵的依赖是其主要限制。未来的改进方向可能会集中在如何在线地、自适应地估计或更新这个投影关系比如结合SLAM中的一些思路或者利用深度学习模型直接从图像中预测出稳定的特征对应关系。此外对于非地面场景如篮球比赛、室内机器人如何定义一个新的稳定参考系也是一个有趣的开放问题。对于正在考虑在动态视觉场景中应用多目标跟踪的工程师我的建议是如果你的场景满足“目标基本在地面或一个已知平面运动”这一条件并且你能够通过标定或其他方式获取到那个关键的投影矩阵那么UCMCTrack绝对应该成为你的首选方案之一。它的代码简洁思路清晰集成方便在保证实时性的前提下能为你带来显著的跟踪稳定性提升。