光流法(Optical Flow)及OpenCV实现

Optical Flow

Optical flow 有两个假设:

  1. 亮度恒定:在相邻连续两帧中一个目标的像素强度不会变化。
  2. 空间一致性:周围像素有类似运行。
  3. 时间规律:相邻帧时间足够短,以至于在考虑运行变化时可以忽略它们之间的差异。

假设在第一帧中像素 I ( x , y , t ) I(x,y,t) I(x,y,t) d t dt dt时间后,在下一帧中它运动了 ( d x , d y ) (dx, dy) (dx,dy)。因为像素相同,强度没有变化。

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)

根据Taylor 级数得到:

I ( x + d x , y + d y , t + d t ) = I ( x , y , t ) + ∂ I ∂ x Δ x + ∂ I ∂ y Δ y + ∂ I ∂ t Δ t I(x+dx, y+dy, t+dt) = I(x,y,t) + \frac{\partial I}{\partial x}\Delta x + \frac{\partial I}{\partial y}\Delta y + \frac{\partial I}{\partial t}\Delta t I(x+dx,y+dy,t+dt)=I(x,y,t)+xIΔx+yIΔy+tIΔt

因此:

∂ I ∂ x Δ x + ∂ I ∂ y Δ y + ∂ I ∂ t Δ t = 0 \frac{\partial I}{\partial x}\Delta x + \frac{\partial I}{\partial y}\Delta y + \frac{\partial I}{\partial t}\Delta t = 0 xIΔx+yIΔy+tIΔt=0

或者

∂ I ∂ x Δ x Δ t + ∂ I ∂ y Δ y Δ t + ∂ I ∂ t Δ t Δ t = 0 \frac{\partial I}{\partial x} \frac{\Delta x}{\Delta t} + \frac{\partial I}{\partial y}\frac{\Delta y}{\Delta t} + \frac{\partial I}{\partial t}\frac{\Delta t}{\Delta t} = 0 xIΔtΔx+yIΔtΔy+tIΔtΔt=0

用速度来代替后:

∂ I ∂ x V x + ∂ I ∂ y V y + ∂ I ∂ t = 0 \frac{\partial I}{\partial x} V_x + \frac{\partial I}{\partial y} V_y + \frac{\partial I}{\partial t} = 0 xIVx+yIVy+tI=0

Lucas-Kanade method

此方法取点周围 3x3 的patch 。9个点有相同的运动。
推导过程 :

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)

f x u + f y v + f t = 0 f_x u + f_y v + f_t = 0 fxu+fyv+ft=0

f x = ∂ f ∂ x f_x = \frac{\partial f}{\partial x} fx=xf
f y = ∂ f ∂ y f_y = \frac{\partial f}{\partial y} fy=yf
u = d x d t u = \frac{dx}{dt} u=dtdx
v = d y d t v = \frac{dy}{dt} v=dtdy

[ u v ] = [ Σ i f x i 2 Σ i f x i f y i Σ i f x i f y i Σ i f x i 2 ] − 1 [ − Σ i f x i f t i − Σ i f y i f t i ] \begin{bmatrix} u\\ v \end{bmatrix} = \begin{bmatrix} \Sigma_i f_{x_i} ^ 2 & \Sigma_i f_{x_i} f_{y_i} \\ \Sigma_i f_{x_i} f_{y_i} & \Sigma_i f_{x_i} ^ 2 \end{bmatrix}^{-1} \begin{bmatrix} -\Sigma_i f_{x_i} f_{t_i}\\ -\Sigma_i f_{y_i} f_{t_i} \end{bmatrix} [uv]=[Σifxi2ΣifxifyiΣifxifyiΣifxi2]1[ΣifxiftiΣifyifti]

当有大的运动时会失败。所以我们使用金字塔,大的运动也可以变成小运动。

Lucas-Kanade Optical Flow in OpenCV

(1) cv2.goodFeaturesToTrack() 获取点
(2) cv2.calcOpticalFlowPyrLK() 传递连续三帧。

