卡尔曼滤波应用

news2024/11/15 8:32:02

卡尔曼滤波器的过程

  • 卡尔曼滤波器的过程分为:

    • 状态方程:
      x k = A x k − 1 + B u k − 1 + ω k − 1 z k = H x k + ν k 这 样 就 得 到 了 状 态 方 程 和 观 测 方 程 的 表 达 式 其 中 x k 是 状 态 向 量 , A 是 转 移 矩 阵 , B 是 输 入 转 换 为 状 态 的 矩 阵 , u k 是 系 统 输 入 , ω k 是 系 统 噪 声 z k 是 测 量 值 ( 不 一 定 为 标 量 ) , H 是 状 态 向 量 到 测 量 值 的 转 换 矩 阵 , ν k 是 测 量 噪 声 x_k=Ax_{k-1}+Bu_{k-1}+\omega_{k-1} \\ z_k=Hx_k+\nu_k \\ 这样就得到了状态方程和观测方程的表达式 \\ 其中 x_k 是状态向量,A是转移矩阵,B是输入转换为状态的矩阵,u_k是系统输入,\omega_{k}是系统噪声 \\ z_k是测量值(不一定为标量),H是状态向量到测量值的转换矩阵,\nu_k是测量噪声 xk=Axk1+Buk1+ωk1zk=Hxk+νkxkABukωkzkHνk

    • 预测:

      1. 计算先验: x ^ k − = A x ^ k − 1 + B u k − 1 \hat{x}_k^-=A\hat{x}_{k-1}+Bu_{k-1} x^k=Ax^k1+Buk1

      2. 计算先验误差协方差矩阵: P k − = A P k − 1 A T + Q P_k^-=AP_{k-1}A^T+Q Pk=APk1AT+Q

    • 校正:
      3. 计算卡尔曼增益: K k = P k − H T ( H P k − H T + R ) − 1 K_k=P_k^-H^T(HP_k^-H^T+R)^{-1} Kk=PkHT(HPkHT+R)1
      4. 计算后验估计: x ^ k = x ^ k − + K k ( z k − H x ^ k − ) \hat{x}_k=\hat{x}_k^- + K_k(z_k-H\hat{x}_k^-) x^k=x^k+Kk(zkHx^k)
      5. 更新先验误差协方差矩阵: P k = ( I − K k H ) P k − P_k=(I-K_kH)P_k^- Pk=(IKkH)Pk

加深理解

字数超出,参考 【【卡尔曼滤波器】1_递归算法_Recursive Processing】 https://www.bilibili.com/video/BV1ez4y1X7eR/?share_source=copy_web&vd_source=f888a64d6daca22a15191528d732f02a

卡尔曼滤波器详细推导

字数超出,参考 【【卡尔曼滤波器】1_递归算法_Recursive Processing】 https://www.bilibili.com/video/BV1ez4y1X7eR/?share_source=copy_web&vd_source=f888a64d6daca22a15191528d732f02a

卡尔曼滤波器的应用

python中使用

  • 在 filterpy 这个包中已经实现了卡尔曼滤波器,其一般用法为:

    # 首先从 filterpy 中导入卡尔曼滤波器
    from filterpy.kalman import KalmanFilter
    
    # 创建实例
    # dim_x 是状态向量的维度,dim_z 是测量向量的维度
    kf = KalmanFilter(dim_x=*, dim_z=*)
    
    # 设置初始状态,即 \hat{x}_0,维度为 (dim_x, 1)
    kf.x = np.array([[*], [*], ...])
    
    # 设置状态转移矩阵,即公式中的 A 矩阵,维度为 (dim_x, dim_x)
    kf.F = np.array()
    
    # 设置状态向量到测量值的转换矩阵,即前面公式中的 H 矩阵,维度为 (dim_z, dim_x)
    kf.H = np.array()
    
    # 设置过程噪声的协方差矩阵 Q,即公式中 \omega 的协方差矩阵,维度为 (dim_x, dim_x)
    # 该值往往不确定,需要根据场景自己设置,寻找一个能得到良好结果的值
    kf.Q = np.array()
    
    # 设置测量噪声的协方差矩阵 R,即公式中 \nu 的协方差矩阵,维度为 (dim_z, dim_z)
    # 该值往往也不确定,需要自己确定
    kf.R = np.array()
    
    # 设置先验误差的协方差矩阵 P,即公式中的 P,维度为 (dim_x, dim_x)
    # 由于该值后续会迭代更新,因此初值不影响,但如果给定的初始值 x 不可信,我们会将P中对应位置设置一个较大的数,来表示第一次先验值的不可信,默认是一个单位矩阵
    kf.P = np.array()
    
    # 创建一个列表,记录所有过程中的估计值
    x_estimate = []
    
    # 开始迭代预测
    for i in range(数据长度):
        kf.predict() # 先执行第一次预测
        kf.update(z_i) # 然后根据当前测量值进行更新,测量值z的维度为 (dim_z, 1)
        x_estimate.append(kf.x) # 保存估计值
    
  • 我们来用它模拟一个二维平面上的物体轨迹跟踪

