My slightly longer version with an additional button to turn periodic callbacks on and off for continuous rotations. Both buttons work to increase the image rotation angle.
from base64 import b64decode
import sys
import cv2
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Button
from bokeh.plotting import figure
import imutils # rotate_bound()
T_update = 200 # (ms) browser update rate specified in add_periodic_callback()
current_angle = 0
img_jpeg = b64decode(
'iVBORw0KGgoAAAANSUhEUgAAADIAAAAOBAMAAACfqVJUAAAAHlBMVEUAAAAAAwCKLY3jF17xWyICq65Qlc8js1T2qhmnzTbxGUeMAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAACVSURBVBjTpY89DsIwDIWfRRF087sB6sCMyhHIAbp078TMgroy9gq5LXYSooqOfYqsF3+Jf0BU6c9IcjvI6bUlJiDO78EJIXYVJRXptHEeRyP22PPC7Cy2EUcnKQOmkPrwHIHnpNQNWT5OvLi1oa7/LMB1ouZqqES0CUCXm6+Iz9aER9/5bKyYZZ9w7y91n0KKDjf86QvVrhIPSsvLCwAAAABJRU5ErkJggg==')
arr = np.fromstring(img_jpeg, np.uint8)
frame = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)
orig_img = frame[::-1].copy().view(dtype=np.uint32)
pw = 5*orig_img.shape[0]
ph = 5*orig_img.shape[1]
current_angle = 0
p = figure(aspect_ratio=1, x_range=(0, pw), y_range=(0, ph))
def mk_data(image):
ih, iw, _ = image.shape
return dict(image=[image], dw=[iw], dh=[ih], x=[(pw - iw) / 2], y=[(ph - ih) / 2])
src = ColumnDataSource(data=mk_data(orig_img))
p.image_rgba(image='image', x='x', y='y', dw='dw', dh='dh', source=src)
b1 = Button(label='Rotate once!')
def rotate_img_by_15_deg():
global current_angle
current_angle += 15
img = imutils.rotate_bound(orig_img.view(dtype=np.uint8), current_angle) # use imutils with cv2 format image
src.data = mk_data(img.view(dtype=np.uint32)) # re-create attributes for bokeh using updated image
def callback_button():
global periodic_cb_id
if b2.label == 'Press to: > Play':
print('adding add_periodic_callback()')
b2.label = 'Press to: = Pause'
periodic_cb_id = curdoc().add_periodic_callback(callback_update_data, T_update) # <-- this controls update frequency
else:
print('removing add_periodic_callback()')
b2.label = 'Press to: > Play'
curdoc().remove_periodic_callback( periodic_cb_id )
def callback_update_data():
global current_angle
current_angle += 1
sys.stdout.flush()
img = imutils.rotate_bound(orig_img.view(dtype=np.uint8), current_angle) # use imutils with cv2 formated (.view'ed) image
src.data = mk_data(img.view(dtype=np.uint32)) # re-create all image attributes for bokeh using updated img
b1.on_click(rotate_img_by_15_deg)
b2 = Button(label='Press to: > Play')
b2.on_click(callback_button)
curdoc().add_root(column(p, b1, b2))
Run with:
bokeh serve --show rotate_img_callbacks.py