使用 cv2.calcOpticalFlowPyrLK() 找到 ndarray 形式的光流

问题描述 投票:0回答:1

我需要使用 Lucas Kanade 的光流找到视频的每 2 个相邻帧之间的光流。我在项目中使用 Python 和 openCV。

据我了解,Lucas Kanade 是一种寻找光流的稀疏方法。是否有密集的实施?如果是这样,如何在 Python 中使用它?

使用 cv2.calcOpticalFlowFarneback(),这是一种密集方法,我们得到一个包含光流的 ndarray(下例中的“流”)作为输出。

cv2.calcOpticalFlowFarneback(prev, next, pyr_scale, levels, winsize, iterations, poly_n, poly_sigma, flags[, flow]) → flow

有没有办法使用 cv2.calcOpticalFlowPyrLK() 获得类似的输出?

cv2.calcOpticalFlowPyrLK(prevImg, nextImg, prevPts[, nextPts[, status[, err[, winSize[, maxLevel[, criteria[, flags[, minEigThreshold]]]]]]]]) → nextPts, status, err

当使用 cv2.calcOpticalFlowPyrLK()(上图)时,获得的输出包含 nextPts,它包含要跟踪的下一个点,但它不直接给出 2 帧的光流。如果我从 nextPts 中减去 prevPts,结果是两帧之间的光流吗? 我在这个链接中解释 calcOpticalFlowFarneback() 的部分找到了'prev(y,x)~next(y+flow(y,x)[1],x+flow(y,x)[0])':https ://docs.opencv.org/2.4/modules/video/doc/motion_analysis_and_object_tracking.html 因此这个问题。 (cv2.calcOpticalFlowPyrLK() 和 cv2.calcOpticalFlowFarneback 的语法也来自此链接) 下面是我的实现。

import cv2
import numpy as np
import os
import subprocess as sp

yuv_filename = 'can_0.yuv'
flow=[]

width, height = 320, 240

file_size = os.path.getsize(yuv_filename)
n_frames = file_size // (width*height*3 // 2)
f = open(yuv_filename, 'rb')


old_yuv = np.frombuffer(f.read(width*height*3//2), dtype=np.uint8).reshape((height*3//2, width))


# Convert YUV420 to Grayscale
old_gray = cv2.cvtColor(old_yuv, cv2.COLOR_YUV2GRAY_I420)


# 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 = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))


p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)


for i in range(1,n_frames):
    # Read Y, U and V color channels and reshape to height*1.5 x width numpy array
    yuv = np.frombuffer(f.read(width*height*3//2), dtype=np.uint8).reshape((height*3//2, width))

    # Convert YUV420 to Grayscale
    gray = cv2.cvtColor(yuv, cv2.COLOR_YUV2GRAY_I420)

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


    flow.append(np.subtract(p1,p0))
    good_old = p0[st==1]
    good_new = p1[st==1]

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


f.close()

flow 是一个包含输入视频相邻帧之间光流的 ndarray 列表吗?

这是我得到的 flow[7](随机选择)的输出,但我不确定这些是否是光流值。

[[[ 6.7138672e-03  4.5318604e-03]]

 [[-1.6220093e-02  1.9645691e-03]]

 [[-8.5296631e-03  1.8482208e-03]]

 [[-5.8441162e-03  1.5701294e-02]]

 [[ 7.5836182e-03  2.3475647e-02]]

 [[-1.4129639e-02  1.6357422e-02]]

 [[ 4.4555664e-03  4.1809082e-03]]

 [[ 5.6457520e-04 -9.5863342e-03]]

 [[ 2.8991699e-04 -3.0517578e-05]]

 [[-2.3452759e-02 -1.5502930e-02]]

 [[-6.8283081e-03  3.3264160e-03]]

 [[-8.4381104e-03  7.7590942e-03]]

 [[ 5.7144165e-03  1.1177063e-02]]

 [[-1.4160156e-02  2.1179199e-02]]

 [[-1.0498047e-02  8.0099106e-03]]

 [[-1.8310547e-04  2.8953552e-03]]

 [[ 4.4937134e-03 -2.0904541e-03]]

 [[-4.7698975e-02  3.7708282e-02]]

 [[ 6.3323975e-03  1.3298035e-02]]

 [[-3.3233643e-02 -1.7229080e-02]]

 [[ 7.5683594e-03  2.4566650e-03]]

 [[-3.0364990e-03  3.4656525e-03]]

 [[-1.0345459e-02 -7.4539185e-03]]

 [[ 1.3168335e-02  2.1423340e-02]]

 [[-6.3476562e-03 -1.0681152e-02]]

 [[ 1.5869141e-03  1.0375977e-03]]

 [[ 2.1820068e-03  6.7329407e-03]]

 [[-9.6130371e-03  2.9449463e-03]]

 [[-2.1362305e-03  8.5525513e-03]]

 [[-1.7547607e-04  2.1362305e-04]]

 [[ 2.9144287e-03  1.4343262e-03]]

 [[ 2.9602051e-03 -7.1868896e-03]]

 [[-1.2878418e-02  5.0182343e-03]]

 [[-3.1585693e-03 -5.0544739e-05]]

 [[ 1.0070801e-03  1.3740540e-02]]

 [[ 6.7138672e-04  1.7852783e-03]]

 [[-2.0568848e-02 -1.2943268e-02]]

 [[-2.1057129e-03  4.5013428e-03]]]```

Also is there a way to get optical flow using the Lucas Kanade method such that it has the same size or dimensions as the input frames? If so how? 
python numpy opencv video opticalflow
1个回答
1
投票

您可以使用纯金字塔 Lucas Kanade 方法通过计算每个像素对应的点对来估计密集流场。为此:

  • 忽略 cv2.goodFeaturesToTrack
  • 初始化包含图像所有像素位置的点列表p0

    grid_y, grid_x = np.mgrid[0:prevImg.shape[0]:1, 0:prevImg.shape[1]:1]
    p0 = np.stack((grid_x.flatten(),grid_y.flatten()),axis=1).astype(np.float32)
    
  • 运行备用流量计算

    p1, status, err = cv2.calcOpticalFlowPyrLK(prevImg, currImg, p0, None)
    
  • 通过计算和重塑运动矢量 (p1 - p0) 组装密集流场,流将具有 (h x w x 2) 和 prevImg (h x w x 1) 的形状

    flow = np.reshape(p1 - p0, (prevImg.shape[0], prevImg.shape[1], 2))
    

然而,这不是计算密集光流场的最有效方法。在 OpenCV 中,您可以查看其他DenseOpticalFlow 方法(例如 DIS、Farneback、RLOFDense、Dual-TVL1)

© www.soinside.com 2019 - 2024. All rights reserved.