You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何逐像素更新2D彩色图不重绘旧像素及解决绘图冻结问题

实时2D电压测量可视化优化问题

我有一台可持续测量电压的仪器,通过调用get_sample_r函数将某一时刻的测量电压存入v_measured_array数组,希望实现实时2D彩色图可视化。以下是我用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()  

原理上代码能运行,但当2D数组较大时,因为每次都要重绘整个v_measured_array,速度会变得极慢。我有两个技术问题:

  1. 是否可以仅更新新采集的数据点(像素),而非重绘整个数据集来更新绘图?
  2. 循环结束后绘图总会冻结,我试过多种后端值['GTK3Agg', 'GTK3Cairo', 'GTK4Agg', 'GTK4Cairo', 'MacOSX', 'nbAgg', 'QtAgg', 'QtCairo', 'Qt5Agg', 'Qt5Cairo', 'TkAgg', 'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo', 'agg', 'cairo', 'pdf', 'pgf', 'ps', 'svg', 'template'],都没解决(我用Spyder作为Python IDE)。

问题1解答:仅更新单个像素点,避免全量重绘

可以实现局部更新,核心思路是只初始化一次绘图对象,后续仅修改数据而非重建绘图:

  • 把imshow和子图创建的代码移到循环外,获取对应的AxesImage对象
  • 每次更新单个数据点后,调用set_data()更新图像数据,用draw_idle()替代全量draw(),只重绘需要更新的区域

优化后的核心代码示例:

# 初始化绘图(移到循环外)
plt.show(block=False)
fig = plt.figure()
ax = fig.add_subplot(111)
# 初始化图像对象,后续只更新它的数据
im = ax.imshow(v_measured_array, cmap='Greens', interpolation='None')

for each_x in range(npoints_x):      
    for each_y in range(npoints_y):
        # 更新数据数组
        v_measured_array[each_x][each_y] = get_sample_r(daq_1, signal_path)
        # 仅更新图像数据,无需重新调用imshow
        im.set_data(v_measured_array)
        # 只重绘需要更新的部分,比draw()更高效
        fig.canvas.draw_idle()
        fig.canvas.flush_events()

如果要进一步提速,还可以:

  • 减少flush_events()的调用频率,比如每采集5个点更新一次,平衡实时性和速度
  • 若颜色范围固定,提前用im.set_clim()锁定范围,避免每次更新时重新计算颜色映射

问题2解答:解决循环结束后绘图冻结的问题

Spyder中绘图冻结的核心原因是循环结束后事件循环未正常运行,或代码存在内存泄漏。可以尝试以下方案:

方案1:循环结束后保留事件循环

在循环结束后添加事件循环处理代码,让绘图窗口保持响应:

# 循环结束后添加
while plt.get_fignums():
    fig.canvas.flush_events()

方案2:让Spyder自动管理后端

打开Spyder的工具 > 偏好设置 > IPython控制台 > 图形,将后端设置为Automatic或QtAgg,然后重启Spyder。删除代码中手动指定matplotlib.use()的行,避免和Spyder的内置后端冲突。

方案3:删除循环内重复创建子图的代码

原代码每次循环都调用ax = fig.add_subplot(111),会不断创建新子图导致内存占用飙升,这也是冻结的潜在原因。把创建ax的代码移到循环外,如问题1的优化代码所示。

方案4:改用matplotlib.animation模块

用动画模块管理实时更新,让matplotlib自动处理事件循环,从根源避免冻结:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

signal_path="/dev6541/demods/0/sample"
v_range=  [[0,15], [0,15]]
npoints_x=20
npoints_y=20
v_measured_array = np.ones(shape=(npoints_x,npoints_y))

fig, ax = plt.subplots()
im = ax.imshow(v_measured_array, cmap='Greens', interpolation='None')

# 记录当前要更新的坐标
current_x, current_y = 0, 0

def update(frame):
    global current_x, current_y
    # 更新当前点数据
    v_measured_array[current_x][current_y] = get_sample_r(daq_1, signal_path)
    im.set_data(v_measured_array)
    # 切换到下一个坐标
    current_y += 1
    if current_y >= npoints_y:
        current_y = 0
        current_x += 1
    # 所有点更新完成后停止动画
    if current_x >= npoints_x:
        anim.event_source.stop()
    return im,

# interval控制更新间隔(毫秒),blit=True只更新变化区域
anim = FuncAnimation(fig, update, interval=10, blit=True)
plt.show()

内容的提问来源于stack exchange,提问作者Blackwidow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 07:55:22