【计算机视觉】目标跟踪| 光流算法详细介绍|附代码

news/2024/5/30 17:10:59/文章来源:https://blog.csdn.net/Yaoyao2024/article/details/136680855

0、前言

在上篇文章中https://blog.csdn.net/Yaoyao2024/article/details/136625461?spm=1001.2014.3001.5501,我们对目标跟踪任务和目标跟踪算法有了大致的了解。今天我们就来详细介绍一下其中的生成式算法的一种:光流法。

在介绍光流法之前,让我们先回顾一下生成式目标跟踪算法:
在这里插入图片描述
在这里插入图片描述

  • 生成式的方法,强调先对所需跟踪的目标进行外观特征建模(属于特征检测部分的内容)——得到目标的一系列关键点or直方图。
  • 然后再利用目标跟踪算法进行对目标在当前帧中位置的确定(目标跟踪)
    • 比如:对上一帧中一系列关键特征点用LK光流法确定其每个关键像素点的位移,从而得到当前帧中的位置。
    • 对于Meanshift算法,根据目标建模,在当前帧中寻找局部最相似的位置,向其靠近,从而得到目标在当前帧中位置。

💁🏻‍♀️总之,对目标区域进行建模,在特征检测中是很常见的步骤。这里的光流法和Meanshift方法主要强调的是,在对目标特征区域进行建模之后,利用相邻帧之间目标位置变换不大等原理(上下文信息的利用),从而利用上一帧和当前帧的信息,判断目标从上一帧移动到当前帧的位置。它的这种确定位置是迭代逼近、有原理的,并不是一般的盲搜。

一、光流法

1.1:什么是光流?

光流(optical flow)是空间运动物体在观察成像平面上的像素运动的 瞬时速度

所谓光流就是瞬时速率,在时间间隔很小(比如视频的连续前后两帧之间)时,也等同于 目标点的位移

💭🤔所以也就是说,只要我们求出这个“速度”,也就是只要确定当前目标区域的位移,即可确定这个目标在下一帧中的位置(有时候也会说,根据上一帧和当前帧的信息,确定目标在当前帧中的位置)。——这也是生成式方法中逐次逼近搜索的体现,光流法的关键也就在此

🌸光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。(可以从后面1.4小节讲光流法的基本原理来更好的理解)。总而言之就是:光流法就是利用了一系列前提和假设条件,得到了相邻帧之间物体运动的对应关系;比如:我们知道了当前帧中物体的位置(特征检测),通过光流算法,结合相邻两帧图像中的信息,即可确定物体在下一帧中的位置。

1.2:光流的物理意义

一般而言,光流是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。

当人的眼睛观察运动物体时,物体的景象在人眼的视网膜上形成一系列连续变化的图像,这一系列连续变化的信息不断“流过”视网膜(即图像平面),好像一种光的“流”,故称之为光流。光流表达了图像的变化,由于它包含了目标运动的信息,因此可被观察者用来确定目标的运动情况。

下图展示的便是三维空间内物体的运动在二维成像平面上的投影。得到的是一个描述位置变化的二维矢量,但在运动间隔极小的情况下,我们通常将其视为一个描述该点瞬时速度的二维矢量u=(u,v),称为光流矢量。(由相邻帧共同计算得到)
在这里插入图片描述

1.3:光流场

在空间中,运动可以用运动场描述,而在一个图像平面上,物体的运动往往是通过图像序列中不同图像灰度分布的不同体现的,从而,空间中的运动场转移到图像上就表示为光流场(optical flow field)。

光流场是一个二维矢量场,它反映了图像上每一点灰度的变化趋势,可看成是带有灰度的像素点在图像平面上运动而产生的瞬时速度场。它包含的信息即是各像点的瞬时运动速度矢量信息上文说到过,在极短的时间内,这些速度即可等同于位移)。

🌸一个像素的光流失量,是由当前帧和下一帧共同确定:

  • 一个像素的光流矢量是通过考虑当前帧和下一帧之间的像素强度变化计算得出的。这是因为光流的主要假设是随着时间的推移,物体运动导致像素的强度改变。

  • 举个例子,假设你在观看一部电影,并试图计算主角在某个瞬间的运动。你会需要考虑主角在当前帧和下一帧的位置,然后通过他们的变化计算出光流矢量。这个矢量将会告诉你主角运动的方向和速度。

  • 这就是为什么光流矢量通常由两帧图片计算得出,这两帧图片一般是时间上连续的(例如在视频中相邻的两帧)。光流计算是对这两帧图片中的每一个像素进行的。然而,像素光流的准确度依赖于实际处理数据和使用的算法。一些复杂的运动,例如快速旋转和变形,可能会使光流计算变得具有挑战性。

