散景自定义工具绘制图像的垂直线轮廓

Bokeh custom tool plotting a vertical line profile of an image

我需要绘制图像的轮廓,即绘制矩阵列的值。

并将其实现为拖动工具,它会根据光标在上图上的位置自动更新下图:

基于文档中的 "A New Custom Tool" example,我编写了一个代码,它工作正常但有几个问题:

import numpy as np
import bokeh.plotting as bp
from bokeh.models import CustomJS
from bokeh.layouts import layout, column, row

from bokeh.io import reset_output
from PIL import Image

im = Image.open(r'C:\Documents\image1.jpg')

z = np.array(im)[:,:,0]

from bokeh.core.properties import Instance, Float
from bokeh.io import output_file, show, output_notebook
from bokeh.models import ColumnDataSource, Tool
from bokeh.plotting import figure
from bokeh.util.compiler import TypeScript
from bokeh.layouts import layout, column, row

#output_file("a.html")
#reset_output()


# image vertical profile tool
TS_CODE = """
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
import {ColumnDataSource} from "models/sources/column_data_source"
import {GestureEvent} from "core/ui_events"
import * as p from "core/properties"

export class DrawToolView extends GestureToolView {
  model: DrawTool

  //this is executed when the pan/drag event starts
  _pan_start(_ev: GestureEvent): void {
    this.model.source.data = {x: [], y: []}
  }

  //this is executed on subsequent mouse/touch moves
  _pan(ev: GestureEvent): void {
    const {frame} = this.plot_view

    const {sx, sy} = ev
    if (!frame.bbox.contains(sx, sy))
      return

    const x = frame.xscales.default.invert(sx)
    const y = frame.yscales.default.invert(sy)

    var res = Array(128);
    var rx = Math.round(x);
    for(var i=0; i<128; i++) res[i] = this.model.zz.data["z"][i*225+rx];

    this.model.source.data = {
      x: Array(128).fill(0).map(Number.call, Number), 
      y: res
    };
    this.model.source.change.emit()
  }

  // this is executed then the pan/drag ends
  _pan_end(_ev: GestureEvent): void {}
}

export namespace DrawTool {
  export type Attrs = p.AttrsOf<Props>

  export type Props = GestureTool.Props & {
    source: p.Property<ColumnDataSource>,
    zz: p.Property<ColumnDataSource>,
    width: p.Float
  }
}

export interface DrawTool extends DrawTool.Attrs {}

export class DrawTool extends GestureTool {
  properties: DrawTool.Props

  constructor(attrs?: Partial<DrawTool.Attrs>) {
    super(attrs)
  }

  tool_name = "Drag Span"
  icon = "bk-tool-icon-lasso-select"
  event_type = "pan" as "pan"
  default_order = 12

  static initClass(): void {
    this.prototype.type = "DrawTool"
    this.prototype.default_view = DrawToolView

    this.define<DrawTool.Props>({
      source: [ p.Instance ],
      zz: [ p.Instance ],
      width: [ p.Float ]
    })
  }
}
DrawTool.initClass()
"""

class DrawTool(Tool):
    __implementation__ = TypeScript(TS_CODE)
    source = Instance(ColumnDataSource)
    zz = Instance(ColumnDataSource)
    width = Float()
output_notebook()

source = ColumnDataSource(data=dict(x=[], y=[]))
zz = ColumnDataSource(data=dict(z=z.flatten()))

p1 = figure(plot_width=600, plot_height=200, x_range=(0, 225), y_range=(0, 128), 
            tools=[DrawTool(source=source, zz=zz, width=225)])
im = p1.image(image=[np.flipud(z)], x=0, y=0, dw=225,
              dh=128, palette='Greys256')
p2 = figure(plot_width=600, plot_height=200)
p2.line('x', 'y', source=source)

bp.show(column(p1, p2))

1) 图像尺寸现在是硬编码的:如何将图像尺寸从 python 提供给 js?

2) 图像被传输到客户端两次:首先作为 image() 的参数,然后作为按钮图的来源。如何从 DrawTool 访问图像 "source"?

3) 如果(所有这些代码都在一个 jupyter 单元格中)我 运行 它第二次拒绝在控制台 javascript 错误中绘制任何内容 Model 'DrawTool' does not exist. 运行 它第三次、第四次和以后都可以正常工作。 bokeh 在此错误消息中到底想告诉我什么?

1) Image dimensions are hard-coded now: how do I feed image dimensions from python to js?

2) The image is transferred to the client twice: first as an argument to image(), then as source for the button plot. How to access the image "source" from the DrawTool?

答案是一样的,为要存储在DrawTool上的数据添加更多属性(在Python和JS端)。例如。另一个 Instance 另一个 ColumnDataSource 保存图像数据,以及宽度和高度的整数属性。

3) If (all this code being in one jupyter cell) I run it the second time it refuses to plot anything with a javascript error in console Model 'DrawTool' does not exist. Running it the third time, fourth time and further on works fine. What exactly is bokeh trying to tell me in this error message?

此消息表明 BokehJS 对任何 DrawTool 都一无所知,其原因是,由于笔记本中的工作方式,自定义扩展仅在您调用 output_notebook。因此,您必须在定义自定义扩展 后再次调用 output_notebook 。我不喜欢这种事态,但我们对此无能为力。