在Bokeh中使用滑块浏览图像序列

5

我正在尝试使用Bokeh展示一系列图像,并希望能够交互式地滑动或播放这个序列。当我运行我的脚本时,它会显示第一张图片,但是当我拖动滑块或点击播放按钮时,图片没有更新。我的代码如下:

import numpy as np
import bokeh.io
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import  ColumnDataSource, HoverTool, SingleIntervalTicker, Slider, Button, Label
from bokeh.palettes import Spectral1
from skimage.external import tifffile as T


img=T.imread('C:/Users/UserXX/Desktop/Image_Sequence.tif')
sources={}
frames=list(range(0,img.shape[0]))

for frame in frames:
    sources[frame]=ColumnDataSource(data=dict(image=[img[frame,:,:]]))

source1 = sources[0]


p_img = figure(plot_width=694, plot_height=520, x_range=[0,1388], y_range=[0, 1040])
label = Label(x=1.1, y=18, text=str(frames[0]), text_font_size='70pt', text_color='#eeeeee')
p_img.add_layout(label)
p_img.image(image='image', x=[0], y=[0], dw=[1388], dh=[1040],source=source1, palette="Spectral11")

slider = Slider(start=frames[0], end=frames[-1], value=frames[0],step=1, title="Frame")

def animate_update():
    frame = slider.value + 1
    if frame > frames[-1]:
        frame = frames[0]
    slider.value = frame



def slider_update(attr, old, new):
    global source
    global sources
    frame = slider.value
    label.text = str(frame)
    source= sources[frame]



slider.on_change('value', slider_update)


def animate():
    if button.label == '► Play':
        button.label = '❚❚ Pause'
        curdoc().add_periodic_callback(animate_update, 200)
    else:
        button.label = '► Play'
        curdoc().remove_periodic_callback(animate_update)

button = Button(label='► Play', width=60)
button.on_click(animate)

l = layout([[p_img],[slider, button],], sizing_mode='scale_width')
curdoc().add_root(l)
curdoc().title = "Image_Sequence"

我举这个例子: https://github.com/bokeh/bokeh/blob/master/examples/app/gapminder/main.py 我不确定我传递新图像数据到源的方式是否正确。 有什么建议吗?

尝试将 source = sources[frame] 替换为 source1.data = sources[frame].data。由于绘图的源是 source1,因此您需要更改 source1 的相关数据。否则,移动滑块只是定义了 source 而不是正在绘制的 source1。如果这样不起作用,我会尝试复制您的示例。 - Anthonydouc
2个回答

7

您的代码中有几个需要修复或改进的地方,因此最好是学习并模仿这个简化的完整工作示例:

import numpy as np

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure

N = 100

x_ = np.linspace(0, 10, 200)
y_ = np.linspace(0, 10, 200)
z_ = np.linspace(0, 10, N)

x, y, z = np.meshgrid(x_, y_, z_, indexing='xy')

data = np.sin(x+z)*np.cos(y)

source = ColumnDataSource(data=dict(image=[data[:, :, 0]]))

p = figure(x_range=(0, 10), y_range=(0, 10))
p.image(image='image', x=0, y=0, dw=10, dh=10, source=source, palette="Spectral11")

slider = Slider(start=0, end=(N-1), value=0, step=1, title="Frame")

def update(attr, old, new):
    source.data = dict(image=[data[:, :, slider.value]])

slider.on_change('value', update)

curdoc().add_root(column(p, slider))

请注意,在每次滑动更改时都会进行更新,即使是200x200的东西也可能开始变得稍微有些卡顿(至少在我的系统上是这样)。因此,您可能还需要考虑在Bokeh应用程序中进行限制的技术描述。 enter image description here

我模仿了你的例子,并在拖动滑块时更新了我的图像。然而,图像更新非常缓慢,每帧>3秒。每个帧的大小为1040X1388。但是当我使用尺寸小得多的图像(100X100)时,更新速度非常快。我猜测这与此帖子中提到的问题相同: https://dev59.com/95zha4cB1Zd3GeqPNftI?rq=1 - K.drs
我仍然想以逐帧方式可视化变化过程,但速度更快。因此,我不想在Bokeh中使用节流控制。会查看是否有其他方法来解决这个问题。 - K.drs
虽然您的特定用例可能过大,不适合使用 Bokeh,但我认为这回答了“在 Bokeh 中使用滑块浏览图像序列”的一般问题,并将对其他用户有所帮助。如果您能标记它为已接受,我会非常感谢。 - bigreddot
谢谢你的示例,非常有帮助。你说得对,目前对我来说,视频解决方案会更好。 - K.drs
谢谢!如果您想跟进,未来支持无复制二进制传输的问题在这里:https://github.com/bokeh/bokeh/issues/5984 - bigreddot
显示剩余2条评论

2

基于@bigreddot的示例,我成功地实现了拖动滑块或点击按钮更新图像的代码。但是对于较大尺寸的图片,它们的更新速度非常缓慢。

import numpy as np
from bokeh.plotting import figure,ColumnDataSource
from bokeh.io import curdoc
from bokeh.layouts import layout
from bokeh.models import  Slider, Button, Label
from bokeh.palettes import Spectral11
from skimage.external import tifffile as T


img_o=T.imread('C:/Users/UserXX/Desktop/Image_Sequence.tif')

frames=list(range(0,img_o.shape[0]))
img=np.flip(img_o,1)



source = ColumnDataSource(data=dict(image=[img[0,:,:]]))


p_img = figure(x_range=(0,1388), y_range=(0, 1040))
label = Label(x=1.1, y=18, text=str(frames[0]), text_font_size='70pt', text_color='#eeeeee')
p_img.add_layout(label)
im=p_img.image(image='image', x=0, y=0, dw=1388, dh=1040, source=source, palette="Spectral11")

slider = Slider(start=frames[0], end=frames[-1], value=frames[0],step=1, title="Frame")

def animate_update():
    frame = slider.value + 1
    if frame > frames[-1]:
        frame = frames[0]
    slider.value = frame

ds=im.data_source    

def slider_update(attr, old, new):

    new_data=dict()
    frame = slider.value
    label.text = str(frame)
    new_data['image']=[img[frame,:,:]]
    ds.data= new_data



slider.on_change('value', slider_update)


def animate():
    if button.label == '► Play':
        button.label = '❚❚ Pause'
        curdoc().add_periodic_callback(animate_update, 200)
    else:
        button.label = '► Play'
        curdoc().remove_periodic_callback(animate_update)

button = Button(label='► Play', width=60)
button.on_click(animate)

l = layout([[p_img],[slider,button],], sizing_mode='scale_width')
curdoc().add_root(l)
curdoc().title = "Image_sequence"

我主要改变了数据传递到源的方式。另一件事是这行代码不起作用(第一个框架被绘制但未更新)。
im=p_img.image(image='image', x=[0], y=[0], dw=[1388], dh=[1040], source=source, palette="Spectral11")

我更改为:

im=p_img.image(image='image', x=0, y=0, dw=1388, dh=1040, source=source, palette="Spectral11")

图像可以更新。 唯一的区别是参数x、y、dw、dh中的[ ]被删除了。但我不知道为什么这会引起问题。

字形方法参数可以是所有的数据源列名称,也可以是序列文字(在这种情况下,CDS将在幕后为您创建)。它们不能是上面第一行中发生的那些事情的混合 - bigreddot

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