专栏首页数学与计算机Opencv----Optical Flow

Opencv----Optical Flow

目标

  1. 理解光流的概念和lucas-kanade估计算法
  2. 我们将使用cv.calcOpticalFlowPyrLK() 函数来跟踪视频中的特征点
  3. 我们将使用cv.calcOpticalFlowFarneback()函数来创建一个密集的光流场

光流

光流是相机或物体运动引起的两连续帧图像中物体的运动模式, 是一个二维的位移向量场, 每一个向量表示第一个点到第二个点之间的位移

image

图片展示了在五个连续帧中球的移动, 箭头表示位移向量, 光流应用在许多领域:

  • 运动结构(structure from motion)
  • 视频压缩
  • 视频稳定

光流基于几个基本假设:

  1. 物体的像素强度在连续帧中不改变
  2. 相邻像素具有相似的运动

考虑一个像素I(x_0, y_0, t_0)(这里增加了一个时间维度, 前期只用图像, 时间维度暂时不用), 下一帧图像移动了(dx, dy)的距离, 耗费dt的时间, 这些像素相同并且强度不变, 因此

I(x_0,y_0,t_0)=I(x_0+dx,y_0+dy,t_0+dt)

右边使用泰勒级数展开

I(x_0,y_0,t_0)=I(x_0,y_0,t_0) + (dx \frac{\partial}{\partial x} + dy \frac{\partial}{\partial y} + dt \frac{\partial}{\partial t})I(x_0,y_0,t_0) + \frac{1}{2!} (dx \frac{\partial}{\partial x} + dy \frac{\partial}{\partial y} + dt \frac{\partial}{\partial t})^2 I(x_0,y_0,t_0) + \cdots

忽略二阶及以上的高阶小量, 并对t求导

(\frac{dx}{dt} \frac{\partial}{\partial x} + \frac{dy}{dt} \frac{\partial}{\partial y} + \frac{\partial}{\partial t})I(x_0,y_0,t_0) = 0

将上式写成下面的形式, 称为光流方程式, 如何写成的请自己脑补

f_x u + f_y v + f_t = 0

不难得出f_xf_y是图像梯度, f_t是时间梯度, 这些都是已知量, uv都是未知的

我们无法直接求解此方程

Lucas-Kanade method

我们已经假设相邻的像素点具有相同的运动, Lucas-Kanade method使用该点周围3x3的色块, 这九个点有着相同的运动, 我们可以确定这九个点的f_x, f_y, f_t, 现在我们用九个方程解决两个未知量, 这个方程组是超定的, 最好的办法是使用最小二乘方法求解, 下面是二元二次的求解方案:

观察其逆矩阵与哈里斯角点detector的相似性, 这表明角点是更好的跟踪对象

从用户的视角, 这个想法很简单, 我们对一些点进行跟踪, 然后获得了这些点的光流矢量. 但是仍有一些难题, 直至现在, 我们只能处理一些小位移, 在大位移情况下会失败. 为了解决这个问题, 我们使用了金字塔, 当在上金字塔时, 会移除小位移, 将大位移变为小位移. 这里应用Lucas-kannade方法, 获得了带尺度的光流

Lucas-Kanade optical flow in opencv

所有这些在一个函数中提供: cv.calcOpticalFlowPyrLK(). 这里我们创建了在视频中跟踪几个点的简单应用. 我们使用cv.goodFeatureToTrack()选取跟踪点. 我们选择第一帧, 在其中提取一些托马斯角点, 然后使用Lucas-Kanade光流迭代地跟踪这些点. 对于函数cv.calcOpticalFlowPyrLK()我们传入前一帧, 和前一帧的跟踪点, 及后一帧. 函数返回下一帧的跟踪点和这些点的状态编码, 1表示找到, 0表示未找到. 我们迭代的将下一帧的点作为前一帧的跟踪点传入函数, 代码如下:

import numpy as np
import cv2 as cv
import argparse
import glob
import sys
import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
parser = argparse.ArgumentParser(description='This sample demonstrates Lucas-Kanade Optical Flow calculation.')
parser.add_argument('image', type=str, help='path to image file')
args = None
if len(sys.argv) > 1:
    args = parser.parse_args()
else:
    args = parser.parse_args(glob.glob("../../data/*.mp4"))
config = dict(if_dense=True)

cap = cv.VideoCapture(args.image)

# params for ShiTomasi corner detection
feature_params = dict(maxCorners=100,
                      qualityLevel=0.3,
                      minDistance=7,
                      blockSize=7)

# Parameters for lucas kanade optical flow
lk_params = dict( winSize=(15, 15),
                  maxLevel=2,
                  criteria=(cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))

# Create some random colors
color = np.random.randint(0, 255, (100, 3))

# Take first frame and find corners in it
ret, old_frame = cap.read()
old_frame = cv.transpose(old_frame)
old_gray = cv.cvtColor(old_frame, cv.COLOR_BGR2GRAY)
p0 = cv.goodFeaturesToTrack(old_gray, mask=None, **feature_params)

# Create a mask image for drawing purposes
mask = np.zeros_like(old_frame)

