《卡尔曼滤波器用于图像上运动目标跟踪预测》


卡尔曼滤波器用于图像上运动目标跟踪预测


简介

  • Kalman滤波器是通过前一状态预测当前状态,并使用当前观测状态进行校正

  • 直接观测量为left, top, right, bottom, 分别代表目标的左上和右下坐标

  • 一般地认为运动速度是匀速

  • 但是监控场景中相机俯角太大,存在景深,导致运动是非匀速的

    • 来向,从远到近,加速运动
    • 去向,从近到远,减速运动
  • 针对非匀速情况下

    • 目标状态量引入加速度
    • 目标状态转到世界坐标系
      • 双目或多目相机:直接得到目标在相机坐标系下的三维坐标
      • 单目可以采用单应变换矩阵转到到世界坐标系
  • 选取来向的车辆,获取53帧运动坐标,选取前43帧预测更新,后10帧预测实验

请添加图片描述

匀速运动模型

  • 状态量: l , t , r , b , v l , v t , v r , v b l,t,r,b, v_l, v_t, v_r, v_b l,t,r,b,vl,vt,vr,vb 分别是左上和右下角点位置和对应速度
  • 观测量: l , t , r , b l,t,r,b l,t,r,b
  • 物体匀速运动情况
  • 跟踪非连续帧
  • 观测噪声方差和过程噪声误差和bbox高度有关,h越大,误差越大,实时更新
class KalmanFilter:
    def __init__(self):
        self._std_weight_pos = 1. / 20     # 位置
        self._std_weight_vel = 1. / 160    # 速度
        self.H = np.matrix(np.zeros((4, 8)))  # 观测矩阵 Z_t = Hx_t + v
        for i in range(4):
            self.H[i, i] = 1
        self.F = np.matrix(np.eye(8)) # 状态转移矩阵

    def init(self, val):
        l,t,r,b = val
        mean_pos = np.matrix([[l], [t], [r], [b]])
        mean_vel = np.zeros_like(mean_pos) # 速度初始化为0
        self.x_hat = np.r_[mean_pos, mean_vel] # x_k = [p, v]
        h = b - t
        std_pos = [
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h]
        std_vel = [
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h]
        self.P = np.diag(np.square(np.r_[std_pos, std_vel])) # 状态协方差矩阵

    def predict(self, delt_t, val=None):
        if val is not None:
            h = val[3] - val[1]
            std_pos = [
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h]
            std_vel = [
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h]
            self.Q = np.diag(np.square(np.r_[std_pos, std_vel])) # 过程噪声实时变化
        for i in range(4):
            self.F[i, i+4] = delt_t
        self.x_hat_minus = self.F * self.x_hat
        self.P_minus = self.F * self.P * self.F.T + self.Q

    def update(self, val):
        l,t,r,b = val
        h = b - t
        std_pos = [
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h]
        self.R = np.diag(np.square(std_pos)) # 观测噪声方差
        measure = np.matrix([[l], [t], [r], [b]])
        self.K = self.P_minus * self.H.T * np.linalg.inv(self.H * self.P_minus * self.H.T + self.R)
        self.x_hat = self.x_hat_minus + self.K * (measure - self.H * self.x_hat_minus)
        self.P = self.P_minus - self.K * self.H * self.P_minus

引入加速度

  • 状态量: l , t , r , b , v l , v t , v r , v b , a l , a t , a r , a b l,t,r,b,v_l, v_t,v_r,v_b,a_l,a_t,a_r,a_b l,t,r,b,vl,vt,vr,vb,al,at,ar,ab
  • 观测量: l , t , r , b l,t,r,b l,t,r,b
  • 物体加速运动
  • 跟踪非连续帧
  • 观测噪声方差和过程噪声误差和bbox高度有关,h越大,误差越大,实时更新
