光流法简介及实现

jupiter
2021-09-13 / 0 评论 / 710 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2021年12月07日,已超过871天没有更新,若内容或图片失效,请留言反馈。

1.光流法简介

1.1光流

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

通常将二维图像平面特定坐标点上的灰度瞬时变化率定义为光流矢量

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

三言以概之:所谓光流场就是很多光流的集合。

       当我们计算出了一幅图片中每个图像的光流,就能形成光流场。

       构建光流场是试图重现现实世界中的运动场,用以运动分析。

1.2光流法

光流法是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。

1.3光流法的基本假设条件

亮度恒定不变。即同一目标在不同帧间运动时,其亮度不会发生改变。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程;

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

1.4光流场

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

光流场是一个二维矢量场,它反映了图像上每一点灰度的变化趋势,可看成是带有灰度的像素点在图像平面上运动而产生的瞬时速度场。它包含的信息即是各像点的瞬时运动速度矢量信息。

研究光流场的目的就是为了从序列图像中近似计算不能直接得到的运动场。光流场在理想情况下,光流场对应于运动场。

1.5稠密光流与稀疏光流

  • 稠密光流

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

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

1.6光流法的优缺点

优点

  • 光流法的优点在于它无须了解场景的信息,就可以准确地检测识别运动日标位置,且在摄像机处于运动的情况下仍然适用。
  • 而且光流不仅携带了运动物体的运动信息,而且还携带了有关景物三维结构的丰富信息,它能够在不知道场景的任何信息的情况下,检测出运动对象。

缺点

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

假设一:亮度恒定不变。

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

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

  • 对稀疏光流算法而言存在着孔径问题,对稠密光流算法而言存在着计算量大的问题。

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

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

2.算法实现

2.1稀疏光流-只跟踪某些角点(角点检测使用Shi-Tomasi检测算法)

  • 代码
"""
稀疏光流,只跟踪某些角点
"""

import numpy as np
import cv2
import matplotlib.pyplot as plt

video_path = "./test.mp4"
cap = cv2.VideoCapture(video_path) # 打开视频

# ShiTomasi 角点检测参数
feature_params = dict( maxCorners = 100,
                       qualityLevel = 0.3,
                       minDistance = 7,
                       blockSize = 7 )

# lucas kanade光流法参数
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) #找到原始灰度图

#获取第一帧的灰度图中的角点p0
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params) 

#创建一个蒙版用来画轨迹,i.e.和每帧图像大小相同的全0张量
mask = np.zeros_like(old_frame) 

# 对每帧图像计算光流并绘制光流轨迹
while(True):
    ret,frame = cap.read()
    if not ret:
        print("This video has been processed.")
        break
    
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 计算每帧的光流
    p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    # 选取好的跟踪点
    good_new = p1[st==1]
    good_old = p0[st==1]

    # 画出轨迹
    for i,(new,old) in enumerate(zip(good_new,good_old)):
        a,b = new.ravel()
        c,d = old.ravel()
        mask = cv2.line(mask, (int(a),int(b)),(int(c),int(d)), color[i].tolist(), 2) #添加了该帧光流的轨迹图
        frame = cv2.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1)
    
    # 效果可视化
    img = cv2.add(frame,mask) #将该图和轨迹图合并
    img_show = np.hstack((img,mask))
    cv2.imshow('frame',img_show)

    if cv2.waitKey(50)&0xFF==ord("q"):
            break

    # 更新"上一帧图像"和追踪点
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1,1,2)

cv2.destroyAllWindows()
cap.release()
  • 实现效果

2.2稠密光流-跟踪所有的像素点

  • 代码
# 稠密光流
import numpy as np
import cv2


video_path = "./test.mp4"
cap = cv2.VideoCapture(video_path) # 打开视频

# 获取第一帧
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)

# 创建光流矢量绘制蒙版
hsv = np.zeros_like(frame1)

# 遍历每一行的第1列
hsv[...,1] = 255


while(1):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)

    # 返回一个两通道的光流向量,实际上是每个点的像素位移值
    flow = cv2.calcOpticalFlowFarneback(prvs,next, None, 0.5, 3, 15, 3, 5, 1.2, 0)

    # 笛卡尔坐标转换为极坐标,获得极轴和极角
    mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
    hsv[...,0] = ang*180/np.pi/2
    hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
    
    # 光流法结果可视化
    img_show = np.hstack((frame2,rgb))
    cv2.imshow('frame',img_show)
    if cv2.waitKey(50)&0xFF==ord("q"):
        break
        
    prvs = next

cap.release()
cv2.destroyAllWindows()
  • 实现效果

参考资料

  1. 计算机视觉--光流法(optical flow)简介:https://blog.csdn.net/qq_41368247/article/details/82562165
  2. 光流法简述(重庆邮电大学.孔令上):https://wenku.baidu.com/view/7a2cb968ff00bed5b8f31d6c.html
  3. OPENCV对光流法的实现(PYTHON3):https://www.freesion.com/article/32791433918/
  4. 角点检测:Harris 与 Shi-Tomasi:https://zhuanlan.zhihu.com/p/83064609
  5. python opencv入门 光流法(41):https://blog.csdn.net/tengfei461807914/article/details/80978947
0

评论 (0)

打卡
取消