while not config["if_dense"]:
    ret, frame = cap.read()
    frame = cv.transpose(frame)
    if frame is None:
        break
    frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)

    # calculate optical flow
    p1, st, err = cv.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)

    # Select good points
    good_new = p1[st == 1]
    good_old = p0[st == 1]

    # draw the tracks
    for i, (new, old) in enumerate(zip(good_new, good_old)):
        a, b = new.ravel()
        c, d = old.ravel()
        mask = cv.line(mask, (a, b), (c, d), color[i].tolist(), 2)
        frame = cv.circle(frame, (a, b), 5, color[i].tolist(), -1)
    img = cv.add(frame, mask)

    cv.imshow('frame', img)
    k = cv.waitKey(30) & 0xff
    if k == 27:
        break

    # Now update the previous frame and previous points
    old_gray = frame_gray.copy()
    p0 = good_new.reshape(-1, 1, 2)

dense lucas kanade in opencv

Lucas-Kanade方法计算稀疏特征集的光流(在我们的示例中为使用Shi-Tomasi算法检测到的角), OpenCV提供了另一种算法来查找密集的光流, 它计算帧中所有点的光流, 它基于Gunner Farneback的算法, 在2003年Gunner Farneback的“基于多项式展开的两帧运动估计”中对此进行了解释.

下面的例子展示了如何使用上述算法计算稠密光流. 我们得到了双通道的光流矢量(u, v), 我们找到了它们的大小和方向。 我们对结果进行颜色编码,以实现更好的可视化。 方向对应于图像的色相值。 幅度对应于值平面。 请参见下面的代码:

frame1 = old_frame
prvs = cv.cvtColor(frame1, cv.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255
while config["if_dense"]:
    ret, frame2 = cap.read()
    frame2 = cv.transpose(frame2)
    next = cv.cvtColor(frame2, cv.COLOR_BGR2GRAY)
    logging.info("1")
    flow = cv.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 3, 5, 1.2, 0)
    logging.info("2")
    mag, ang = cv.cartToPolar(flow[..., 0], flow[..., 1])
    hsv[...,0] = ang*180/np.pi/2
    hsv[...,2] = cv.normalize(mag, None, 0, 255, cv.NORM_MINMAX)
    bgr = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)
    cv.imshow('frame2', bgr)
    k = cv.waitKey(30) & 0xff
    if k == 27:
        break
    elif k == ord('s'):
        cv.imwrite('opticalfb.png', frame2)
        cv.imwrite('opticalhsv.png', bgr)
    prvs = next

原文链接:https://docs.opencv.org/4.1.2/d4/dee/tutorial_optical_flow.html

原文作者:opencv

我来说两句

0 条评论
登录 后参与评论

相关文章

  • GitHub Top 45:新手入门 Python,值得推荐的优质资源!

    [导读]热门资源博客 Mybridge AI 比较了 18000 个关于 Python 的项目,并从中精选出 45 个最具竞争力的项目。我们进行了翻译,在此一并...

    GitHubDaily
  • 使用 yield 压平嵌套字典有多简单?

    你肯定想到了使用递归来解决这个问题,那么你可以试一试,看看你的递归函数有多少行代码。

    青南
  • 不给糖果就捣乱,用Python绘制有趣的万圣节南瓜怪

    最近由于工作比较忙,所以会转载一些大佬们的优秀文章。今天有些时间,想着写点什么呢?快到万圣节了既然秉承着寓教于乐的python学习态度,不如就用Python画一...

    猴哥yuri
  • 关于Linux网络的几个命令介绍

    nslookup的命令主要用于查询DNS的记录,通常用来查看域名的解析是否正确,在网络故障的时候可以用来诊断网络方面的问题,这个命令的使用方法比...

    AsiaYe
  • 新版 Kite:实时补全代码,Python 之父都发声力挺!

    不久前,一个免费的专门针对 Python 的代码补全工具 Kite,有了新的动态。这次,Kite 开发者在之前的基础上,增加了「Intelligent Snip...

    AI研习社
  • Python中is和==的区别及intern机制

    嗨小伙伴们好久不见,本来今天是更新一篇关于字符串的文章,但是还没写完,先更新一篇关于is和==区别的文章,因为字符串文章中会涉及到本篇的部分内容。另外最近会勤于...

    啃饼小白
  • Penta:一款开源的多合一自动化渗透测试命令行工具

    今天给大家介绍的是一款名叫Penta的自动化渗透测试工具,Penta,即渗透测试Pentest与自动化实现Automation。Penta是一个开源项目,并且整...

    FB客服
  • kafka 上手指南:单节点

    如果你是一名后端工程师,设计的应用正常的线上运行,某次秒杀活动,突然间把系统搞崩了,排查系统发现很多的流量没有处理,导致系统挂了,这个时候有两种思路: 1. n...

    谢伟
  • Python 之父宣布退休,但 Python 仍在吞噬世界

    10 月 30 日,Python 之父 Guido 大牛宣布退休,离开 Dropbox。

    GitHubDaily
  • 使用 yield 压平多层嵌套字典列表混合数据

    在上一篇文章里面,我们讲到了如何使用Python的yield关键字简化代码,压平多层嵌套字典的。

    青南

扫码关注云+社区

领取腾讯云代金券