简而言之,光流场就是由图像中每个像素的光流失量组成的一个集合;光流场在理想情况下,光流场对应于运动场(还是上文所说的,速度等同于位移)。

三维空间的矢量场及其在二维平面内的投影:

三维空间的矢量场及其在二维平面内的投影
现实场景的可视化光流场:
在这里插入图片描述

总结一下:

  • 所谓光流场就是很多光流的集合
  • 当我们计算出了一幅图片中每个像素的光流,就能形成光流场
  • 构建光流场是试图重现现实世界中的运动场,用以运动分析

1.4:基本原理

1.4.1.基本假设条件(前提!)

(1)亮度恒定不变(Brightness Constancy Assumption)⭐。即相邻帧中的同一目标关键像素在灰度值上保持不变(位置当然可变)。根据这个假设,我们可以假设两个相邻帧之间的像素强度变化可以通过运动向量来表示。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程;

(2)时间连续或运动是“小运动”(Temporal Coherence Assumption)⭐。即时间的变化不会引起目标位置的剧烈变化,相邻帧之间位移要比较小。同样也是光流法不可或缺的假定。

(3)空间一致性(Spatial Coherence Assumption):该假设认为相邻像素点在图像中的运动是相似的。也就是说,如果两个像素在连续帧中位于相似的位置,它们的运动向量也应该相似。这个假设在光流法中用于增强运动向量的连续性和平滑性

1.4.2 基本约束方程

考虑一个像素I(x,y,t)在第一帧的光强度(其中t代表其所在的时间维度)。它移动了 (dx,dy) 的距离到下一帧,用了 dt 时间。因为是同一个像素点,依据上文提到的第一个假设我们认为该像素在运动前后的光强度是不变的,即式(1):

I ( x , y , t ) = I ( x + d x , y + d y , t + d t ) I(x,y,t) = I(x+dx,y+dy,t+dt) I(x,y,t)=I(x+dx,y+dy,t+dt)

对于函数 I(x+dx, y+dy, t+dt),我们可以在点 (x, y, t) 处进行泰勒展开,得到式(2):

I ( x , y , t ) = I ( x , y , t ) + ∂ I ∂ x d x + ∂ I ∂ y d y + ∂ I ∂ t d t + ξ I(x,y,t) = I(x,y,t) +\frac{\partial I}{\partial x}dx +\frac{\partial I}{\partial y}dy+\frac{\partial I}{\partial t}dt +\xi I(x,y,t)=I(x,y,t)+xIdx+yIdy+tIdt+ξ

将(2)式代入(1)式右边,无穷小项忽略,且两端同除dt可得式(3)

∂ I ∂ x d x d t + ∂ I ∂ y d y d t + ∂ I ∂ t d t d t = 0 \frac{\partial I}{\partial x}\frac{\text{d}x}{\text{d}t}+\frac{\partial I}{\partial y}\frac{\text{d}y}{\text{d}t}+\frac{\partial I}{\partial t}\frac{\text{d}t}{\text{d}t}=0 xIdtdx+yIdtdy+tIdtdt=0

设u,v分别为光流分别沿x轴和y轴的速度矢量,得:

u = d x d t , v = d y d t u = \frac{\text{d}x}{\text{d}t},v=\frac{\text{d}y}{\text{d}t} u=dtdxv=dtdy

令,Ix.Iy,It分别表示图像中像素点的灰度沿X,Y,T方向的偏导数:

I x = ∂ I ∂ x , I y = ∂ I ∂ y , I t = ∂ I ∂ t I_{x} = \frac{\partial I}{\partial x},I_{y} = \frac{\partial I}{\partial y},I_{t} = \frac{\partial I}{\partial t} Ix=xI,Iy=yI,It=tI

综上,式(3)可写为如下式(4):

I x u + I y v + I t = 0 I_{x}u+I_{y}v+I_{t}=0 Ixu+Iyv+It=0

其中:Ix,Iy,It均可由图像数据求得,而 (u,v)为所求光流矢量

公式4,便是咱们的核心公式了。其中u,v代表两个方向(x方向和y方向)的移动速度,IxIyIt代表了亮度在三个轴上的偏导(也就是梯度)。把u、v计算出来,咱们的光流也就算出来了,该像素在下一帧中的位置也就求得了。

在这里插入图片描述

约束方程只有一个,而方程的未知量有两个,这种情况下无法求得u和v的确切值。此时需要引入另外的约束条件,从不同的角度引入约束条件,导致了不同光流场计算方法。

下图是求解公式(4)的一种利用差分的算法:LK算法

在这里插入图片描述

在这里插入图片描述

从上面也可以看到,我们求解光流涉及到的不是单独一张图片,而是上一帧和当前帧两个,利用上述原理公式,共同求解光流,即求解当前帧中的关键点在下一帧中位置。

