使用OpenCV的光流 - 水平和垂直组件

时间:2014-12-25 05:14:59

标签: python opencv image-processing video-processing opticalflow

我有以下代码可以找到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()

鉴于我的两张照片,这就是光流的样子:

1 个答案:

答案 0 :(得分:5)

如果要分别可视化水平和垂直分量,可以将它们分别显示为灰度图像。我将使得灰色的颜色表示没有运动,黑色表示向左的帧中的最大运动量(负),而白色表示向右的帧中的最大运动量(正)

calcOpticalFlowFarneback的输出是一个3D numpy数组,其中第一个切片表示水平(x)位移量,而第二个切片表示垂直量({{1 }}))。

因此,您需要做的就是定义两个单独的2D y数组,这些数组将存储这些值,以便我们可以将它们显示给用户。但是,您需要对显示的流量进行标准化,使得没有运动是粗糙的灰色,最左边的运动是黑色,或强度为0,最右边的运动是白色,或强度为255。

因此,您需要做的就是修改代码以显示两个用于水平和垂直运动的OpenCV窗口,如下所示:

numpy

我修改了代码,因此没有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的等待时间设置为0,以便您无限期地等待直到按下某个键。这几乎模拟了之前的waitKey循环行为,但它不会浪费你的CPU不必要的浪费周期。我还删除了一些不必要的变量,比如while变量,因为我们没有显示颜色编码的水平和垂直分量。我们也只计算一次光流。

在任何情况下,使用上面的代码我们计算光流,分别提取水平和垂直分量,在hsv范围内标准化分量,转换为[0,255]以便我们可以显示然后结果显示结果。我还修改了你的代码,这样如果你想保存组件,它会将水平和垂直组件保存为两个单独的图像。


修改

在您的评论中,您希望使用我们在上面创建的相同逻辑显示一系列图像。您有一个要循环的文件名列表。这不是很难做到的。只需将您的字符串放入列表中,然后使用此列表中存储的文件名计算图像对之间的光流。我将修改代码,以便当我们到达列表的最后一个元素时,我们将等待用户推送一些内容。在那之前,我们将遍历每对图像直到结束。换句话说:

uint8

上面的代码将循环通过成对的帧并在每对之间等待1秒,以便您有机会突破显示,或将水平和垂直组件保存到文件。请记住,我已经做到了这样,无论你保存什么帧,它们都会被索引两个数字,告诉你它们正在显示哪些帧。在下一次迭代发生之前,下一帧将是之前的帧,因此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() 会被next的副本替换。在循环开始时,适当地读取下一帧。


希望这会有所帮助。祝你好运!