光流
光流会通过查看相同的点从一个图像帧移动到下一个图像帧的位置来跟踪对象。接下来,让我们加载一些类似pacman人脸的示例帧,并使其向右和向下移动,然后看一看光流如何找到描述人脸运动的运动矢量!
与之前一样,首先,导入资源并读入图像。
In [ ]:
import numpy as np
import matplotlib.image as mpimg # for reading in images
import matplotlib.pyplot as plt
import cv2 # computer vision library
%matplotlib inline
In [ ]:
# Read in the image frames
frame_1 = cv2.imread('images/pacman_1.png')
frame_2 = cv2.imread('images/pacman_2.png')
frame_3 = cv2.imread('images/pacman_3.png')
# convert to RGB
frame_1 = cv2.cvtColor(frame_1, cv2.COLOR_BGR2RGB)
frame_2 = cv2.cvtColor(frame_2, cv2.COLOR_BGR2RGB)
frame_3 = cv2.cvtColor(frame_3, cv2.COLOR_BGR2RGB)
# Visualize the individual color channels
f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(20,10))
ax1.set_title('frame 1')
ax1.imshow(frame_1)
ax2.set_title('frame 2')
ax2.imshow(frame_2)
ax3.set_title('frame 3')
ax3.imshow(frame_3)
寻找跟踪点
在使用光流之前,我们必须给它一组用来跟踪两个图像帧的关键点!
在下面的例子中,我们要用到Shi-Tomasi角点检测器,这个检测器会使用与Harris角点检测器相同的过程来查找构成图像中“角点”的强度模式,只是它添加了一个额外的参数来帮助选择最突出的角点。你可以在 这个文档中阅读有关此检测算法的更多信息。
或者,你也可以选择使用Harris甚至ORB来查找特征点。我发现这两种方法也很有效。
你可以看到检测到的点出现在人脸的角点处。
In [ ]:
# parameters for ShiTomasi corner detection
feature_params = dict( maxCorners = 10,
qualityLevel = 0.2,
minDistance = 5,
blockSize = 5 )
# convert all frames to grayscale
gray_1 = cv2.cvtColor(frame_1, cv2.COLOR_RGB2GRAY)
gray_2 = cv2.cvtColor(frame_2, cv2.COLOR_RGB2GRAY)
gray_3 = cv2.cvtColor(frame_3, cv2.COLOR_RGB2GRAY)
# Take first frame and find corner points in it
pts_1 = cv2.goodFeaturesToTrack(gray_1, mask = None, **feature_params)
# display the detected points
plt.imshow(frame_1)
for p in pts_1:
# plot x and y detected points
plt.plot(p[0][0], p[0][1], 'r.', markersize=15)
# print out the x-y locations of the detected points
print(pts_1)
执行光流
在你感兴趣的初始图像上检测到关键点之后,就可以使用OpenCV的calcOpticalFlowPyrLK
计算此图像帧(第1帧)和下一帧(第2帧)之间的光流。其中,calcOpticalFlowPyrLK
可以在这里 找到。它会接收初始图像帧、下一张图像和第一组点,并返回下一帧中检测到的点和一个值,该值表示的是从一帧到下一帧之间的点匹配程度。
参数还包括窗口大小和maxLevels,它们分别表示窗口的大小以及将使用金字塔缩放比例缩放给定图像的级别数。此版本会对匹配点进行迭代搜索,此匹配条件则反映在最后一个参数中。如果使用其他图像,则可能需要更改这些值,但更改的值应适用于提供的示例。
In [ ]:
# parameters for lucas kanade optical flow
lk_params = dict( winSize = (5,5),
maxLevel = 2,
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# calculate optical flow between first and second frame
pts_2, match, err = cv2.calcOpticalFlowPyrLK(gray_1, gray_2, pts_1, None, **lk_params)
# Select good matching points between the two image frames
good_new = pts_2[match==1]
good_old = pts_1[match==1]
接下来,让我们把生成的运动矢量显示出来吧!你应该看到第一个图像上绘有运动矢量,该运动矢量表示的是从第一帧到下一帧的运动方向。
In [ ]:
# create a mask image for drawing (u,v) vectors on top of the second frame
mask = np.zeros_like(frame_2)
# draw the lines between the matching points (these lines indicate motion vectors)
for i,(new,old) in enumerate(zip(good_new,good_old)):
a,b = new.ravel()
c,d = old.ravel()
# draw points on the mask image
mask = cv2.circle(mask,(a,b),5,(200),-1)
# draw motion vector as lines on the mask image
mask = cv2.line(mask, (a,b),(c,d), (200), 3)
# add the line image and second frame together
composite_im = np.copy(frame_2)
composite_im[mask!=0] = [0]
plt.imshow(composite_im)
TODO: 在图像帧2和3之间执行光流¶
重复此过程,但只应用于最后两个图像帧,看一看生成的运动矢量是什么样的。想象一下,将这个过程应用于一系列图像帧,并绘制某个给定对象的整个运动路径。
In [ ]:
## TODO: Perform optical flow between image frames 2 and 3