Closed tomvars closed 3 years ago
Hello @tomvars !
Something like
import streamlit as st
from streamlit_drawable_canvas import st_canvas
col1, col2, col3 = st.beta_columns(3)
with col1:
canvas_image1 = st_canvas(
fill_color = "rgba(255, 165, 0, 0.3)",
stroke_width = 2,
stroke_color = '#e00',
drawing_mode = "line",
width=100,
key = "1",
)
with col2:
canvas_image2 = st_canvas(
fill_color = "rgba(255, 165, 0, 0.3)",
stroke_width = 2,
stroke_color = '#e00',
drawing_mode = "line",
width=100,
key = "2",
)
with col3:
canvas_image3 = st_canvas(
fill_color = "rgba(255, 165, 0, 0.3)",
stroke_width = 2,
stroke_color = '#e00',
drawing_mode = "line",
width=100,
key = "3",
)
if canvas_image1.image_data is not None:
st.image(canvas_image1.image_data)
would do?
Do take note that for now canvas is not responsive, you'll have to supply fixed width values. I just opened #39 for this, I don't know how exactly I'll manage this yet. Would responsive canvas be a need for you, or do you prefer fixed dimensions anyway?
(also can't do anything about the alignement of canvas in columns for now since this is not yet in Streamlit, so your best bet would be to fiddle with columns yourself by adding empty columns in between to space the canvases manually)
Thank you, that worked perfectly.
Fixed dimensions is fine for my use case. I have a separate issue, i'll include it here for now but let me know if you'd prefer me to close this issue and reopen a new one.
I'm using sliders to travel up and down an image along a particular dimension. I'm trying to separately annotate each slice which is a new background image and have the app remember what had been previously annotated on a particular slice when returning to it. My attempt was to store the json data in a dict indexed by the slice number and use the initial_drawing
argument to populate the previously annotated data. However the initial drawing
does not seem to be working as expected.
here is a reduced example:
import streamlit as st
from streamlit import components
import numpy as np
import pandas as pd
import nibabel as nib
import sys
from PIL import Image
from matplotlib import cm
from streamlit_drawable_canvas import st_canvas
from collections import defaultdict
json_results = {idx: {'version': "3.6.3", "objects": []} for idx in range(256)}
def main():
st.title("Basic viewer")
img_file_buffer = None
img_file_buffer = st.file_uploader("Upload a T1 niftii", type=["nii", "nii.gz"])
if img_file_buffer is None:
st.stop()
else:
with open(img_file_buffer.name, "wb") as f:
f.write(img_file_buffer.getbuffer())
img = nib.load(img_file_buffer.name).get_data()
z_idx = st.slider('z', min_value=0, max_value=img.shape[2],
value=0)
axial_slice = img[:, :, z_idx, ...]
# Specify canvas parameters in application
stroke_width = st.sidebar.slider("Stroke width: ", 1, 25, 3)
stroke_color = st.sidebar.color_picker("Stroke color hex: ")
bg_color = st.sidebar.color_picker("Background color hex: ", "#eee")
drawing_mode = st.sidebar.selectbox(
"Drawing tool:", ("freedraw", "line", "rect", "circle", "transform")
)
realtime_update = st.sidebar.checkbox("Update in realtime", True)
# Create a canvas component
axial_slice = axial_slice/axial_slice.max() # normalize
im = Image.fromarray(np.uint8(cm.Greys_r(np.rot90(np.fliplr(slices[2]), k=3))*255))
canvas_result = st_canvas(
fill_color="rgba(255, 165, 0, 0.3)", # Fixed fill color with some opacity
stroke_width=stroke_width,
stroke_color=stroke_color,
background_color="" if im else bg_color,
background_image=im,
update_streamlit=realtime_update,
height=slices[2].shape[0],
width=slices[2].shape[1],
drawing_mode=drawing_mode,
key="canvas" + "2" + f"_{z_idx}",
initial_drawing=None if not json_results[z_idx]["objects"] else json_results[z_idx]
)
if canvas_result.json_data is not None:
if canvas_result.json_data["objects"] and not json_results[z_idx]["objects"]:
json_results[z_idx] = canvas_result.json_data
st.write(json_results)
if canvas_result.json_data is not None:
st.dataframe(pd.json_normalize(canvas_result.json_data["objects"]))
Might be something to do with the key
argument.
Hmmm without testing, I'd say json_results is reset at every canvas interaction (since at every canvas interaction to rerun the script from top to bottom, you reinitialize json_results
every time right?). Try st.json(json_results)
at the very end and see if values are correctly populated.
If not, you'll need to store your json_results
in state using the infamous "SessionState" gist (https://gist.github.com/tvst/036da038ab3e999a64497f42de966a92), which is preserved across runs (until the official cache is released soon ;) ...)
In case you need examples, there are plenty on the forums like https://discuss.streamlit.io/t/multiselect-based-on-user-input/4188/2 for more info ;)
Hope this helps! I'll test your code tomorrow. Fanilo
Hi @tomvars,
I'll be closing this for now, feel free to reopen if you're working on this again!
Fanilo
Hi,
Thank you for creating this fantastic tool. I'm trying to build a use case for selecting ROIs in 3D radiological images in a Streamlit dashboard.
How could I use the st.beta_columns API to put 3 separate drawable canvases side by side?
Best, Tom