Dense Optical Flow in OpenCV

Lucas-Kanade 方法只计算一个稀疏的特征集(角点)。OpenCV 提供了另一种方法来发现稠密光流。它计算所有点的光流。

参考资料:

光流Optical Flow介绍与OpenCV实现

OpenCv Optical Flow

维基百科 光流法

《Python计算机视觉编程》

代码

#encoding:utf-8
'''
Lucas-Kanade tracker
====================
Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack
for track initialization and back-tracking for match verification
between frames.
Usage
-----
lk_track.py [<video_source>]
Keys
----
ESC - exit
'''
 
import numpy as np
import cv2
#from common import anorm2, draw_str
from time import clock
 
lk_params = dict( winSize  = (15, 15), 
                  maxLevel = 2, 
                  criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))    
 
feature_params = dict( maxCorners = 500, 
                       qualityLevel = 0.3,
                       minDistance = 7,
                       blockSize = 7 )
 
class App:
    def __init__(self, video_src):#构造方法,初始化一些参数和视频路径
        self.track_len = 10
        self.detect_interval = 5
        self.tracks = []
        self.cam = cv2.VideoCapture(video_src)
        self.frame_idx = 0
 
    def run(self):#光流运行方法
        while True:
            ret, frame = self.cam.read()#读取视频帧
            if frame is None:
                break
            h, w = frame.shape[:2]
            frame = cv2.resize(frame, (w//2, h//2))
            frame = np.rot90(frame)
            if not ret:
                continue
            
            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)#转化为灰度虚图像
            vis = frame.copy()

            if len(self.tracks) > 0:#检测到角点后进行光流跟踪
                img0, img1 = self.prev_gray, frame_gray
                p0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2)
                p1, st, err = cv2.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)#前一帧的角点和当前帧的图像作为输入来得到角点在当前帧的位置
                p0r, st, err = cv2.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)#当前帧跟踪到的角点及图像和前一帧的图像作为输入来找到前一帧的角点位置
                d = abs(p0-p0r).reshape(-1, 2).max(-1)#得到角点回溯与前一帧实际角点的位置变化关系
                good = d < 1#判断d内的值是否小于1,大于1跟踪被认为是错误的跟踪点
                new_tracks = []
                for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):#将跟踪正确的点列入成功跟踪点
                    if not good_flag:
                        continue
                    tr.append((x, y))
                    if len(tr) > self.track_len:
                        del tr[0]
                    new_tracks.append(tr)
                    cv2.circle(vis, (x, y), 2, (0, 255, 0), -1)
                self.tracks = new_tracks
                cv2.polylines(vis, [np.int32(tr) for tr in self.tracks], False, (0, 255, 0))#以上一振角点为初始点,当前帧跟踪到的点为终点划线
                #draw_str(vis, (20, 20), 'track count: %d' % len(self.tracks))

            if self.frame_idx % self.detect_interval == 0:#每5帧检测一次特征点
                mask = np.zeros_like(frame_gray)#初始化和视频大小相同的图像
                mask[:] = 255#将mask赋值255也就是算全部图像的角点
                for x, y in [np.int32(tr[-1]) for tr in self.tracks]:#跟踪的角点画圆
                    cv2.circle(mask, (x, y), 5, 0, -1)
                p = cv2.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params)#像素级别角点检测
                if p is not None:
                    for x, y in np.float32(p).reshape(-1, 2):
                        self.tracks.append([(x, y)])#将检测到的角点放在待跟踪序列中

            self.frame_idx += 1
            self.prev_gray = frame_gray
            cv2.imshow('lk_track', vis)
 
            ch = 0xFF & cv2.waitKey(30)
            if ch == 27:
                break
 
def main():
    import sys
    try: video_src = sys.argv[1]
    except: video_src = "slow.flv"
 
    print(__doc__)
    App(video_src).run()
    cv2.destroyAllWindows()             
 
if __name__ == '__main__':
    main()
  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张欣-男

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值