Reputation: 9844
I'm trying to filter a RGBA image based on its alpha value, for example, using Bokeh callbacks. I've been studying this library for less than one week so my knowledge about it is really raw. From the API example I didn't quite get how to work with these callbacks. My approach to achieve what I wanted was the following:
### RGBA Image
N = 20
img = np.empty((N,N, 4), dtype=np.uint8)
for i in range(N):
for j in range(N):
img[i, j, 0] = int(i/N*255)
img[i, j, 1] = 158
img[i, j, 2] = int(j/N*255)
img[i, j, 3] = np.random.randint(1, 255)
mask = img[:, :, 3]
img = np.squeeze(img.view(np.uint32))
source = ColumnDataSource(data=(dict(image=[img],
x=[0],
y=[0],
dw=[10],
dh=[10])))
p = figure(x_range=(0,10), y_range=(0,10))
p.image_rgba(source=source, image='image', x='x', y='y', dw='dw', dh='dh')
### Threshold Slider
def slider_callback(source=source):
data = source.data
img = data['image']
img = img * (mask > cb_obj.value).astype(int)
source.change.emit();
t_slider = Slider(start=0, end=255, value=255, step=1,
title="Threshold", width=140,
callback=CustomJS.from_py_func(slider_callback))
l = layout([t_slider, p])
curdoc().add_root(l)
show(l)
As I don't see any change in the plot while changing the slider value, I think I didn't get how to use this callback.
Upvotes: 0
Views: 416
Reputation: 34568
First, as a gentle suggestion: please do not omit imports in your example code. The fastest way for others to help is to be able to run example code immediately, directly, as-is, which is not possible with incomplete code
There are several different issues with this code, I will try to untangle them:
CustomJS.from_py_func
is deprecated and will be removed in the future, it should not be used
Even if this was not the case, from_py_func
ultimately produces JavaScript code that runs in your brower. It can only convert simple plain Python dode, and can't convert any python code that relies on real Python libraries like Numpy or Pandas. Your call to astype
and all the fancy slicing are Numpy functions that the browser knows nothing at all about, so this approach cannot possibly work.
So to be able to run real Python code in callbacks, you will have to make a Bokeh Server application Just to be clear, Bokeh server apps have to be run with the bokeh server, i.e. similar to
bokeh serve --show myapp.py
The logic of the callback is also not right. It assigns a new value to a local variable img
and then throws it away. It does not uopdate the value of source.data
which is what would trigger Bokeh to update the plot based on new data. You would need a callback and hookup more like:
t_slider = Slider(start=0, end=255, value=255, step=1,
title="Threshold", width=140)
def slider_callback(attr, old, new):
source.data['image'] = [(mask > t_slider.value).astype(int)]
t_slider.on_change('value', slider_callback)
Note also the list value for source.data['image']
The value needs to be a list/array of images (since image
can display multiple images at once), so the list is relevant.
If if make the changes above and run your code with bokeh serve
, then things "work" in the sense that the plot updates when the slider is scrubbed. But the callback logic sets most of the image array to zero, resulting in a blank plot. Without knowing more about what you are trying to actually accomplish, it's not possible to help more with the callback logic.
Edit: If your intent is to use the mask to update the image shown somwehow, you should also be aware that you will have to copy the original image each time in the callback. Otherwise you will update based on the original the first time the callback runs, but then subsequent callbacks will be modifying the already modified version, forever. I.e. you'll need something like this:
def slider_callback(attr, old, new):
newimg = img.copy()
newimg[(mask > t_slider.value).astype(int)] = 0
source.data['image'] = [newimg]
Upvotes: 2