Commit 25eca69f authored by bru08's avatar bru08
Browse files

New interactions in the UI, selction for color, marker, and save buttons

parent daeea045
......@@ -7,11 +7,11 @@ Change modality modifying 'set_mod' parameter
"""
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, HoverTool, TapTool, Slider
from bokeh.models import RadioButtonGroup, TextInput
from bokeh.models import ColumnDataSource, HoverTool, TapTool, Slider, Button
from bokeh.models import RadioButtonGroup, TextInput, Select, Div, MultiSelect, CheckboxButtonGroup
from bokeh.palettes import brewer, Category10
from bokeh.layouts import column, row
from bokeh.io import show, output_notebook, curdoc, save
from bokeh.layouts import column, row, layout
from bokeh.io import show, output_notebook, curdoc, save, export_svgs, export_png
from pathlib import Path
from DataReader import ScanDataImport, FeatureDataImport
from DimReducer import umap_vector_red
......@@ -20,15 +20,16 @@ import pandas as pd
import os
from PIL import Image
from configs_bviz import data_reader_conf, dim_red_conf
from configs_bviz import plot_bokeh_conf
from configs_bviz import plot_bokeh_conf, plot_mode
from bokeh.server.server import Server
import time
import datetime
def select_image_callback(attr, old, new):
global pos_2_upd
indices = source_tot.selected.indices
indices = source_scatter.selected.indices
scan_mod = subplot_mod_default
# take only first id from single tap selection
if (indices == []) or (indices == None):
......@@ -40,7 +41,7 @@ def select_image_callback(attr, old, new):
index_displayed[pos_2_upd] = idx
print("Index displayed: ", index_displayed)
#
obj = red_df.iloc[idx, :]
obj = working_df.iloc[idx, :]
obj_path = obj['paths']
# obj_mod = obj['Modality']
# scan_mod = 0 if (obj_mod == 'CT')else 1 # to be removed as scan mod can be selected in UI
......@@ -71,7 +72,7 @@ def slider_callback(attr, old, new):
if value != old_slider[i]:
print("slider ", i, 'changed')
idx = index_displayed[i]
obj = red_df.iloc[idx, :]
obj = working_df.iloc[idx, :]
obj_path = obj['paths']
# obj_mod = obj['Modality']
# scan_mod = 0 if (obj_mod == 'CT')else 1
......@@ -96,7 +97,7 @@ def mod_subplot_callback(new):
for i, value in enumerate(sli_values):
if index_displayed[i] != -1: # if the subplot is in use
idx = index_displayed[i]
obj = red_df.iloc[idx, :]
obj = working_df.iloc[idx, :]
obj_path = obj['paths']
tmp = np.load(obj_path)[new, value, :, :] # the first slice over x
# send data change to plot sources
......@@ -136,43 +137,125 @@ def scan_explore_sli_callback(attr, old, new):
print("File not found please check the input")
def mod_scatter_callback(new):
# select data to present in the main scatterplot
#clean the scatterplot
# get new data
if new==0:
print('Feature not implemented yet')
elif new==1:
print('Feature not implemented yet')
elif new==2:
p.scatter(x='x1', y='x2', source=scatter_sources[-1], size=10,
legend_label=factor, alpha=0.0, name='front-layer') # set transparent plot
print("Feature not implemented yet")
else:
print("something strange happende in selecting the scatter plot :/")
# def process_and_save_df(dir_list, info_df_path, mods, set_mod):
def process_and_save_df(conf):
try:
df = pd.read_csv('data_reduced_' + conf['mod_scan_return'] + '.csv')
print('Using saved dim red data')
except FileNotFoundError:
# set environment variables
# set environment variables
print('No saved data, performing full computation.')
# 1 get dataframe with scan data and info
scan_data = ScanDataImport(conf) # (dir_list, info_df_path, mods)
df = scan_data.get_data_and_info()
# 2 modify dataframe, get 2-dim, reduced data and drop raw data col
# red_df = umap_flat_red(df, 'Scan Data', modality=set_mod, n_components=2, n_neighbors=15, metric='euclidean')
umap_vector_red(df, n_components=2, n_neighbors=15, metric='euclidean')
print('Dim red performed.')
df.to_csv('data_reduced_'+ conf['mod_scan_return'] + '.csv')
finally:
return df
def mod_scatter_callback(attr, old, new):
# select data points to present in the main scatterplot
working_df = dfs[new]
source_scatter.data = working_df
div_scatter.text="UMAP_{}_{}_{}".format(new, 'N', 'N')
#
def col_mark_opts_update(attr, old, new):
value_dims = plot_bokeh_conf['val_dims'][new]
color_select_widget.value = marker_select_widget.value = None
color_select_widget.options = marker_select_widget.options = ['---', *value_dims]
def scatter_marker_update(attr, old, new):
if new is not None:
mark_factor = new
working_df = dfs[scatter_data_select.value]
mark_unique = np.unique(working_df[mark_factor])
for i, mark_class in enumerate(mark_unique):
mark_class_mask = (working_df[mark_factor] == mark_class)
working_df.loc[mark_class_mask, 'marker'] = markers[i]
source_scatter.data = working_df
# update color legend homemade
scatter_marker_legend.labels = list(mark_unique)
scatter_marker_legend.active = []
# update title
tmp = div_scatter.text.split("_")
tmp[3] = mark_factor
div_scatter.text = "_".join(tmp)
def scatter_color_update(attr, old, new):
if new is not None:
col_factor = new
working_df = dfs[scatter_data_select.value]
col_unique = np.unique(working_df[col_factor])
colors = Category10[max(3, len(col_unique))]
for i, col_class in enumerate(col_unique):
col_class_mask = (working_df[col_factor] == col_class)
working_df.loc[col_class_mask, 'fill_color'] = colors[i]
source_scatter.data = working_df
# update color legend homemade
scatter_color_legend.labels = list(col_unique)
scatter_color_legend.active = []
# update title
tmp = div_scatter.text.split("_")
tmp[2] = col_factor
div_scatter.text = "_".join(tmp)
def color_legend_update(attr, old, new):
alpha_inactive = 0.2
alpha_active = 0.7
labels = scatter_color_legend.labels
active_labels = [labels[i] for i in new]
working_df = dfs[scatter_data_select.value]
col_factor = color_select_widget.value
working_df.loc[:, 'alpha'] = alpha_active
for label in active_labels:
alphas_mask = (working_df[col_factor] == label)
working_df.loc[alphas_mask, 'alpha'] = alpha_inactive
old_datas = dict(source_scatter.data)
old_datas['alpha'] = working_df['alpha']
source_scatter.data = old_datas
def marker_legend_update(attr, old, new):
alpha_inactive = 0.2
alpha_active = 0.7
labels = scatter_marker_legend.labels
active_labels = [labels[i] for i in new]
working_df = dfs[scatter_data_select.value]
mark_factor = marker_select_widget.value
working_df.loc[:, 'alpha'] = alpha_active
for label in active_labels:
alphas_mask = (working_df[mark_factor] == label)
working_df.loc[alphas_mask, 'alpha'] = alpha_inactive
old_datas = dict(source_scatter.data)
old_datas['alpha'] = working_df['alpha']
source_scatter.data = old_datas
def save_svg_scatter():
# save current scatter plot in svg format
# with name as displayed in UI .svg
# remake the plot
plot_name = div_scatter.text.split('-')[0].strip()
ps = figure(plot_width=700, plot_height=600,
toolbar_location='above', title=plot_name)
ps.scatter(
source=source_scatter, x='x1', y='x2', fill_color='fill_color',
marker='marker', alpha='alpha', size='size'
)
ps.toolbar.logo = None
# save
ps.output_backend = "svg"
filename = plot_name + ".svg"
export_svgs(ps, filename=filename)
if 'Saved svg' not in div_scatter.text:
div_scatter.text = div_scatter.text + ' --> <font color="green">Saved svg!</font>'
def save_png_scatter():
# save current scatter plot in svg format
# with name as displayed in UI .svg
# remake the plot
plot_name = div_scatter.text.split('-')[0].strip()
ps = figure(plot_width=700, plot_height=600,
toolbar_location='above', title=plot_name)
ps.scatter(
source=source_scatter, x='x1', y='x2', fill_color='fill_color',
marker='marker', alpha='alpha', size='size'
)
ps.toolbar.logo = None
# save
filename = plot_name + ".png"
export_png(ps, filename=filename)
if 'Saved png' not in div_scatter.text:
div_scatter.text = div_scatter.text + ' --> <font color="green">Saved png!</font>'
def read_data_cached(conf):
......@@ -215,56 +298,48 @@ dir_list = data_reader_conf['dir_paths']
####
dfs = read_data_cached(plot_bokeh_conf)
red_df = dfs[set_mod]
# add plot settings cols to dataframes
for k, v in dfs.items():
v['marker']='circle'
v['fill_color']='blue'
v['alpha']=0.65
v['size']=9
# plot resources
colors=['blue', 'red', 'yellow', 'orange', 'black', 'grey']
markers = ["triangle", "circle_x", "hex", "square", "asterisk", "circle_cross", "diamond"]
# red_df = dfs[set_mod]
# 3 plot it
pos_2_upd = 0
index_displayed = [-1, -1, -1, -1]
old_slider = [0, 0, 0, 0]
# set colors
num_factor = red_df[color_factor].unique()
print(num_factor)
colors = Category10[max(3, len(num_factor))]
try:
colormap = {factor: colors[i] for i, factor in enumerate(red_df[color_factor].unique())}
except KeyError:
print('check color palettes element')
colors = [colormap[x] for x in red_df[color_factor]]
red_df.loc[:, 'colors'] = colors
print('Colors mixed, ready to do some art-ish')
# make the new scatterplot and update curdoc() root
# TODO control sublot image selection when indices is None
working_df = dfs[set_mod] # take relevant df into consideration
# TODO add widget to select color/marker variables
# TODO use functools.partial for additional arguments in callbacks
col_factor = 'Manufacturer' # choose marker and col wrt to present df (define a widget)
mark_factor = 'Dataset ID'
col_unique = np.unique(working_df[col_factor])
mark_unique = np.unique(working_df[mark_factor])
colors = Category10[max(3, len(col_unique))]
# Set Scatter Plot (main)
p = figure(plot_width=600, plot_height=600, toolbar_location='above')
#
scatter_sources = []
for i, factor in enumerate(red_df[color_factor].unique()):
data_df = red_df.loc[red_df[color_factor] == factor, :]
source_df = ColumnDataSource(data_df.drop(columns=['paths']))
scatter_sources.append(source_df)
for i, factor in enumerate(red_df[color_factor].unique()):
p.scatter(x='x1', y='x2', source=scatter_sources[i],
fill_color='colors', size=10,
legend_label=factor, muted_alpha=0.2, alpha = 0.8,
name = factor)
p.legend.location = "top_left"
p.legend.click_policy = "mute"
p.legend.title = color_factor
source_tot = ColumnDataSource(red_df.drop(columns=['paths']))
scatter_sources.append(source_tot)
p.scatter(x='x1', y='x2', source=scatter_sources[-1], size=10,
legend_label=factor, alpha=0.0, name='front-layer') # set transparent plot
# define tools on scatterplot linked to last transparent layer
hover = HoverTool(names=['front-layer'], tooltips=[
('Manufacturer', '@Manufacturer'),
('Dataset ID', '@{Dataset ID}' ), # use @{ } for field names with spaces
('Subject ID', '@{Subject ID}' )
])
tap = TapTool(names=['front-layer'])
p.add_tools(hover)
p.add_tools(tap)
p.toolbar.logo = None # remove bokeh logo
p = figure(plot_width=700, plot_height=600, toolbar_location='above',
tools='tap,hover')
source_scatter=ColumnDataSource(data=dict(x1=[], x2=[], fill_color=[],
marker=[], alpha=[], size=[]))
p.scatter(
source=source_scatter, x='x1', y='x2', fill_color='fill_color', marker='marker',
alpha='alpha', size='size')
p.toolbar.logo = None
# Unit Logo
p_logo = figure(plot_width=300, plot_height=160, toolbar_location=None)
p_logo = figure(plot_width=350, plot_height=160, toolbar_location=None)
img = Image.open('mpba_logo.png').convert('RGBA')
def_img = np.flipud(np.array(img))
p_logo.image_rgba(image=[def_img], x=0, y=0, dw=600, dh=600)
......@@ -279,10 +354,10 @@ source_img_b = ColumnDataSource(data={'image': [def_img]})
source_img_c = ColumnDataSource(data={'image': [def_img]})
source_img_d = ColumnDataSource(data={'image': [def_img]})
# other plots FOR VISUALIZING SELECTED SCANS
pscan_a = figure(plot_width=150, plot_height=150, toolbar_location=None)
pscan_b = figure(plot_width=150, plot_height=150, toolbar_location=None)
pscan_c = figure(plot_width=150, plot_height=150, toolbar_location=None)
pscan_d = figure(plot_width=150, plot_height=150, toolbar_location=None)
pscan_a = figure(plot_width=200, plot_height=200, toolbar_location=None)
pscan_b = figure(plot_width=200, plot_height=200, toolbar_location=None)
pscan_c = figure(plot_width=200, plot_height=200, toolbar_location=None)
pscan_d = figure(plot_width=200, plot_height=200, toolbar_location=None)
pscan_a.image(image='image', x=0, y=0, dw=64, dh=64, source = source_img_a)
pscan_b.image(image='image', x=0, y=0, dw=64, dh=64, source = source_img_b)
pscan_c.image(image='image', x=0, y=0, dw=64, dh=64, source = source_img_c)
......@@ -302,30 +377,44 @@ slider_text_img = Slider(start=0, end=63, value=0, step=1, title="Slice-x", max_
slider_text_img.on_change('value', scan_explore_sli_callback)
radio_subplot_mod = RadioButtonGroup(
labels=["CT", "PET"], active=0) # return 0 for CT selection 1 for PET
radio_scatter_select = RadioButtonGroup(
labels=["PT", "CT", "Radiomics features"], active=0) # return 0 for CT selection 1 for PET
div_scatter = Div(text="Please choose a dataset below", width=350, height=30)
save_button = Button(label="Export to svg", button_type='success', width=90)
save_button_b = Button(label="Export to png", button_type='success', width=90)
scatter_data_select = Select(title="Scatter Data", value=None, options=['---', *plot_mode])
color_select_widget = Select(title="Color Factor", value=None, options=["---"])
marker_select_widget = Select(title="Marker Factor", value=None, options=["---"])
scatter_color_legend = CheckboxButtonGroup(labels=['---'], active=[])
scatter_marker_legend = CheckboxButtonGroup(labels=['---'], active=[])
# input in the format <subj id> <modality>
type_cube = TextInput(value="HN-CHUM-001 PET", title="Scan ID to visualize")
# Callback setting
source_tot.selected.on_change('indices', select_image_callback)
source_scatter.selected.on_change('indices', select_image_callback)
slider_scan_a.on_change('value', slider_callback)
slider_scan_b.on_change('value', slider_callback)
slider_scan_c.on_change('value', slider_callback)
slider_scan_d.on_change('value', slider_callback)
radio_subplot_mod.on_click(mod_subplot_callback)
radio_scatter_select.on_click(mod_scatter_callback)
save_button.on_click(save_svg_scatter)
save_button_b.on_click(save_png_scatter)
#radio_scatter_select.on_click(mod_scatter_callback)
scatter_data_select.on_change('value', mod_scatter_callback,
col_mark_opts_update)
color_select_widget.on_change('value', scatter_color_update)
marker_select_widget.on_change('value', scatter_marker_update)
scatter_color_legend.on_change('active', color_legend_update)
scatter_marker_legend.on_change('active', marker_legend_update)
type_cube.on_change('value', scan_explore_callback)
# render document with server
doc = curdoc()
doc.add_root( column(
row(p, column(p_logo, radio_scatter_select, type_cube, row(p_ftext,slider_text_img))),
row(
column(pscan_a, slider_scan_a),
column(pscan_b, slider_scan_b),
column(pscan_c, slider_scan_c),
column(pscan_d, slider_scan_d),
radio_subplot_mod
) # end second row
)# end first col
)
\ No newline at end of file
my_layout = layout([
[p, column(p_logo, div_scatter, scatter_data_select, color_select_widget,
scatter_color_legend, marker_select_widget, scatter_marker_legend, row(save_button, save_button_b))],
[column(column(pscan_a, slider_scan_a), column(pscan_b, slider_scan_b)),
column( column(pscan_c, slider_scan_c), column(pscan_d, slider_scan_d)),
column(radio_subplot_mod, type_cube, row(p_ftext,slider_text_img))]
])
doc.add_root(my_layout)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment