matplotlib animation looks different than sequenced imshow - python

I have two codes where I tried to turn it into an animation and the animation code and class shows an incomplete image (with missing colors) and the function at the bottom shows a full image and I can't see where the difference in the code is. I have a feeling it has something to do with the matplolib API but I am not sure...
from urllib.request import urlopen
from matplotlib import animation, image, pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.axis("off")
class SVDCompress():
"""compresses an image using singular value deocomposition"""
def __init__(self):
self.img = image.imread(
urlopen('https://www.mariowiki.com/images/thumb/e/e1/Small_Mario_Cut-in_PD-SMBE.png/110px-Small_Mario_Cut-in_PD-SMBE.png')
)
#self.img = image.imread(filename)
self.svd_image = np.zeros(self.img.shape)
self.bytes = 0
self.total_bytes = self.img.shape[0] * self.img.shape[1] * self.img.shape[2] / 1000
self.layers = []
# svd all the layers once so we don't have to do it again later
for layer in range(self.img.shape[2]):
self.layers.append(np.linalg.svd(self.img[:, :, layer]))
def frames(self):
"""yield the frame number which gets passed to layers method"""
for col in range(self.img.shape[1]):
yield col
def layer_no_animation(self, col: "animation index"):
"""layers calling plt.show() instead of figure"""
for i in range(self.img.shape[2]):
U, S, V = self.layers[i][0], self.layers[i][1], self.layers[i][2]
self.bytes += (len(U[:, col]) + len(V[col, :])) / 1000
self.svd_image[:, :, i] += np.outer(U[:, col] * S[col], V[col, :])
if col % 30 == 0:
plt.imshow(self.svd_image)
plt.show()
def layer(self, col: "animate index"):
"""add the new layers to the image"""
for i in range(self.img.shape[2]):
U, S, V = self.layers[i][0], self.layers[i][1], self.layers[i][2]
self.bytes += (len(U[:, col]) + len(V[col, :])) / 1000
self.svd_image[:, :, i] += np.outer(U[:, col] * S[col], V[col, :])
if col % 30 == 0:
ax.set_title(f"SVD column: {col} kbytes: {self.bytes}/{self.total_bytes}")
ax.imshow(self.svd_image)
if __name__ == "__main__":
#svd = SVDCompress()
#anim = animation.FuncAnimation(fig, svd.layer, frames=svd.frames, interval=100)
#anim.save('./mario-compression.gif', writer='imagemagick')
svd = SVDCompress()
for c in svd.frames():
svd.layer_no_animation(c)
INSTRUCTIONS:
run once as is and as you close the plot windows, new ones will appear with a sharper image and correct colors.
comment out the part that runs in __name__ == "__main" and uncomment the code that I have commented out. This will save an animation and the colors will be messed up even though the code looks to do exactly the same thing.
I am thinking there is something different about the colors when calling plt.imshow() and ax.imshow() but I can't figure out what it is
...of course it could be something else that I missed
calling plt.imshow()...
calling ax.imshow()

Related

Plotting multiple realtime diagrams via multithreading in python