约束方程只有一个,而方程的未知量有两个,这种情况下无法求得u和v的确切值。此时需要引入另外的约束条件,从不同的角度引入约束条件,导致了不同光流场计算方法。按照理论基础与数学方法的区别把它们分成四种:基于梯度(微分)的方法、基于匹配的方法、基于能量(频率)的方法、基于相位的方法和神经动力学方法。

1.5.几种光流估计算法的简介

1) 基于梯度的方法

基于梯度的方法又称为微分法,它是利用时变图像灰度(或其滤波形式)的时空微分(即时空梯度函数)来计算像素的速度矢量。

由于计算简单和较好的结果,该方法得到了广泛应用和研究。典型的代表是Horn-Schunck算法与 Lucas-Kanade(LK) 算法。

Horn-Schunck算法在光流基本约束方程的基础上附加了全局平滑假设,假设在整个图像上光流的变化是光滑的,即物体运动矢量是平滑的或只是缓慢变化的。

基于此思想,大量的改进算法不断提出。Nagel采用有条件的平滑约束,即通过加权矩阵的控制对梯度进行不同平滑处理;Black和Anandan针对多运动的估计问题,提出了分段平滑的方法。

2)基于匹配的方法

基于匹配的光流计算方法包括基于特征和区域的两种。

基于特征的方法不断地对目标主要特征进行定位和跟踪,对目标大的运动和亮度变化具有鲁棒性。存在的问题是光流通常很稀疏,而且特征提取和精确匹配也十分困难。

基于区域的方法先对类似的区域进行定位,然后通过相似区域的位移计算光流。这种方法在视频编码中得到了广泛的应用。然而,它计算的光流仍不稠密。另外,这两种方法估计亚像素精度的光流也有困难,计算量很大。

3)基于能量的方法

基于能量的方法又称为基于频率的方法,在使用该类方法的过程中,要获得均匀流场的准确的速度估计,就必须对输入的图像进行时空滤波处理,即对时间和空间的整合,但是这样会降低光流的时间和空间分辨率。基于频率的方法往往会涉及大量的计算,另外,要进行可靠性评价也比较困难。

4)基于相位的方法

基于相位的方法是由Fleet和Jepson提出的,Fleet和Jepson最先提出将相位信息用于光流计算的思想。当我们计算光流的时候,相比亮度信息,图像的相位信息更加可靠,所以利用相位信息获得的光流场具有更好的鲁棒性。基于相位的光流算法的优点是:对图像序列的适用范围较宽,而且速度估计比较精确,但也存在着一些问题:第一,基于相位的模型有一定的合理性,但是有较高的时间复杂性;第二,基于相位的方法通过两帧图像就可以计算出光流,但如果要提高估计精度,就需要花费一定的时间;第三,基于相位的光流计算法对图像序列的时间混叠是比较敏感的。

5)神经动力学方法

神经动力学方法是利用神经网络建立的视觉运动感知的神经动力学模型,它是对生物视觉系统功能与结构比较直接的模拟。

尽管光流计算的神经动力学方法还很不成熟,然而对它的研究却具有极其深远的意义。随着生物视觉研究的不断深入,神经方法无疑会不断完善,也许光流计算乃至计算机视觉的根本出路就在于神经机制的引入。神经网络方法是光流技术的一个发展方向。

1.6.稠密光流与稀疏光流

除了根据原理的不同来区分光流法外,还可以根据所形成的光流场中二维矢量的疏密程度将光流法分为稠密光流与稀疏光流两种。

  • 稠密光流——类比于搜索策略的中的密集搜索
    稠密光流是一种针对图像或指定的某一片区域进行逐点匹配的图像配准方法,它计算图像上所有的点的偏移量,从而形成一个稠密的光流场。通过这个稠密的光流场,可以进行像素级别的图像配准

  • 稀疏光流——类比于搜索策略中的图像金字塔

    与稠密光流相反,稀疏光流并不对图像的每个像素点进行逐点计算。它通常需要指定一组关键点进行跟踪,这组点最好具有某种明显的特性,例如Harris角点等,那么跟踪就会相对稳定和可靠。稀疏跟踪的计算开销比稠密跟踪小得多。

上文提到的基于特征的匹配方法是典型的属于稀疏光流的算法

1.7.具体算法:KLT算法 (基于梯度的方法

LK光流法于1981年提出,最初是用于求稠密光流的,由于算法易于应用在输入图像的一组点上,而成为求稀疏光流的一种重要方法。

LK光流法在原先的光流法两个基本假设的基础上,增加了一个**“空间一致”的假设,即所有的相邻像素有相似的行动**。也即在目标像素周围m×m的区域内,每个像素均拥有相同的光流矢量。以此假设解决式 I x u + I y v + I t = 0 I_{x}u+I_{y}v+I_{t} = 0 Ixu+Iyv+It=0 无法求解的问题。

1.4.7.1: LK光流法约束方程

在一个小邻域内,LK光流法通过该邻域的所有关键像素采用下式的加权平方和最小化来估计光流矢量

∑ x , y ∈ Ω W 2 ( x ) ( I x u + I y v + I t ) 2 \sum_{x,y\inΩ}^{}W^{2}(x)(I_{x}u+I_{y}v+I_{t})^{2} x,yΩW2(x)(Ixu+Iyv+It)2

⭐原理:在目标像素周围m×m的区域内,每个像素均拥有相同的光流矢量

上式中 W 2 ( x ) W^{2}(x) W2(x)是一个窗口权重函数,该函数使得领域中心的加权比周围大,对于窗口范围Ω内的n个点X1…Xn,有

V = ( u , v ) T , ▽ I ( x ) = ( I x , I y ) T V=(u,v)^{T},▽I(x)=(I_{x},I_{y})^{T} V=(u,v)T,I(x)=(IxIy)T

在这里插入图片描述

最后可得

V = ( A T W 2 A a ) − 1 A T W 2 b V=(A^{T}W^{2}Aa)^{-1}A^{T}W^{2}b V=(ATW2Aa)1ATW2b

通过结合几个邻近像素点的信息,LK光流法通常能够消除光流方程里的多义性。而且与逐点计算的方法相比,LK方法对图像噪声不敏感

1.4.7.2:改进:金字塔LK光流法

LK算法的约束条件即:小速度,亮度不变以及区域一致性都是较强的假设,并不很容易得到满足。如当 物体运动速度较快时,假设不成立,那么后续的假设就会有较大的偏差,使得最终求出的光流值有较大的误差。图像金字塔可以解决这个问题。

考虑物体的运动速度较大时,算法会出现较大的误差。那么就希望能减少图像中物体的运动速度。一个直观的方法就是,缩小图像的尺寸。假设当图像为400×400时,物体速度为[16 16],那么图像缩小为200×200时,速度变为[8,8]。缩小为100*100时,速度减少到[4,4]。所以在源图像缩放了很多以后,原算法又变得适用了。所以光流可以通过生成 原图像的金字塔图像,逐层求解,不断精确来求得。简单来说上层金字塔(低分辨率)中的一个像素可以代表下层的四个
在这里插入图片描述

下图黑色方块代表两个连续的帧内同一目标的不同位置。为了观察方便我才将其放在一张图里

在这里插入图片描述
面对“大运动”时光流法的局限是什么?显然是运动距离大,原算法不适用。(hhh)那么其实只要通过图片尺寸的不断缩小,而且目标选框大小保持不变,让运动前后两个物体的位置看上去“不断靠近”,直到变成小运动就可以使用光流法了。读者可能会存在很多疑问:

1. 为什么图片整体尺寸缩减时假定的目标其“尺寸”可以保持不变?
不论是在什么尺寸(尺度,缩小图片其实是模拟观察者的远近)的情况下,我们都坚持LK光流法的假设,即“空间一致”,不管我们看到图像是1000x1000还是10x10,我们都认为一个像素和它周围的一片区域内的若干个像素具有相同的速度。这是你使用LK光流法就必须承认的一个假设。这一片区域大小不一定是50x50,但始终是存在且不随客观情况改变只由你自己的判断而确定。
2.只在分辨率很小的尺度内计算光流,有什么用?能替代真实的光流矢量的值吗?

在低尺度下只计算一次当然不准,为什么?你把图片弄得很模糊又很小,丢失了很多信息,还想检测准确?但是在低尺度下的光流矢量的测量能给我们一个指示,就像是低尺度下它综观全局,告诉你“运动大概是5点钟方向,大小大概是【32,57】”之类的信息。这条信息够粗糙,但是大体上是有用的。它使得你在进入金字塔的下一层时有一个大概的头绪,你由此指示可以在下一层金字塔再一次顺利“靠近”真实目标,强行符合“小运动”。这里可能说得很抽象,大家细细看下面的具体算法再回来看这一段或许会有所启示。

🔗具体算法:https://blog.csdn.net/sgfmby1994/article/details/68489944

如下图:低尺度下找到光流矢量d0,将其扔到下一层去指引我们前行。扔下去后首先要放大两倍,此时你开始抱怨:“上一层找到的d0根本不靠谱,差距好大”(图2中蓝方块与右下角黑色方块的差距)但是你也要庆幸,正是“不靠谱”的d0让你到达了蓝色方块的位置,让你离真实区域前进了很多,否则你还在左上角苦于“小运动”寸步难行呢。在蓝色位置你就满足“小运动”了,继续计算光流,得到d1,然后把(2d0+d1)扔到下一层指导我们继续前行,如此往复。
在这里插入图片描述

1.8.基于光流的目标追踪

使用特征点跟踪的方式进行目标跟踪,计算量小,运行速度快(并不是对每个像素进行光流计算)

1. 光流跟踪的思路与流程图
在这里插入图片描述

2.特征点检测
(和我们之前了解和学习的一样)——可以在这里用上限制区域搜索算法(下面的发明专利里就用到了)
包括 2D和3D角点,SIFT特征点,时空兴趣点

3.特征点的删除
运动大小

刚体:轨迹的子空间约束

局部刚体:结合1和2

4.目标位置如何估计——光流法进行特征点跟踪
在这里插入图片描述

CN104200494B发明专利https://patents.google.com/patent/CN104200494B/zh中具体步骤:

在这里插入图片描述

  • 步骤一,获取跟踪目标第一帧图像的初始位置;
    跟踪目标的初始位置已知,给定一个矩形框,表示目标所在位置,在目标追踪的过程中,每帧图像中目标的位置都由一个矩形框表示。

  • 步骤二,获取特征点序列 (特征点检测)
    在图像It中目标所在位置的矩形框内,筛选富含纹理信息的特征点,方法如下:

    设p=[px,py]为矩形框内任意一点,px、py表示p点在像素坐标系下的坐标,设以p为中心,半径为w的范围为搜索区域,搜索区域大小为(2w+1)×(2w+1),设矩阵M为:

    在这里插入图片描述

    其中:Ix与Iy为搜索区域中所有点在像素坐标系下x、y方向上的微分;

    对于矩形框中每一个点,计算出其矩阵M的两个特征值λ1、λ2,设其中最小的特征值为λmin,设定特征值的阈值λ{thres hold},丢弃λmin<λ{thres hold}的点,剩余的点按λmin从大到小的顺序排列,设为预选点集合,选定预选点集合中λmin最大的点,设定最小距离,丢弃与λmin最大点距离小于最小距离的点,将λmin最大的点添加到特征点序列中,剩下的点形成新的预选点集合,在新的预选点集合中,选定λmin最大的点,丢弃与λmin最大点距离小于最小距离的点,将λmin最大的点添加到特征点序列中,剩下的点再次形成新的预选点集合,依次循环,直到预选点集合为空,最终,获取得到特征点序列;

  • 步骤三:获取跟踪目标的新时刻的图像It+1

  • 步骤四:计算光流,进行特征点跟踪
    根据图像It中的特征点序列,采用Lucas-Kanade方法,得到图像It中的特征点序列在图像It+1中的对应点,相对应的点形成N个点对

    本发明中使用了Lucas-Kanade方法对特征点进行匹配,从而求得两张图像之间特征点的运动,也即对特征点进行了跟踪

    (p🤷‍♀️s:我感觉于其说它是一种跟踪算法,倒不如说它是一种匹配or预测算法,但从在跟踪任务里它起到的作用来看,只是一个特征匹配的作用,只是这种匹配在某种意义上来说,也是一种跟踪,而且是精确到点的)

  • 步骤五:对点对进行筛选

  • 步骤六:消除背景特征点的影响;

  • 步骤七:系统状态估计

1.9:基于光流的运动目标检测(前景检测)算法

基于光流运动目标检测是在对摄像机采集到的图像序列进行重采样和去噪预处理后,利用光流法计算出各点的光流值,得出各点的光流场。然后对光流场进行阈值分割,区分出前景与背景,得到清运动目标区域。一般还会再采用形态学滤波中的开、闭运算滤除孤立噪声点,最后经过区域连通便可识别出目标区域并统计其特征信息。流程图如下:

如果图像中没有运动目标,则光流矢量在整个图像区域是连续变化的。当图像中有运动物体时,目标和背景存在着相对运动。运动物体所形成的速度矢量必然和背景的速度矢量有所不同,如此便可以计算出运动物体的位置

在这里插入图片描述
通过观察上图我们可以看到,发生运动的物体的光流矢量与背景光流矢量之间存在差异。使用阀值分割可以将整幅图片的光流矢量分成两个部分,即区分出背景与前景。阀值的选取可以使用最大类间方差法(大津算法)来确定。它是按图像的灰度特性,将图像分成背景和目标两部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

光流场经过阈值分割后,有一些独立的点或者有凹区域,影响了运动目标的提取。可先利用开运算,去除那些光流值与结构元素不相吻合的凹区域,同时保留那些相吻合的凹区域。然后,利用形态学滤波的闭运算,填充凹区域。

通过前面的处理,一帧图像中可能的目标区域已经成为一个可以连成一体的区域,采用合理的区域连通合并和分割技术来找出最终的目标区域。

1.10.总结

光流法的优缺点

  • 优点

    光流法的优点在于它无须了解场景的信息,就可以准确地检测识别运动日标位置,且在摄像机处于运动的情况下仍然适用。

    而且光流不仅携带了运动物体的运动信息,而且还携带了有关景物三维结构的丰富信息,它能够在 不知道场景的任何信息的情况下,检测出运动对象

    且支持目标的更新(下一帧目标的检测总是基于上一帧检测处的目标进行检测)

  • 缺点

    光流法的适用条件,即两个基本假设,在现实情况下均不容易满足。

    假设一:亮度恒定不变。

    但是实际情况是光流场并不一定反映了目标的实际运动情况,如图,所示。图中,光源不动,而物体表面均一,且产生了自传运动,却并没有产生光流图中,物体并没有运动,但是光源与物体发生相对运动,却有光流产生。因此可以说光流法法对光线敏感, 光线变化极易影响识别效果。

    假设二:小运动。

    前文也有提到,现实情况下较大距离的运动也是普遍存在的。因此当需要检测的目标运动速度过快是,传统光流法也不适用。

孔径问题

在这里插入图片描述

观察上图(a)我们可以看到目标是在向右移动,但是由于**“观察窗口”过小我们无法观测到边缘也在下降**。LK算法中选区的小邻域就如同上图的观察窗口,邻域大小的选取会影响到最终的效果。当然,这是针对于一部分稀疏光流算法而言,属于稠密光流范畴的算法一般不存在这个问题。

但是稠密光流法的显著缺点主要体现在,计算量大,耗时长,在对实时性要求苛刻的情况下并不适用。

🙎🏻‍♀️几种光流算法的比较:
在这里插入图片描述

补充:【【入门向】光流法(optical flow)基本原理+深度学习中的应用【FlowNet】【RAFT】 - CSDN App】http://t.csdnimg.cn/bi61R

二、代码实例

2.1:

carFlow.py
删除静止点的光流分析:

实现流程:

  • 加载视频。

  • 调用cv2.GoodFeaturesToTrack 函数寻找兴趣点(关键点)。

  • 调用cv2.CalcOpticalFlowPyrLK 函数计算出两帧图像中兴趣点的移动情况。

  • 删除未移动的兴趣点。

  • 在两次移动的点之间绘制一条线段

在这里插入图片描述

import numpy as np
import cv2 as cvcap = cv.VideoCapture('car_flow.mp4')# 角点检测参数
feature_params = dict(maxCorners=100, qualityLevel=0.1, minDistance=7, blockSize=7)# KLT光流参数
lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.02))# 随机颜色
color = np.random.randint(0, 255, (100, 3))# 读取第一帧
ret, old_frame = cap.read()
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
p0 = cv.goodFeaturesToTrack(old_gray, mask=None, **feature_params, useHarrisDetector=False, k=0.04)
good_ini = p0.copy()def caldist(a, b, c, d):return abs(a - c) + abs(b - d)mask = np.zeros_like(old_frame)
cv2.namedWindow("frame", cv2.WINDOW_NORMAL)
# 光流跟踪
while True:ret, frame = cap.read()if not ret:breakframe_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)# 计算光流p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)# 根据状态选择good_new = p1[st == 1]good_old = p0[st == 1]# 删除静止点k = 0for i, (new0, old0) in enumerate(zip(good_new, good_old)):a0, b0 = new0.ravel()c0, d0 = old0.ravel()dist = caldist(a0, b0, c0, d0)if dist > 2:good_new[k] = good_new[i]good_old[k] = good_old[i]good_ini[k] = good_ini[i]k = k + 1# 提取动态点good_ini = good_ini[:k]good_new = good_new[:k]good_old = good_old[:k]# 绘制跟踪线for i, (new, old) in enumerate(zip(good_new, good_old)):a, b = new.ravel()c, d = old.ravel()a, b, c, d = np.int32(a), np.int32(b), np.int32(c), np.int32(d)  # 将坐标值转换为整数类型mask = cv.line(mask, (a, b), (c, d), color[i].tolist(), 2)frame = cv.circle(frame, (a, b), 5, color[i].tolist(), -1)cv.imshow('frame', cv.add(frame, mask))k = cv.waitKey(30) & 0xffif k == 27:cv.imwrite("flow.jpg", cv.add(frame, mask))break# 更新old_gray = frame_gray.copy()p0 = good_new.reshape(-1, 1, 2)if good_ini.shape[0] < 40:p0 = cv.goodFeaturesToTrack(old_gray, mask=None, **feature_params)good_ini = p0.copy()cv.destroyAllWindows()
cap.release()