class KalmanFilter2:
    def __init__(self):
        self._std_weight_pos = 1. / 20     # 位置
        self._std_weight_vel = 1. / 160    # 速度
        self._std_weight_acc = 1. / 300    # 速度
        self.H = np.matrix(np.zeros((4, 12)))  # 观测矩阵 Z_t = Hx_t + v
        for i in range(4):
            self.H[i, i] = 1
        self.F = np.matrix(np.eye(12))  # 状态转移矩阵

    def init(self, val):
        l,t,r,b = val
        mean_pos = np.matrix([[l], [t], [r], [b]])
        mean_vel = np.zeros_like(mean_pos) # 速度初始化为0
        mean_acc = np.zeros_like(mean_pos) # 速度初始化为0
        self.x_hat = np.r_[mean_pos, mean_vel, mean_acc] # x_k = [p, v]
        h = b - t
        std_pos = [
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h,
            2 * self._std_weight_pos * h]
        std_vel = [
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h,
            10 * self._std_weight_vel * h]
        std_acc = [
            50 * self._std_weight_acc * h,
            50 * self._std_weight_acc * h,
            50 * self._std_weight_acc * h,
            50 * self._std_weight_acc * h
        ]
        self.P = np.diag(np.square(np.r_[std_pos, std_vel, std_acc])) # 状态协方差矩阵

    def predict(self, delt_t, val=None):
        if val is not None:
            l,t,r,b = val
            h = b - t
            std_pos = [
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h]
            std_vel = [
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h]
            std_acc = [
                self._std_weight_acc * h,
                self._std_weight_acc * h,
                self._std_weight_acc * h,
                self._std_weight_acc * h]
            self.Q = np.diag(np.square(np.r_[std_pos, std_vel, std_acc])) # 过程噪声实时变化
        for i in range(4):
            self.F[i,i + 4] = delt_t
            self.F[i,i + 8] = delt_t ** 2 / 2
        for i in range(4, 8):
            self.F[i,i+4] = delt_t
        self.x_hat_minus = self.F * self.x_hat
        self.P_minus = self.F * self.P * self.F.T + self.Q

    def update(self, val):
        l,t,r,b = val
        h = b - t
        std_pos = [
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h]
        self.R = np.diag(np.square(std_pos)) # 观测噪声方差
        measure = np.matrix([[l], [t], [r], [b]])
        self.K = self.P_minus * self.H.T * np.linalg.inv(self.H * self.P_minus * self.H.T + self.R)
        self.x_hat = self.x_hat_minus + self.K * (measure - self.H * self.x_hat_minus)
        self.P = self.P_minus - self.K * self.H * self.P_minus

在这里插入图片描述

引入单应变换

  • 状态量: l,t,r,b, vl,vt,vr,vb
  • 观测量: l,t,r,b
  • 观测噪声方差和过程噪声误差和bbox高度有关,h越大,误差越大,实时更新
  • 标定图像和世界坐标系之间的单应变换矩阵*
    • 选标志点构建世界坐标系
    • 选取图像上对应点
    • 标定(bbox并不都在世界坐标系设定的平面,近似采用,可能会有问题)
  • 目标在世界坐标系内匀速运动
