将KCF应用于Tracking by detection的总结

1、前言

由于目前基于深度学习的detection在实际使用中仍然存在很多的误检和漏检,显然在实际使用中无法只依赖detection模块作为感知,而传统MOT也存很多因素导致追踪目标丢失必须重新手动选择的问题,因此,Tracking by detection成为一个很重要的研究方向,而Tracking by detection中前后帧的目标匹配算法成为研究重点,目前已经有很多相关的研究,诸如前几年的Beyond Pixels: Leveraging Geometry and Shape Cues for OnlineMulti-Object Tracking,和最近利用深度学习提取特征的Towards Real-Time Multi-Object Tracking,前者同时利用了先验的几何信息结合深度学习提取特征,但由于计算量大,几乎无法实现real-time,autoware的开源代码中实现了一版将其深度学习部分去掉的版本,实际应用效果不错,由于只涉及几何计算,速度很快,几乎无延迟,但对于场景和设备安装要求很高;后者是一个很有前景的方向,将检测与特征提取集成在一个网络中实现,是一个很有前景的发展方向,但是目前其数据集有限,检测目标种类较少,需要自行筹备数据集进行训练,因此先持观望态度。
最终选择了apollo系统中应用的方案,将前几年十分火爆的KCF算法中的部分提取出来,作为目标匹配算法使用,结合一个单独的检测器,整体效果还算不错。

2、KCF

