3

I am currently developing a behavioral system which will utilize moving bars, rotating bars, alternating fields, and even small random moving objects. Originally all of this was programmed in matlab with great results. However due to incompatibility with the hardware which will eventually use the code, the project has since been transferred over to python.

I have started programming the system using the matplotlib modules in python with some good results. I utilized the animation functions in matplotlib to generate consistent and fluid movement of fast moving objects. However as I dig deeper into programming with matplotlib, I noticed a couple issues. One in particular is that the fluid movement of the objects wasn't exactly as fluid as previously thought.

As I will be utilizing opencv for another part of the behavioral system, I was wondering if opencv has any particular advantages over matplotlib, Particularly in regards to drawing rate and animation.

I will go into more detail below.

Here is part of my script for building the animation, note this this version crashes and I haven't figured out why yet.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import time

fig = plt.figure()
ax = fig.add_subplot(111)
fig.patch.set_facecolor([0,0,0])
fig.tight_layout()

plt.xlim(-100, 100)
plt.ylim(-100, 100)
plt.axis('off')

# List of variables
width = 1
bars = 20
spacing = 20
step = .01
direction = -1

RB = [] # Establish RB as a Python list
for a in range(bars):
    RB.append(patches.Rectangle((a*spacing-200,-100), width, 200, 
          color=[1,0,0], alpha=0.50))

def init():
    for a in range(bars):
        ax.add_patch(RB[a])
    return RB

def animate(i):
    for i in range(bars):
        temp = np.array(RB[i].get_xy())
        if temp[0] > 200:
            temp[0] = -199
        elif temp[0] < -200:
            temp[0] = 199
        else:
            temp[0] = temp[0] + step*direction;
        RB[i].set_xy(temp)
    return RB

t = time.time()
plt.show()

while t < timewidow
    anim = animation.FuncAnimation(fig, animate, 
                           init_func=init, 
                           frames=30, 
                           interval=1,
                           blit=True)

fig.clf()

Code that does work is here. It just that each individual object does not move in sync with each other.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import time

fig = plt.figure()
ax = fig.add_subplot(111)
fig.patch.set_facecolor([0,0,0])
fig.tight_layout()

plt.xlim(-100, 100)
plt.ylim(-100, 100)
plt.axis('off')

# List of variables
width = 1
bars = 20
spacing = 20
step = .01
direction = -1

RB = [] # Establish RB as a Python list
for a in range(bars):
    RB.append(patches.Rectangle((a*spacing-200,-100), width, 200, 
          color=[1,0,0], alpha=0.50))

def init():
    for a in range(bars):
        ax.add_patch(RB[a])
    return RB

def animate(i):
    for i in range(bars):
        temp = np.array(RB[i].get_xy())
        if temp[0] > 200:
            temp[0] = -199
        elif temp[0] < -200:
            temp[0] = 199
        else:
            temp[0] = temp[0] + step*direction;
        RB[i].set_xy(temp)
    return RB

anim = animation.FuncAnimation(fig, animate, 
                           init_func=init, 
                           frames=30, 
                           interval=1,
                           blit=True)
plt.show()
Hojo.Timberwolf
  • 985
  • 1
  • 11
  • 32

1 Answers1

3

Doing an animation inside a while loop seems pretty strange. Therefore the way to go would definitely be the second code version.

The reason the animation seems to be out of sync is not actually related to the times of redraw or similar. It is more an effect of interpolation. The width and position of the rectangles need to be rounded to the screen pixels. Depending on the timesteps the rectangle position can therefore deviate by one pixel; additionally, the width of the bar can deviate by 2 pixels (plus/minus one on each side). This leads to the undesired animation.

To overcome this problem, you would need to think in pixels. Make sure the axes range is a whole number and equals the number of pixels on the screen. Then make the bar width a multiple of one pixel. Control the speed of the animation with the interval argument to the FuncAnimation instead of the steps. Use exactly one pixel as step size.

A complete example:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation

fig = plt.figure(figsize=(5,3), dpi=100)  #figure is 500 pixels wide
ax = fig.add_subplot(111)
fig.patch.set_facecolor([0,0,0])
fig.subplots_adjust(left=.04, right=1-.04) #axes is now 460 pixels wide

plt.xlim(-230, 230) # distribute 460 pixels over x range
plt.ylim(-100, 100)
plt.axis('off')

# List of variables
width = 4
spacing = 20
bars = int(460/spacing) # make sure bars fit into range
direction = -1

RB = [] # Establish RB as a Python list
for a in range(bars):
    RB.append(patches.Rectangle((a*spacing-230,-100), width, 200, 
          color=[1,0,0], alpha=0.50))

def init():
    for a in range(bars):
        ax.add_patch(RB[a])
    return RB

def animate(i):
    for j in range(bars):
        temp = np.array(RB[j].get_xy())
        if temp[0] >= 230:  #### use >= instead of > to mimic exactly one step
            temp[0] = -229
        elif temp[0] <= -230:
            temp[0] = 229
        else:
            temp[0] = temp[0] + direction # each step should be one pixel
        RB[j].set_xy(temp)
    return RB

anim = animation.FuncAnimation(fig, animate, 
                           init_func=init, 
                           frames=20, 
                           interval=10, # control speed with thie interval
                           blit=True)
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Didn't think about this aspect, Thanks for pointing it out. The purpose of the while loop in the first set of code was to try and animate the figure for a short amount of time and then have it stop running. However when I tried to run the code in sequence plt.show()-->animate-->plt.clf. The figure would show up and instantly dissappear. – Hojo.Timberwolf May 25 '17 at 16:44
  • 1
    To stop the animation you can use `anim.event_source.stop()`, see [this question](https://stackoverflow.com/questions/16732379/stop-start-pause-in-python-matplotlib-animation). You could trigger this by a mouse click or [another timer](https://matplotlib.org/examples/event_handling/timers.html) – ImportanceOfBeingErnest May 25 '17 at 16:53