I am showing a picture in a figure in bokeh and am using the BoxSelectTool
in order to draw a rectangle.
box_select = BoxSelectTool(callback=callback)
p2 = figure(x_range=(0,700), y_range=(0,500),plot_width=1100,plot_height=1100,tools=[box_select])
p2.image_url( url='url',
x=1, y=1, w=700, h=500, anchor="bottom_left",source=im_src)
rect_source = ColumnDataSource(data=dict(x=[], y=[], width=[], height=[]))
callback = CustomJS(args=dict(rect_source=rect_source), code="""
// get data source from Callback args
var data = rect_source.data;
/// get BoxSelectTool dimensions from cb_data parameter of Callback
var geometry = cb_data['geometry'];
/// calculate Rect attributes
var width = geometry['x1'] - geometry['x0'];
var height = geometry['y1'] - geometry['y0'];
var x = geometry['x0'] + width/2;
var y = geometry['y0'] + height/2;
/// update data source with new Rect attributes
data['x'].push(x);
data['y'].push(y);
data['width'].push(width);
data['height'].push(height);
rect_source.data = data;
rect_source.change.emit();
'''
Now I want to show that image region as cropped in a different, smaller figure, after the rectangle is drawn, without clicking a button or anything:
d2 = figure(x_range=(0,200), y_range=(0,100),plot_width=200,plot_height=100)
d2.image( image='image',
x=1, y=1, dw=100, dh=100, source=img)
img = ColumnDataSource( data=dict(image=[]))
So I need something like this in JS:
tmp_im = cv2.imread('static/' + str(im_nr) + '.jpg')
tmp_im = tmp_im[geometry['y0']:geometry['y1'],geometry['x0']:geometry['x1']]
tmp_im = cv2.cvtColor(tmp_im, cv2.COLOR_BGR2GRAY)
img.data = dict(image=[tmp_im])
How can I do that in JS + bokeh?
I suggest to use the module holoviews
(part of the pyviz ecosystem) for this task, which provides a high-level access to bokeh.
Holoviews
provides so called streams
, which can be used together with DynamicMaps
to generate dynamic figures based on the (changing) values of the stream.
The module panel
(also part of the pyviz ecosystem) can be used to define layouts for visualization.
import numpy as np
import holoviews as hv
from holoviews import opts
from holoviews.streams import BoundsXY
import panel as pn
pn.extension() # loading the panel extension for use with notebook
opts.defaults(opts.Image(tools=['box_select'])) # making sure, that box_select is available
minval, maxval = 0, 200
# x-y data
ls = np.linspace(minval, 10, maxval)
xx, yy = np.meshgrid(ls, ls)
# z-data, e.g. intensity
zz = xx*yy
# min and max, later used to recalibrate the colormapping
zzmin = zz.min()
zzmax = zz.max()
bounds=(0,0, 1,1) # bounds used for the image
im = hv.Image(zz, bounds=bounds)
# stream, xy-data are provided by the box_select-tool
# As start values the same bounds as for the image are used.
box = BoundsXY(bounds=bounds)
# The box-stream is used to draw a rectangle dynamically
# based on the current selection using the box_select-tool.
rect = hv.DynamicMap(
lambda bounds: hv.Bounds(bounds),
streams=[box])
# The box-stream is used to draw an image dynamically
# based on the current selection using the box_select-tool.
im_select = hv.DynamicMap(
lambda bounds: im[bounds[0]:bounds[2],bounds[1]:bounds[3]],
streams=[box])
# Arranging the layout.
# With redim.range we make sure the colormapping uses the original min- and max-values as in 'im',
# and not the min- and max-values from 'im_select'.
layout = pn.Row(im * rect \
+\
im_select.redim.range(z=(zzmin, zzmax)))
layout.app()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With