如何以交互方式更新 matplotlib imshow() 窗口?
Posted
技术标签:
【中文标题】如何以交互方式更新 matplotlib imshow() 窗口?【英文标题】:How to update matplotlib's imshow() window interactively? 【发布时间】:2013-07-24 00:07:28 【问题描述】:我正在研究一些计算机视觉算法,我想展示一个 numpy 数组在每一步中的变化。
现在可行的是,如果我在代码末尾有一个简单的imshow( array )
,窗口就会显示并显示最终图像。
但是,我想做的是在每次迭代中图像更改时更新和显示 imshow 窗口。
例如,我想这样做:
import numpy as np
import matplotlib.pyplot as plt
import time
array = np.zeros( (100, 100), np.uint8 )
for i in xrange( 0, 100 ):
for j in xrange( 0, 50 ):
array[j, i] = 1
#_show_updated_window_briefly_
plt.imshow( array )
time.sleep(0.1)
问题是这样,Matplotlib 窗口不会被激活,只有在整个计算完成后才会被激活。
原生matplotlib和pyplot我都试过了,但是结果是一样的。对于绘图命令,我找到了一个 .ion()
开关,但在这里它似乎不起作用。
第一季度。持续显示对 numpy 数组(实际上是 uint8 灰度图像)的更新的最佳方式是什么?
第二季度。是否可以使用动画功能来做到这一点,比如dynamic image example?我想在循环中调用一个函数,因此我不知道如何使用动画函数来实现。
【问题讨论】:
这可能取决于您使用的后端,但请在开始循环之前尝试至少调用一个show()
或draw()
-- 请参阅此answer。
这对我有用***.com/questions/51520143/…
【参考方案1】:
您无需一直致电imshow
。使用对象的set_data
方法要快得多:
myobj = imshow(first_image)
for pixel in pixels:
addpixel(pixel)
myobj.set_data(segmentedimg)
draw()
draw()
应确保后端更新图像。
更新:您的问题已被大幅修改。在这种情况下,最好问另一个问题。这是处理您的第二个问题的一种方法:
Matplotlib 的动画只处理一个增加的维度(时间),所以你的双循环不会做。您需要将索引转换为单个索引。这是一个例子:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
nx = 150
ny = 50
fig = plt.figure()
data = np.zeros((nx, ny))
im = plt.imshow(data, cmap='gist_gray_r', vmin=0, vmax=1)
def init():
im.set_data(np.zeros((nx, ny)))
def animate(i):
xi = i // ny
yi = i % ny
data[xi, yi] = 1
im.set_data(data)
return im
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=nx * ny,
interval=50)
【讨论】:
不幸的是它不起作用,同样的事情发生了。也许我应该使用动画功能,例如动态图像示例:matplotlib.org/examples/animation/dynamic_image.html,但我不知道如何将其转换为基于循环的代码。 @zsero:如果更简单的版本不起作用,我想知道更复杂的动画是否会起作用。我刚刚添加了一个适合我的示例(matplotlib 1.2),看看它是否适合你。 刚刚尝试修改您的示例,我认为im = imshow(data, ...)
应该改为im = plt.imshow(data, ...)
。此外,为了运行动画,您需要plt.show()
。干杯
@Chrigi 你是对的。我通常有--pylab
,所以我没有看到问题。刚刚更新了答案。【参考方案2】:
我实现了一个方便的脚本,正好适合您的需求。试试看here
在自定义目录中显示图像的示例如下:
import os
import glob
from scipy.misc import imread
img_dir = 'YOUR-IMAGE-DIRECTORY'
img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
def redraw_fn(f, axes):
img_file = img_files[f]
img = imread(img_file)
if not redraw_fn.initialized:
redraw_fn.im = axes.imshow(img, animated=True)
redraw_fn.initialized = True
else:
redraw_fn.im.set_array(img)
redraw_fn.initialized = False
videofig(len(img_files), redraw_fn, play_fps=30)
【讨论】:
他问的不是更新剧情,而是二维图像 提供的脚本是通用的。您可以使用它来更新任何由 matplotlib 绘制的艺术家。更新示例以显示如何更新 2D 图像。我已经用了 3 年了,它从来没有让我失望过。希望它可以帮助其他人。 @ProfHuster【参考方案3】:如果您使用的是 Jupyter,也许这个答案会让您感兴趣。
我在this site 中读到clear_output
的嵌入功能可以解决问题:
%matplotlib inline
from matplotlib import pyplot as plt
from IPython.display import clear_output
plt.figure()
for i in range(len(list_of_frames)):
plt.imshow(list_of_frames[i])
plt.title('Frame %d' % i)
plt.show()
clear_output(wait=True)
这个方法确实很慢,但是可以用于测试目的。
【讨论】:
我在回复中提到过,但是重复 imshow 是错误的,因为它每次都会重新创建新的数据结构,这会导致严重的内存泄漏并减慢整个显示过程。【参考方案4】:我努力让它工作,因为很多帖子都在谈论这个问题,但似乎没有人关心提供一个工作示例。然而,在这种情况下,原因是不同的:
我无法使用 Tiago 或 Bily 的答案,因为它们不在 与问题相同的范式。在问题中,刷新是 由算法本身安排,同时具有funcanimation或 videofig,我们处于事件驱动的范式中。事件驱动 对于现代用户界面编程来说,编程是不可避免的,但是 当你从一个复杂的算法开始时,可能很难 将其转换为事件驱动方案 - 我希望能够做到 它也在经典的程序范式中。 Bub Espinja 回复遇到另一个问题:我没有在 jupyter 笔记本的上下文,但重复 imshow 是错误的,因为它 每次都重新创建新的数据结构,这会导致重要的 内存泄漏并减慢整个显示过程。Tiago 还提到调用 draw()
,但没有指定从哪里获取它 - 顺便说一句,你不需要它。你真正需要调用的函数是flush_event()
。有时它可以在没有的情况下工作,但这是因为它是从其他地方触发的。你不能指望它。真正棘手的一点是,如果你在一个空表上调用imshow()
,你需要指定 vmin 和 vmax 否则它将无法初始化它的颜色映射并且 set_data 也会失败。
这是一个可行的解决方案:
IMAGE_SIZE = 500
import numpy as np
import matplotlib.pyplot as plt
plt.ion()
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
fig3, ax3 = plt.subplots()
# this example doesn't work because array only contains zeroes
array = np.zeros(shape=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim1 = ax1.imshow(array)
# In order to solve this, one needs to set the color scale with vmin/vman
# I found this, thanks to @jettero's comment.
array = np.zeros(shape=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim2 = ax2.imshow(array, vmin=0, vmax=99)
# alternatively this process can be automated from the data
array[0, 0] = 99 # this value allow imshow to initialise it's color scale
axim3 = ax3.imshow(array)
del array
for _ in range(50):
print(".", end="")
matrix = np.random.randint(0, 100, size=(IMAGE_SIZE, IMAGE_SIZE), dtype=np.uint8)
axim1.set_data(matrix)
fig1.canvas.flush_events()
axim2.set_data(matrix)
fig1.canvas.flush_events()
axim3.set_data(matrix)
fig1.canvas.flush_events()
print()
更新:我根据@Jettero 的评论添加了 vmin/vmax 解决方案(一开始我错过了)。
【讨论】:
在 Jupyter Lab 中测试了您的代码,并在for
循环中添加了 sleep(0.1)
。看来,for
循环中需要 plt.draw()
才能更新 img。奇怪的是,它只会循环 25 次。我猜draw()
被认为是一个触发事件,会触发下一次刷新和绘制...?
如果我没记错的话(我好几个月没玩这个了),draw()
的问题在于它会在其他对象之上创建新对象,因此在循环中使用它而不删除先前的对象正在创建内存泄漏,这可能是您的程序在 25 次迭代后崩溃的原因。但是,我不能告诉你更多,因为我不是 Jupyter Lab 用户。我只在 linux 上使用常规的 python 解释器和 Idle 编写和测试了这段代码。
不崩溃,但只重绘25次,也就是说循环50次,但刷新只有25次【参考方案5】:
import numpy as np
import matplotlib.pyplot as plt
k = 10
plt.ion()
array = np.zeros((k, k))
for i in range(k):
for j in range(k):
array[i, j] = 1
plt.imshow(array)
plt.show()
plt.pause(0.001)
plt.clf()
【讨论】:
您的答案可以通过添加可以教育和告知的解释来改进。仅代码答案并不清楚为什么此解决方案比其他任何解决方案都好。请edit添加更多详细信息。【参考方案6】:我遇到了类似的问题 - 想更新图像,不想重复替换轴,但 plt.imshow()
(也不是 ax.imshow()
)没有更新显示的图形。
我终于发现需要某种形式的draw()
。但是fig.canvas.draw()
、ax.draw()
... 都不起作用。终于找到解决办法here:
%matplotlib notebook #If using Jupyter Notebook
import matplotlib.pyplot as plt
import numpy as np
imData = np.array([[1,3],[3,1]])
# Setup and plot image
fig = plt.figure()
ax = plt.subplot(111)
im = ax.imshow(imData)
# Change image contents
newImData = np.array([[2,2],[2,2]])
im.set_data( newImData )
im.draw()
【讨论】:
以上是关于如何以交互方式更新 matplotlib imshow() 窗口?的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Qt 异步运行以进行交互使用,如 Matplotlib 离子模式?
Tkinter 中的 Matplotlib 绘图 - 每次更新都会添加新的 NavigationToolbar?