I used threading library to plot 2 real-time diagrams using matplotlib.
from fbm import MBM
import matplotlib.pyplot as plt
import threading
plt.style.use('ggplot')
fig, ax = plt.subplots(nrows=2, ncols=1)
def h_90(t):
return 0.9
def h_75(t):
return 0.75
def thread_fgn_9():
x_vec = []
y_vec = []
i = 1
while True:
f = MBM(n=1, hurst=h_90, length=1, method='riemannliouville')
fgn_sample = f.mgn()
x_vec.append(i)
y_vec.append(fgn_sample[0])
i += 1
ax[0].plot(x_vec, y_vec, "g-o")
plt.pause(0.1)
plt.show()
def thread_fgn_75():
x_vec_ = []
y_vec_ = []
i = 1
while True:
f = MBM(n=1, hurst=h_75, length=1, method='riemannliouville')
fgn_sample = f.mgn()
x_vec_.append(i)
y_vec_.append(fgn_sample[0])
i += 1
ax[1].plot(x_vec_, y_vec_, "r-o")
plt.pause(0.2)
plt.show()
if __name__ == "__main__":
plt.ion()
x2 = threading.Thread(target=thread_fgn_75(), name="H_75")
x2.daemon = True
x2.start()
x1 = threading.Thread(target=thread_fgn_9(), name="H_90")
x1.daemon = True
x1.start()
I expect to see to plots being plotted real-time but I see something like below:
can anybody understand what is wrong in my code ?? The code is completed and you can simply just copy/paste in your IDE to run it.
Thanks
================= New change ================
I just changed the main section as below:
if __name__ == "__main__":
x1 = threading.Thread(target=thread_fgn_9(), name="H_90").start()
x2 = threading.Thread(target=thread_fgn_75(), name="H_75").start()
plt.show()
but still the result is the same as before.
======New New change ===============
if __name__ == "__main__":
x1 = threading.Thread(target=thread_fgn_9, name="H_90").start()
x2 = threading.Thread(target=thread_fgn_75, name="H_75").start()
#plt.pause(0.2)
plt.show()
I just erased the parentheses in target=function_name
it seems it is correct but the plot is not showing smoothly. Also I see an error in console like this:
File "/usr/local/lib/python3.9/site-packages/matplotlib/transforms.py", line 312, in xmin
return np.min(self.get_points()[:, 0])
File "<__array_function__ internals>", line 5, in amin
RecursionError: maximum recursion depth exceeded
-------Final Change-----------------------
The best way to do this in matplotlib is below code:
plt.style.use('ggplot')
fig, ax = plt.subplots(nrows=2, ncols=1)
mutex = Lock()
def thread_fgn_9():
print(threading.current_thread().getName())
x_vec = []
y_vec = []
i = 1
while True:
#mutex.acquire()
f = MBM(n=1, hurst=h_90, length=1, method='riemannliouville')
fgn_sample = f.mgn()
x_vec.append(i)
y_vec.append(fgn_sample[0])
i += 1
ax[0].plot(x_vec, y_vec, "g-o")
plt.pause(0.01)
#mutex.release()
def thread_fgn_75():
print(threading.current_thread().getName())
x_vec_ = []
y_vec_ = []
i = 1
while True:
#mutex.acquire()
f = MBM(n=1, hurst=h_75, length=1, method='riemannliouville')
fgn_sample = f.mgn()
x_vec_.append(i)
y_vec_.append(fgn_sample[0])
i += 1
ax[1].plot(x_vec_, y_vec_, "r-o")
plt.pause(0.01)
#mutex.release()
if __name__ == "__main__":
x1 = multiprocessing.Process(target=thread_fgn_9, name="H_90").start()
x2 = multiprocessing.Process(target=thread_fgn_75, name="H_75").start()
plt.show()
I believe the reason is because both processes try to write in one single main plot. In order to have a multiple smooth plot changing over time, we need to take another technique.
thread_fgn_9 is being called and blocking even before it is sent to the thread. Be sure to send the function itself.
plt.pause or plt.show need to be called from the main thread. Additionally, Matplotlib makes no thread safety guarantees in general, so you should avoid this concept entirely unless you know exactly what you are doing. Consider the techniques in this question instead: Fast Live Plotting in Matplotlib / PyPlot

Animation using matplotlib query

