如何使用光流法进行目标追踪

如何使用光流法进行目标追踪 outman 2023-09-19 11:17:20 807

   如今,“图像分类”、“目标检测”、“语义分割”、“实例分割”和“目标追踪”等5大领域是计算机视觉的热门应用。其中“图像分类”与“目标检测”是最基础的应用,在此基础上,派生出了“语义分割”、“实例分割”和“目标跟踪”等相对高级的应用。

   “语义分割”与“实例分割”是将图像中的像素点分到对应的类别,实现图像前景和背景分离。“目标追踪”是跟踪图像序列或视频中的目标,定位出目标在图像帧中的运行轨迹。

   如图1所示,目标追踪(Object Tracking)是获取图像序列(一般为视频)中感兴趣的区域,并在接下来的视频帧中对其进行跟踪,目标跟踪是计算机视觉里一个重要的领域,在赛事转播、人机交互、监控安防、无人驾驶、无人机等应用中有着关键的作用。

易百纳社区

一、目标追踪概述

   目标追踪的输入通常是视频,视频是一种非结构化数据,可以看成是图像序列的组合(即一组有序的图像)。虽然在形式上,视频没有固定的结构,但在内容上,视频本身有着较强的逻辑关系,如图2所示,按照颗粒度大小将视频分成帧(Frame)、镜头(Shot)、场景(Scene)、视频(Video)四个层次。

易百纳社区

(1)帧是视频最基本单元,视频帧其实是一幅图像,关键帧,又叫代表帧,是指具有代表性的帧。

(2)镜头是由一系列帧组成的,这些帧表达同一个事件或者是摄像机的一组连续的运动。

(3)场景有一定的语义,由一系列相似的镜头组成,这些镜头从不同角度表达同一批对象或环境。

1.视频与图像序列的相互转换

    视频可以看成是一组有序的图像组成,对视频的目标检测和对图像的目标检测没有本质不同。通过OpenCV提供的VideoCapture和VideoWriter模块,可以实现视频和图像序列的相互转换,转换的示例代码如下:

(1)导入用到的库。

 1 import cv2,os
 2 from os.path import isfile, join

(2)使用VideoCapture将视频转换为图像序列。


  1 videoFile = './video/ball.mp4' #输入的视频文件
  2 imgPath = "./video/frame/"     #输出的图像文件路径
  3 #打开视频文件、读取视频帧、并以jpg的图像格式,写到指定目录下
  4 cap = cv2.VideoCapture(videoFile)
  5 frame_id = 0
  6 while True:
  7     ret,frame = cap.read()     #读取视频的帧
  8     if not ret:                          #视频结束
  9         break
 10     rgb_img= cv2.cvtColor(frame.copy(),cv2.COLOR_BGR2RGB) #将视频的帧转换为RGB格式
 11     cv2.imwrite("%s/%04d.jpg"%(imgPath,frame_id),rgb_img)
 12     frame_id = frame_id + 1
 13 print("转换结束:(视频文件:%s,图像序列路径:%s)"%(videoFile,imgPath))

(3)使用VideoWriter将图像序列转换为视频。


1 imgPath = "./video/frame/"      #输入的图像文件路径
  2 videoFile = './video/video.mp4' #输出的视频文件
  3 fps = 25.0 #速率,每秒25帧
  4 #找到目录下所有的图像文件,并按照文件名排序
  5 files=[f for f in os.listdir(imgPath) if isfile(join(imgPath,f)) and f.endswith('.jpg')]
  6 files.sort()
  7 #得到图像文件的尺寸
  8 img = cv2.imread(imgPath+files[0])
  9 height, width, channel = img.shape
 10 #创建视频对象,使用mpg4格式压缩
 11 video = cv2.VideoWriter(videoFile,cv2.VideoWriter_fourcc(*'DIVX'),fps,(width,height))
 12 #将图像序列写入视频文件
 13 for file_name in files:
 14     img = cv2.imread(imgPath+file_name)
 15     rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #转换为RGB格式
 16     video.write(rgb)
 17 video.release()
 18 print("转换结束:(图像序列路径:%s,视频文件:%s)"%(imgPath,videoFile))

2.目标追踪的分类

   根据任务的实时性要求,目标追踪分为“在线追踪”和“离线跟踪”两种:“在线追踪”通过过去和现在的视频帧确定目标的位置,对实时性要求较高;“离线追踪”通过过去、现在和未来的视频帧确定目标的位置,对实时性要求不高,“离线追踪”的准确率通常会高于“在线追踪”的准确率。根据应用场景,目标追踪又可以分为以下几种类型:

(1)单目标追踪,追踪一个固定目标在视频帧中出现的位置。

(2)多目标追踪,同时追踪多个目标在视频帧中出现的位置。

(3)多目标多摄像头追踪,追踪多个摄像头,拍摄到的多个目标,在不同的视频帧中出现的位置。

(4)姿态追踪,追踪目标在视频帧中姿态的变化,如视频中人的不同姿态。

3.生成式模型

   生成式模型首先定义出目标的特征,然后在后续视频帧中寻找相似特征的位置,从而实现目标的定位。早期的目标追踪模型中常使用这类方法,如光流法等,但是,生成式模型使用简单的特征定义,对追踪目标的描述方法有很大的局限性,在光照变化,目标被遮挡,分辨率低,拍摄角度变化等情况下,模型的识别效果不是很理想。

4.鉴别式模型

   鉴别式模型通过比较视频帧中目标和背景的差异,将目标从视频帧中提取出来,从而实现目标的定位。鉴别式模型同时考虑了目标和背景的信息,在模型的准确度和实时性上,比生成式模型有更好的效果,也逐渐成为目标追踪的主流方法。在本世纪初,传统的机器学习模型(如SVM,随机森林,GBDT等)逐渐被引入到目标追踪中,大约在2015年前后,基于深度学习模型的目标追踪方法开始成为研究热点。

5.目标追踪的方法

   有多种框架和算法可以实现目标追踪,其原理也不尽相同,按照时间顺序,可将目标追踪分成“经典方法”、“基于滤波方法”和“基于深度学习方法”三大类,如图3所示。

易百纳社区

(1)经典方法。

  经典的目标追踪方法是先对目标的外观进行建模(如特征点,轮廓,SIFT等特征),然后在视频帧中查找该目标出现的位置,为了提高查找效率,通常使用预测算法,对目标可能出现的区域进行预测,只在预测的区域内查找目标。

(2)基于滤波方法。

   这种方法是通过度量视频帧中目标的相似程度,将不同视频帧中的目标进行关联,实现目标追踪。如MOSSE算法使用相关滤波器(Correlation Filter),计算目标之间的相关值,根据相关值找到不同视频帧中相同的目标并建立关联,从而实现目标追踪。

(3)基于深度学习方法。

   这种方法是将深度学习引入到目标追踪中,如基于目标检测的追踪方法(Tracking By Detecting,简称TBD)等。这类方法通过深度学习模型,在每个视频帧上执行目标检测,并在检测到的目标之间建立关联,从而实现目标追踪。

二、使用光流法进行目标追踪

   光流法是一种经典的目标追踪方法,通过将不同的视频帧中的像素点形成对应关系,描述出运动信息,从而完成目标的追踪。如图4所示,点、点和点是在不同时刻的视频帧中的同一个目标,光流法通过找到这些点的映射关系,描述出这些点的运动过程,实现目标的追踪。

易百纳社区

1.光流

    光流(Optical Flow)是指运动物体在成像平面上的像素运动的瞬时速度。如图5所示,三维空间的物体运动,可以用一个三维矢量来描述,将其投影到二维成像平面上,得到一个二维矢量。在时间间隔极小的情况下(如相邻的两个视频帧中),称这个二维矢量为光流矢量用来描述该点的瞬时速度。

易百纳社区

2.光流法的原理

   光流法通过计算视频帧中像素点的光流,得到光流场(光流的集合),光流场中包含了目标的运动信息,通过分析光流场实现对目标的追踪。

   光流法是根据像素值在时间序列上的变化和相邻帧之间的关联程度,找到当前帧和前一帧的对应关系,并根据这种对应关系计算出目标的运行轨迹,使用光流法进行目标追踪时,需要满足以下两个假设条件:

(1)亮度不变性假设,同一目标在不同视频帧间运动时,其亮度不会发生变化。

(2)时间连续性假设,时间的变化不会引起目标位置的剧烈变化,相邻帧之间位移要小。

   如图6所示,假设某一个像素点在时刻的坐标是,像素值是,在时刻的坐标是,像素值是,其中为动作向量,表示该像素点向右移动了个像素点,向上移动了个像素点。

易百纳社区

 光流法假设了同一个点的亮度不变,根据这一假设,可以得到公式10.4。

