Possible to update a image_rgba on change of a widget?

I see plenty of great examples of how to update glyphs on a plot following an on change of a widget by altering the column data source object. My question is can we update an image in the same way? figure.image_rgba takes a numpy array, not a ColumnDataSource. Is it possible to just replot a new image by specifying a new numpy array when an on_change event is triggered? And if so is there any difference in this functionality compared to glyphs that use a ColumnDataSource? Thanks!

To the best of my knowledge, all the plotting glyphs should be compatible with column data source. With glyphs like image_rgba, you have to assign a list containing your array to a column of your column data source. Currently, this will only work if the column containing your image array is named ‘image’ — this is a closed issue and will be fixed in 0.12.5. From there, updating the plot follows the same pattern or altering the column data source model within a callback.

Here is an example illustrating updating an image_rgba glyph, using bokeh server. Largely borrowing from the example here.

from [bokeh.io](http://bokeh.io) import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
import numpy as np
import pandas as pd
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4
))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255        )
view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255        )
view[i, j, 3] = 255

# assign a list containing our image to column data source column
source = ColumnDataSource({'image'
: [img]})
p = figure(x_range=(0,10), y_range=(0,10
))
p.image_rgba(image='image', x=0, y=0, dw=10, dh=10
, source=source)
slide = Slider(start=0, end=500, step=10, value=0
)
layout = column(p, slide)
# update image on callback
def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img
new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255
    # update column data source
    source.data = {'image'
: [new_image]}
slide.on_change('value'
, update)
curdoc().add_root(layout)

···

On Tue, Feb 14, 2017 at 3:40 PM, [email protected] wrote:

I see plenty of great examples of how to update glyphs on a plot following an on change of a widget by altering the column data source object. My question is can we update an image in the same way? figure.image_rgba takes a numpy array, not a ColumnDataSource. Is it possible to just replot a new image by specifying a new numpy array when an on_change event is triggered? And if so is there any difference in this functionality compared to glyphs that use a ColumnDataSource? Thanks!

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/8478457a-9b5f-463f-9ed5-d8434cbe3b65%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Awesome thanks Tyler! Just for others, one small correction to make your script above work is it seems we still want to start the ColumnDataSource with an empty image, {‘image’:} and then update it with update. Thanks, again!

···

On Wednesday, February 15, 2017 at 11:48:02 AM UTC-8, Tyler Nickerson wrote:

To the best of my knowledge, all the plotting glyphs should be compatible with column data source. With glyphs like image_rgba, you have to assign a list containing your array to a column of your column data source. Currently, this will only work if the column containing your image array is named ‘image’ — this is a closed issue and will be fixed in 0.12.5. From there, updating the plot follows the same pattern or altering the column data source model within a callback.

Here is an example illustrating updating an image_rgba glyph, using bokeh server. Largely borrowing from the example here.

from [bokeh.io](http://bokeh.io) import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
import numpy as np
import pandas as pd
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4
))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255        )
view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255        )
view[i, j, 3] = 255

# assign a list containing our image to column data source column
source = ColumnDataSource({'image'
: [img]})
p = figure(x_range=(0,10), y_range=(0,10
))
p.image_rgba(image='image', x=0, y=0, dw=10, dh=10
, source=source)
slide = Slider(start=0, end=500, step=10, value=0
)
layout = column(p, slide)
# update image on callback
def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img
new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255
    # update column data source
    source.data = {'image'
: [new_image]}
slide.on_change('value'
, update)
curdoc().add_root(layout)

On Tue, Feb 14, 2017 at 3:40 PM, [email protected] wrote:

I see plenty of great examples of how to update glyphs on a plot following an on change of a widget by altering the column data source object. My question is can we update an image in the same way? figure.image_rgba takes a numpy array, not a ColumnDataSource. Is it possible to just replot a new image by specifying a new numpy array when an on_change event is triggered? And if so is there any difference in this functionality compared to glyphs that use a ColumnDataSource? Thanks!

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/8478457a-9b5f-463f-9ed5-d8434cbe3b65%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

