第1关:基于光流法的目标跟踪

任务描述

本关任务:编写一个光流法进行目标检测的程序

相关知识

光流法

光流

光流是空间运物体在观察成像平面上的像素运动的瞬时速度,一般来说,光流的产生是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的

光流法基本原理

光流法的基本假设条件

亮度恒定不变。即统一目标在不同帧间运动的时候,亮度不会发生改变。这是光流法的基本假定,必须满足这一条件,用于得到光流法基本方程; 时间连续或运动是小运动。即时间的变化不会引起目标位置的剧烈变化,相邻帧之间位移也比较小,这也是光流法需要满足的条件之一

基本约束方程

一个像素I(x, y, t)在第一帧的光强度。(t代表所在的时间维度)。当移动了(dx​,dy​),的距离到了下一帧,用了dt​时间。因为 是同一个像素点,依据上文提到的第一个假设像素在运动的前后光强度是不变的 I(x,y,t)=I(x+dx​,y+dy​,t+dt​) =I(x,y,t)+∂x∂I​dx​+∂y∂I​dy​+∂t∂I​dt​+ϵ 其中ε代表二阶无穷小项,可以忽略不计,可得: ∂xdt​∂Idx​​+∂ydt​∂Idy​​+∂tdt​∂Idt​​=0

设u,v为光流沿X轴和Y轴的速度矢量,可得: u=dt​dx​​,v=dt​dy​​

令Ix​=∂x∂I​,Iy​=∂y∂I​,It​=∂t∂I​分别为图像中像素点的灰度沿X,Y,T方向的偏导数,最终可以得到 Ix​u+Iy​v+It​=0

其中,Ix​,Iy​,It​都可以从图像数据中求出,(u,v)即为所求光流矢量,我们无法用两个未知变量解决这个方程。所以有几种方法可以解决这个问题,其中之一就是Lucas-Kanade。

卢卡斯- Kanade光流OpenCV中 OpenCV在一个函数cv2.calcOpticalFlowPyrLK()中提供了所有这些。在这里,我们创建一个简单的应用程序来跟踪视频中的某些点。为了决定点,我们使用cv2.goodFeaturesToTrack()。我们采用第一帧,检测一些Shi-Tomasi角点,然后使用Lucas-Kanade光流迭代地跟踪这些点。对于函数cv2.calcOpticalFlowPyrLK(),我们传递前一帧,前一点和下一帧。如果找到下一个点,它将返回下一个点以及一些状态值为1的值,否则为零。我们迭代地将这些下一点作为下一步中的前几点。

实现原理

首先选取第一帧,在第一帧图像中检测Shi-Tomasi角点,然后使用LK算法来迭代的跟踪这些特征点。迭代的方式就是不断向cv2.calcOpticalFlowPyrLK()中传入上一帧图片的特征点以及当前帧的图片。 函数会返回当前帧的点,这些点带有状态1或者0,如果在当前帧找到了上一帧中的点,那么这个点的状态就是1,否则就是0。

实现流程

加载视频。 调用 GoodFeaturesToTrack 函数寻找兴趣点(关键点)。 调用 CalcOpticalFlowPyrLK 函数计算出两帧图像中兴趣点的移动情况。 删除未移动的兴趣点。 在两次移动的点之间绘制一条线段。

opencv中重要函数的说明

cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params) 用于获得光流估计所需要的角点

参数说明: old_gray:表示输入图片, mask表示掩模, feature_params:maxCorners=100:角点的最大个数, qualityLevel=0.3:角点品质,minDistance=7:即在这个范围内只存在一个品质最好的角点

pl, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

用于获得光流检测后的角点位置

参数说明: pl:表示光流检测后的角点位置, st:表示是否是运动的角点, err:表示是否出错, old_gray:表示输入前一帧图片,frame_gray:表示后一帧图片, p0:表示需要检测的角点, lk_params: winSize:表示选择多少个点进行u和v的求解, maxLevel:表示空间金字塔的层数

cv2.add(frame, mask) 将两个图像的像素进行加和操作

参数说明: frame表:示输入图片 mask:表示掩模 光流估计:通过当前时刻与前一时刻的亮度不变的特性,根据基本约束方程 I(x,y,t)=I(x+dx​,y+dy​,t+dt​)使用lucas-kanade算法进行求解问题, 我们需要求得的是x,y方向的速度

具体代码部分实现

  1. # 视频的读入
  2. cap = cv2.VideoCapture('test.mp4')
  3. # 构建角点检测所需参数
  4. feature_params = dict(maxCorners=100,
  5. qualityLevel=0.3,
  6. minDistance=7,
  7. blockSize=7)
  8. # maxLevel 为使用的图像金字塔层数
  9. lk_params = dict(winSize=(15, 15),
  10. maxLevel=2,
  11. criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
  12. color = np.random.randint(0, 255, (100, 3))
  13. # 拿到第一帧图像并灰度化作为前一帧图片
  14. ret, old_frame = cap.read()
  15. old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
  16. # 返回所有检测特征点,需要输入图片,角点的最大数量,品质因子,minDistance=7如果这个角点里有比这个强的就不要这个弱的
  17. p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)

编程要求

根据提示,在右侧编辑器补充代码,完善代码,通过已经完成的步骤,补全后面的步骤,完成程序

测试说明

平台会对你编写的代码进行测试:

预期输出: 测试通过

参考代码:

import numpy as np
import pandas as pd
import cv2
np.random.seed(10)

this_answer = None

# 视频的读入
cap = cv2.VideoCapture(r'step2/video_20210506_183642.mp4')

# 构建角点检测所需参数
feature_params = dict(maxCorners=100,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)
# maxLevel 为使用的图像金字塔层数
lk_params = dict(winSize=(15, 15),
                 maxLevel=2,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
color = np.random.randint(0, 255, (100, 3))

# 拿到第一帧图像并灰度化作为前一帧图片
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 返回所有检测特征点,需要输入图片,角点的最大数量,品质因子,minDistance=7如果这个角点里有比这个强的就不要这个弱的
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)


tmp_time = 0

while True:

    ret, frame = cap.read()
    if ret:
        # ********** Begin ********** #
        # 读取图片灰度化作为后一张图片的输入
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 进行光流检测需要输入前一帧和当前图像及前一帧检测到的角点
        p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
        # 读取运动了的角点st == 1表示检测到的运动物体,即v和u表示
        good_new = p1[st == 1]
        good_old = p0[st == 1]
        # ********** end ********** #

        cv2.waitKey(30)
        tmp_time += 30
        if tmp_time == 300:
            this_answer = good_new.ravel() - good_old.ravel()

        # ********** Begin ********** #
        # 更新前一帧图片和角点的位置
        old_gray = frame_gray.copy()
        p0 = good_new.reshape(-1, 1, 2)
        # ********** end ********** #
    else:
        break

cv2.destroyAllWindows()
cap.release()


df = pd.read_csv(r"step2/answer.csv", index_col=0)

mse = np.sum((df.values - this_answer.reshape(-1, 1)) ** 2)
if mse < 10:
    print("测试通过")
else:
    print("测试失败")

可以参考4-光流估计实战_哔哩哔哩_bilibili,这个up的视频做的很不错,讲的也很清楚

提问:

  1. # 更新前一帧图片和角点的位置
  2. old_gray = frame_gray.copy()
  3. p0 = good_new.reshape(-1, 1, 2)

第三排更新角点的位置为什么是good_new.reshape(-1, 1, 2)?

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

畜牧当道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值