I am trying to animate a plot of two distinct points (blue and green points) moving about the complex unit circle using Python's Matplotlib library. The problem I am having is that the animation does not remove and update the previous data points but rather sequentially smears it on the unit sphere as in the accompanying image. Hence the animation is just a smudging of the various data points as shown in the image. What I am trying to achieve is two distinct points moving about the unit circle as a function of time.
The following is the part of my code where I call 'animation.FuncAnimation' using data in arrays which I call 'A' and 'B'.
##Python Code for Executing Animation##
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
from pylab import *
#Example Data
A = array([0., 0.03435915, 0.06328989, 0.0880305, 0.14199928, 0.2044361, 0.26287941, 0.32484623])
B = array([ 1.75, 1.71564086, 1.69358362, 1.68499179, 1.68255084, 1.67808712, 1.66169597, 1.64407287])
# Total time.
T = 1.0
# Number of steps.
NS = 100
# Time step size
dt = T/NS
t = np.linspace(0.0, NS*dt, NS+1)
# So here are a few utility functions for multiplying scalars and vectors.
# a scalar times a vector returns a vector
def scale_vector(scale, vector):
result = [0]*len(vector)
for i in range(len(result)):
result[i] = scale * vector[i]
return result
# dot product of two vectors = sum(x[0]*y[0] + ... + x[n-1]*y[n-1])
def vector_dot(vector1, vector2):
result = 0
for i in range(len(vector1)):
result += vector1[i] * vector2[i]
return result
# return real part of a vector
def real_vector(vector):
return map(lambda x: x.real, vector)
# return imaginary part of a vector
def imag_vector(vector):
return map(lambda x: x.imag, vector)
## Creating complex unit circle
r = []
im = []
def main():
# Generate numbers around the complex unit circle.
N = 128
theta = scale_vector(2*pi/N, range(N))
exp_theta = map(lambda x: exp(1j * x), theta)
real_part = real_vector(exp_theta)
imag_part = imag_vector(exp_theta)
r.append(real_part)
im.append(imag_part)
# And wait until the user is done with it.
done = raw_input("done? ")
if __name__ == "__main__":
main()
#Form two arrays which have the real and imaginary components of the unit circle
r2 = r[0][:]
im2 = im[0][:]
##Code for Animation##
Aan = np.zeros([len(A),2], float)
for i in range(2):
for j in range(len(A)):
if i == 0:
Aan[j][i] = math.cos(A[j])
elif i == 1:
Aan[j][i] = math.sin(A[j])
Ban = np.zeros([len(B),2], float)
for i in range(2):
for j in range(len(B)):
if i == 0:
Ban[j][i] = math.cos(B[j])
elif i == 1:
Ban[j][i] = math.sin(B[j])
##Plots and animation
fig = figure()
plt.title('Phase Space')
plt.xlabel('Re')
plt.ylabel('Im')
#Plots complex unit circle
plot1 = plt.plot(r2,im2, color = 'g',alpha = 0.4)
#Animation functions
def animate(i):
plot(Aan[i, 0], Aan[i, 1], color='blue', marker= 'o')
plot(Ban[i, 0], Ban[i, 1], color='orange', marker= 'o')
ani = animation.FuncAnimation(fig, animate, interval=101)
show()
Can anyone advise on how this problem could be solved?
Plot creates a new object on the canvas which is not cleared automatically at the next plot.
If you would like to redraw the figure, you can call the cla method and plot the data again.
Or you can update the previously plotted data as it is described in the last example of animation API documentation.

Python plotting to different figures fails