I’m glad I could help. Sorry, about the example not working! I removed a line from the callback function that I thought was redundant, but it had the necessary side effect of assigning a copy of img to new_img… Adding the copy method to the first line of the callback function will fix the issue as well. This approach will work with initializing the the column data source with {'image': [img]}. Here’s the modified callback function:

def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img.copy() new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255

    # update column data source
    source.data = {'image': [new_image]}
`

···

On Thu, Feb 16, 2017 at 9:01 AM, [email protected] wrote:

Awesome thanks Tyler! Just for others, one small correction to make your script above work is it seems we still want to start the ColumnDataSource with an empty image, {‘image’:} and then update it with update. Thanks, again!

On Wednesday, February 15, 2017 at 11:48:02 AM UTC-8, Tyler Nickerson wrote:

To the best of my knowledge, all the plotting glyphs should be compatible with column data source. With glyphs like image_rgba, you have to assign a list containing your array to a column of your column data source. Currently, this will only work if the column containing your image array is named ‘image’ — this is a closed issue and will be fixed in 0.12.5. From there, updating the plot follows the same pattern or altering the column data source model within a callback.

Here is an example illustrating updating an image_rgba glyph, using bokeh server. Largely borrowing from the example here.

from [bokeh.io](http://bokeh.io) import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
import numpy as np
import pandas as pd
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4
))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255        )
view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255        )
view[i, j, 3] = 255

# assign a list containing our image to column data source column
source = ColumnDataSource({'image'
: [img]})
p = figure(x_range=(0,10), y_range=(0,10
))
p.image_rgba(image='image', x=0, y=0, dw=10, dh=10
, source=source)
slide = Slider(start=0, end=500, step=10, value=0
)
layout = column(p, slide)
# update image on callback
def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img
new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255
    # update column data source
    source.data = {'image'
: [new_image]}
slide.on_change('value'
, update)
curdoc().add_root(layout)

On Tue, Feb 14, 2017 at 3:40 PM, [email protected] wrote:

I see plenty of great examples of how to update glyphs on a plot following an on change of a widget by altering the column data source object. My question is can we update an image in the same way? figure.image_rgba takes a numpy array, not a ColumnDataSource. Is it possible to just replot a new image by specifying a new numpy array when an on_change event is triggered? And if so is there any difference in this functionality compared to glyphs that use a ColumnDataSource? Thanks!

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/8478457a-9b5f-463f-9ed5-d8434cbe3b65%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/3a8ca68e-f8d4-414a-9312-7112f9c4e574%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Ah ok this makes more sense. Thanks Tyler!

···

On Thursday, February 16, 2017 at 12:19:12 PM UTC-8, Tyler Nickerson wrote:

I’m glad I could help. Sorry, about the example not working! I removed a line from the callback function that I thought was redundant, but it had the necessary side effect of assigning a copy of img to new_img… Adding the copy method to the first line of the callback function will fix the issue as well. This approach will work with initializing the the column data source with {'image': [img]}. Here’s the modified callback function:

def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img.copy() new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255

    # update column data source
    source.data = {'image': [new_image]}
`

On Thu, Feb 16, 2017 at 9:01 AM, [email protected] wrote:

Awesome thanks Tyler! Just for others, one small correction to make your script above work is it seems we still want to start the ColumnDataSource with an empty image, {‘image’:} and then update it with update. Thanks, again!

On Wednesday, February 15, 2017 at 11:48:02 AM UTC-8, Tyler Nickerson wrote:

To the best of my knowledge, all the plotting glyphs should be compatible with column data source. With glyphs like image_rgba, you have to assign a list containing your array to a column of your column data source. Currently, this will only work if the column containing your image array is named ‘image’ — this is a closed issue and will be fixed in 0.12.5. From there, updating the plot follows the same pattern or altering the column data source model within a callback.

Here is an example illustrating updating an image_rgba glyph, using bokeh server. Largely borrowing from the example here.

from [bokeh.io](http://bokeh.io) import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider
from bokeh.plotting import figure
import numpy as np
import pandas as pd
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4
))
for i in range(N):
for j in range(N):
view[i, j, 0] = int(i/N*255        )
view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255        )
view[i, j, 3] = 255