关于KCF的详细解析已有很多文章加以描述,这里主要记录下自己学习中的一些理解和遇到的坑,具体就不扩充了。论文主要介绍了一种使用核方法来区分目标是否是追踪目标或是背景的方法,其主要使用方法很简单,是一种基于核方法的岭回归,整体的训练方式和预测都跟岭回归基本相同(因此论文第二部分详细介绍了岭回归),其最大的亮点在于使用循环矩阵和傅立叶变换的思路大大加快了计算。其整体流程如下:

  • 1、遵循循环矩阵的方式扩展样本
    如下图所示
    将KCF应用于Tracking by detection的总结_第1张图片假如追踪块是m*n矩阵,则可以扩充为m*n个样本,每个样本大小都跟追踪块一样大为m*n,生成的样本矩阵叫做块循环矩阵,详见循环矩阵傅里叶对角化中二维的部分。
  • 2、使用样本矩阵计算核矩阵
    关于核方法,其本质是将一个非线性可分问题转化为一个线性可分问题,当样本在其原本空间(低维度)中线性不可分时,使用一个 ϕ ( x ) \phi(x) ϕ(x)将其转化到高维空间中,使原问题在高维空间变成一个线性可分问题,核方法使得我们并不需要对所有样本调用 ϕ ( x ) \phi(x) ϕ(x),因为模型的训练和使用过程中都只需要用到 z T z , z = ϕ ( x ) z^Tz,z=\phi(x) zTz,z=ϕ(x),因此定义 K ( x , y ) = ϕ ( x ) T ϕ ( y ) K(x,y)=\phi(x)^T\phi(y) K(x,y)=ϕ(x)Tϕ(y),K为使用的具体核函数,例如高斯核函数,其本质上就是将原样本提高了维度(反推可以得到 ϕ ( x ) \phi(x) ϕ(x))因此我们整个过程中只需要使用这个核函数即可。论文中关于核方法的岭回归,其训练模型主要是为了得到 α \alpha α,可以对应为岭回归中的权重w,而 α \alpha α的求解与核矩阵K(核矩阵可以对应为岭回归中的样本矩阵X)直接相关,而核矩阵实际上就是每个样本依次与所有样本作核计算得到的向量叠成的矩阵,原样本为 x 0 , x 1 , . . . , x n − 1 x_0,x_1,...,x_{n-1} x0,x1,...,xn1,则核矩阵如下:
    X = [ k ( x 0 , x 0 ) k ( x 0 , x 1 ) … k ( x 0 , x n − 1 ) k ( x 1 , x 0 ) k ( x 1 , x 1 ) … k ( x 1 , x n − 1 ) ⋮ ⋮ ⋱ ⋮ k ( x n − 1 , x 0 ) k ( x n − 1 , x 1 ) … k ( x n − 1 , x n − 1 ) ]   ( 1 ) X=\begin{bmatrix}\\ k(x_0,x_0) & k(x_0,x_1) & \dots & k(x_0,x_{n-1}) \\ k(x_1,x_0) & k(x_1,x_1) & \dots &k(x_1,x_{n-1}) \\ \vdots & \vdots & \ddots & \vdots \\ k(x_{n-1},x_0) & k(x_{n-1},x_1) & \dots &k(x_{n-1},x_{n-1}) \\ \end{bmatrix} \ (1) X=k(x0,x0)k(x1,x0)k(xn1,x0)k(x0,x1)k(x1,x1)k(xn1,x1)k(x0,xn1)k(x1,xn1)k(xn1,xn1) (1)
    由于样本是循环的,则有 P k x = P k m o d n x P^{k}x=P^{kmodn}x Pkx=Pkmodnx,主要用于k为负数时计算,若核函数满足 k ( x , y ) = k ( P n x , P n y ) k(x,y)=k(P^nx,P^ny) k(x,y)=k(Pnx,Pny)(其中P为初等行变换矩阵)则可以写成如下形式:
    X = [ k ( x 0 , x 0 ) k ( x 0 , x 1 ) … k ( x 0 , x n − 1 ) k ( x 0 , x n − 1 ) k ( x 0 , x 0 ) … k ( x 0 , x n − 2 ) ⋮ ⋮ ⋱ ⋮ k ( x 0 , x 1 ) k ( x 0 , x 2 ) … k ( x 0 , x 0 ) ]   ( 2 ) X=\begin{bmatrix}\\ k(x_0,x_0) & k(x_0,x_1) & \dots & k(x_0,x_{n-1}) \\ k(x_0,x_{n-1}) & k(x_0,x_0) & \dots &k(x_0,x_{n-2}) \\ \vdots & \vdots & \ddots & \vdots \\ k(x_{0},x_1) & k(x_{0},x_2) & \dots &k(x_{0},x_{0}) \\ \end{bmatrix}\ (2) X=k(x0,x0)k(x0,xn1)k(x0,x1)k(x0,x1)k(x0,x0)k(x0,x2)k(x0,xn1)k(x0,xn2)k(x0,x0) (2)
    K即为循环矩阵。
    这个一维形式还比较容易推导和理解,而二维形式个人花了很多时间去理解,实际上其推导与一维形式基本一致,只是在表现形式上不同,一维原样本集可以写成:
    X = [ x 0 , x 1 , . . . , x n − 1 ] T   K = ϕ ( X ) ϕ ( X ) T X=[x_0,x_1,...,x_{n-1}]^T \ \\ K=\phi(X)\phi(X)^T X=[x0,x1,...,xn1]T K=ϕ(X)ϕ(X)T
    二维原样本集为:
    X = [ x 11 x 12 … x 1 n x 21 x 22 … x 2 n ⋮ ⋮ ⋱ ⋮ x m 1 x m 2 … x m n ] X=\begin{bmatrix}\\ x_{11} & x_{12} & \dots & x_{1n} \\ x_{21} & x_{22} & \dots & x_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ x_{m1} & x_{m2} & \dots & x_{mn} \\ \end{bmatrix} X=x11x21xm1x12x22xm2x1nx2nxmn
    其中每个元素为m*n矩阵,X为块循环矩阵,共有m*n个样本。
    接下来可以完全按照一维核矩阵的计算方式即式(1)进行计算,且将 α \alpha α和标签y视为一维列向量,只是核函数输入为两个矩阵而不是两个向量,其输出仍为一个固定值,得到的K为mn*mn大小的矩阵(一维例子中样本数为n而二维为mn),但这样计算很麻烦且无法加速,因此将K中的每一列(mn个元素)转为一个m*n的矩阵,如下:
    X j = [ k ( x 11 , x i j ) k ( x 12 , x i j ) ⋮ k ( x m n , x i j ) ]   − > [ k ( x 11 , x i j ) k ( x 12 , x i j ) … k ( x 1 n , x i j ) k ( x 21 , x i j ) k ( x 22 , x i j ) … k ( x 2 n , x i j ) ⋮ ⋮ ⋱ ⋮ k ( x m 1 , x i j ) k ( x m 2 , x i j ) … k ( x m n , x i j ) ]   ( 1 ) X_j=\begin{bmatrix}\\ k(x_{11},x_{ij}) \\ k(x_{12},x_{ij}) \\ \vdots \\ k(x_{mn},x_{ij}) \\ \end{bmatrix} \ -> \begin{bmatrix}\\ k(x_{11},x_{ij}) & k(x_{12},x_{ij}) & \dots & k(x_{1n},x_{ij}) \\ k(x_{21},x_{ij}) & k(x_{22},x_{ij}) & \dots &k(x_{2n},x_{ij}) \\ \vdots & \vdots & \ddots & \vdots \\ k(x_{m1},x_{ij}) & k(x_{m2},x_{ij}) & \dots &k(x_{mn},x_{ij}) \\ \end{bmatrix} \ (1) Xj=k(x11,xij)k(x12,xij)k(xmn,xij) >k(x11,xij)k(x21,xij)k(xm1,xij)k(x12,xij)k(x22,xij)k(xm2,xij)k(x1n,xij)k(x2n,xij)k(xmn,xij) (1)
    这样组成的mn个矩阵,按照X的结构重新组成新的K‘,同时,将 α \alpha α和标签y也转为二维矩阵,则y为K’和 α \alpha α的循环卷积,所有计算均可以按照一维的方式进行。
    此外需要注意的是,在训练过程计算核矩阵时,由于两个输入都是同一个矩阵,即KCF在初始化时提供的目标框像素提取得到的特征,其输入顺序并不重要,但其实核矩阵计算函数中的一个参数是要进行样本扩充操作的(由于使用循环矩阵的特性计算,实际并不需要,但在转化为频域计算时它要求共扼),而另一个输入不需要,因此其输入顺序是有要求的,这在预测过程中需要注意(两个输入一个为原样本特征,一个为待预测样本特征)。
  • 3、将得到的K带入计算,得到 α \alpha α(训练过程),或结合训练得到的 α \alpha α计算预测结果y矩阵(预测过程)
    尽管2的整个推导中一直提到扩充样本,并利用扩充样本矩阵进行训练和预测,但实际代码中从未出现扩充样本的操作,而一直只使用唯一的生成矩阵进行计算,这是为什么呢?
    如果不使用循环矩阵的特性加速计算,正常是需要先得到扩充的样本矩阵的,并按照一般流程进行大量的训练计算和预测计算。但是因为扩充样本矩阵和得到的核矩阵均为循环矩阵,而根据循环矩阵的特性,在固定的某一些计算中,只需要将生成向量或生成矩阵进行DFT参与计算后再IDFT,即可得到与完整的循环矩阵参与计算同样的结果,大大加速了计算。从另一个角度理解,其实就是因为完整的循环矩阵都可以由生成向量或者矩阵表示,而计算时采用时频领域的一些特性即可。