易百纳社区

    使用泰勒级数将展开,如公式10.5所示。

易百纳社区

     略掉公式10.5中的二阶无穷小,再根据“亮度不变”假设(即公式10.4),可以得到动作向量是图像强度和时间内的变化方程,如公式10.6所示。

易百纳社区

易百纳社区分别为像素值关于,和的偏导数,分别用、和表示,可得公式10.7,其中、和为已知变量,和为待求参数。有多种算法可以对和进行求解,经典的求解算法是Lucas-KanadeLK)算法。

易百纳社区

3.Lucas-Kanade(LK)算法

   公式10.7中,有两个待求参数和,而只有一个方程,为了能求解出和,LK算法在“亮度不变”和“时间连续”假设的基础上,又增加了一个“空间一致”的假设,即在目标像素周围MxM的窗口内的所有像素均有相同的光流矢量。

   根据“空间一致”假设,LK算法使用大小3x3窗口内的9个像素点建立9个方程,如公式10.8所示,最后通过最小二乘法,求解出和的最优解。

易百纳社区

4.稠密光流于稀疏光流

光流法有“稠密光流”和“稀疏光流”两种类型,稠密光流计算视频帧中所有像素点的光流,形成密集光流场,然后再对目标进行像素级别的配准,而稀疏光流则计算指定特征点的光流(如Harris角点等),形成稀疏光流场,然后再对目标的特征点进行配准。

5.使用OpenCV计算光流

OpenCV内置了LK算法的实现函数calcOpticalFlowPyrLK(),可以直接调用该函数计算光流,并实现目标的追踪,以下代码演示了稀疏光流的计算方法,代码的输出如图7所示。

(1)导入库。

1 import cv2
  2 import numpy as np
  3 from matplotlib import pyplot as plt

(2)读取文件,读取两幅图像,代表连续的两个视频帧,并将图像转换为灰度图。


  1 img_0=cv2.imread("./images/da_feng_che_0.jpg") #读取第一帧的图像
  2 img_1=cv2.imread("./images/da_feng_che_1.jpg") #读取第二帧的图像
  3 img_0 = cv2.cvtColor(img_0, cv2.COLOR_BGR2RGB) #转换为RGB格式
  4 img_1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2RGB) #转换为RGB格式
  5 gray_0 = cv2.cvtColor(img_0.copy(), cv2.COLOR_RGB2GRAY) #转换为灰度图
  6 gray_1 = cv2.cvtColor(img_1.copy(), cv2.COLOR_RGB2GRAY) #转换为灰度图
  7 mask = img_0+img_1 #mask用来显示最后的结果

(3)生成特征点,使用cv2.goodFeaturesToTrack函数,生成第一帧图像的特征点。

 1 params = {"maxCorners":10,"qualityLevel":0.01,"minDistance":50,"blockSize":1}
  2 key_points_0 = cv2.goodFeaturesToTrack(gray_0, mask = None, **params)

(4)计算光流(稀疏光流),生成第二帧图像的特征点,并将第一帧和第二帧图像的特征点进行关联。

1 #key_points_1是光流计算出的特征点位置,match表示匹配上的特征点
  2 key_points_1, match, _ = cv2.calcOpticalFlowPyrLK(gray_0,gray_1,key_points_0, None)
  3 matched_0 = key_points_0[match==1] #第一帧图像上的特征点
  4 matched_1 = key_points_1[match==1] #第二帧图像上的特征点

(5)显示运动信息,如图10.20所示,红色和蓝色点表示两帧图像上的特征点,白色连线表示对应关系。

 1 for i,(frame_0,frame_1) in enumerate(zip(matched_0,matched_1)):
  2     a,b = frame_0.ravel()
  3     c,d = frame_1.ravel()
  4     mask = cv2.circle(mask,(a,b),3,(255,0,0),-1)  #用红色标记,第一帧图上的特征点
  5     mask = cv2.circle(mask,(c,d),3,(0,0,255),-1)  #用蓝色标记,第二帧图上的特征点
  6     mask = cv2.line(mask, (a,b),(c,d),(255,255,255),2) #用白色标记,两帧图像上特征点的对应关系
  7 f,ax = plt.subplots(1,3, figsize=(12,12))
  8 ax[0].imshow(img_0)
  9 ax[1].imshow(img_1)
 10 ax[2].imshow(mask)

易百纳社区

文章来源公众号:OpenCV与AI深度学习

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
outman
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
outman
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区