Drawable canvas

Hello @Alexander_Hexemer, responded on the GIthub issue but maybe that could help others:

Under the hood, json_data is a FabricJS object exported in JSON with .toObject.
It seems for a scaled object, you will need to apply the scaleX/scaleY factor to the height/width to retrieve the correct values. FabricJS preserves the original dimensions and applies a ratio independently.

st.text(f"Scaled height: {dic['height'] * dic['scaleY']}")
st.text(f"Scaled width: {dic['width'] * dic['scaleX']}")

doesn’t seem like rotating the rectangle adds more complexity :slight_smile: .

@andfanilo Thanks for this great component!

After playing around with this a while, I’m still trying to figure out if there is a way to pass drawings to the canvas or load a state from Streamlit? Has anyone had any success with this?

Hello @tcfkaj, welcome to the community !

Coming soon :wink:

Cheers
Fanilo

1 Like

Hi im not that familiar with the component yet but id like to know if it is possible to have a jpg as background picture so u can draw above it. oh wait i just found it.

thanks

1 Like

is there a way to remove/make invisible the lower pane from the display? and the table that shows the list of shapes drawn ?

No not yet, as it’s the only way for now to redo/undo/delete changes. Could you open an issue in GitHub - andfanilo/streamlit-drawable-canvas: Do you like Quick, Draw? Well what if you could train/predict doodles drawn inside Streamlit? Also draws lines, circles and boxes over background images for annotation. if you need this? Thanks!

Yes, this is post-processing inside the app.py, you can drop the

if canvas_result.json_data is not None:
    st.dataframe(pd.json_normalize(canvas_result.json_data["objects"]))

part to remove the table.

Fanilo :balloon:

Drawable Canvas Release 0.7.0 :pencil2: with some quick fixes, mostly around loading previous drawings.

  • initial_drawing is now used as the initial canvas state. If None provided then we create one on the Python side. This provokes the following changes:
    • a change in background_color will reset the drawing.
    • background_color will override the background color present in initial_drawing.
    • if background_image is present then background_color is removed from st_canvas call.
  • Upgrade Fabric.js to version 4.4.0.
  • Toolbar is now on the bottom left to account for large canvas width.
  • Add argument to make the toolbar invisible.
  • Make stroke_width the minimum size constraint to create a rectangle and circle. Thanks hiankun for the PR!

App: https://share.streamlit.io/andfanilo/streamlit-drawable-canvas-demo/master/app.py
Code: GitHub - andfanilo/streamlit-drawable-canvas: Do you like Quick, Draw? Well what if you could train/predict doodles drawn inside Streamlit? Also draws lines, circles and boxes over background images for annotation.

There’s a kinda breaking change in that changing background_color now resets drawing, so do tell me if this impacts you.

Happy Streamli-drawing :balloon: ,
Fanilo

Weekend tip!

With a SVG path library like svgpathtools, you can load and manipulate the paths you draw on the canvas. For example you can compute the length in pixels of any drawn arc on the canvas!

If you’re interested in comparing distances by drawing on an image, I’ve added the code snippet to compute arc lengths in the demo app.

Happy drawing in Streamlit :balloon:

1 Like

Hey there!
It seems like something happened to the Streamlit Drawable Canvas code today. I use streamlit-drawable-canvas for educational purposes.
Earlier today, the canvas was working for my program (it’s been working perfectly fine for the past several months). I define the canvas as follows:

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= bg_color,
    # background_image=Image.open(bg_image) if bg_image else None,
    update_streamlit=realtime_update,
    width = 500,
    height= 500,
    drawing_mode=drawing_mode,
    key="canvas",
)

and then this afternoon, I get this error:

2021-07-01 23:00:34.341 Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/streamlit/script_runner.py", line 349, in _run_script
    exec(code, module.__dict__)
  File "/content/app.py", line 37, in <module>
    key="canvas"
  File "/usr/local/lib/python3.7/dist-packages/streamlit_drawable_canvas/__init__.py", line 138, in st_canvas
    w = component_value["width"]
TypeError: string indices must be integers

Do you know if any updates might have caused this? I haven’t touched this code in a while and then it broke.

Hi @ejyuen !

Hmmm thanks for reporting. I created an issue on github: TypeError: string indices must be integers · Issue #57 · andfanilo/streamlit-drawable-canvas · GitHub.

If possible could we continue the discussion there :slight_smile: ? Most notably I’d love to know your version of streamlit-drawable-canvas, if you made a recent upgrade and if it works again by downgrading.

Thanks a lot,
Fanilo

