使用散景中的滑块过滤图像

时间:2019-02-19 18:47:11

标签: python callback bokeh

例如,我正在尝试使用Bokeh回调根据其alpha值过滤RGBA图像。我学习这个库不到一个星期,所以我对它的了解真的很原始。在API example中,我不太了解如何使用这些回调。实现我想要的目标的方法如下:

### RGBA Image
N = 20
img = np.empty((N,N, 4), dtype=np.uint8)
for i in range(N):
    for j in range(N):
        img[i, j, 0] = int(i/N*255)
        img[i, j, 1] = 158
        img[i, j, 2] = int(j/N*255)
        img[i, j, 3] = np.random.randint(1, 255)

mask = img[:, :, 3]
img = np.squeeze(img.view(np.uint32))
source = ColumnDataSource(data=(dict(image=[img],
                                    x=[0],
                                    y=[0],
                                    dw=[10],
                                    dh=[10])))

p = figure(x_range=(0,10), y_range=(0,10))
p.image_rgba(source=source, image='image', x='x', y='y', dw='dw', dh='dh')

### Threshold Slider
def slider_callback(source=source):
    data = source.data
    img = data['image']
    img = img * (mask > cb_obj.value).astype(int)
    source.change.emit();

t_slider = Slider(start=0, end=255, value=255, step=1,
                  title="Threshold", width=140, 
                  callback=CustomJS.from_py_func(slider_callback))

l = layout([t_slider, p])
curdoc().add_root(l)
show(l)

由于在更改滑块值时我看不到绘图中的任何变化,因此我想不到如何使用此回调。

1 个答案:

答案 0 :(得分:2)

首先,作为一个温和的建议:请不要在示例代码中忽略导入。为他人提供帮助的最快方法是能够直接按原样直接运行示例代码,这对于不完整的代码是不可能的

此代码有几个不同的问题,我将尝试解决它们:

  • CustomJS.from_py_func已过时,以后将被删除,不应使用

  • 即使不是这种情况,from_py_func最终也会生成在您的浏览器中运行的 JavaScript 代码。它只能转换简单的普通Python dode,而不能转换任何依赖于诸如Numpy或Pandas之类的真实Python库的python代码。您对astype的调用和所有花哨的切片都是 Numpy 函数,浏览器对此一无所知,因此这种方法可能行不通。

  • 因此,要能够在回调中运行真实的Python代码,您必须制作一个Bokeh Server application,但要明确一点,Bokeh服务器应用必须使用 运行bokeh服务器,即类似于

    bokeh serve --show myapp.py
    
  • 回调的逻辑也不正确。它将新值分配给局部变量img,然后将其丢弃。它不会指定source.data的值,而该值将触发Bokeh根据新数据更新绘图。您将需要回调和连接,例如:

    t_slider = Slider(start=0, end=255, value=255, step=1,
                      title="Threshold", width=140)
    
    def slider_callback(attr, old, new):
        source.data['image'] = [(mask > t_slider.value).astype(int)]
    
    t_slider.on_change('value', slider_callback)
    

    还要注意source.data['image']的列表值,该值必须是图像的列表/数组(因为image可以一次显示多个图像),因此该列表是相关的。

如果进行上述更改并使用bokeh serve运行代码,则在清理滑块时绘图会更新的意义上说“工作”。但是回调逻辑将大多数图像数组设置为零,从而导致空白图。如果不了解您要实际完成的工作,就无法通过回调逻辑提供更多帮助。

编辑:如果您打算使用遮罩以某种方式更新显示的图像,则还应注意,每次在回调中都必须复制原始图像。否则,您将基于原始的第一次在回调运行时进行更新,但是随后的回调将永远修改已修改的版本。即您将需要以下内容:

def slider_callback(attr, old, new):
    newimg = img.copy()
    newimg[(mask > t_slider.value).astype(int)] = 0
    source.data['image'] = [newimg]