2.2:

slectToTrack.py
运行用户在第一帧图片中框选目标得到目标在第一帧图像中的初始位置

计算特征点

然后进行光流法得到下一帧中目标特征点位置

并且用矩形框绘制将下一帧中图像框出。
在这里插入图片描述
在这里插入图片描述

import cv2
import numpy as np# 光流法参数
lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))# 初始化目标位置和特征点
bbox = None
prev_points = None
prev_gray = None# 创建跟踪器
tracker = cv2.TrackerCSRT_create()# 读取视频文件
cap = cv2.VideoCapture("D:/CollegeStudy/AI/windElectProject/VS2019_cpp/data/video_good/test_video.mp4")# 读取第一帧图像
ret, frame = cap.read()
if not ret:print("无法读取视频帧")exit()# 创建一个可以调整大小的窗口
cv2.namedWindow("选择目标", cv2.WINDOW_NORMAL)# 在第一帧图像中选择目标区域
bbox = cv2.selectROI("选择目标", frame, fromCenter=False, showCrosshair=True)
cv2.destroyAllWindows()# 初始化跟踪器
tracker.init(frame, bbox)
cv2.namedWindow("Frame", cv2.WINDOW_NORMAL)while True:# 读取当前帧图像ret, frame = cap.read()if not ret:break# 使用光流法跟踪特征点gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)if prev_points is None:# 在第一帧中计算特征点mask = np.zeros_like(gray)mask[int(bbox[1]):int(bbox[1] + bbox[3]), int(bbox[0]):int(bbox[0] + bbox[2])] = 255prev_points = cv2.goodFeaturesToTrack(gray, mask=mask, maxCorners=100, qualityLevel=0.3, minDistance=7,blockSize=7)if prev_points is not None:if prev_gray is not None:# 计算光流next_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_points, None, **lk_params)# 选取符合条件的特征点和目标位置good_points = next_points[status == 1]x, y, w, h = cv2.boundingRect(good_points)# 更新目标位置和特征点bbox = (x, y, w, h)prev_points = good_points.reshape(-1, 1, 2)# 绘制矩形框和特征点cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)for point in good_points:x, y = point.ravel()cv2.circle(frame, (int(x), int(y)), 3, (0, 0, 255), -1)# 显示图像cv2.imshow("Frame", frame)# 按下Esc键退出if cv2.waitKey(1) == 27:break# 更新前一帧图像和特征点prev_gray = gray.copy()# 释放资源
cap.release()
cv2.destroyAllWindows()

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的GPS定位信息显示系统

