Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

browsing image sequence with a slider in bokeh

Tags:

python

bokeh

I am trying to show a sequence of images using Bokeh and hope to interactively slide through or play the sequence. When I run my script it shows the first image but the image is not updating when I drag the slider or click the play button. My code is as following:

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"

I am using this as an example: https://github.com/bokeh/bokeh/blob/master/examples/app/gapminder/main.py I am not sure if the way that I am pass new image data to the source is correct or not. Any suggestion?

like image 933
K.drs Avatar asked Mar 12 '17 04:03

K.drs


2 Answers

There's a few different things that need fixing or improving in your code, so it's probably just easier to study and emulate this simplified complete working example:

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))

Please note that updates happen on every slider change, and even for 200x200 things can start to get slightly laggy (at least on my system). So you may also want to consider the technique described in Throttling in Bokeh application

enter image description here

like image 70
bigreddot Avatar answered Nov 03 '22 10:11

bigreddot


Base on @bigreddot's example. I got my code work to update images when dragging the slider or click button. Though for images with larger size, they are updating very slowly.

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"

I mainly change the way how the data is passed to the source. Another thing is this line would not work (the first frame is plotted but not updated)

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

After I change to:

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

The images can be updated. The only difference is the [ ] is get rid off from the parameter x, y, dw, dh. But I don't know why it will cause a problem.

like image 23
K.drs Avatar answered Nov 03 '22 09:11

K.drs