在这里插入图片描述

假如在一个平面上有一个运动的物体,它的轨迹如上图所示
在 x 和 y 方 向 各 有 一 个 定 位 传 感 器 , 每 隔 一 个 时 间 间 隔 就 会 得 到 物 体 当 前 的 x 和 y 位 置 的 测 量 值 因 此 我 们 的 测 量 值 向 量 就 是 z = ( x y ) 而 对 于 物 体 来 说 , 它 本 身 的 状 态 包 括 x 和 y 方 向 的 位 置 以 及 x 和 y 方 向 的 速 度 因 此 我 们 可 以 定 义 状 态 向 量 x = ( x y v x v y ) , 此 时 我 们 可 以 计 算 出 状 态 向 量 到 测 量 向 量 的 转 移 矩 阵 H 显 然 有 z = ( x y ) = ( 1 0 0 0 0 1 0 0 ) ( x y v x v y ) , 因 此 H = ( 1 0 0 0 0 1 0 0 ) 假 如 上 一 个 状 态 向 量 x k − 1 = ( x k − 1 y k − 1 v x k − 1 v y k − 1 ) 假 如 速 度 不 变 , 那 么 显 然 有 { x k = x k − 1 + v x k − 1 Δ t y k = y k − 1 + v y k − 1 Δ t v x k = v x k − 1 v y k = v y k − 1 即 x k = ( 1 0 Δ t 0 0 1 0 Δ t 0 0 1 0 0 0 0 1 ) x k − 1 故 状 态 转 移 矩 阵 A = ( 1 0 Δ t 0 0 1 0 Δ t 0 0 1 0 0 0 0 1 ) , 过 程 输 入 一 项 为 0 在x和y方向各有一个定位传感器,每隔一个时间间隔就会得到物体当前的x和y位置的测量值 \\ 因此我们的测量值向量就是 z=\begin{pmatrix}x\\y\end{pmatrix} \\ 而对于物体来说,它本身的状态包括x和y方向的位置以及x和y方向的速度 \\ 因此我们可以定义状态向量 x=\begin{pmatrix}x\\y\\v_x\\v_y\end{pmatrix},此时我们可以计算出状态向量到测量向量的转移矩阵 H \\ 显然有 z=\begin{pmatrix}x\\y\end{pmatrix}=\begin{pmatrix}1&0&0&0\\0&1&0&0\end{pmatrix}\begin{pmatrix}x\\y\\v_x\\v_y\end{pmatrix},因此 H=\begin{pmatrix}1&0&0&0\\0&1&0&0\end{pmatrix} \\ 假如上一个状态向量 x_{k-1} = \begin{pmatrix}x_{k-1}\\y_{k-1}\\v_{x_{k-1}}\\v_{y_{k-1}}\end{pmatrix} \\ 假如速度不变,那么显然有 \begin{cases} x_k=x_{k-1}+v_{x_{k-1}}\Delta t \\ y_k=y_{k-1}+v_{y_{k-1}}\Delta t \\ v_{x_k} = v_{x_{k-1}} \\ v_{y_k} = v_{y_{k-1}} \end{cases} \\ 即 x_k = \begin{pmatrix} 1&0& \Delta t &0 \\ 0&1& 0 &\Delta t \\ 0&0&1&0 \\ 0&0&0&1 \end{pmatrix} x_{k-1} \\ 故状态转移矩阵 A=\begin{pmatrix} 1&0& \Delta t &0 \\ 0&1& 0 &\Delta t \\ 0&0&1&0 \\ 0&0&0&1 \end{pmatrix},过程输入一项为 0 xyxyz=(xy)xyxyx=xyvxvyHz=(xy)=(10010000)xyvxvyH=(10010000)xk1=xk1yk1vxk1vyk1xk=xk1+vxk1Δtyk=yk1+vyk1Δtvxk=vxk1vyk=vyk1xk=10000100Δt0100Δt01xk1A=10000100Δt0100Δt010
上面我们已经得到了 A A A H H H 的值,还有 Q Q Q R R R P P P 未知,因为我们使用正态分布对真实值添加噪声,因此正态分布的方差就是 R R R 矩阵对角线的值,则 R R R 矩阵也为已知, P P P 矩阵会随迭代过程更新,初始值并不重要,我们使用默认的单位矩阵,而对于 R R R 的设置,详见下面的代码

import numpy as np
from scipy import stats
from filterpy.kalman import KalmanFilter
import matplotlib.pyplot as plt

# 采样1000次
size = 1000

# 定义真实的轨迹值,其中 x 轴匀速变化,y轴按函数变化
x_true = np.linspace(0, 100, size)
y_true = (np.sin(np.pi * np.sqrt(x_true)) + np.log(x_true + 1)) * (np.sqrt(x_true) - 7)

# 定义每一步的观测值,在真实值上添加测量噪声作为测量值
sigma_x2 = 0.5  # x方向上测量噪声的方差
sigma_y2 = 0.5  # y方向上测量噪声的方差
x_mea = x_true + stats.norm.rvs(loc=0, scale=sigma_x2, size=size)
y_mea = y_true + stats.norm.rvs(loc=0, scale=sigma_y2, size=size)

# 创建实例
kf = KalmanFilter(dim_x=4, dim_z=2)

# 设置采样时间间隔,与前面 x_true=np.linspace 的采样间隔相等,即每隔0.1个单位采样一次
t = 100 / size
# 设置初始状态为 x方向位置0,y方向位置0,x方向速度1,y方向速度1
kf.x = np.array([[0], [0], [1], [1]])
# 设置状态转移矩阵
kf.F = np.array([[1, 0, t, 0], [0, 1, 0, t], [0, 0, 1, 0], [0, 0, 0, 1]])
# 设置状态向量到测量值的转换矩阵
kf.H = np.array([[1, 0, 0, 0], [0, 1, 0, 0]])
# 设置过程噪声的协方差矩阵 Q,该值参考了 《Bayesian Filtering and Smoothing》,Simo Sarkka著
# https://zhuanlan.zhihu.com/p/148046908
qc1, qc2 = 1, 1
kf.Q = np.array([[qc1 * t ** 3 / 3, 0, qc1 * t ** 2 / 2, 0],
                 [0, qc2 * t ** 3 / 3, 0, qc2 * t ** 2 / 2],
                 [qc1 * t ** 2 / 2, 0, qc1 * t, 0],
                 [0, qc2 * t ** 2 / 2, 0, qc2 * t]])
# 设置测量噪声的协方差矩阵 R,使用了前面人为制造噪声时的方差
kf.R = np.array([[sigma_x2, 0], [0, sigma_y2]])
# 设置先验误差的协方差矩阵 P,按默认的单位矩阵进行初始化
kf.P = np.eye(4)

# 记录估计过程
x_pred = []
y_pred = []
for i in range(size):
    kf.predict()
    kf.update(np.array([[x_mea[i]], [y_mea[i]]]))
    # 将预测结果保存
    x_pred.append(kf.x[0])
    y_pred.append(kf.x[1])

# 绘制结果
plt.plot(x_true, y_true, color='red', label="true value")
plt.plot(x_mea, y_mea, alpha=0.2, color='green', label='measurement value')
plt.plot(x_pred, y_pred, color='blue', label="estimate value")
plt.legend()
plt.show()

在这里插入图片描述

从图中可以看出,蓝色的估计值相较于绿色的观测值来说,更加靠近红色的真实值,这说明卡尔曼滤波器在本例中确实减小了测量噪声的影响。

单目标追踪

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JwaPBrnz-1670688580090)(./assets/1.gif)]

  • 现在我们将目光放在单目标的追踪上面,以上面的只因为例,我们想要对其脸部进行追踪,如果使用 RetinaFace 模型对每一帧进行预测,将会在每一帧得到一个预测框,下面我们将预测框绘制在视频中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBM0cBO0-1670688580091)(./assets/pred.gif)]

    可以看出预测框精度虽好,但放在视频中会出现不平滑的现象,为了将框变得更加平滑,我们可以使用卡尔曼滤波来完成这项任务
    对 于 预 测 框 来 说 , 它 形 如   ( x m i n , y m i n , x m a x , y m a x ) , 代 表 了 框 的 左 上 角 坐 标 和 右 下 角 坐 标 在 追 踪 任 务 中 , 我 们 一 般 将 其 变 成 另 一 种 表 达 形 式 : ( c x , c y , s , r ) 其 中   c x 、 c y   表 示 框 的 中 心 坐 标 , s   表 示 框 的 面 积 , r   表 示 框 的 宽 高 比 在 此 基 础 上 , 对 中 心 坐 标 和 框 的 面 积 增 加 变 化 速 度 , 那 么 框 的 状 态 向 量 表 示 为   x = ( c x c y s r c x v c y v s v ) 此 时 我 们 可 以 计 算 出 状 态 向 量 到 测 量 向 量 的 转 移 矩 阵   H 显 然 有   z = ( c x c y s r ) = ( 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 ) ( c x c y s r c x v c y v s v ) , 因 此   H = ( 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 ) 假 设 上 一 个 状 态 向 量   x k − 1 = ( c x k − 1 c y k − 1 s k − 1 r k − 1 c x v k − 1 c y v k − 1 s v k − 1 ) , 假 如 速 度 不 变 , 显 然 有 { c x k = c x k − 1 + c x v k − 1 Δ t c y k = c y k − 1 + c y v k − 1 Δ t s k = s k − 1 + s v k − 1 Δ t r k = r k − 1 c x v k = c x v k − 1 c y v k = c y v k − 1 s v k = s v k − 1 即   x k = ( 1 0 0 0 0 Δ t 0 0 0 1 0 0 0 0 Δ t 0 0 0 1 0 0 0 0 Δ t 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 ) x k − 1 , 在 本 例 中 , Δ t   是 一 帧 , 因 此 这 里   Δ t = 1 故 状 态 转 移 矩 阵   A = ( 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 ) 对于预测框来说,它形如\space (x_{min}, y_{min}, x_{max}, y_{max}),代表了框的左上角坐标和右下角坐标 \\ 在追踪任务中,我们一般将其变成另一种表达形式:(cx, cy, s, r) \\ 其中\space cx、cy \space表示框的中心坐标,s \space表示框的面积,r \space表示框的宽高比 \\ 在此基础上,对中心坐标和框的面积增加变化速度,那么框的状态向量表示为\space x=\begin{pmatrix} cx \\cy \\s \\r \\cx_v \\cy_v \\s_v\end{pmatrix} \\ 此时我们可以计算出状态向量到测量向量的转移矩阵\space H \\ 显然有\space z=\begin{pmatrix}cx\\cy\\s\\r \end{pmatrix} =\begin{pmatrix} 1&0&0&0&0&0&0 \\ 0&1&0&0&0&0&0 \\ 0&0&1&0&0&0&0 \\ 0&0&0&1&0&0&0 \end{pmatrix} \begin{pmatrix} cx \\cy \\s \\r \\cx_v \\cy_v \\s_v\end{pmatrix},因此\space H=\begin{pmatrix} 1&0&0&0&0&0&0 \\ 0&1&0&0&0&0&0 \\ 0&0&1&0&0&0&0 \\ 0&0&0&1&0&0&0 \end{pmatrix} \\ 假设上一个状态向量 \space x_{k-1} = \begin{pmatrix} cx_{k-1} \\cy_{k-1} \\s_{k-1}\\r_{k-1}\\cx_{v_{k-1}}\\cy_{v_{k-1}} \\s_{v_{k-1}}\end{pmatrix},假如速度不变,显然有 \begin{cases} cx_k = cx_{k-1} + cx_{v_{k-1}} \Delta t \\ cy_k = cy_{k-1} + cy_{v_{k-1}} \Delta t \\ s_k = s_{k-1} + s_{v_{k-1}} \Delta t \\ r_k = r_{k-1} \\ cx_{v_k} = cx_{v_{k-1}} \\ cy_{v_k} = cy_{v_{k-1}} \\ s_{v_k} = s_{v_{k-1}} \end{cases} \\ 即\space x_k = \begin{pmatrix} 1&0&0&0&0&\Delta t&0&0 \\ 0&1&0&0&0&0&\Delta t&0 \\ 0&0&1&0&0&0&0&\Delta t \\ 0&0&0&1&0&0&0&0 \\ 0&0&0&0&1&0&0&0 \\ 0&0&0&0&0&1&0&0 \\ 0&0&0&0&0&0&1&0 \\ 0&0&0&0&0&0&0&1 \end{pmatrix} x_{k-1},在本例中,\Delta t \space是一帧,因此这里\space \Delta t = 1 \\ 故状态转移矩阵\space A = \begin{pmatrix} 1&0&0&0&0&1&0&0 \\ 0&1&0&0&0&0&1&0 \\ 0&0&1&0&0&0&0&1 \\ 0&0&0&1&0&0&0&0 \\ 0&0&0&0&1&0&0&0 \\ 0&0&0&0&0&1&0&0 \\ 0&0&0&0&0&0&1&0 \\ 0&0&0&0&0&0&0&1 \end{pmatrix}  (xmin,ymin,xmax,ymax)(cx,cy,s,r) cxcy s r  x=cxcysrcxvcyvsv H z=cxcysr=1000010000100001000000000000cxcysrcxvcyvsv H=1000010000100001000000000000 xk1=cxk1cyk1sk1rk1cxvk1cyvk1svk1cxk=cxk1+cxvk1Δtcyk=cyk1+cyvk1Δtsk=sk1+svk1Δtrk=rk1cxvk=cxvk1cyvk=cyvk1svk=svk1 xk=1000000001000000001000000001000000001000Δt00001000Δt00001000Δt00001xk1Δt  Δt=1 A=1000000001000000001000000001000000001000100001000100001000100001
    关于噪声协方差矩阵 R , P , Q R,P,Q R,P,Q 的设置,对于追踪任务来说,往往使用经验数值,详见下方的代码

import numpy as np
from filterpy.kalman import KalmanFilter
import cv2


def load_pred_boxes(path):
    """
    加载 RetinaFace 模型预测的每一帧图像中人脸方框的坐标,`#`表示该帧没有检测到人脸

    :param path: 文件路径,该文件通过 RetinaFace 模型预测生成
    :return: 预测框列表
    """
    with open(path, 'r') as f:
        pred_boxes = f.readlines()
        pred_boxes = [i.strip() for i in pred_boxes]
        pred_boxes = [
            None if box == '#'
            else [float(i) for i in box.split(',')]
            for box in pred_boxes
        ]
    return pred_boxes


def save_video(video_path, save_path, draw_pred=False, pred_boxes=None,
               draw_opt=False, opt_boxes=None):
    """
    保存视频,可以在视频上绘制预测框和估计框

    :param video_path: 视频路径
    :param save_path: 视频保存位置,以 .avi 结尾
    :param draw_pred: 是否绘制预测框
    :param pred_boxes: 每一帧的预测框
    :param draw_opt: 是否绘制卡尔曼滤波器的估计框
    :param opt_boxes: 每一帧的估计框
    """
    cap = cv2.VideoCapture(video_path)  # 从指定路径加载视频
    fourcc = cv2.VideoWriter_fourcc(*'XVID')  # 保存格式为 .avi
    out = cv2.VideoWriter(save_path, fourcc, 25, (852, 480))  # 保存视频文件

    frame = 0
    while True:
        success, img = cap.read()
        if not success:
            break
        # 决定是否绘制预测框
        if draw_pred:
            if pred_boxes[frame] is not None:
                box = [round(i) for i in pred_boxes[frame]]
                cv2.rectangle(img, box[:2], box[2:], color=(218, 69, 0), thickness=2)
        # 决定是否绘制估计框
        if draw_opt:
            if opt_boxes[frame] is not None:
                box = [round(i) for i in opt_boxes[frame]]
                cv2.rectangle(img, box[:2], box[2:], color=(81, 154, 218), thickness=2)
        out.write(img)  # 保存视频文件
        frame += 1
        cv2.imshow('1', img)
        cv2.waitKey(1)

    cap.release()
    out.release()
    cv2.destroyAllWindows()


def convert_bbox_to_z(bbox):
    """
    将预测框从格式为 [x_min, y_min, x_max, y_max] 变换为 [cx, cy, s, r] 形式,
    其中 cx, cy 是预测框的中心坐标,s 是预测框的面积,r 是预测框的宽高比

    :param bbox: 代表 [x_min, y_min, x_max, y_max] 的列表
    :return: 改变格式后的列表
    """
    w = bbox[2] - bbox[0]
    h = bbox[3] - bbox[1]
    x = bbox[0] + w / 2.
    y = bbox[1] + h / 2.
    s = w * h  # 面积
    r = w / float(h)
    return np.array([x, y, s, r]).reshape((4, 1))


def convert_x_to_bbox(x):
    """
    将方框从 [cx, cy, s, r] 的格式转变为 [x_min, y_min, x_max, y_max] 的形式,
    该函数与 convert_bbox_to_z 相反

    :param x: 代表 [cx, cy, s, r, ...] 的列表,只会用到前面4个值
    :return: 改变格式后的列表
    """
    w = np.sqrt(x[2] * x[3])
    h = x[2] / w
    return np.array(
        [x[0] - w / 2., x[1] - h / 2., x[0] + w / 2., x[1] + h / 2.]
    ).reshape((1, 4))


class KalmanBoxTracker(object):
    """
    使用卡尔曼滤波对预测框进行优化
    """

    def __init__(self, bbox):
        self.kf = KalmanFilter(dim_x=7, dim_z=4)
        # 设置状态转移矩阵
        self.kf.F = np.array(
            [[1, 0, 0, 0, 1, 0, 0],
             [0, 1, 0, 0, 0, 1, 0],
             [0, 0, 1, 0, 0, 0, 1],
             [0, 0, 0, 1, 0, 0, 0],
             [0, 0, 0, 0, 1, 0, 0],
             [0, 0, 0, 0, 0, 1, 0],
             [0, 0, 0, 0, 0, 0, 1]]
        )
        # 设置状态向量到测量值的转换矩阵
        self.kf.H = np.array(
            [[1, 0, 0, 0, 0, 0, 0],
             [0, 1, 0, 0, 0, 0, 0],
             [0, 0, 1, 0, 0, 0, 0],
             [0, 0, 0, 1, 0, 0, 0]]
        )
        # 设置测量噪声的协方差矩阵 R
        self.kf.R[2:, 2:] *= 10.
        # 设置先验误差的协方差矩阵 P
        self.kf.P[4:, 4:] *= 1000.  # 对不可观测的初始速度给予高度不确定性
        self.kf.P *= 10.
        # 设置过程噪声的协方差矩阵 Q
        self.kf.Q[-1, -1] *= 0.01
        self.kf.Q[4:, 4:] *= 0.01
        # 设置初始状态,即使用初始预测框作为初始状态
        self.kf.x[:4] = convert_bbox_to_z(bbox)

    def update(self, bbox):
        # 跟预测框(测量值)进行更新
        self.kf.update(convert_bbox_to_z(bbox))

    def predict(self):
        # 面积是大于0的,如果小于0,就将面积的改变速度置为0
        if self.kf.x[6] + self.kf.x[2] <= 0:
            self.kf.x[6] = 0
        # 预测阶段
        self.kf.predict()
        return convert_x_to_bbox(self.kf.x)

    def get_state(self):
        # 获取当前的估计框
        return convert_x_to_bbox(self.kf.x)


def kalman_opt(boxes):
    opt_boxes = []  # 保存每一帧的估计框
    kbt = KalmanBoxTracker(boxes[0])
    for i in range(len(boxes)):
        # 如果该帧没有检测到,则估计框也为None
        if boxes[i] is None:
            opt_boxes.append(None)
            continue
        kbt.predict()
        kbt.update(boxes[i])
        # 保存当前帧的估计值
        opt_boxes.append(list(kbt.get_state()[0]))
    return opt_boxes


if __name__ == '__main__':
    pred_boxes = load_pred_boxes('../assets/boxes.txt')
    opt_boxes = kalman_opt(pred_boxes)

    # 仅在视频上绘制预测框
    # save_video('../assets/1.mp4', '../assets/pred.avi',
    #            draw_pred=True, pred_boxes=pred_boxes,
    #            draw_opt=False, opt_boxes=None)

    # 仅在视频上绘制估计框
    # save_video('../assets/1.mp4', '../assets/opt.avi',
    #            draw_pred=False, pred_boxes=None,
    #            draw_opt=True, opt_boxes=opt_boxes)

    # 在视频上同时绘制预测框(蓝色)和估计框(橙色)
    save_video('../assets/1.mp4', '../assets/cmp.avi',
               draw_pred=True, pred_boxes=pred_boxes,
               draw_opt=True, opt_boxes=opt_boxes)

下面看一下使用卡尔曼滤波后的结果,看起来似乎平滑了许多

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1EW03HCw-1670688580091)(./assets/opt.gif)]