基于单片机的GPS定位信息显示系统 摘 要 在当今信息时代的发展中&#xff0c;GPS全球定位系统是一个重要的组成部分&#xff0c;其具有精度很高、应用广泛、性能强大的特点&#xff0c;因此在实际生活中GPS全球定位显示系统被广泛运用于各类领域当中。它也是至今为止最好的定…

基于YOLOv8深度学习的野外火焰烟雾检测系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

Linux 之九:CentOS 上 Tomcat 安装、SpringBoot 项目打包和部署

安装 Tomcat 下载 a. 方式一&#xff1a;可以在windows 真机上下载后&#xff0c;再上传到服务器 b. 方式二&#xff1a;可以在服务器端使用 wget 下载命令来下载 登录官网https://tomcat.apache.org/download-90.cgi&#xff0c;选择 linux 版本 右键&#xff0c;获取下载链接…

从政府工作报告探计算机行业发展

从政府工作报告中&#xff0c;我们可以深入洞察计算机行业在未来一年的发展趋势和政策导向。报告中明确提出了数字经济创新发展的重要性&#xff0c;以及制造业数字化转型、服务业数字化、智慧城市和数字乡村建设等关键任务&#xff0c;这些都为计算机行业提供了广阔的发展空间…

C++提高笔记(三)---STL容器(vector、deque)

1、vector容器 1.1vector基本概念 功能&#xff1a;vector数据结构和数组非常相似&#xff0c;也称为单端数组 vector与普通数组区别&#xff1a;不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a;并不是在原空间之后续接新空间&#xff0…

全网最全c++中的system详解

这篇文章是二发&#xff0c;做了些微调&#xff0c;感兴趣的朋友可以看原文&#xff1a;C中的system_一只32汪的博客-CSDN博客 1&#xff0c;简介 system()函数是在C制作中十分常用&#xff0c;有用的一个函数。 其效果类似于系统中"cmd"控制台和"bat"文件…

华为机考:HJ3 明明的随机数

华为机考&#xff1a;HJ3 明明的随机数 描述 代码 set&#xff0c;插入即排序&#xff0c;而且没有重复数字 #include<iostream> #include<vector> #include<algorithm> using namespace std;int main(){int n;while(cin >> n){ //首先输入每次调查…

vue3/vue2若依框架对比,点击新增编辑跳转到新页面(新增编辑共用代码)

vue2若依框架&#xff1a; router里面定义好&#xff0c;编辑里面添加一个id {path: /filmManagement,component: Layout,hidden: true,redirect: noredirect,children: [{path: editFilmDetail,component: () > import(/views/filmManagement/editFilmDetail),name: editFi…

微信小程序如何实现下拉刷新

1.首先在你需要实现下拉刷新页面的json文件中写入"enablePullDownRefresh": true。 2.在js文件的onPullDownRefresh() 事件中实现下拉刷新。 实现代码 onPullDownRefresh() {console.log(开始下拉刷新)wx.showNavigationBarLoading()//在标题栏中显示加载图标this.d…

常用MII接口详解

开放式系统互连 (OSI) 模型 以太网层 位于最底部两层 &#xff0c;物理层(physical)和数据链路层(Date link)。 从百兆以太网接口开始 首先是百兆以太网规定的两种接口 介质无关接口 (MII) Media Independent Interface 介质相关接口 (MDI) Medium Dependent Interfa…

【大厂AI课学习笔记NO.72】AI与云计算

AI项目依靠云计算&#xff0c;借助云的力量&#xff0c;快速的启动业务&#xff0c;是比较好的一种选择。 AI模型训练过程中&#xff0c;出现算力突增&#xff0c;云计算成本低。 云平台提供一站式解决方案&#xff0c;创业公司的选择。 云端AI和边缘端的AI&#xff0c;是我们…

【机器学习】一文掌握逻辑回归全部核心点(上)。

逻辑回归核心点-上 1、引言2、逻辑回归核心点2.1 定义与目的2.2 模型原理2.2.1 定义解析2.2.2 公式2.2.3 代码示例 2.3 损失函数与优化2.3.1 定义解析2.3.2 公式2.3.3 代码示例 2.4 正则化2.4.1 分类2.4.2 L1正则化2.4.3 L2正则化2.4.4 代码示例 3、总结 1、引言 小屌丝&#…

Java高级编程—泛型

文章目录 1.为什么要有泛型 (Generic)1.1 泛型的概念1.2 使用泛型后的好处 2.在集合中使用泛型3.自定义泛型结构3.1 自定义泛型类、泛型接口3.2 自定义泛型方法 4.泛型在继承上的体现5.通配符的使用5.1 基本使用5.2 有限制的通配符的使用 1.为什么要有泛型 (Generic) Java中的…

uniapp h5 部署

uniapp 配置 服务器文件路径 打包文件结构 //nginx 配置 server {listen 8300;server_name bfqcwebsiteapp;charset utf-8;#允许跨域请求的域&#xff0c;* 代表所有add_header Access-Control-Allow-Origin *;#允许带上cookie请求add_header Access-Control-Allow-C…

【Pytorch】进阶学习:深入解析 sklearn.metrics 中的 classification_report 函数---分类性能评估的利器

【Pytorch】进阶学习&#xff1a;深入解析 sklearn.metrics 中的 classification_report 函数—分类性能评估的利器 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合…

Covalent Network (CQT) 通过统一 API 集成,为 Gnosis Chain 的 AI 潜力赋能

作为一个为超 225 个链提供服务的领先多链索引器&#xff0c;Covalent Network (CQT) 正在与知名的 EVM 区块链基础设施提供者 Gnosis Chain 展开一项激动人心的合作。这一战略合作象征着先进的实时数据索引技术的集成&#xff0c;包括 Covalent Network (CQT) 的统一 API 和 G…

前端项目,个人笔记(一)【定制化主题 + 路由设计】

目录 1、项目准备 1.1、项目初始化 1.2、elementPlus按需引入 注&#xff1a;使用cnpm安装elementplus及两个插件&#xff0c;会报错&#xff1a;vueelement-plus报错TypeError: Cannot read properties of null (reading isCE ) &#xff0c;修改&#xff1a; 测试&#…

一站式解决方案:uni-app条件编译及多环境配置,appid动态修改攻略!

前言 这篇文章主要介绍uniapp在Hbuilderx 中&#xff0c;通过工程化&#xff0c;区分不同环境、动态修改小程序appid以及自定义条件编译&#xff0c;解决代码发布和运行时手动切换到问题。 背景 在企业级的应用中&#xff0c;通常会分为&#xff0c;开发、联调、生产等多个环…

基于CNN的图像超分辨率

单图像超分辨率 (Single image super resolution&#xff0c;SR) 是计算机视觉中的一个经典问题&#xff0c; 其目的是从单个低分辨率图像中恢复高分辨率图像。这个问题本质上是不适定的&#xff0c;因为对于任何给定的低分辨率像素都存在解决方案的多重性。换句话说&#xff0…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:DatePicker)

日期选择器组件&#xff0c;用于根据指定日期范围创建日期滑动选择器。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 DatePicker(options?: DatePickerOptions) 根据指定范…