【问题标题】:How to update matplotlib's imshow() window interactively?如何以交互方式更新 matplotlib imshow() 窗口?
【发布时间】: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?我想在循环中调用一个函数,因此我不知道如何使用动画函数来实现。

【问题讨论】:

标签: python numpy matplotlib spyder


【解决方案1】:

我努力让它工作,因为很多帖子都在谈论这个问题,但似乎没有人关心提供一个工作示例。然而,在这种情况下,原因是不同的:

  • 我无法使用 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 解决方案(一开始我错过了)。

【讨论】:

    【解决方案2】:

    如果您使用的是 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 是错误的,因为它每次都会重新创建新的数据结构,这会导致严重的内存泄漏并减慢整个显示过程。
    【解决方案3】:

    我实现了一个方便的脚本,正好适合您的需求。试试看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
    【解决方案4】:

    您无需一直致电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,所以我没有看到问题。刚刚更新了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-22
    • 2012-12-25
    • 1970-01-01
    • 2014-06-17
    相关资源
    最近更新 更多