我们将预测框和估计框都显示出来,可以看出估计框更加平滑,这说明卡尔曼滤波起到了一定的作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BTybryZu-1670688580092)(./assets/cmp.gif)]
附视频和 RetinaFace 的预测框结果:https://alokia.lanzouo.com/izLZl0idavfi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【波段自适应梯度和细节校正:统一遥感融合】

A Unified Pansharpening Model Based on Band-Adaptive Gradient and Detail Correction &#xff08;基于波段自适应梯度和细节校正的统一全色锐化模型&#xff09; 利用全色锐化技术将全色&#xff08;PAN&#xff09;图像与多光谱&#xff08;MS&#xff09;图像融合&…

谈谈Java应用发布时CPU抖动的优化

研究背景 通常情况下应用发布或重启时都存在cpu抖动飙高&#xff0c;甚至打满的现象&#xff0c;这是由于应用启动时&#xff0c;JVM重新进行类加载与对象的初始化&#xff0c;CPU在整个过程中需要进行比平时更多的编译工作。同样&#xff0c;闲鱼的消息系统在重新发布时经常有…

fsQCA+NCA方法的软件操作及注意事项、论文实证分析部分的写作范式

目录前言1 软件操作步骤2 fsQCA方法的详细操作步骤2.1 软件下载2.2 数据的准备2.3 校准点的确定2.4 变量的校准步骤及闪退问题2.5 fsQCA的数据必要性检验&#xff08;开始一次最后一次&#xff09;2.6 频数、一致性水平、PRI一致性水平的确定2.6.1 频数的确定2.6.2 一致性水平、…

