matplotlib slows after every pass - python

Why is my code slowing down as time goes on? When the code initially starts the loop takes ~.1 second to run. By the 90th pass I am at 1+ second. I am displaying and saving the image. I noticed if I comment out fig.canvas.draw() and fig.canvas.flush_events() the code runs as expected. The issue is with displaying and updating the interactive window. Is there a variable I need to clear?
from flirpy.camera.lepton import Lepton
import matplotlib.pyplot as plt
import numpy as np
import cv2
from PIL import Image as im
import os, sys
import time
%matplotlib notebook
savefold = ('Therm')
if not os.path.exists(savefold):
os.makedirs(savefold)
camera = Lepton()
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
def getFrame():
image = camera.grab()
fimage = np.around((image/100 - 273.15) * 9 / 5 + 32, 2)
cimage = np.around(image/100 - 273.15, 2)
kimage = np.around(image/100, 2)
cimage = np.where(cimage < 20, 20, cimage)
cimage = np.where(cimage > 30, 30, cimage)
getFrame.z = cimage
return getFrame.z
getFrame.z = None
i = 0
while True:
tic = time.perf_counter()
npimage=getFrame()
npimage=npimage
data = im.fromarray(npimage)
plt.imshow(data)
fig.canvas.draw()
fig.canvas.flush_events()
plt.imsave(os.path.join(savefold, f'test_{i:05}.png'), data)
i += 1
toc = time.perf_counter()
print(f'Time to execute: {toc - tic:0.4f} seconds')
camera.close()

Because you are adding one image for each iteration: all the previous images are still in matplotlib buffer, and they get rendered!
You need to clear the buffer. Try to use this line of code before the imshow command:
plt.gca().images.clear()

Related

subscribe not work after upgrading RxPy from 1.x to 3.x

I am using Python 3.7.3.
I try to upgrade RxPy from 1.6.1 (1.x) to 3.0.0a3 (3.x).
Old code using RxPy 1.x
from rx import Observable
import psutil
import numpy as np
import pylab as plt
cpu_data = (Observable
.interval(100) # Each 100 milliseconds
.map(lambda x: psutil.cpu_percent())
.publish())
cpu_data.connect()
def monitor_cpu(npoints):
lines, = plt.plot([], [])
plt.xlim(0, npoints)
plt.ylim(0, 100)
cpu_data_window = cpu_data.buffer_with_count(npoints, 1)
def update_plot(cpu_readings):
lines.set_xdata(np.arange(len(cpu_readings)))
lines.set_ydata(np.array(cpu_readings))
plt.draw()
alertpoints = 4
high_cpu = (cpu_data
.buffer_with_count(alertpoints, 1)
.map(lambda readings: all(r > 20 for r in readings)))
label = plt.text(1, 1, "normal")
def update_warning(is_high):
if is_high:
label.set_text("high")
else:
label.set_text("normal")
high_cpu.subscribe(update_warning)
cpu_data_window.subscribe(update_plot)
plt.show()
if __name__ == '__main__':
monitor_cpu(10)
If you run the code you can see a real-time CPU monitor chart.
However, after I installed the new RxPy by
pip3 install --pre rx
with new code below, it only shows white one without any dynamic chart.
And the function update_plot actually never ran. Any idea?
New code using RxPy 3.x
from rx import interval, operators as op
import psutil
import numpy as np
import pylab as plt
cpu_data = interval(100).pipe( # Each 100 milliseconds
op.map(lambda x: psutil.cpu_percent()),
op.publish())
cpu_data.connect()
def monitor_cpu(npoints):
lines, = plt.plot([], [])
plt.xlim(0, npoints)
plt.ylim(0, 100)
cpu_data_window = cpu_data.pipe(
op.buffer_with_count(npoints, 1))
def update_plot(cpu_readings):
print('update') # here never runs
lines.set_xdata(np.arange(len(cpu_readings)))
lines.set_ydata(np.array(cpu_readings))
plt.draw()
alertpoints = 4
high_cpu = cpu_data.pipe(
op.buffer_with_count(alertpoints, 1),
op.map(lambda readings: all(r > 20 for r in readings)))
label = plt.text(1, 1, "normal")
def update_warning(is_high):
if is_high:
label.set_text("high")
else:
label.set_text("normal")
high_cpu.subscribe(update_warning)
cpu_data_window.subscribe(update_plot)
plt.show()
if __name__ == '__main__':
monitor_cpu(10)
Time units are now in seconds
cpu_data = interval(0.1).pipe( # Each 100 milliseconds
op.map(lambda x: psutil.cpu_percent()),
op.publish())
cpu_data.connect()