# assign a list containing our image to column data source column
source = ColumnDataSource({'image'
: [img]})
p = figure(x_range=(0,10), y_range=(0,10
))
p.image_rgba(image='image', x=0, y=0, dw=10, dh=10
, source=source)
slide = Slider(start=0, end=500, step=10, value=0
)
layout = column(p, slide)
# update image on callback
def update(attr, old, new):
    # create a new image based on slider widget value
    new_image = img
new_view = new_image.view(dtype=np.uint8).reshape((N, N, 4    ))
new_view[:, :, 0] = (new_view[:, :, 0] + slide.value) % 255
    new_view[:, :, 2] = (new_view[:, :, 2] + slide.value) % 255
    # update column data source
    source.data = {'image'
: [new_image]}
slide.on_change('value'
, update)
curdoc().add_root(layout)

On Tue, Feb 14, 2017 at 3:40 PM, [email protected] wrote:

I see plenty of great examples of how to update glyphs on a plot following an on change of a widget by altering the column data source object. My question is can we update an image in the same way? figure.image_rgba takes a numpy array, not a ColumnDataSource. Is it possible to just replot a new image by specifying a new numpy array when an on_change event is triggered? And if so is there any difference in this functionality compared to glyphs that use a ColumnDataSource? Thanks!

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/8478457a-9b5f-463f-9ed5-d8434cbe3b65%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/3a8ca68e-f8d4-414a-9312-7112f9c4e574%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.

Thanks. Very useful. Here is a version where I control contrast and brightness with 2 sliders.

Any idea about how to update an image from a FileInput widget ?

Here a piece of code to run with bokeh server.

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

import os
import cv2

#=============================================================
alphaLevel = 1.0 # Contrast control
betaLevel = 0 # Brightness control

#=============================================================
file_input = FileInput()
def upload_data(attr, old, new):
    sourceImage.data = {'image': [file_input]}
    # ???????

file_input.on_change('value', upload_data)

#=============================================================
imageFileName = 'myapp2/static/BEL17-2-2_1.66x_milieu0001.png'
img0 = cv2.imread(imageFileName)
img = cv2.flip(img0, 0)     # https://github.com/bokeh/bokeh/issues/1666
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
adjusted = cv2.convertScaleAbs(gray, alpha=alphaLevel, beta=betaLevel)

sourceImage = ColumnDataSource({'image': [adjusted]})

#=============================================================
def callbackSliderAlphaBeta(attr, old, new):
    alphaLevel = sliderAlpha.value
    betaLevel = sliderBeta.value
    print("Contrast level (1.0-3.0): %.1f" %(alphaLevel))
    print("Brightness level: %d" %(betaLevel))
    adjusted = cv2.convertScaleAbs(gray, alpha=alphaLevel, beta=betaLevel)
    sourceImage.data = {'image': [adjusted]}

sliderAlpha = Slider(width=300, start=1.0, end=3.0, value=1.0, step=.1, title="Contrast")     # Contrast control (1.0:3.0)
sliderAlpha.on_change('value', callbackSliderAlphaBeta)

sliderBeta = Slider(width=300, start=-100, end=100, value=0, step=5, title="Brightness")     # Brightness control (-100:100)
sliderBeta.on_change('value', callbackSliderAlphaBeta)

#=============================================================
h, w, c = img.shape
print(w,h)

plot_width = 800
ratio = plot_width/w

plot = figure(
    plot_width = plot_width,
    plot_height = int(h*ratio),
    x_range=(1,w),
    y_range=(1,h),
    tools = "pan,wheel_zoom,box_zoom,reset,save",
    toolbar_location = "above",
    active_scroll = "wheel_zoom",
    sizing_mode = "fixed" ,
    title = os.path.basename(imageFileName)
)

plot.add_layout(LinearAxis(), 'above')
plot.add_layout(LinearAxis(), 'right')

imagePlot = plot.image(image='image', source=sourceImage, x=0, y=0, dw=w, dh=h, palette='Greys256')

#=============================================================
curdoc().add_root(column(plot, sliderAlpha, sliderBeta, file_input))