EDIT: I figured out that the Problem always occours if one tries to plot to two different lists of figures. Does that mean that one can not do plots to different figure-lists in the same loop? See latest code for much simpler sample of a problem.
I try to analyze a complex set of data which consists basically about measurements of electric devices under different conditions. Hence, the code is a bit more complex but I tried to strip it down to a working example - however it is still pretty long. Hence, let me explain what you see: You see 3 classes with Transistor representing an electronic device. It's attribute Y represents the measurement data - consisting of 2 sets of measurements. Each Transistor belongs to a group - 2 in this example. And some groups belong to the same series - one series where both groups are included in this example.
The aim is now to plot all measurement data for each Transistor (not shown), then to also plot all data belonging to the same group in one plot each and all data of the same series to one plot. In order to program it in an efficent way without having a lot of loops my idea was to use the object orientated nature of matplotlib - I will have figures and subplots for each level of plotting (initialized in initGrpPlt and initSeriesPlt) which are then filled with only one loop over all Transistors (in MainPlt: toGPlt and toSPlt). In the end it should only be printed / saved to a file / whatever (PltGrp and PltSeries).
The Problem: Even though I specify where to plot, python plots the series plots into the group plots. You can check this yourself by running the code with the line 'toSPlt(trans,j)' and without. I have no clue why python does this because in the function toSPlt I explicetly say that python should use the subplots from the series-subplot-list. Would anyone have an idea to why this is like this and how to solve this problem in an elegent way?
Read the code from the bottom to the top, that should help with understanding.
Kind regards
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
maxNrVdrain = 2
X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
A = [[1*np.cos(X),2*np.cos(X),3*np.cos(X),4*np.cos(X)],[1*np.tan(X),2*np.tan(X),3*np.tan(X),4*np.tan(X)]]
B = [[2* np.sin(X),4* np.sin(X),6* np.sin(X),8* np.sin(X)],[2*np.cos(X),4*np.cos(X),6*np.cos(X),8*np.cos(X)]]
class Transistor(object):
_TransRegistry = []
def __init__(self,y1,y2):
self._TransRegistry.append(self)
self.X = X
self.Y = [y1,y2]
self.group = ''
class Groups():
_GroupRegistry = []
def __init__(self,trans):
self._GroupRegistry.append(self)
self.transistors = [trans]
self.figlist = []
self.axlist = []
class Series():
_SeriesRegistry = []
def __init__(self,group):
self._SeriesRegistry.append(self)
self.groups = [group]
self.figlist = []
self.axlist = []
def initGrpPlt():
for group in Groups._GroupRegistry:
for j in range(maxNrVdrain):
group.figlist.append(plt.figure(j))
group.axlist.append(group.figlist[j].add_subplot(111))
return
def initSeriesPlt():
for series in Series._SeriesRegistry:
for j in range(maxNrVdrain):
series.figlist.append(plt.figure(j))
series.axlist.append(series.figlist[j].add_subplot(111))
return
def toGPlt(trans,j):
colour = cm.rainbow(np.linspace(0, 1, 4))
group = trans.group
group.axlist[j].plot(trans.X,trans.Y[j], color=colour[group.transistors.index(trans)], linewidth=1.5, linestyle="-")
return
def toSPlt(trans,j):
colour = cm.rainbow(np.linspace(0, 1, 2))
series = Series._SeriesRegistry[0]
group = trans.group
if group.transistors.index(trans) == 0:
series.axlist[j].plot(trans.X,trans.Y[j],color=colour[series.groups.index(group)], linewidth=1.5, linestyle="-", label = 'T = nan, RH = nan' )
else:
series.axlist[j].plot(trans.X,trans.Y[j],color=colour[series.groups.index(group)], linewidth=1.5, linestyle="-")
return
def PltGrp(group,j):
ax = group.axlist[j]
ax.set_title('Test Grp')
return
def PltSeries(series,j):
ax = series.axlist[j]
ax.legend(loc='upper right', frameon=False)
ax.set_title('Test Series')
return
def MainPlt():
initGrpPlt()
initSeriesPlt()
for trans in Transistor._TransRegistry:
for j in range(maxNrVdrain):
toGPlt(trans,j)
toSPlt(trans,j)#plots to group plot for some reason
for j in range(maxNrVdrain):
for group in Groups._GroupRegistry:
PltGrp(group,j)
plt.show()
return
def Init():
for j in range(4):
trans = Transistor(A[0][j],A[1][j])
if j == 0:
Groups(trans)
else:
Groups._GroupRegistry[0].transistors.append(trans)
trans.group = Groups._GroupRegistry[0]
Series(Groups._GroupRegistry[0])
for j in range(4):
trans = Transistor(B[0][j],B[1][j])
if j == 0:
Groups(trans)
else:
Groups._GroupRegistry[1].transistors.append(trans)
trans.group = Groups._GroupRegistry[1]
Series._SeriesRegistry[0].groups.append(Groups._GroupRegistry[1])
return
def main():
Init()
MainPlt()
return
main()
latest example that does not work:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
Y1 = np.cos(X)
Y2 = np.sin(X)
figlist1 = []
figlist2 = []
axlist1 = []
axlist2 = []
for j in range(4):
figlist1.append(plt.figure(j))
axlist1.append(figlist1[j].add_subplot(111))
figlist2.append(plt.figure(j))#this should be a new set of figures!
axlist2.append(figlist2[j].add_subplot(111))
colour = cm.rainbow(np.linspace(0, 1, 4))
axlist1[j].plot(X,j*Y1, color=colour[j], linewidth=1.5, linestyle="-")
axlist1[j].set_title('Test Grp 1')
colour = cm.rainbow(np.linspace(0, 1, 4))
axlist2[j].plot(X,j*Y2, color=colour[int(j/2)], linewidth=1.5, linestyle="-")
axlist2[j].set_title('Test Grp 2')
plt.show()
Ok, stupid mistake if one thinks of the Background but maybe someone has a similar Problem and is unable to see the cause as I was first. So here is the solution:
The Problem is that the Name of the listobjects like figlist1[j] do not define the figure - they are just pointers to the actual figure object. and if such an object is created by plt.figure(j) one has to make sure that j is different for each figure - hence, in a Loop where multiple figures shall be initialized one Needs to somehow Change the number of the figure or the first object will be overwritten. Hope that helps! Cheers.

