我需要使用 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?
您可以使用纯金字塔 Lucas Kanade 方法通过计算每个像素对应的点对来估计密集流场。为此:
初始化包含图像所有像素位置的点列表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)