0

I have an instrument that continuously measures voltage. By calling get_sample_r, I am appending the voltage value measured at a given time to v_measured_array, which I would like to visualize as a 2D color plot in real time. Below is my attempt at it using matplotlib.

import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

signal_path="/dev6541/demods/0/sample"

v_range=  [[0,15], [0,15]]
npoints_x=20
npoints_y=20
n_point_x = np.linspace(v_range[0][0], v_range[0][1], npoints_x)
n_point_y = np.linspace(v_range[1][0], v_range[1][1], npoints_y)
v_measured_array = np.ones(shape=(npoints_x,npoints_y))

plt.show(block=False)
fig = plt.figure()

for each_x in range(0,npoints_x):      
    for each_y in range(0,npoints_y):

        v_measured_array[each_x][each_y]= get_sample_r(daq_1, signal_path)  

        ax = fig.add_subplot(111)
        ax.imshow(v_measured_array, cmap='Greens', interpolation='None')
        fig.canvas.draw()
        fig.canvas.flush_events()  

Although the code above does work in principle, I realized it gets impossibly slow for a large 2D array because I am replotting the entirety of v_measured_array.

I have two questions:

  1. Is there a way to add only the newly acquired data point (pixel) to the existing plot rather than updating the plot by replotting the entire data set?

  2. I noticed that my plot always gets frozen after the loop is over. I have tried many different backend values ['GTK3Agg', 'GTK3Cairo', 'GTK4Agg', 'GTK4Cairo', 'MacOSX', 'nbAgg', 'QtAgg', 'QtCairo', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', 'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo', 'agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg', 'template'] None of them solved the problem. (I use Spyder as my Python IDE)

Blackwidow
  • 146
  • 6
  • 1
    Does this answer your question? [Fast Live Plotting in Matplotlib / PyPlot](https://stackoverflow.com/questions/40126176/fast-live-plotting-in-matplotlib-pyplot) – Jody Klymak Oct 25 '22 at 06:06
  • ([This (2nd solution post)](https://stackoverflow.com/a/40139416/10338539) was really helpful. My issue though is, in that implementation, the color range for the plot needs to be hard coded (look at vmin and vmax`img = ax1.imshow(v_measured_array, vmin=0.000795, vmax=0.000809, interpolation="None", cmap="RdBu")`) I need to be able to update the plot while the color scale dynamically changes based on the data stream – Blackwidow Oct 25 '22 at 21:28
  • Your fundamental problem is that you keep adding a new image to the axes each loop, rather than removing the old and adding a new. However, you can also just change the _data_ in the image if it isn't changing size. If you need to change the vmin/vmax dynamically, just do that as well. – Jody Klymak Oct 25 '22 at 21:43

1 Answers1

0

The idea is that you have to fix the color map and not to plot in each iteration and only updated the data. This is still not very fast, but much faster than your implementation. If you want to go further and faster you can use opencv, and build image and update, that could be much more efficient. I replaced your read dac fucntion by random function to work. By the way you could use meshgrid instead of linspace, but I did not optimized or changed those parts,

import numpy as np
import matplotlib.pyplot as plt

npoints_x = 10
npoints_y = 10
rng_x = (0, 15, npoints_x)
rng_y = (0, 15, npoints_y)
n_point_x = np.linspace(*rng_x)
n_point_y = np.linspace(*rng_y)
v_measured_array = np.ones(shape=(npoints_x, npoints_y))

fig = plt.figure()
plt.ion()
axim = plt.imshow(v_measured_array, cmap='Greens', interpolation='None', clim=[0, 1])
plt.show(block=False)
j = 1
for each_x in range(0, npoints_x):      
    for each_y in range(0, npoints_y):
        v_measured_array[each_x, each_y]= np.random.rand() * j
        axim.set_array(v_measured_array)
        axim.set_clim(0, v_measured_array.max())
        # ax.imshow(v_measured_array, cmap='Greens', interpolation='None')
        fig.canvas.draw()
        fig.canvas.flush_events()
        j += 0.5
amirhm
  • 1,239
  • 9
  • 12
  • This is the right idea, `plt.imshow` doesn't return an Axes, it returns an AxesImage, so its a bit confusing for call it `ax`. – Jody Klymak Oct 25 '22 at 21:44
  • you could use set_clim , but then as well you need a colorbar to see the scale which are changing. – amirhm Oct 25 '22 at 22:39
  • @Blackwidow I did update the code to do as well dynamically adjust the clim, – amirhm Oct 26 '22 at 09:09