EDIT: linked to component value of type dict is not properly marshalled with streamlit==0.84.0 · Issue #3507 · streamlit/streamlit · GitHub, @ejyuen the bug has been introduced by Streamlit 0.84, if you downgrade to 0.83 it should work. Do you need Drawable Canvas to work with 0.84 immediately?

Hey everyone, this should be fixed with Streamlit 0.84.1 streamlit/streamlit#3507 (comment). I’ve tested on the demo app and it works well, so it should work on your side too!

Fanilo

Hey @andfanilo
I have a small issue I m trying to draw a polygon and use it as the ROI for detections in a video but I am not sure how exactly can I extract the points from the table as I am getting the following error

for this code st.dataframe(pd.json_normalize(canvas_result.json_data["objects"])) in drawing a polygon

ArrowTypeError: ("Expected bytes, got a 'int' object", 'Conversion failed for column path with type object')

2 Likes

I’ve also had the same issue with the st.dataframe function.

Hey @Pavankunchala @aswincandra

Sorry for taking so long to answer, holidays :slight_smile:

Looks like a Streamlit Arrow upgrade problem, does it work if you downgrade Streamlit before version 0.85?

Fanilo

1 Like

TypeError: a bytes-like object is required, not ‘CanvasResult’

I am using 0.88 version

# Create a canvas component
image_data = st_canvas(
    b_width, b_color, bg_color, height=150, drawing_mode=drawing_mode, key="canvas"
)

# Do something interesting with the image data
if image_data is not None:
    st.image(image_data)

and the code is:

1 Like

I have a small issue I m trying to draw a polygon and use it as the ROI for detections in a video but I am not sure how exactly can I extract the points from the table as I am getting the following error for this code st.dataframe(pd.json_normalize(canvas_result.json_data["objects"])) in drawing a polygon ArrowTypeError: ("Expected bytes, got a 'int' object", 'Conversion failed for column path with type object')
I think the explanation is https://discuss.streamlit.io/t/all-in-on-apache-arrow/15342/3:

        if canvas_result.json_data is not None:
            objects = pd.json_normalize(canvas_result.json_data["objects"])
            for col in objects.select_dtypes(include=['object']).columns:
                objects[col] = objects[col].astype("str")
            st.dataframe(objects)
  • or change the serialization from arrow to legacy, by putting in .streamlit/config.toml the following:
[global]
dataFrameSerialization = "legacy"

@BeyondMyself the image is stored in image_data.image_data, the image_data variable contains 2 properties: image_data for the image and json_data for the canvas JSON representation.

Hi, for some reason when I use the drawable canvas, a noticeable lag occurs while updating in real time.

ezgif-2-7937ef0ff193

Could I optimize the code in a way so that such a lag is not noticed?

Hey @watchdog welcome to the forums :slight_smile:

  • Are you using this locally or on the demo app (https://share.streamlit.io/andfanilo/streamlit-drawable-canvas-demo/master/app.py)? Every drawing executes a full Streamlit script rerun which on larger apps can take some time depending on the specs of your machine (I see about half the lag on Streamlit Cloud and a third of the lag on my personal machine). You can disable the Streamlit rerun for every stroke by passing update_streamlit=False and updating by pushing the arrow in the toolbar if the lag really puts you off (actually you could even run custom code with Session State to pass update_streamlit to True every 5-10 strokes if you want).
  • Also right now I don’t know how exactly I could optimize the code more, maybe there’s an iframe remounting I can optimize or some other advanced React useEffect that is not well implemented, but I’d need to dig deep which I won’t be able to do immediately :frowning:

Happy drawing :balloon:
Fanilo

Hi man, thank you for your awesome work!
When I’m trying to run your default demo code, I got exception info like this:
StreamlitAPIException : (“Expected bytes, got a ‘float’ object”, ‘Conversion failed for column path with type object’)
and it occurred here:
st.dataframe(pd.json_normalize(canvas_result.json_data[“objects”]))
Plus, when I uncheck update in realtime, there’s no button called “send to streamlit”.
Can you take a look?
Btw, I’m using Steamlit 1.1.0

Hello @Sunix_Liu ! Thanks for the kind words

  • On the StreamlitAPIException, I think the correct code is
if canvas_result.json_data is not None:
    objects = pd.json_normalize(canvas_result.json_data["objects"]) # need to convert obj to str because PyArrow
    for col in objects.select_dtypes(include=['object']).columns:
        objects[col] = objects[col].astype("str")
    st.dataframe(objects)

because Streamlit uses Arrow to serialize dataframes, everything has to be cast out of objects. Is there a location I forgot to change this?

  • For the update in realtime part, the button is circled in red in the following

image

Do tell me if you have a better idea for a clearer icon :slight_smile:

Happy drawing :balloon:
Fanilo