【中级ECharts技术】前端框架ECharts的dataset 管理数据对数据可视化的高级dataset 管理

dataset 管理数据 提供一份数据。 声明一个 X 轴,类目轴(category)。默认情况下,类目轴对应到声明多个 bar 系列,默认情况下,每个系列会自动对应到 dataset 的每一列。 option = {legend: {},tooltip: {},dataset:

Mac 中 MongoDB 使用

根据 homebrew-brew 官方的解释得知&#xff0c;MongoDB 不再是开源的了&#xff0c;并且已经从 Homebrew中移除 #43770 正是由于 MongoDB 的商业化不太理想&#xff0c;所以它选择了闭源。所以&#xff0c;在它闭源之前的那些 brew 安装方法都会报错了。网上很多的文章都是基…

[附源码]JAVA毕业设计雅博书城在线系统(系统+LW)

[附源码]JAVA毕业设计雅博书城在线系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

你想要的图片效果(动态实现)

一、前言 没有使用任何框架API&#xff0c;代码或逻辑在html或小程序都适用。主要实现图片随机位置、随机大小、不重叠&#xff0c;在页面上排布&#xff1b;还有扩展功能选定固定图片位置槽数、固定大小、不重叠&#xff0c;在页面上通过添加&#xff0c;图片随机排布。 二、…

我的创作纪念日(4周年)

机缘 回想当初&#xff0c;博主2017年底从北京中石油&#xff08;沙河总部&#xff09;辞职&#xff0c;一心想回到自己的家乡成都工作、不想在北京待了&#xff0c;在总部赵总的推荐下来到四川中石油工作&#xff08;刚好这边有人离职&#xff0c;所谓一个萝卜一个坑&#xf…

C语言入门(二)——常量,变量和表达式

继续Hello World 常量 变量 赋值 表达式 字符类型与字符编码 继续Hello World 前一个章节已经对Hello World程序做各种改动程序做各种改动看编译运行结果&#xff0c;其中有些改动会导致编译出错&#xff0c;有些改动会影响程序的输出&#xff0c;有些改动则没有任何影响…

C++:类的内存布局

文章目录1、虚的含义2、单基继承2.1、单继承2.2、单虚继承2.3、单虚继承 虚函数2.4、测试代码3、多基继承3.1、多继承 虚函数3.2、虚拟多继承 虚函数3.3、测试代码4、菱形继承4.1、菱形继承4.2、菱形虚拟继承4.3、测试代码5、效率分析建议先了解 C 继承与多态的相关知识&…

12.10 二叉搜索树与内部类

目录 一.二叉搜索树 1 概念 2 操作-查找 3.插入 4.删除(难点) 1.cur.leftnull 2.cue.rightnull 3.最复杂的情况 cur.left!null&&cur.right!null 6 性能分析 7 和 java 类集的关系 二.内部类 1.本地内部类 2.实例内部类 1.不可以定义静态 因为静态表示属于…

踩坑记录1——RK3588编译OpenCV

这两天有在板卡上跑代码的需求&#xff0c;拿到了一块RK3588CPU的板子&#xff0c;型号是HINLINK的HK88. 以后记录一下调试这个板子的问题&#xff0c;便于以后查看 0. 基本信息 板卡系统&#xff1a;ArmBian&#xff0c;基于Ubuntu20.04 OpenCV版本&#xff1a;3.4.5 采用方法…

Java项目:SSM公司人力资源管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为后台管理系统,分为管理员与普通员工两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,员工账号管理,部门管理,员工…

陆拾肆- 时序数据的特征化

一、前期大数据状况 进行客户域大数据运营时&#xff0c;一般是在当前状态计算客户的行为特征。 如会建立特征为 近7天是否有登录昨天是否有登录近7天销售情况点击主页后是否有点击下层页面哪个页面点击购买总浏览电子产品的次数占访问次数占比不进行商品浏览&#xff0c;只进…

Codeforces Round #772 (Div. 2) D. Infinite Set

翻译&#xff1a; 给定一个数组&#x1d44e;&#xff0c;该数组由&#x1d45b;个不同的正整数组成。 让我们考虑一个无限整数集&#x1d446;&#xff0c;它包含至少满足以下条件之一的所有整数&#x1d465;: 对于某些1≤&#x1d456;≤&#x1d45b;&#xff0c;&#…

微服务框架 SpringCloud微服务架构 微服务保护 33 授权规则 33.2 自定义异常结果

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护33 授权规则33.2 自定义异常结果33.2.1 自定义异常结果33.2.2 总结33 授权规则 33.2 自定义异常结…

十种类型电感概述

1、工字型电感 它的前身是挠线式贴片电感,工字型电感是它们的改良, 挡板有效加强储能能力,改变EMI方向和大小,亦可降低RDC。它亦可说是讯号通讯电感跟POWER电感的一种妥协。 工字型电感的缺点,仍是开磁路,有EMI的问题, 另外,噪音的问题比挠线式贴片电感大。 2、色环电感 色环电…

java计算机毕业设计ssm学生学习评价与分析系统8ql42(附源码、数据库)

java计算机毕业设计ssm学生学习评价与分析系统8ql42&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&…

C++11标准模板(STL)- 算法(std::set_union)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 数据结构的堆物理结构是数…

IOC 操作 Bean 管理( Bean 的生命周期)

生命周期 从对象创建到对象销毁的过程 Bean 的生命周期 通过构造器创建 Bean 实例&#xff08;无参构造&#xff09;为 Bean 的属性设置值和对其他 Bean 引用&#xff08;调用 set 方法&#xff09;调用 Bean 的初始化的方法&#xff08;需要进行配置&#xff09;Bean 可以使…