Пользовательский инструмент Bokeh для построения вертикальной линии профиля изображения - PullRequest
0 голосов
/ 14 июня 2019

Мне нужно построить профиль изображения, то есть построить значения столбца матрицы.

И реализовать его как инструмент перетаскивания, который автоматически обновит нижний график на основе положения курсора над верхним графиком:

vertical line profile

На основе «Нового пользовательского инструмента» * Пример 1010 * из документов Я написал код, который отлично работает, но имеет несколько проблем:

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?

3) Если (весь этот код находится в одной ячейке jupyter), я запускаю его во второй раз, когда он отказывается чертить что-либо с ошибкой javascript в консоли Model 'DrawTool' does not exist. Запуск в третий раз, в четвертый раз и далее работает нормально. Что именно Боке пытается сказать мне в этом сообщении об ошибке?

1 Ответ

1 голос
/ 14 июня 2019

1) Размеры изображения теперь жестко запрограммированы: как передать размеры изображения из python в js?

2) Изображение передается клиенту дважды: сначала в качестве аргумента для изображения(), а затем в качестве источника для кнопки графика.Как получить доступ к изображению «источник» из DrawTool?

Ответ на них тот же, добавьте больше свойств (как на стороне Python, так и на стороне JS) для данных, которые вы хотите сохранитьDrawTool.Например, еще один Instance для другого ColumnDataSource, который содержит данные изображения и целочисленные свойства для ширины и высоты.

3) Если (весь этот код находится в одной ячейке jupyter), я запускаю его во второй раз, когда он отказывается что-либо изобразить с ошибкой javascript в консоли. Модель DrawTool не существует.Запуск его в третий раз, в четвертый раз и далее работает нормально.Что именно Bokeh пытается сказать мне в этом сообщении об ошибке?

В этом сообщении говорится, что BokehJS ничего не знает ни о каком DrawTool, и причина этого в том, чтоВ записной книжке все работает, пользовательские расширения регистрируются только при звонке output_notebook.Таким образом, вам придется снова вызывать output_notebook после того, как вы определите пользовательское расширение .Мне не нравится такое положение вещей, но мы ничего не можем с этим поделать.

...