How to speed up chaco image plot

I am trying to animate a bunch of 2D images with chaco, but unfortunately it does not seem to be as fast as my application needs it. At the moment I am building a chaco Plot and using img_plot, e.g.:
pd = ArrayPlotData()
pd.set_data("imagedata", myarray)
plot = Plot(pd)
plot.img_plot("imagedata", interpolation="nearest")
And to update the image, I use the following:
pd.set_data("imagedata", my_new_array)
This works, however is not fast enough. Is there any way to speed it up? Any lower-level function that allows a faster update of the image?
Here's an example of how I do animations in Chaco using a timer. Usually the trick (as J Corson said) is to load your data into an array and then just use an index to take successive slices of the array.
from chaco.api import ArrayPlotData, Plot
from enable.api import ComponentEditor
import numpy as np
from pyface.timer.api import Timer
from traits.api import Array, Bool, Event, HasTraits, Instance, Int
from traitsui.api import ButtonEditor, Item, View
class AnimationDemo(HasTraits):
plot = Instance(Plot)
x = Array
y = Array
run = Bool(False)
go = Event
idx = Int
def _x_default(self):
x = np.linspace(-np.pi, np.pi, 100)
return x
def _y_default(self):
phi = np.linspace(0, 2 * np.pi, 360)
y = np.sin(self.x[:, np.newaxis] + phi[np.newaxis, :]) - \
0.1 * np.sin(13 * self.x[:, np.newaxis] - 7 * phi[np.newaxis, :])
return y
def _plot_default(self):
plot_data = ArrayPlotData(y=self.y[:, 0], x=self.x)
plot = Plot(plot_data)
plot.plot(('x', 'y'))
return plot
def _go_fired(self):
if not self.run:
self.run = True
else:
self.run = False
def _run_changed(self):
if self.run:
self.timer.Start()
else:
self.timer.Stop()
def _run_default(self):
self.timer = Timer(5, self._timer_tick)
return False
def _timer_tick(self):
if not self.run:
raise StopIteration
else:
if self.idx >= 360:
self.idx = 0
self.plot.data.set_data('y', self.y[:, self.idx])
self.idx += 1
traits_view = View(
Item('plot', editor=ComponentEditor(), show_label=False),
Item('go', editor=ButtonEditor(label="Start/Stop"), show_label=False),
)
if __name__ == "__main__":
ad = AnimationDemo()
ad.edit_traits()
I get something like this:
This is just a thought, but would adding every image initially into your ArrayPlotData solve your problem? Then you aren't adding a new image at each step in your animation, and just calling img_plot() on the next series. For example, if your images are stored in a numpy array called images[nt, nx, ny]:
pd = ArrayPlotData()
for index in range(images.shape[0]): #Assuming you want to iterate over nt
pd.set_data('', images[index,:,:], generate_name = True)
plot = Plot(pd)
This automatically names each image 'series1', 'series2', etc.
Then you can call:
plot.img_plot('series1', interpolation = 'nearest') #or 'series2' etc.
for every image in your animation without having to call set_data().
You can get a sorted list of your image names ['series1, 'series2', ...] to iterate over using:
from natsort import natsorted #sort using natural sorting
names = natsorted(pd.list_data())
Would that help with the bottleneck?

'Animated' 2D scatter

I want to visualise conversion of filters. I would like to plot a scatter plot, where every half second the next filter's values are plotted.
My objectives are:
Plot all values up to point (k) but to have values(k) indicated on the plot.
Pause between plotting values for (k) and (k+1)
Plot at full screen
Have the plot after finishing all iteration
I did a function but it is very inefficient and everything slows down after plotting some values.
The only way I found is to use interactive plot ion() and every step plot all points again with updated marker. For each step (k) I would like to rather remove previous points (k-1) and add them in them with different marker and add current points (k)
import pylab as pl
import time
xPos1 = pl.arange(100)
m1 = [pl.sin(pl.pi*x/10) for x in xPos1]
m2 = [pl.cos(pl.pi*x/30) for x in xPos1]
m3 = [pl.sin(pl.pi*x/20) for x in xPos1]
trueVal1 = [0 for real in xPos1]
def conversionAnim(xPos, trueVal, *args):
mTuple = [arg for arg in args]
colorList = ['Green','Blue','Orchid','Cyan','Goldenrod','Salmon','Orange','Violet','Magenta']
f = pl.figure(figsize =(17,8))
pl.ion()
pl.xlim(min(xPos)-1, max(xPos)+1)
pl.ylim(min(j for i in mTuple for j in i)-.5, max(j for i in mTuple for j in i)+.5)
for i in range(len(xPos)):
print '\ni = %i' % i
for j in range(len(mTuple)):
m = mTuple[j]
mVal = [element for element in m]
print 'Value%i is %s' %(j,mVal[i])
if i == 0:
pl.hold(True)
pl.scatter(xPos[i],mVal[i],s=50, marker = 'o', color = 'Dark'+colorList[j])
pl.plot(xPos[i],trueVal[i])
else:
pl.scatter(xPos[i],mVal[i],s=50, marker = 'o',color = 'Dark'+colorList[j])
pl.scatter(xPos[i-1], mVal[i-1],s=50, marker = 'o', color = 'white')
pl.scatter(xPos[i-1], mVal[i-1],s=50, marker = 'x', color = colorList[j])
pl.plot(xPos[i-1:i+1],trueVal[i-1:i+1], color = 'red')
pl.draw()
time.sleep(.01)
time.sleep(3) # to hold figure after its shown
if __name__ == '__main__':
conversionAnim(xPos1, trueVal1, m1, m2, m3)
I don't know how to get around ion() and make this function efficient.
The simplest way to make this more efficent is to use 2N line plots instead of a huge number of scatter plots. (It looks like you end up with three scatter plot for every data point!)
As a side note, you have several lines ( mTuple = [arg for arg in args]) that turn tuples in to lists. It is clearer to write mTuple = list(args), but I don't think you actually need to do those conversions, as you just need an iterable for everything you do.
import itertools
def covnersion_Anim(xPos,trueVal,*args):
mTuple = args
plt_bulk_lst = []
plt_head_lst = []
color_list = ['Green','Blue','Orchid','Cyan','Goldenrod','Salmon','Orange','Violet','Magenta']
f = plt.figure(figsize =(17,8))
ax = plt.gca()
ax.set_xlim([min(xPos),max(xPos)])
ax.set_ylim([0,1])
ms = 5
for j,c in zip(range(len(mTuple)),itertools.cycle(color_list)):
plt_bulk_lst.append(ax.plot([],[],color=c,ms=ms,marker='x',linestyle='none')[0])
plt_head_lst.append(ax.plot([xPos[0]],[mTuple[j][0]],color='Dark'+c,ms=ms,marker='o',linestyle='none')[0])
real_plt, = plot([],[],color='red')
for j in range(1,len(xPos)):
print j
for hd_plt,blk_plt,m in zip(plt_head_lst,plt_bulk_lst,mTuple):
hd_plt.set_xdata([xPos[j]])
hd_plt.set_ydata([m[j]])
blk_plt.set_ydata(m[:j])
blk_plt.set_xdata(xPos[:j])
real_plt.set_xdata(xPos[:j])
real_plt.set_ydata(trueVal[:j])
plt.pause(1)
return f
covnersion_Anim(range(12),rand(12),rand(12),rand(12),rand(12))

Categories

Resources