将 3D 动画 mayavi 人物保存到内存文件(缓冲区)中

Saving a 3D animated mayavi figure into a in-memory file (buffer)

我想保存我用 mayavi 构建的动画 3D 人物的 gif 或 mp4。

当我用 2D 图形和 imagio 做类似的事情时,我可以将数据保存到缓冲区并将它们附加到 imagio writer。这非常快,省去了生成数千个中间 png 文件的麻烦。但是,我找不到将它们保存到缓冲区而不是文件的方法。

@mlab.animate(delay=10, ui=False)
def update_animation(Z, surface, writer):
    # Sets nan pixels to white
    surface.module_manager.scalar_lut_manager.lut.nan_color = 0, 0, 0, 0
    surface.update_pipeline()
    f = mlab.gcf()
    # Sets background to white
    f.scene.background = (1,1,1)
    t = 2.0
    while t <= Z.shape[0]:
        f.scene.camera.azimuth(1)
        f.scene.render()
        surface.mlab_source.scalars = get_band(Z, int(t)-1)
        t += 0.1
        #mlab.savefig('tmp/' + str(t) + '.png') # this used to work, but generates thousands of png
        with io.BytesIO() as buff: # so I want instead to try this strategy
            mlab.savefig(buff, format='raw')
            buff.seek(0)
            data = numpy.frombuffer(buff.getvalue(), dtype=numpy.uint8)
            w, h = fig.canvas.get_width_height()
            im = data.reshape((int(h), int(w), -1))
            writer.append_data(im)
        yield

在主函数中:

# Z is a 3D np array built from reading a multiband raster with rasterio
with imageio.get_writer(output, mode='I') as writer:
    surface = mlab.imshow(get_band(Z, 0), colormap='viridis')
    a = update_animation(Z, surface, writer)
    mlab.show()

但错误消息指出: TypeError: expected str, bytes or os.PathLike object, not _io.BytesIO

来自mayavi documentation

Starting from Mayavi version 3.4.0, the mlab screenshot() can be used to take a screenshot of the current figure, to integrate in a matplotlib plot.

所以其实完全可以绕过BytesIO相关的代码,直接用writer.append_data(mlab.screenshot())调用imagio writer:

# Z is a 3D np array built from reading a multiband raster with rasterio
with imageio.get_writer(output, mode='I') as writer:
    surface = mlab.imshow(get_band(Z, 0), colormap='viridis')
    lut_manager = mlab.scalarbar(orientation='vertical')
    mlab.colorbar()
    a = update_animation(Z, surface, writer)
    mlab.show()

以及动画函数本身:

@mlab.animate(delay=10, ui=False)
def update_animation(Z, surface, writer):
    # Sets nan pixels to white
    surface.module_manager.scalar_lut_manager.lut.nan_color = 0, 0, 0, 0
    surface.update_pipeline()
    f = mlab.gcf()
    t = 2.0
    while t <= Z.shape[0]:
        f.scene.camera.azimuth(1)
        f.scene.render()
        surface.mlab_source.scalars = get_band(Z, int(t)-1)
        t += 0.1
        writer.append_data(mlab.screenshot()) # here !
        yield