plt.show appears in terminal instead of Ipython notebook

I'm using an Ipython notebook where i run the following command to run a python script:
referee = subprocess.Popen("/Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml", shell=True)
The python script is the following:
#!/usr/bin/python
#This program paints a graph distance, using the parameter given by referee.cfg
#VisorPainter class re-paints on a pyplot plot and updates new data.
#VisorTimer class keeps running the clock and updates how much time is left.
#Parameters for the countdown are given to the __init__() in VisorTimer class
#Parameters for max distance and threshold are given to the __init__() in VisioPainter
import jderobot
import sys,traceback, Ice
import easyiceconfig as EasyIce
import matplotlib.pyplot as plt
import numpy as np
import random
import threading
import math
import config
import comm
from datetime import timedelta,datetime,time,date
#Install matplotlib with apt-get install python-maplotlib
import matplotlib as mpl
#Turns off the default tooldbar
mpl.rcParams['toolbar'] = 'None'
class Pose:
def __init__(self,argv=sys.argv):
self.lock = threading.Lock()
self.dist=0
self.ic = None
try:
cfg = config.load(sys.argv[1])
jdrc = comm.init(cfg, 'Referee')
self.ic = jdrc.getIc()
self.properties = self.ic.getProperties()
proxyStr = jdrc.getConfig().getProperty("Referee.CatPose3D.Proxy")
self.basePoseAr = self.ic.stringToProxy(proxyStr)
if not self.basePoseAr:
raise Runtime("Cat Pose3D -> Invalid proxy")
self.poseProxy = jderobot.Pose3DPrx.checkedCast(self.basePoseAr)
print self.poseProxy
proxyStr = jdrc.getConfig().getProperty("Referee.MousePose3D.Proxy")
self.baseRedPoseAr = self.ic.stringToProxy(proxyStr)
self.poseRedProxy = jderobot.Pose3DPrx.checkedCast(self.baseRedPoseAr)
print self.poseRedProxy
if not self.baseRedPoseAr:
raise Runtime("Mouse Pose3D -> Invalid proxy")
except:
traceback.print_exc()
status = 1
def update(self):
self.lock.acquire()
self.poseAr=self.poseProxy.getPose3DData()
self.poseRed=self.poseRedProxy.getPose3DData()
self.lock.release()
return self.getDistance()
def getDistance(self):
v_d=pow(self.poseRed.x-self.poseAr.x,2)+pow(self.poseRed.y-self.poseAr.y,2)+pow(self.poseRed.z-self.poseAr.z,2)
self.dist=round(abs(math.sqrt(v_d)),4)
return self.dist
def finish(self):
if self.ic:
#Clean up
try:
self.ic.destroy()
except:
traceback.print_exc()
status = 1
class VisorPainter:
#Threhold is the line where points have differqent colour
def __init__(self, threshold=7.0, max_d=20):
self.fig, self.ax = plt.subplots()
self.d = []
self.t = []
self.score=0.0
self.th = threshold
self.max_dist = max_d
self.suptitle = self.fig.suptitle('Timer is ready',fontsize=20)
self.fig.subplots_adjust(top=0.8)
self.score_text = self.ax.text((120.95), self.max_dist+1.5, 'Score: '+ str(self.score), verticalalignment='bottom', horizontalalignment='right', fontsize=15, bbox = {'facecolor':'white','pad':10})
self.drawThreshold()
self.ax.xaxis.tick_top()
self.ax.set_xlabel('Time')
self.ax.xaxis.set_label_position('top')
self.ax.set_ylabel('Distance')
# Sets time and distance axes.
def setAxes(self, xaxis=120, yaxis=None):
if (yaxis == None):
yaxis=self.max_dist
if (xaxis!=120):
self.score_text.set_x((xaxis+2.95))
self.ax.set_xlim(0.0,xaxis)
self.ax.set_ylim(yaxis,0)
# Draws the threshold line
def drawThreshold(self):
plt.axhline(y=self.th)
# Draws points. Green ones add 1 to score.
# Not in use.
def drawPoint(self,t_list,d_list):
if d<=self.th:
self.score+=1
plt.plot([t],[d], 'go', animated=True)
else:
plt.plot([t],[d], 'ro', animated=True)
# Decides if it's a Green or Red line. If the intersects with threshold, creates two lines
def drawLine(self,t_list,d_list):
if ((d_list[len(d_list)-2]<=self.th) and (d_list[len(d_list)-1]<=self.th)):
self.drawGreenLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
elif ((d_list[len(d_list)-2]>=self.th) and (d_list[len(d_list)-1]>=self.th)):
self.drawRedLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
#Thus it's an intersection
else:
t_xpoint=self.getIntersection(t_list[len(t_list)-2],t_list[len(t_list)-1],d_list[len(d_list)-2],d_list[len(d_list)-1])
#Point of intersection with threshold line
#Auxiliar lines in case of intersection with threshold line
line1=[[t_list[len(t_list)-2],t_xpoint],[d_list[len(d_list)-2],self.th]]
line2=[[t_xpoint,t_list[len(t_list)-1]],[self.th,d_list[len(d_list)-1]]]
self.drawLine(line1[0],line1[1])
self.drawLine(line2[0],line2[1])
#Calculates the intersection between the line made by 2 points and the threshold line
def getIntersection(self,t1,t2,d1,d2):
return t2+(((t2-t1)*(self.th-d2))/(d2-d1))
def drawGreenLine(self,t_line,d_line):
self.score+=(t_line[1]-t_line[0])
plt.plot(t_line,d_line,'g-')
def drawRedLine(self,t_line,d_line):
plt.plot(t_line,d_line,'r-')
# Updates score
def update_score(self):
if self.score <= vt.delta_t.total_seconds():
self.score_text.set_text(str('Score: %.2f secs' % self.score))
else:
self.score_text.set_text('Score: ' + str(vt.delta_t.total_seconds())+ ' secs')
#Updates timer
def update_title(self):
#self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopClkTimer()
self.suptitle.set_text(
str(vt.zero_t.total_seconds()))
self.ax.figure.canvas.draw()
else:
self.suptitle.set_text(str(vt.timeLeft())[:-4])
self.ax.figure.canvas.draw()
#Updates data for drawing into the graph
#The first data belongs to 0.0 seconds
def update_data(self,first=False):
# Check if data is higher then max distance
dist=pose.update()
if first:
self.t.insert(len(self.t),0.0)
else:
self.t.insert(len(self.t),(vt.delta_t-vt.diff).total_seconds())
if dist > self.max_dist :
self.d.insert(len(self.d),self.max_dist)
else:
self.d.insert(len(self.d),dist)
# self.drawPoint(self.t[len(self.t)-1],self.d[len(self.d)-1])
if len(self.t)>=2 and len(self.d)>=2:
self.drawLine(self.t,self.d)
self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopDataTimer()
self.update_score()
self.ax.figure.canvas.draw()
self.fig.savefig('Result_'+str(datetime.now())+'.png', bbox_inches='tight')
#https://github.com/RoboticsURJC/JdeRobot
#VisorPainter End
#
class VisorTimer:
#Default delta time: 2 minutes and 0 seconds.
#Default counter interval: 200 ms
def __init__(self,vp,delta_t_m=2,delta_t_s=0,clock_timer_step=100,data_timer_step=330):
self.delta_t = timedelta(minutes=delta_t_m,seconds=delta_t_s)
self.zero_t = timedelta(minutes=0,seconds=0,milliseconds=0)
self.final_t = datetime.now()+self.delta_t
self.diff = self.final_t-datetime.now()
vp.setAxes(xaxis=self.delta_t.seconds)
# Creates a new clock_timer object.
self.clock_timer = vp.fig.canvas.new_timer(interval=clock_timer_step)
self.data_timer = vp.fig.canvas.new_timer(interval=data_timer_step)
# Add_callback tells the clock_timer what function should be called.
self.clock_timer.add_callback(vp.update_title)
self.data_timer.add_callback(vp.update_data)
def startTimer(self):
self.clock_timer.start()
vp.update_data(first=True)
self.data_timer.start()
def stopClkTimer(self,):
self.clock_timer.remove_callback(vp.update_title)
self.clock_timer.stop()
def stopDataTimer(self):
self.data_timer.remove_callback(vp.update_data)
self.data_timer.stop()
def timeLeft(self):
self.diff=self.final_t-datetime.now()
return self.diff
#
#VisorTimer End
#
# Main
status = 0
try:
pose = Pose(sys.argv)
pose.update()
vp = VisorPainter()
vt = VisorTimer(vp)
vp.suptitle.set_text(str(vt.delta_t))
vt.startTimer()
plt.show()
pose.finish()
except:
traceback.print_exc()
status = 1
sys.exit(status)
The result must be an image with the plt.show(), but the image does not appears in the Ipython notebook, it appears in the terminal like this:
Figure(640x480)
When i use the run command in the Ipython notebook:
import matplotlib
%run /Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml
The image displays correctly but not recursively so i don't know how to do it.
Thanks for help.
I'm really unsure what your problem is. I wrote a script that looks like this:
#! /usr/bin/env python3
# plotter.py
import sys
import matplotlib.pyplot as plt
def main(x):
plt.plot(x)
plt.show()
if __name__ == '__main__':
main([float(v) for v in sys.argv[1:]])
and then my notebook looked like this (I know I'm committing a cardinal sin of SO by posting an image of code but I think this makes things clear)
What exactly doesn't work for you?

Real-time plotting with delay

Hi to everybody and thanks for your help.
I'm trying to show people a real-time seismogram with Python for educational purposes. I get the data through serial port connected to an arduino, but the problem is that there is a big delay (8-20 seconds) between the signal and the graph on screen seismogram plot.
I have checked the code, but I can't see anything wrong, at least as far as I understand the code. Can anybody help me?
Thanks in advance.
import time
import serial
import sys
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
arduino = serial.Serial('COM4', baudrate=115200, timeout=1.0)
start = time.time()
ad = []
ed = []
ec = []
es = []
delta=0
duracion=120
# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure(figsize=(16,8))
ax = plt.axes(xlim=(0, duracion), ylim=(-1000, 1000))
ax.set_xlabel('segundos')
ax.set_ylabel('cuentas')
plt.title('Haz tu propio sismograma')
fig.canvas.set_window_title("Sismograma")
line, = ax.plot([], [], lw=0.5)
line2, = ax.plot([],[],lw=0.5)
line3, = ax.plot([],[],lw=0.5)
# initialization function: plot the background of each frame
def init():
arduino.readline()
ad = []
ed = []
ec = []
es = []
start = time.time()
delta=0
line.set_data([],[])
line2.set_data([],[])
line3.set_data([],[])
return line, line2, line3
# animation function. This is called sequentially
def animate(i):
global start
global ad,ed,ec,es
global delta
delta = time.time() - start
linea = arduino.readline()
texto=linea.decode('ascii', errors='replace')
valora=texto.split(',')
try:
valor=int(valora[0])+400
valor2=int(valora[1])
valor3=int(valora[2])-400
#print(valor)
ad.append(delta)
ed.append(valor)
ec.append(valor2)
es.append(valor3)
line.set_data(ad, ed)
line2.set_data(ad, ec)
line3.set_data(ad, es)
except ValueError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except RuntimeError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
except IndexError:
ed.append(ed[-1])
ec.append(ec[-1])
es.append(es[-1])
ad.append(delta)
#print(delta)
if delta>duracion:
i=0
ad=[0]
ed=[0]
ec=[0]
es=[0]
delta=0
start = time.time()
fig.clf()
run_animation
return line, line2,line3
#time.sleep(0)
# call the animator. blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate,frames=30, interval=1, init_func=init, blit=True)
def run_animation():
init
delta=0
start = time.time()
animate(0)
plt.show()
plt.show()

python real time plot of serial port data has huge lag

I am trying to read from serial port and plot the data in graph using matplot.
Following is my code :
I see that because of plot, there is huge lag (data in queue goes up to 10000 bytes) hence i dont see real time plot coming. Can you please help me if i am doing anything wrong.
<
import serial # import Serial Library
import numpy # Import numpy
import matplotlib.pyplot as plt
Accelerometer= []
COM_read = serial.Serial('COM5', 9600, timeout=None,parity='N',stopbits=1,rtscts=0) #Creating our serial object name
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
COM_read.flushInput()
COM_read.flushOutput()
def makeFig(): #Create a function that makes our desired plot
plt.title('My Live Streaming Sensor Data') #Plot the title
plt.grid(True) #Turn the grid on
plt.ylabel('Acc in g') #Set ylabels
plt.plot(Accelerometer, 'ro-', label='Accelerometer g') #plot the temperature
plt.legend(loc='upper left') #plot the legend
plt.ylim(15000,30000) #Set limits of second y axis- adjust to readings you are getting
print "came through"
while True: # While loop that loops forever
print COM_read.inWaiting()
while (COM_read.inWaiting()==0): #Wait here until there is data
pass #do nothing
s = COM_read.readline() #read the line of text from the serial port
decx = int(s[0:4],16)
decy = int(s[5:9],16)
decz = int(s[10:14],16)
if decx > 32768:
decx = decx - 65536;
if decy > 32768:
decy = decy - 65536;
if decz > 32768:
decz = decz - 65536;
#print decx,decy,decz
res = ((decx*decx) + (decy*decy) + (decz*decz))**(1.0/2.0)
Accelerometer.append(res) #Build our Accelerometer array by appending temp readings
drawnow(makeFig) #Call drawnow to update our live graph
plt.pause(.000001) #Pause Briefly. Important to keep drawnow from crashing
cnt=cnt+1
if(cnt>50): #If you have 50 or more points, delete the first one from the array
Accelerometer.pop(0) #This allows us to just see the last 50 data points
>
----------------Based on Dr.John's suggestion, the code is written as follows -----
import serial
import matplotlib
import threading, time
import pylab
import matplotlib.pyplot as plt
import matplotlib.animation as anim
class MAIN:
#ser =0;
def __init__(self):
self.res = 0.0
self.ser = serial.Serial('COM5', 9600, timeout=None,parity='N',stopbits=1,rtscts=0)
self.ser.flushInput()
elf.ser.flushOutput()
c = self.ser.read(1);
while(c != '\n'):
=self.ser.read(1)
return
def acquire_data(self):
s = self.ser.readline()
decx = int(s[0:4],16)
decy = int(s[5:9],16)
decz = int(s[10:14],16)
if decx > 32768:
decx = decx - 65536;
if decy > 32768:
decy = decy - 65536;
if decz > 32768:
decz = decz - 65536;
self.res = ((decx*decx) + (decy*decy) + (decz*decz))**(1.0/2.0)
print self.res
return
ex = MAIN()
threading.Timer(2,ex.acquire_data).start() #starts function acquire_data every 2 sec and resets self.res with 0.5Hz
fig, ax = plt.subplots()
ax.plot(time.time(), self.res, 'o-')
print "closed"
A while true loop is a bad idea in most cases.
Non-OO-Programming is a bad idea in most cases.
Appending plot-data to lists is a bad idea in most cases (laggy).
Start the data aquisition at defined times with
import threading, time
threading.Timer(2, acquire_data).start() #starts function acquire_data every 2 sec and resets self.res with 0.5Hz
then instead of the while-loop, please define class based functions for your own benefit:
class MAIN:
def __init__(self):
self.res = 0
def acquire_data(self):
....
return self.res
and plot with
fig, ax = plt.subplots()
ax.plot(time.time(), self.res, 'o-')
Greets Dr. Cobra

Saving a mayavi animation

I am currently trying to save a mayavi animation generated by my simulation, so I don't have to rerun the code each time to see it.
plt = points3d(x_coord, y_coord, z_coord)
msplt = plt.mlab_source
#mlab.animate(delay=100)
def anim():
f = mlab.gcf()
while True:
#animation updates here
msplt.set(x = x_coord, y = y_coord, z = z_coord)
yield
anim()
mlab.savefig(filename = 'ani.mp4')
mlab.show()
I have tried saving it through the pipleline editor and just get a still of the frame it is on, and mlab.savefig doesn't generate a file. Any help appreciated.
The following will will work for both viewing the animation, saving each frame as a 'png', and then converting them to a movie, BUT it is perhaps fastest in this case to forgo playing the animation, and just cycle through the data saving figures, and then using this method to make a video.
from mayavi import mlab
import numpy as np
import os
# Output path for you animation images
out_path = './'
out_path = os.path.abspath(out_path)
fps = 20
prefix = 'ani'
ext = '.png'
# Produce some nice data.
n_mer, n_long = 6, 11
pi = np.pi
dphi = pi/1000.0
phi = np.arange(0.0, 2*pi + 0.5*dphi, dphi, 'd')
mu = phi*n_mer
x = np.cos(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
y = np.sin(mu)*(1+np.cos(n_long*mu/n_mer)*0.5)
z = np.sin(n_long*mu/n_mer)*0.5
# Init plot
plt = mlab.points3d(x[0], y[0], z[0])
padding = len(str(len(x)))
# Define data source and update routine
msplt = plt.mlab_source
#mlab.animate(delay=10)
def anim():
f = mlab.gcf()
for i in range(len(x)):
#animation updates here
msplt.set(x=x[i], y=y[i], z=z[i])
# create zeros for padding index positions for organization
zeros = '0'*(padding - len(str(i)))
# concate filename with zero padded index number as suffix
filename = os.path.join(out_path, '{}_{}{}{}'.format(prefix, zeros, i, ext))
mlab.savefig(filename=filename)
yield
anim()
mlab.view(distance=15)
mlab.show()
import subprocess
ffmpeg_fname = os.path.join(out_path, '{}_%0{}d{}'.format(prefix, padding, ext))
cmd = 'ffmpeg -f image2 -r {} -i {} -vcodec mpeg4 -y {}.mp4'.format(fps,
ffmpeg_fname,
prefix)
print cmd
subprocess.check_output(['bash','-c', cmd])
# Remove temp image files with extension
[os.remove(f) for f in os.listdir(out_path) if f.endswith(ext)]
Instead of saving the images to disk and then stitching them together it's also possible to pipe them directly to ffmpeg using the python-ffmpeg package.
import ffmpeg
# Set up the figure
width = 200
height = 200
mlab.options.offscreen = True # Stops the view window popping up and makes sure you get the correct size screenshots.
fig = mlab.figure(size=(width, height))
# ... set up the scene ...
# Define update function
def update_scene(idx):
# -- update the scene
return
# Initialise ffmpeg process
output_args = {
'pix_fmt': 'yuv444p',
'vcodec': 'libx264',
'r': 25,
}
process = (
ffmpeg
.input('pipe:', format='rawvideo', pix_fmt='rgb24', s=f'{width}x{height}')
.output('animation.mp4', **output_args)
.overwrite_output()
.run_async(pipe_stdin=True)
)
fig.scene._lift() # Throws an error without this.
for i in range(100):
update_scene(i)
screenshot = mlab.screenshot(mode='rgb', antialiased=True)
frame = Image.fromarray(screenshot, 'RGB')
process.stdin.write(frame.tobytes())
# Flush video
process.stdin.close()
process.wait()

Categories

Resources