Atualizações eficientes de plotagens de imagens em Bokeh para visualização interativa

Estou tentando criar uma visualização interativa suave de diferentes fatias de uma matriz multidimensional usando Bokeh. Os dados nas fatias mudam de acordo com a interação do usuário e, portanto, precisam ser atualizados várias vezes por segundo. Eu escrevi um aplicativo Bokeh com vários gráficos de imagem pequenos (valores de 64x64) para mostrar o conteúdo das fatias e um retorno de chamada para atualizar o ColumnDataSources quando o usuário interage com o aplicativo. Tudo funciona como esperado, mas não consigo mais de 2 ou 3 quadros por segundo e gostaria de obter pelo menos 10 quadros.

Aqui está um exemplo simplificado do meu código usando 16 imagens com um retorno de chamada periódico a cada 100 ms para simular a interação do usuário. Usando o Bokeh 0.12.3 e o Python 2.7 no Mac e Linux, recebo quase exatamente os mesmos tempos (~ 300ms por quadro) nas duas máquinas.

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)

Tentei usar apenas uma fonte de dados com campos diferentes para cada plotagem e também atualizar os dados com o método stream () (embora não faça sentido, pois toda a imagem está sendo substituída) e ainda não consegui ganho de desempenho. Alguém sabe o que eu poderia fazer para melhorar a interatividade dessa visualização? Estou fazendo algo errado para atualizar os dados da imagem?

Meu palpite é que o gargalo é a sobrecarga causada pela codificação / decodificação JSON dos dados da imagem, o que pode melhorar no futuro, pois parece que os desenvolvedores do Bokeh estão cientes desse problema e estão tentando resolvê-lo. Infelizmente, não parece que a correção está chegando em breve.

https://github.com/bokeh/bokeh/issues/2204

https://github.com/bokeh/bokeh/pull/5429

Alguma outra sugestão?

questionAnswers(1)

yourAnswerToTheQuestion