class KalmanFilter:
    def __init__(self, homography):
        self._std_weight_pos = 1. / 10     # 位置
        self._std_weight_vel = 1. / 100    # 速度
        self.H = np.matrix(np.zeros((4, 8)))  # 观测矩阵 Z_t = Hx_t + v
        for i in range(4):
            self.H[i, i] = 1
        self.F = np.matrix(np.eye(8)) # 状态转移矩阵
        self.homography = np.reshape(homography, (3,3))

    def _img2world(self, val):
        l,t,r,b = val
        tmp = np.dot(np.linalg.inv(self.homography), np.array([[l], [t], [1]]))
        l_ = tmp[0] / tmp[2]
        t_ = tmp[1] / tmp[2]
        tmp = np.dot(np.linalg.inv(self.homography), np.array([[r], [b], [1]]))
        r_ = tmp[0] / tmp[2]
        b_ = tmp[1] / tmp[2]
        return [l_, t_, r_, b_]


    def _world2img(self, val):
        l,t,r,b = val
        tmp = np.dot(self.homography, np.array([[l], [t], [1]]))
        l_ = tmp[0] / tmp[2]
        t_ = tmp[1] / tmp[2]
        tmp = np.dot(self.homography, np.array([[r], [b], [1]]))
        r_ = tmp[0] / tmp[2]
        b_ = tmp[1] / tmp[2]
        return [l_, t_, r_, b_]

    def init(self, val):
        ltrb = self._img2world(val)
        mean_pos = np.matrix(ltrb)
        mean_vel = np.zeros_like(mean_pos) # 速度初始化为0
        self.x_hat = np.r_[mean_pos, mean_vel] # x_k = [p, v]
        h = val[3]-val[1]
        std_pos = [
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h]
        std_vel = [
            self._std_weight_vel * h,
            self._std_weight_vel * h,
            self._std_weight_vel * h,
            self._std_weight_vel * h]
        self.P = np.diag(np.square(np.r_[std_pos, std_vel])) # 状态协方差矩阵

    def predict(self, delt_t, val=None):
        if val is not None:
            h = val[3] - val[1]
            std_pos = [
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h,
                self._std_weight_pos * h]
            std_vel = [
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h,
                self._std_weight_vel * h]
            self.Q = np.diag(np.square(np.r_[std_pos, std_vel])) # 过程噪声实时变化
        for i in range(4):
            self.F[i, i+4] = delt_t
        self.x_hat_minus = self.F * self.x_hat
        self.P_minus = self.F * self.P * self.F.T + self.Q

    def update(self, val):
        l,t,r,b = val
        h = b - t
        std_pos = [
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h,
            self._std_weight_pos * h]
        ltrb = self._img2world(val)
        self.R = np.diag(np.square(std_pos)) # 观测噪声方差
        measure = np.matrix(ltrb)
        self.K = self.P_minus * self.H.T * np.linalg.inv(self.H * self.P_minus * self.H.T + self.R)
        self.x_hat = self.x_hat_minus + self.K * (measure - self.H * self.x_hat_minus)
        self.P = self.P_minus - self.K * self.H * self.P_minus

在这里插入图片描述

实验结果分析

  • 显然匀速运动模型不适合

  • 目标车辆在图像上并非是匀加(减)速,引入加速度模型,有了非线性预测效果,但是还不是很好

  • 通过单应变换的转换,非线性明显,趋势不是很对,这种四个点都采用一个单应变换矩阵,误差有点大

  • 详细工程加数据:卡尔曼滤波

参考

1 [运动目标跟踪中kalman滤波器的使用]

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
卡尔曼滤波是一种用于估计系统状态的数学方法,可以在噪声干扰存在的情况下,通过观测数据进行预测和修正。在图像目标跟踪中,卡尔曼滤波可以用来预测目标的运动轨迹。 在目标跟踪的实现中,有两个常见的版本。版本1是直接读取由检测算法生成的目标框文件,其中包含了目标的位置和尺寸信息。这些信息可以作为观测数据传递给卡尔曼滤波器用于预测目标的下一个位置。版本2则是使用检测算法实时生成目标框坐标。这意味着每次检测到目标时,都可以将该位置作为观测数据输入到卡尔曼滤波器中。 在实现过程中,可能会遇到一些问题。例如,获取张量中的值可能涉及到一些特定的操作和语法。你可以参考相关的链接来了解如何在代码中获取张量的值。此外,你还可以参考提供的链接,其中包含了一些使用卡尔曼滤波实现图像目标跟踪的开源项目和教程。 更具体地说,你可以参考以下资源来实现卡尔曼滤波实现图像目标跟踪: 1. [bilibili视频教程](https://www.bilibili.com/video/BV1Qf4y1J7D4?p=1&vd_source=9f55bd955a3e8c3a2f2960ba719c5890) 2. [liuchangji的GitHub项目](https://github.com/liuchangji/kalman-filter-in-single-object-tracking) 3. [ZhangPHEngr的GitHub项目](https://github.com/ZhangPHEngr/Kalman-in-MOT) 希望这些资源可以帮助你实现卡尔曼滤波实现图像目标跟踪。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [使用卡尔曼滤波实现单目标跟踪过程中的目标运动轨迹预测](https://blog.csdn.net/weixin_43745234/article/details/128389607)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值