[英]Optical Flow using OpenCV - Horizontal and Vertical Components
我有以下代码可以找到 2 张图像(或视频的 2 帧)的光流,并用颜色编码。 我想要的是光流的水平和垂直分量分别(如在单独的图像中)
这是我到目前为止的代码:
import cv2
import numpy as np
frame1 = cv2.imread('my1.bmp')
frame2 = cv2.imread('my2.bmp')
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[...,1] = 255
while(1):
next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prvs, next, 0.5, 3, 15, 3, 5, 1.2, 0)
mag, ang = cv2.cartToPolar(flow[...,0], flow[...,1])
hsv[...,0] = ang*180/np.pi/2
hsv[...,2] = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX)
rgb = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
cv2.imshow('frame2',rgb)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
elif k == ord('s'):
cv2.imwrite('opticalmyhsv.pgm',rgb)
cap.release()
cv2.destroyAllWindows()
鉴于我的两个图像,这就是光流的样子:
如果要分别可视化水平和垂直分量,可以将两者分别可视化为灰度图像。 我会用灰色表示没有运动,黑色表示向左(负)的帧中的最大运动量,而白色表示向右(正)的帧中的最大运动量.
calcOpticalFlowFarneback
的输出是一个 3D numpy
数组,其中第一个切片表示水平 ( x
) 位移量,而第二个切片表示垂直 ( y
) 位移量。
因此,您需要做的就是定义两个单独的 2D numpy
数组来存储这些值,以便我们可以将它们显示给用户。 但是,您需要对显示流进行标准化,以便没有运动是粗糙的灰色,最左边的运动是黑色,或强度为 0,而最右边的运动是白色,或强度为 255。
因此,您需要做的就是修改代码以显示两个用于水平和垂直运动的 OpenCV 窗口,如下所示:
import cv2
import numpy as np
frame1 = cv2.imread('my1.bmp')
frame2 = cv2.imread('my2.bmp')
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prvs, next, 0.5, 3, 15, 3, 5, 1.2, 0)
# Change here
horz = cv2.normalize(flow[...,0], None, 0, 255, cv2.NORM_MINMAX)
vert = cv2.normalize(flow[...,1], None, 0, 255, cv2.NORM_MINMAX)
horz = horz.astype('uint8')
vert = vert.astype('uint8')
# Change here too
cv2.imshow('Horizontal Component', horz)
cv2.imshow('Vertical Component', vert)
k = cv2.waitKey(0) & 0xff
if k == ord('s'): # Change here
cv2.imwrite('opticalflow_horz.pgm', horz)
cv2.imwrite('opticalflow_vert.pgm', vert)
cv2.destroyAllWindows()
我已经修改了代码,以便没有while
循环,因为您只找到两个预定帧之间的光流。 您不会从实时源(例如相机)中抓取帧,因此我们可以只显示两个图像, while
不是在while
循环中显示。 我已经将waitKey
的等待时间设置为 0,这样您就可以无限期地等待,直到您按下某个键。 这几乎模拟了您之前的while
循环行为,但它不会因浪费的周期而不必要地给您的 CPU 带来负担。 我还删除了一些不必要的变量,比如hsv
变量,因为我们没有显示颜色编码的水平和垂直分量。 我们也只计算一次光流。
无论如何,通过上面的代码,我们计算光流,分别提取水平和垂直分量,将[0,255]
范围内的分量归一化,转换为uint8
以便我们可以显示结果,然后显示结果。 我还修改了您的代码,以便如果您想保存组件,它将水平和垂直组件保存为两个单独的图像。
在您的评论中,您希望使用我们在上面创建的相同逻辑来显示一系列图像。 您有一个要循环浏览的文件名列表。 这不是很难做到。 只需将您的字符串放入一个列表中,然后使用存储在此列表中的文件名来计算图像对之间的光流。 我将修改代码,以便当我们到达列表的最后一个元素时,我们将等待用户推送某些内容。 在此之前,我们将循环遍历每对图像直到结束。 换句话说:
import cv2
import numpy as np
# Create list of names here from my1.bmp up to my20.bmp
list_names = ['my' + str(i+1) + '.bmp' for i in range(20)]
# Read in the first frame
frame1 = cv2.imread(list_names[0])
prvs = cv2.cvtColor(frame1,cv2.COLOR_BGR2GRAY)
# Set counter to read the second frame at the start
counter = 1
# Until we reach the end of the list...
while counter < len(list_names):
# Read the next frame in
frame2 = cv2.imread(list_names[counter])
next = cv2.cvtColor(frame2,cv2.COLOR_BGR2GRAY)
# Calculate optical flow between the two frames
flow = cv2.calcOpticalFlowFarneback(prvs, next, 0.5, 3, 15, 3, 5, 1.2, 0)
# Normalize horizontal and vertical components
horz = cv2.normalize(flow[...,0], None, 0, 255, cv2.NORM_MINMAX)
vert = cv2.normalize(flow[...,1], None, 0, 255, cv2.NORM_MINMAX)
horz = horz.astype('uint8')
vert = vert.astype('uint8')
# Show the components as images
cv2.imshow('Horizontal Component', horz)
cv2.imshow('Vertical Component', vert)
# Change - Make next frame previous frame
prvs = next.copy()
# If we get to the end of the list, simply wait indefinitely
# for the user to push something
if counter == len(list_names)-1
k = cv2.waitKey(0) & 0xff
else: # Else, wait for 1 second for a key
k = cv2.waitKey(1000) & 0xff
if k == 27:
break
elif k == ord('s'): # Change
cv2.imwrite('opticalflow_horz' + str(counter) + '-' + str(counter+1) + '.pgm', horz)
cv2.imwrite('opticalflow_vert' + str(counter) + '-' + str(counter+1) + '.pgm', vert)
# Increment counter to go to next frame
counter += 1
cv2.destroyAllWindows()
上面的代码将循环显示成对的帧,并在每对帧之间等待 1 秒,让您有机会退出显示,或将水平和垂直分量保存到文件中。 请记住,我已经做到了,无论您保存什么帧,它们都会用两个数字进行索引,告诉您它们显示的是哪对帧。 在下一次迭代发生之前,下一帧将是前一帧,因此next
被prvs
的副本prvs
。 在循环开始时,下一帧被适当地读入。
希望这可以帮助。 祝你好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.