Skip to content

Instantly share code, notes, and snippets.

@busino
Last active November 10, 2017 19:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save busino/7f59c8c03e13c62983090722dc5de40e to your computer and use it in GitHub Desktop.
Save busino/7f59c8c03e13c62983090722dc5de40e to your computer and use it in GitHub Desktop.
Annotation/Draw/PointCloud with bokeh ... and the frog.
from io import BytesIO
import base64
import numpy
from PIL import Image as PilImage
from bokeh.plotting import figure
from bokeh.models.tools import HoverTool, WheelZoomTool, PanTool
from bokeh import events
from bokeh.layouts import widgetbox, row
from bokeh.models.widgets.buttons import Button
from bokeh.embed import components
from bokeh.io import output_file, save, show
from bokeh.core.properties import Instance, List
from bokeh.models.renderers import Renderer
from bokeh.models.tools import Tap
from bokeh.models.callbacks import CustomJS
from bokeh.models.markers import Cross, Square, SquareCross, CircleCross, Circle
from bokeh.models.sources import ColumnDataSource
from bokeh.models.widgets import Div
IMG_DATA = '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'
JS_CODE = """
import * as p from "core/properties"
import {TapTool, TapToolView} from "models/tools/gestures/tap_tool"
import {SelectTool, SelectToolView} from "models/tools/gestures/select_tool"
import {logger} from "core/logging"
export class TapDeleteToolView extends TapToolView
_keyup: (e) ->
if e.keyCode == 27
for r in @computed_renderers
ds = r.data_source
sm = ds.selection_manager
sm.clear()
else if e.keyCode == 46
if @model.source.selected['1d'].indices.length
source = @model.source
data = source.data
index = source.selected['1d'].indices[0]
data.x.splice(index, 1)
data.y.splice(index, 1)
data.db_id.splice(index, 1)
for r in @computed_renderers
r.data_source.selection_manager.clear()
source.change.emit()
export class TapDeleteTool extends TapTool
default_view: TapDeleteToolView
tool_name: "TapDeleteTool"
event_type: "tap"
icon: "bk-tool-icon-lasso-select"
@define {
source: [ p.Instance ]
}
"""
class TapDeleteTool(Tap):
__implementation__ = JS_CODE
source = Instance(ColumnDataSource)
renderers = List(Instance(Renderer))
def image_label_plot(img_data, components=True):
# For notebook
#from bokeh.io import output_notebook
#output_notebook()
#
# Image
#
#o_img = PilImage.open('1143_Alob_2015_05_18_001.jpg').convert('RGBA')
o_img = PilImage.open(BytesIO(base64.b64decode(img_data))).convert('RGBA')
xdim, ydim = o_img.size
img = numpy.empty((ydim, xdim), dtype=numpy.uint32)
view = img.view(dtype=numpy.uint8).reshape((ydim, xdim, 4))
# Copy the RGBA image into view, flipping it so it comes right-side up
# with a lower-left origin
view[:,:,:] = numpy.flipud(numpy.asarray(o_img))
#
# Display the 32-bit RGBA image
#
plot = figure(plot_width=600, plot_height=500,
x_range=[0,xdim], y_range=[0,ydim],
tools='', logo=None,
title='Bokeh Frog')
plot.image_rgba(image=[img], x=0, y=0, dw=xdim, dh=ydim)
#
# Data Source holding the Point Coordinates
#
source = ColumnDataSource(data=dict(x=[240, 242, 50], y=[97, 158, 120], db_id=[12, 13, 99]))
selection_source = ColumnDataSource(data=dict(db_id=[]))
#
# Double Click for Point Creation
#
dtap_cb = CustomJS(args=dict(source=source),
code="""
console.log('DoubleTap');
var data = source.data;
data['x'].push(cb_obj.x);
data['y'].push(cb_obj.y);
data['db_id'].push(Math.max.apply(null, data['db_id'])+1);
source.change.emit();
""")
plot.js_on_event(events.DoubleTap, dtap_cb)
#
# Show the position of the Point when moving selected point
#
move_cb = CustomJS(args=dict(source=source, selection_source=selection_source),
code="""
if (selection_source.data['db_id'].length & source.selected['1d'].indices.length) {
console.log('Mouse Move');
var index = source.selected['1d'].indices[0];
var data = source.data;
data['x'][index] = cb_obj.x;
data['y'][index] = cb_obj.y;
source.change.emit();
}
""")
plot.js_on_event(events.MouseMove, move_cb)
#
# Select and Release Point on click
# is a little bit hacky
#
sg_cb = CustomJS(args=dict(source=source, selection_source=selection_source),
code="""
console.log('SelectionGeometry');
if (source.selected['1d'].indices.length) {
// when releasing the selection the mousemove is stopped and the points stays on current position
if (selection_source.data['db_id'].length) {
console.log('Clear selection');
selection_source.data['db_id'].length = 0;
source.selection_manager.clear();
source.change.emit();
}
// select the point
else {
console.log('Add new selection');
selection_source.data['db_id'].length=0;
selection_source.data['db_id'].push(source.selected['1d'].indices[0]);
selection_source.change.emit();
}
}
""")
plot.js_on_event(events.SelectionGeometry, sg_cb)
#
# Glyph
#
GLYPH_CLASS = CircleCross#Square# Circle, Cross
FILL_ALPHA = 0.1
LINE_WIDTH = 1.2
LINE_COLOR = 'yellow'
glyph = GLYPH_CLASS(size=24, x='x', y='y',
line_width=LINE_WIDTH, line_color=LINE_COLOR,
fill_alpha=FILL_ALPHA)
cr = plot.add_glyph(source, glyph)
#
# Define Selection and Hover of Glyph
#
cr.selection_glyph = GLYPH_CLASS(line_color='firebrick', line_width=LINE_WIDTH,
fill_alpha=FILL_ALPHA)
cr.nonselection_glyph = GLYPH_CLASS(line_width=LINE_WIDTH, line_color=LINE_COLOR,
fill_alpha=FILL_ALPHA)
cr.hover_glyph = GLYPH_CLASS(line_color='firebrick', line_width=LINE_WIDTH,
fill_alpha=FILL_ALPHA)
#
# Plot Tools
#
# Create A New TapDeleteTool
plot.add_tools(TapDeleteTool(renderers=[cr], source=source))
#plot.add_tools(TapTool(behavior='select', renderers=[cr]))
# Hover to inform the user that the glyph can be selected
plot.add_tools(HoverTool(tooltips=None, renderers=[cr]))
# Scroll
wz = WheelZoomTool()
plot.add_tools(wz)
plot.toolbar.active_scroll = wz
plot.add_tools(PanTool())
#
# Doc Div
#
p = Div(text='''
<h2>HowTo Label the Frog</h2>
<font style="font-size: 1.2em">
<dl>
<dt>New Point:</dt><dd>Double-Click</dd>
<dt>Edit Point:</dt><dd>Click (Select) > Drag > Click (Place)</dd>
<dt>Delete Point:</dt><dd>Click (Select) > Press Delete-Key</dd>
</dl>
<p>Please label all my warts.</p>
<p>And don't call me frog, I'm a <b>toad</b>!</p>
</font>
''')
#
# Layout
#
layout = row(widgetbox(p, width=420), plot)
output_file('bokeh_frog.html')
save(layout)
#show(layout)
#for notebook output
#r = show(layout, notebook_handle=True)
if not components:
return layout
return components(layout)
if __name__ == '__main__':
plot = image_label_plot(IMG_DATA, components=False)
output_file('bokeh_frog.html')
save(plot)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment