Create FlowMap in Python OpenCV
Question: Updated question:

Would anyone be able to point me in the direction of any material that could
help me to plot an optical flow map in python? Ideally i want to find
something that provides a similar output to the video shown here:
<http://study.marearts.com/2014/04/opencv-study-calcopticalflowfarneback.html>
. Or something with a similar functional output

I have implemented the dense optical flow algorithm
(cv2.calcOpticalFlowFarneback). And from this i have been able to sample the
magnitudes at specified points of the image. The video feed that is being
input is 640x480, and i have set sample points to be at every fifth pixel
vertically and horizontally.

    
    
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    cap = cv2.VideoCapture("T5.avi")
    
    ret, frame1 = cap.read()
    
    prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
    hsv = np.zeros_like(frame1)
    hsv[..., 1] = 255
    
    [R,C]=prvs.shape
    count=0
    while (1):
        ret, frame2 = cap.read()
        next = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    
        flow = cv2.calcOpticalFlowFarneback(prvs, next, None, 0.5, 3, 15, 2, 5, 1.2, 0)
        mag, ang = cv2.cartToPolar(flow[..., 0], flow[..., 1])
    
        RV=np.arange(5,480,5)
        CV=np.arange(5,640,5)
        # These give arrays of points to sample at increments of 5
        if count==0:
            count =1 #so that the following creation is only done once
            [Y,X]=np.meshgrid(CV,RV)
            # makes an x and y array of the points specified at sample increments
    
        temp =mag[np.ix_(RV,CV)]
        # this makes a temp array that stores the magnitude of flow at each of the sample points
    
        motionvectors=np.array((Y[:],X[:],Y[:]+temp.real[:],X[:]+temp.imag[:]))
    
        Ydist=motionvectors[0,:,:]- motionvectors[2,:,:]
        Xdist=motionvectors[1,:,:]- motionvectors[3,:,:]
        Xoriginal=X-Xdist
        Yoriginal=Y-Ydist
    
    
    
        plot2 = plt.figure()
        plt.quiver(Xoriginal, Yoriginal, X, Y,
                   color='Teal',
                   headlength=7)
    
        plt.title('Quiver Plot, Single Colour')
        plt.show(plot2)
    
    
        hsv[..., 0] = ang * 180 / np.pi / 2
        hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
        bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        cv2.imshow('frame2', bgr)
    
        k = cv2.waitKey(30) & 0xff
    
        if k == 27:
            break
        prvs = next
    
    cap.release()
    cv2.destroyAllWindows()
    

I think i have calculated the original and final X,Y positions of the pixels
and the distances the moved and have put these into a matplotlib quiver plot.

The result i get does not coincide with the hsv plot of the dense optical flow
(which i know to be correct as it was taken from the OpenCV tutorials) and the
quiver plot also only shows one frame at a time and the plot must be exited
before the next one displays.

Can anyone see where i have gone wrong in my calculations and how i can make
the plot update automatically with each frame?


Answer: I do not know how to change the behaviour of matplotlib quiver plots, but I'm
sure it is possible.

An alternative is to create a function to draw lines on top of the original
image, based on the calculated optical flow. The following code should achieve
this:

    
    
    def dispOpticalFlow( Image,Flow,Divisor,name ):
    "Display image with a visualisation of a flow over the top. A divisor controls the density of the quiver plot."
    PictureShape = np.shape(Image)
    #determine number of quiver points there will be
    Imax = int(PictureShape[0]/Divisor)
    Jmax = int(PictureShape[1]/Divisor)
    #create a blank mask, on which lines will be drawn.
    mask = np.zeros_like(Image)
    for i in range(1, Imax):
      for j in range(1, Jmax):
         X1 = (i)*Divisor
         Y1 = (j)*Divisor
         X2 = int(X1 + Flow[X1,Y1,1])
         Y2 = int(Y1 + Flow[X1,Y1,0])
         X2 = np.clip(X2, 0, PictureShape[0])
         Y2 = np.clip(Y2, 0, PictureShape[1])
         #add all the lines to the mask
         mask = cv2.line(mask, (Y1,X1),(Y2,X2), [255, 255, 255], 1)
    #superpose lines onto image
    img = cv2.add(Image,mask)
    #print image
    cv2.imshow(name,img)
    return []
    

This code only creates lines rather than arrows, but with some effort it could
be modified to display arrows.

