Bokeh 中图像可视化的高效更新方式

7
我正在尝试使用Bokeh创建平滑的交互式可视化,以显示多维数组的不同切片。切片中的数据根据用户交互而变化,因此必须每秒更新多次。我编写了一个Bokeh应用程序,其中包含几个小图像绘图(64x64值),用于显示切片的内容,并使用回调函数在用户与应用程序交互时更新ColumnDataSources。一切都按预期工作,但我最多只能得到2或3帧每秒,我希望至少获得10帧。
这是我的代码简化样本,使用16个图像,并使用周期性回调每100ms模拟用户交互。在Mac和Linux上使用Bokeh 0.12.3和Python 2.7,两台机器的计时几乎完全相同(每帧约300ms)。
from __future__ import print_function, division
from random import randint
from timeit import default_timer as timer
import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure

# Set up app and fake data
grid_size = 4
data_size = 64
names = ['d{}'.format(i) for i in range(grid_size)]
plots = [[None for _ in range(grid_size)] for _ in range(grid_size)]
sources = dict()

num_images = 16
image_data = [[np.random.rand(data_size, data_size)] for i in range(num_images)]

# Create plots and datasources
plot_size = 256
for row, row_name in enumerate(names):
    for col, c_name in enumerate(names):
        d_name = row_name + "_" + c_name
        sources[d_name] = ColumnDataSource(
            {'value': image_data[randint(0, num_images - 1)]})
        plots[row][col] = figure(plot_width=plot_size,
                                 plot_height=plot_size,
                                 x_range=(0, data_size),
                                 y_range=(0, data_size))

        plots[row][col].image('value', source=sources[d_name],
                              x=0, y=0, dw=data_size, dh=data_size,
                              palette="Viridis256")


# Updates
def update():
    global sources
    start_update, end_update = [], []
    start_time = timer()
    for row, row_name in enumerate(names):
        for col, c_name in enumerate(names):
            d_name = row_name + "_" + c_name
            new_data = dict()
            new_data['value'] = image_data[randint(0, num_images - 1)]
            start_update.append(timer())  # ----- TIMER ON
            sources[d_name].data = new_data
            end_update.append(timer())    # ----- TIMER OFF

    print("\n---- \tTotal update time (secs): {:07.5f}".format(timer() - start_time))
    print("+ \tSources update times (secs): {}".format(
           ["{:07.5f}".format(end_update[i] - s) for i,s in enumerate(start_update)]))


# Document
grid = gridplot(plots)
curdoc().add_root(grid)
curdoc().add_periodic_callback(update, 100)

我已经尝试使用只有一个数据源,每个情节都有不同的字段,以及使用stream()方法更新数据(尽管这没有意义,因为整个图像都被替换了),但我没有获得任何性能收益。有人知道我该怎么做来改善这种可视化的交互性吗?我更新图像数据时做错了什么吗?
我猜想瓶颈是图像数据的JSON编码/解码引起的开销,可能会在未来得到改善,因为 Bokeh 的开发人员认识到了这个问题并尝试解决它。不幸的是,看起来修复的时间不会很快到来。
还有其他建议吗? https://github.com/bokeh/bokeh/issues/2204 https://github.com/bokeh/bokeh/pull/5429

2
也许你会在假期收到一个不错的礼物?https://github.com/bokeh/bokeh/pull/5544 - Dav Clark
以防你还在处理这个问题。Bokeh 0.12.9刚刚发布了一个二进制传输数据的版本...应该比你使用的版本快得多。很高兴看到你测量结果的差异。 - renzop
1个回答

0

正如其他人所提到的,已经实现了高效的二进制数组协议。因此,答案是升级到最新版本。

为了完整起见,这里是结果的比较。

使用 0.12.3(原帖中的版本 - 确保使用 tornado < 4.5):

---- 总更新时间(秒):0.14389 + 源更新时间(秒):['0.00943','0.00962','0.01100','0.00908','0.00004','0.00975','0.00984','0.00997','0.00946','0.00926','0.00912','0.00898','0.00900','0.00908','0.00999','0.01014'] ^C

使用 0.12.13(截至目前为止的最新版本):

---- 总更新时间(秒):0.01999 + 源更新时间(秒):['0.00157','0.00231','0.00131','0.00143','0.00114','0.00123','0.00109','0.00118','0.00116','0.00102','0.00113','0.00118','0.00099','0.00099','0.00104','0.00104']
如果将所有图像存储在单个ColumnDataSource的不同(长度为1)列中,并一次性更新,而不是迭代多个不同的数据源,则可能会有更多边际改进。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接