3、Tracking by detection

Tracking by detection的核心在于目标匹配算法,即能够将前后两帧中的同一目标匹配上,而原KCF算法中,其预测部分实际上就是使用训练得到的模型,计算待检测样本与原样本之间的关联度,并预测目标所在待测框中的真实位置(response矩阵中最大值所在位置),由于Tracking by detection中每一帧的检测都可以认为是已知目标在图像中的真实位置,虽然存在误差,但该误差为绝对误差而非累计误差,因此只需要用到KCF计算不同目标关联度的功能。当然也有使用KCF与检测同时进行,并对于检测中识别的目标与KCF追踪到的目标匹配上时,将两者得到的同一目标位置进行卡尔曼滤波的做法,这里不予讨论。将KCF应用于计算Tracking by detection单次流程如下:

Created with Raphaël 2.2.0 开始 检测器检测目标 所有已有KCF追踪器对 所有目标计算关联度, 得到关联矩阵 关联度大于阈值 且使用Hungrian算法 能分配到已有轨迹? 将新检测到目标加入 对应轨迹中并使用 新样本更新追踪器 结束 使用新样本训练一个 追踪器并加入已有 追踪器池中 yes no

上述使用KCF追踪器计算关联度即提取目标样本特征,使用该特征与追踪器原有样本特征计算核矩阵K,使用K结合追踪器已有的 α \alpha α计算response矩阵,取response矩阵中最大值作为目标样本与原样本关联度值。

4、与直接使用KCF的区别

上文已经提到过,直接使用KCF需要手动初始化一个检测框作为原样本训练,下一帧计算中仍以该检测框位置进行循环样本扩充,利用已训练模型在循环样本中找一个与原样本关联度最大的样本作为新样本输出,同时以该样本位置更新检测框,并再训练模型,如此循环重复,知道找不到目标(最大关联度小于一个阈值)。
在Tracking by detection中,KCF的位置更新和初始化都由detection完成,其内部不做位置更新,detection提供一个目标位置初始化一个KCF追踪器,下一帧中detection给出N个可能样本让追踪器判断关联性,样本k如果得到最大关联度,则用该样本更新追踪器,并用于之后的计算,如此重复。

5、将KCF原有功能用于补充detection漏检目标

仔细观察3中流程可以发现,在已有追踪器池中存在一些未匹配到新目标的追踪器,即上一帧检测到了某些目标并与已有追踪器关联,但下一帧由于检测器漏检,这部分追踪器没有关联到任何目标,这个时候可以使用KCF原有的流程对这些追踪器进行更新,并预测一个目标位置填充到最终检测结果,用于补充漏检目标,以上流程可以更新为:

Created with Raphaël 2.2.0 开始 检测器检测目标 所有已有KCF追踪器对 所有目标计算关联度, 得到关联矩阵 关联度大于阈值 且使用Hungrian算法 能分配到已有轨迹? 将新检测到目标加入 对应轨迹中并使用 新样本更新追踪器 未关联任何目标的追 踪器使用KCF原有流 程更新目标位置, 并将结果加入检测结果 结束 使用新样本训练一个 追踪器并加入已有 追踪器池中 yes no

6、总结

目前使用以上流程得到的追踪器效果还是不错的,KCF使用循环矩阵大大加速了计算,实测正常道路上以上追踪流程用时不超过10ms,基本可以满足需要,有空再放一个demo上来。

你可能感兴趣的:(计算机视觉)