简介面向视觉跟踪研究者与相关滤波方向学习者STRCF算法Spatial-Temporal Regularized Correlation Filters是哈工大提出的实时跟踪算法通过加入时间正则项防止模型腐败并采用ADMM迭代求解提升实时性可应用于目标跟踪、视频监控、无人系统等场景。这份资源将原始论文、原理讲解与Matlab实现整合在一起方便对照复现和二次开发。rar压缩包共22个文件整体约24.46MB核心内容包括CVPR 2018论文PDF、STRCF-Master源码压缩包、原理图文HTML页面及配套示意图阅读时可结合图示理解算法流程调试时可直接基于Matlab代码修改参数快速跑通实验。已有443人学习。借助该资源学习者可以快速理清时间正则项与ADMM更新的数学推导掌握相关滤波跟踪器的工程实现细节同时能依据自带源码开展遮挡、形变等难点场景的实验对比为后续改进或论文复现提供扎实基础。1. 从 DCF 到 STRCF目标跟踪里空间和时间正则化到底补了什么目标跟踪里有这么一类常见翻车目标被遮挡几帧或者光照突变DCF 跟踪器的框就跳走死死钉在一块背景上。STRCFSpatial-Temporal Regularized Correlation Filters时空正则化相关滤波器就是为压住这种漂移提出的经典判别相关滤波算法论文原稿、MATLAB 实现和原理说明经常被打包成一张 rar 在同学之间流传。它做的事可以概括成两句空间正则化让滤波器不学背景时间正则化让滤波器在相邻帧之间更新得平滑。这篇笔记从目标函数拆起把 ADMM 求解在 MATLAB 里跑通再用 OTB 序列验证效果最后列出复现时最容易踩的坑。适合正在复现论文、做相关滤波改进或准备课程设计的学生和工程师。2. STRCF 的原理拆解目标函数与 ADMM 求解的 5 个关键符号2.1 目标函数怎么读空间正则化和时间正则化各自的物理含义判别相关滤波DCF的做法是用循环移位构造大量训练样本训练一个岭回归分类器。它速度快但循环移位会在图像边界产生不存在的“虚拟样本”。滤波器一旦学到这些边界位置的特征跟踪框就会往背景偏这就是常说的边界效应。SRDCF 用空间正则化权重惩罚滤波器在边缘位置的系数相当于把学习注意力限制在目标区域内。STRCF 继承了这项思路同时额外加了一项时间正则化约束相邻两帧的滤波器变化不能太大。直观理解就是模板更新加了阻尼目标被遮挡或快速形变的那几帧滤波器不会一下子被污染成背景。代价是目标函数比 DCF 多了一整项不能像传统相关滤波那样直接写出闭式解必须用 ADMM 迭代求解。STRCF 的目标函数写出来是下面这个最小化问题min_f 0.5 * sum(|| y(t) - sum_k x_k(t) * f_k ||^2) 0.5 * sum(|| w * f ||^2) 0.5 * mu * || f - f_{t-1} ||^2第一项是回归误差y(t) 是期望响应x_k(t) 是第 k 个通道的循环样本f_k 是第 k 个通道的滤波器。第二项里的 w 是空间正则化权重图目标中心区域 w 接近 0远离中心的背景区域 w 很大这样滤波器系数就只在目标区域“用力”。第三项就是时间正则化mu 控制当前滤波器与上一帧滤波器 f_{t-1} 的偏差惩罚强度。2.2 ADMM 拆解把带时间正则的优化拆成三个可算的子问题带第三项的目标函数已经没有闭式解STRCF 的解法是用 ADMM交替方向乘子法。常规套路是引入一个辅助变量 g并加上约束 f g把原问题改写成增广拉格朗日形式然后交替更新三个变量滤波器 f、辅助变量 g、拉格朗日乘子 L。更新 f 子问题时利用循环矩阵在频域可对角化的性质把 f 的求解转到频率域。因为整个回归项在频域变成逐元素运算每个频点都能独立求解这一步是 STRCF “还能跑得快”的根本原因。更新 g 子问题时空间正则化和时间正则化都在这一项里做的是一次逐元素软阈值shrinkage操作没有矩阵求逆。最后更新对偶变量 L完成一轮迭代。三个子问题对应的核心符号可以记成一张表复现前先熟悉它们的角色。符号含义在代码里的常见命名f / F当前帧滤波器时域 / 频域形式filt, FiltFg / GADMM 辅助变量承载正则项约束G, G_preL拉格朗日乘子L, L_premu时间正则化权重控制更新平滑度mu, temporal_regrhoADMM 惩罚步长控制收敛速度rho, admm_penalty参数说明mu 和 rho 是 STRCF 里最影响手感的一对。mu 太大滤波器更新迟缓目标快速运动时容易脱框mu 太小时间正则化形同虚设退化成带空间正则的 SRDCF。rho 决定每一轮 ADMM 对约束 fg 的拉紧程度一般在 0.01 到 0.1 之间或者做固定迭代轮数时改用递增 rho 的计划。2.3 三个超参数的敏感度与一个可执行的最小调参步骤光看公式很难体会这些参数具体影响什么建议用一个固定序列做对比实验。我的做法是拿 OTB 里的 Basketball 序列分别把 mu 设成 0.1、1、10观察跟踪框在球员快速变向时的表现。mu 0.1 时框会跟着目标抖偶尔漂到身体边缘mu 10 时目标已经跑远了框还停在原地。只有在 mu 落在 1 附近时框既跟得上又不会乱跳。空间正则化权重 w 的强度由 base 值和衰减速度决定。常见的做法是以目标中心为原点背景区域给一个固定大小的惩罚值中心区域设成 0中间用平滑过渡。这个权重不常调但当你发现跟踪框“半只脚踩在背景上”时优先加大背景惩罚值而不是去动学习率。ADMM 迭代轮数是个容易被忽略但影响很大的参数。原论文里每帧只迭代 2 到 5 轮就能得到可用结果因为每一轮都在频域做精确更新多迭代对精度的提升非常有限。实际复现时我从 50 轮往下减减到 3 轮发现结果几乎不变帧率却快了近 4 倍。最小可执行调参步骤是先固定 rho把 mu 从 0.1 到 10 按对数间隔取 4 个值再固定 mu把 ADMM 迭代轮数从 2 到 5 逐个试最后回到空间正则化背景惩罚值观察跟踪框边缘是否紧贴目标。三步都做完一个序列上的行为基本就摸清楚了。3. 在 MATLAB 里跑通 STRCF脚本结构与最小复现步骤3.1 压缩包里的典型工程结构先别急着跑主函数解压一个流传的 STRCF MATLAB 工程包后文件组织的形态大同小异。常见做法是一个 run_tracker.m 或 run_demo.m 作为总入口一个 STRCF.m 或者类似名字的跟踪器核心类放主循环和滤波器更新feature 目录放 HOG 提取函数utils 目录放视频读取、结果保存、画框的辅助脚本顶层还躺着一份论文 PDF 和 README。我第一次复现时直接点运行 run_tracker.m结果报错找不到视频序列。看了 README 才发现要先下载 OTB 数据集并修改数据集路径变量。这个顺序不要反先准备好 OTB 的目录结构再打开 run_tracker.m 改路径最后才运行。否则会浪费大量时间在环境配置的报错上。不同的复现版本对数据集路径的要求不同有的写成 OTB100 子目录有的只给一个根目录让代码自行拼接改路径时先看清全局变量名。3.2 跟踪器主循环初始化、检测与更新无论压缩包里代码怎么组织跟踪器主干的逻辑都逃不开三件事第一帧用给定标注框初始化后续每一帧在上一帧位置附近提取特征做检测最后用检测结果更新滤波器。一张伪代码骨架可以直接套用。% 主循环逐帧处理视频序列 for frame 1 : seq.frameNum im imread(fullfile(video_path, img_files{frame})); if frame 1 % 初始化根据 groundtruth 第一帧的 [x, y, w, h] % 提取目标区域 HOG 特征初始化滤波器 f 和辅助变量 g target_pos [gt(1) gt(3)/2, gt(2) gt(4)/2]; target_sz [gt(3), gt(4)]; [f, G, L] init_filter(feature, target_sz, params); else % 检测在 target_pos 周围裁剪一块上下文区域 % 提取特征后与滤波器做相关得到响应图 im_patch get_search_window(im, target_pos, target_sz, pad); feat get_feature(im_patch, cell_size); resp real(ifft2(sum(conj(F) .* F_feat, 3))); % 响应图最大值位置换算回图像坐标 [peak, disp] locate_peak(resp, target_sz, cell_size); target_pos target_pos disp; end end逻辑说明初始化分支只在第一帧进入用标注框裁剪目标区域并提取特征滤波器 f 的初值可以直接设成期望响应 y 在频域与特征共轭的比值。检测分支的响应图是频域逐通道相乘后累加再反变换回时域的结果峰值位置就是目标在当前帧的位移。注意峰值定位要把响应图坐标乘以 cell_size 再换算回原始像素坐标这里漏掉尺度换算是跟踪框错位的头号原因。参数说明pad 表示搜索区域相对目标大小的扩充倍数常见取 2.5 到 3 倍。cell_size 是 HOG 特征细胞大小STRCF 通常取 4 像素太大会丢失细节导致定位不准太小会让特征维度暴涨拖慢速度。target_sz 直接决定搜索窗口大小它被尺度估计更新后下一帧的搜索范围会跟着变。3.3 滤波器更新的 ADMM 实现要点滤波器更新是 STRCF 的核心也是代码里最需要仔细核对的部分。网上流传的 MATLAB 实现风格各异但 ADMM 三个子问题的骨架基本一致。% ADMM 更新滤波器每帧迭代若干轮 for iter 1 : max_iter % 子问题 1更新频域滤波器 F % 频域逐元素求解分母是特征自相关加惩罚项 F (conj(F_feat) .* Y rho * (G - L)) ./ ... (conj(F_feat) .* F_feat rho eps); % 子问题 2更新辅助变量 G带空间与时间正则 % 先算中间量再做逐元素收缩 Z F L mu / rho * F_pre; G max(0, 1 - params.w / (rho * abs(Z))) .* Z; % 子问题 3更新对偶变量 L L L rho * (F - G); % 保存当前滤波器作为下一帧的时间正则参考 F_pre F; end逻辑说明子问题 1 里的名称都是频域量F_feat 是当前帧特征图的 FFTY 是期望响应高斯峰的 FFTrho 是 ADMM 惩罚步长eps 防止分母为 0。这里所有运算都是逐元素点乘和点除没有显式矩阵求逆。子问题 2 的收缩操作是关键像素位置的正则权重 w 越大收缩越厉害对应滤波器系数被压向 0同时 F_pre 带着上一帧信息进入当前更新这就是时间正则的具体落点。子问题 3 只是把约束残差累加到乘子 L 上标准的 ADMM 收尾。参数说明max_iter 常见取值是 2 或 3不需要等 ADMM 完全收敛。rho 在这个实现里同时出现在三个子问题里它的绝对值会影响收敛速度和最终精度。实践中如果发现滤波器更新后响应图出现高频噪声先检查 rho 是否偏小如果跟踪框反应迟钝先检查 rho 是否偏大。F_pre 的引入让时间正则项无需单独计算二范数代码上更简洁但一定要在每帧结束时更新它否则时间正则就失去作用。3.4 HOG 特征与尺度估计参数怎么设STRCF 的默认特征通常是 HOGMATLAB 里常用 Piotr Dollar 的 fhog 实现少数版本会加灰度特征做通道拼接。提取特征时输入的是裁剪好的图像块输出是三维数组高、宽、通道数。fhog 的 cell_size 与金字塔层的 bin 数决定特征维度固定模板大小不同特征图大小也会不同。复现时最容易出现的坑是训练时特征图尺寸和检测时不一致导致频域相乘时维度报错或隐式广播出错误结果。一个实用的特征提取片段如下% 提取 HOG 特征输入为裁剪后的图像块 im_patch % cell_size 取 4输出特征图高度宽度为原图 / cell_size feat fhog(im2double(im_patch), cell_size, 9); % 去掉最外圈的边界行和列fhog 默认带 1 像素额外边框 feat feat(2:end-1, 2:end-1, :); % 可选拼接一个灰度通道增强稳定性 feat cat(3, feat, im2double(rgb2gray(im_patch)) / 255);逻辑说明fhog 默认会在特征图外层补一圈边界不裁掉会让后续频域尺寸对不上。拼接灰度通道是相关滤波里的常见增强手段对光照变化和低纹理目标有改善代价是每个像素多一个通道矩阵运算量略微上升。如果目标本身是灰度视频直接读入灰度图再提取 fhog 也能跑但某些版本的 fhog 内部会强制做颜色空间转换处理灰度图时容易警告。尺度估计并不是 STRCF 原始目标函数的一部分工程上是叠加在检测结果之后的后处理。常见做法是准备一个尺度池比如 7 个尺度从 0.98 到 1.04 均匀分布每个尺度重新裁剪图像块提取特征计算响应峰值选响应最大的尺度作为当前帧目标大小。尺度池范围太大会显著拖慢速度每帧多 7 次完整特征提取太小则对目标缩放不敏感。4. 用 OTB 验证效果成功率/精度图背后的三个评价细节4.1 OTB 序列怎么读、怎么批量跑OTB 数据集的目录结构一般是一个总目录下面每个序列一个子目录子目录里放着 img 文件夹和 groundtruth_rect.txt。groundtruth 文件每一行是 [x, y, w, h]注意是左上角坐标加宽高不是中心点坐标。批量跑之前一定要先写一个读取脚本把每个序列的图像文件名、真值框、起始帧和结束帧整理成结构体数组。% 批量跑 OTB 所有序列结果保存到 results 目录 seq_names get_otb_sequence_list(OTB100); for i 1 : numel(seq_names) seq load_otb_sequence(fullfile(otb_path, seq_names{i})); rects run_strcf_on_sequence(seq, params); % 保存成 txt与 groundtruth_rect.txt 格式保持一致 save_result(rects, fullfile(results/STRCF, [seq_names{i} .txt])); end逻辑说明run_strcf_on_sequence 内部就是上一个章节的主循环返回每一帧的矩形框。保存格式必须和 groundtruth 完全一致否则评估脚本读出来都是错位数据。很多流传的评估代码要求结果文件里没有表头、没有空行行数必须等于序列总帧数。少一帧或多的空行都会让评估脚本在某个序列上报错。4.2 成功率图与精度图的算法规格OTB 评估在所有帧上统一计算两个指标。精度图统计中心位置误差小于给定阈值的帧占总帧数的比例画出来是一条从左上向右下延伸的曲线报告值通常是 20 像素处的精度。成功率图统计重叠率也就是预测框与真值框的 IoU 大于阈值的帧占比报告值用曲线下面积 AUC。这两张图缺一不可只看数值不看曲线的对比没有意义。一个常见误解是直接用所有帧的平均重叠率来比较算法。OTB 标准做法是先逐帧算 IoU再统计 IoU 大于阈值的比例最后画成功率曲线。不同阈值下的成功率差异能反映算法的定位稳定性和尺度估计能力。比如目标从远到近逐渐变大时固定尺度的跟踪器成功率会在后半段大幅下降。评估脚本的骨架大致如下% 单序列评估计算精度与成功率 overlap zeros(numel(rects), 1); center_err zeros(numel(rects), 1); for t 1 : numel(rects) overlap(t) compute_iou(rects{t}, gt{t}); center_err(t) sqrt((rects{t}(1)rects{t}(3)/2 - gt{t}(1)-gt{t}(3)/2)^2 ... (rects{t}(2)rects{t}(4)/2 - gt{t}(2)-gt{t}(4)/2)^2); end % 精度center_err 小于 20 像素的比例 precision mean(center_err 20); % 成功率遍历 IoU 阈值从 0 到 1画 AUC 曲线逻辑说明OTB 里的中心位置误差用的是目标框中心点而不是左上角换算时把宽高一半加上去再求欧氏距离。如果直接用左上角坐标算误差所有序列的分数都会虚高因为目标越大左上角偏移被放大的程度越不一样。compute_iou 记得处理空框和越界框目标完全离开画面时返回 0 而不是报错。4.3 和 DCF/SRDCF 对比时该看哪些数字复现 STRCF 后最直接的验证方式是同数据集上和 DCF、SRDCF 做横向对比。公开论文里 STRCF 相比 SRDCF 在成功率 AUC 上通常高 2 到 4 个点在速度上快数倍因为用了 ADMM 之后不需要每帧做大规模矩阵求逆。自己复现时正常情况下 AUC 差 0.03 以内都算正常差超过 0.05 就要怀疑实现细节了。速度是最容易暴露问题的地方。MATLAB 版本、机器核心数、HOG 特征实现、ADMM 迭代轮数都会影响帧率。我在同一台机器上测过迭代 50 轮的版本只有 2 fps迭代 3 轮的版本跑到 12 fps而成功率只掉了不到 1 个点。如果你复现的版本连 5 fps 都达不到优先检查是不是迭代轮数被写成了固定大值或者每帧都在重新分配大数组。对比实验还要注意评估协议的差异。有的代码默认用 OTB-50有的用 OTB-100两者数值没有可比性。少数复现版本会自行修改初始帧或者跳过某些序列导致 AUC 虚高。跑对比前先确认评估脚本包含的序列列表和官方一致。5. STRCF 常见翻车现场与排错经验5.1 一开跑就报 Index exceeds matrix dimensions现象运行 run_tracker.m 后立刻报错提示索引超出矩阵维度断点停在读图或读取 groundtruth 的地方。原因八成是路径配置问题。视频序列没有找到、groundtruth 路径指向了另一个序列、或者图像文件名从 0001.jpg 到 0750.jpg 但代码里写成从 1.jpg 开始都会让索引越界。少数情况是目标框数值本身超过图像尺寸数据本身的标注与图像不匹配。解决在读取图像和 groundtruth 的位置加上断点打印视频路径和文件列表前三个文件名确认完整路径是否拼接正确再打印每一行 groundtruth 的最大值和图像的行列数对比。路径问题占这类报错的八成以上代码逻辑本身很少有问题。5.2 跟踪框在快速运动序列上抖出残影现象目标快速左右移动时跟踪框跟着目标但明显滞后框的尺寸一帧大一帧小响应图出现双峰。原因搜索窗口太小目标跑出窗口范围后滤波器检测到的是半个目标加半个背景峰值位置不稳定。另一个原因是时间正则化权重 mu 太大滤波器更新速度跟不上目标位置变化。解决把搜索窗口 padding 从 2.5 增大到 3.5 或 4同时把 mu 调低 30% 到 50% 对比。如果在快速运动序列上仍然抖动再把 ADMM 迭代轮数从 2 提到 3多数情况下轮数增加能改善滤波器瞬态响应的精度。5.3 遮挡恢复后框被背景粘住现象目标被遮挡 30 帧左右遮挡结束后框停在遮挡物上而且到序列结束都没有回到目标上。原因遮挡期间滤波器持续用被遮挡区域的样本更新时间正则化只减缓污染速度不能完全阻止污染。如果代码里更新条件里没有加峰值阈值判断那么每帧都在“认真学背景”恢复后自然找不回目标。解决在检测阶段计算响应图的峰值旁瓣比峰值过低时暂停滤波器更新只保留位置预测。这是相关滤波家族通用的抗遮挡技巧STRCF 的时间正则化能延缓漂移但为了在遮挡场景下稳定配合响应质量判断是工程上的标配做法。5.4 用不同 fhog 实现跑出差异很大的结果现象把代码从一台机器搬到另一台机器或者换了一个 fhog 实现成功率 AUC 掉了好几个点。原因不同版本的 fhog 在边界处理、归一化方式、梯度方向数上存在细节差异。有的实现返回的特征图带额外边框有的不带有的内部做了像素值范围归一化有的直接吃原始 uint8。这些差异会直接影响滤波器训练的数据分布。解决把特征提取函数固定下来不要在多个实现之间切换。换机之后先跑一个序列对比特征图的尺寸和数值范围是否一致。如果两个版本的 fhog 输出完全相同再继续大批量实验否则只保留一个版本作为基准避免复现结果不可比。5.5 图像 padding 不一致导致 OTB 分数差好几个点现象自己的跟踪器在某个序列上跟踪很稳但成功率曲线与论文结果差了一大截。原因特征提取前的图像填充方式不同。相关滤波需要在目标周围填充像素来构造大样本常见做法有填充零、填充边缘像素、镜像填充、填充平均灰度四种。不同填充方式对边界区域的响应影响差别很大尤其当目标靠近图像边缘时。解决统一采用镜像填充也就是 imfilter 的 symmetric 模式这是相关滤波里比较稳妥的默认选择。填充零会让边界响应异常放大镜像填充不会引入不存在的强边缘。检查代码里裁剪图像块用的函数确认 padding 参数是 symmetric 而不是常数填充。5.6 ADMM 收敛性导致的隐式失败现象程序正常运行、不报错但跟踪框总比真实目标小一圈或者目标大小变化时框不跟着变。原因ADMM 子问题 1 的分母里 rho 和 eps 相对特征自相关的尺度不匹配。自相关项数值在几百的数量级时rho 取 0.01 会让分母接近特征自相关本身rho 几乎不起作用rho 取 10 则会把分母主导成常数滤波器退化成单纯的时间平滑。这种问题很难从单帧图像上看出表现为整体性能平庸。解决把子问题 1 的分母量级打印出来统计最大值和均值再把 rho 设成与分母均值同一个数量级。一个最直接的验证方法是把 rho 从大到小按对数取 5 个值在同一序列上画响应图峰值变化选择收敛后最平稳的那个值。这种参数和数值量级绑定的问题不打印中间变量很难定位。6. 把 STRCF 改到自己的序列上超参设定技巧与实际收益如果你的序列有自己的特点STRCF 的默认参数不必照单全收。无人机航拍视角下目标通常很小HOG cell_size 从 4 改成 3 能明显提升小目标定位精度代价是特征维度变大速度下降约 20%。红外序列里目标缺乏纹理只靠 HOG 很难稳定跟踪此时把灰度特征权重提高或者直接拼一个方向直方图特征比单纯调 mu 有效。尺度池设计也有讲究。固定用 7 个尺度并不适合所有序列目标出现快速尺度变化时7 层尺度池覆盖范围不够。我一般起始尺度池取 5 层尺度步长 1.03帧率先测一轮如果跟踪框尺度的更新频率明显低于位置更新再把池扩到 9 层、步长改成 1.02。追求速度时优先砍尺度池而不是砍 ADMM 迭代轮数尺度池每多一层就多一次完整特征提取而 ADMM 多一轮迭代只是在原有特征上做几次点乘。一个值得尝试的进阶方案是用深度特征替换 HOG。把 fhog 换成轻量卷积网络在相关滤波层输出的特征图STRCF 的空间和时间正则化结构完全不用动ADMM 求解照常。深度特征对形变和语义变化更鲁棒但提取特征本身的耗时不可忽略如果帧率预算紧张可以只在每隔几帧做一次全分辨率特征提取中间帧用浅层特征保持速度。这个替换在实际实验中能以少量速度换明显的成功率增益是 STRCF 相关方向最常见的改进发力点。最后说我个人的习惯每拿到一个新序列先跑一遍原始默认参数记录下来再调整参数后对比而不是直接凭感觉改。跟踪器这种系统参数之间的耦合远比单个参数单独作用更明显mu、rho、尺度池、学习率四个变量每次只动一个才能看清谁在起作用。我自己栽过最深的跟头就是同时改了三个参数效果变差后完全不知道是谁拖了后腿。后来固定这个“一次一动”的习惯调参效率提高了不少。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?