首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Opencv----Optical Flow

Opencv----Optical Flow

作者头像
devecor
修改2019-11-26 16:10:12
1.2K0
修改2019-11-26 16:10:12
举报
文章被收录于专栏:数学与计算机数学与计算机

目标

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

光流

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

image
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

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目标
  • 光流
  • Lucas-Kanade method
  • Lucas-Kanade optical flow in opencv
  • dense lucas kanade in opencv
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档