首页 > 解决方案 > 在散景中使用滑块过滤图像

问题描述

我正在尝试根据其 alpha 值过滤 RGBA 图像,例如,使用 Bokeh 回调。我研究这个图书馆不到一周,所以我对它的了解真的很原始。从 API示例中,我不太了解如何使用这些回调。我实现我想要的方法如下:

### 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)

由于在更改滑块值时我没有看到绘图有任何变化,我想我不知道如何使用这个回调。

标签: pythoncallbackbokeh

解决方案


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

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

  • CustomJS.from_py_func已弃用并将在将来删除,不应使用

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

  • 因此,为了能够在回调中运行真正的 Python 代码,您必须制作一个Bokeh Server 应用程序需要明确的是,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]

推荐阅读