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

Я пытаюсь отфильтровать изображение 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)

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

1 Ответ

0 голосов
/ 19 февраля 2019

Во-первых, как мягкое предложение: пожалуйста, не опускайте импорт в вашем примере кода.Самый быстрый способ помочь другим - это иметь возможность запускать пример кода немедленно, напрямую, как есть, что невозможно с неполным кодом

Есть несколько различных проблем с этим кодом, я будупопытайтесь распутать их:

  • CustomJS.from_py_func устарело и будет удалено в будущем, его не следует использовать

  • Даже если этоэто был не тот случай, from_py_func в конечном итоге создает JavaScript код, который запускается в вашем браузере .Он может конвертировать только простой додон Python и не может конвертировать любой код Python, который опирается на настоящие библиотеки Python, такие как Numpy или Pandas.Ваш вызов astype и все необычные нарезки - это функции Numpy , о которых браузер вообще ничего не знает, поэтому этот подход не может работать.

  • Таким образом, чтобы иметь возможность запускать настоящий код 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]
...