Drawable canvas

@andfanilo, thank you for your prompt response.

I used the demo here:

I guess it’s yet to be updated.

Btw, that icon makes me think it’s download button :grinning_face_with_smiling_eyes:, so I never try it.

2 Likes

Hi @andfanilo , actually I use it locally. By modifying the

update_streamlit=False

flag
I do not see a significant difference. I just find it strange that it has significant lag in contrast with the your demos. Regarding the Session State, is there any example showcasing its use for the canvas?

1 Like

Hello! Thank you for providing Streamlit and guidance on how to use it, this package has really been a game-changer!
My question is about the centering of the images drawn in the drawing pad:
When I use the simple canvas method:

canvas_result = st_canvas(
fill_color=“rgba(255, 165, 0, 0.3)”,
stroke_width=10,
stroke_color=“Black”,
background_color=“White”,
# width = 150,
height= 150,
key=“canvas”,
)

I draw the image in the center; however, when I go to look at the image which I drew, it looks like this:

I should also mention I am using keras to reshape the data into to the size (600,600).

The goal was to make an app in which you could draw faces, and predict the emotions of the drawings; however, I don’t want the image to be split like this if the user decides to draw the image in the center of the drawing pad, which is the most likely place they will draw it.

If I draw slightly off to the right or left I get a centered image:

2 Likes

Hello @Paul_Fentress,

Hmmm that is odd.

  • When you st.image(canvas_result.image_data) what do you see?
  • Can you write the method you are using to reshape the image?
  • If you want you can put width=600 and height=600 to the st_canvas call, so your image already has the correct size.

That looks super cool, looking forward to the result!

Have a nice Streamlitin’ :balloon:
Fanilo

Hey Fanilo,
Thanks for the quick response!
I set the width and height to an already square shape like you suggested:

canvas_result = st_canvas(
stroke_width=10,
background_color=“White”,
width = 300,
height= 300,
)

By doing this I am no longer reshaping the incoming data, and I get nice square images. Here are some examples of what I got after the images were augmented for training purposes, and this is exactly what I was trying to get:

This issue is resolved.

Thank you!

2 Likes

image
Hi @andfanilo , I’m using st_canvas but i cant display correct image (1080x1920). Please help me !!!

Thank you!! ^^

1 Like

Is there a way I can override a drawing with a numpy array? It might be possible to structure it in the initial_drawing JSON form and then use it like this but that seems really complicated.

ty a lot for your awesome work btw! :slight_smile:

2 Likes

Hey @foersterrobert !

Uhhh that looks a bit harder than I expected (I thought rastering an image to SVG).
What type of drawing are you envisioning to import, or type of use case you want to solve?

Fanilo

I created an MNIST-Drawer application once using your component. Now I also added the feature to generate MNIST-Digits using GANs and I would like to “draw” the result on the canvas. So technically it’s just a 28x28 tensor/array/image that I would like to resize to 280x280 pixels.

Thank you a lot btw.

2 Likes

Hi, I am seeing a weird behavior when using the polygon tool. I ask users to draw a polygon on the background image in order to create a mask later on and there seem to be issues with the toolbar:

  1. if i close a polygon and then use ‘back’ arrow to remove a few vertices, it would be reasonable to expect that one could resume the polygon. Instead a new polygon is created, so now we have one that is not closed and one that is closed.
  2. some users tried to use ‘back’ arrow before they closed the polygon, which resulted in quite a mess - at first it looks like the vertices are removed, but when you try to add new ones, all the old ones reappear.
    any idea how difficult it is to solve this?
1 Like

Hi @urska

Yeah the polygon has not yet been extensively tested so your feedback is important. Can you add it to this issue: Add Polyline/Polygon · Issue #10 · andfanilo/streamlit-drawable-canvas · GitHub ?

Will try to have a look soonish.

Thanks again! Have a nice day,
Fanilo

For information,

I released a version 0.9.0 to test something on Streamlit Cloud. Do note that images you use in the canvas won’t work on Streamlit Cloud, so keep using 0.8.0 in the Cloud for now

I’ll do a release note for 0.9.0 when I finish debugging this URL referring in the Cloud.

Thanks for your comprehension,
Fanilo

Hello I’m trying to load a bg image but doesn’t work for me, can you pls help me. Thanks in advance.
repo
deployed app

2 Likes

Hi Fanilo,
Do you have a sample of a saved_state.json with minimum req to draw various square/ ‘rect’.

Hi @angelicaba23

There’s an example for a circle here streamlit-drawable-canvas-demo/saved_state.json at master · andfanilo/streamlit-drawable-canvas-demo (github.com) . The JSON return object (in CanvasResult.json_data) of drawable canvas matches the inner JSON object of the saved_state, so if you draw a square on the canvas and check the json_data of the returned object this should be your saved_state.json for future drawings.

Hope this gets you started!
Fanilo

1 Like

Hi, thanks for your amazing tool.
After I uploaded multiple images, I like to update the canvas with the next image from the list of uploaded files (st.file_uploader module). Accordingly, I like the canvas size match the shape of the corresponding current image. However, my below code only change the image successfully, but the shape of canvas remains as the very first image of the list.
I really appreciate your support.

imported_images = st.file_uploader("Background image:", type=["png", "jpg", "jpeg", "bmp"], accept_multiple_files=True)
        images_counter = 0
        for uploaded_image in imported_images:
            images_counter += 1
            #bytes_image = uploaded_image.read()
            # Save the uploaded images into the specified dir
            with open(os.path.join("statics/imported_dataset", uploaded_image.name), "wb") as f:
                f.write(uploaded_image.getbuffer())

        if imported_images:
            st.success(str(images_counter)+" images uploaded successfully.")
            # Specify canvas parameters in application
            drawing_mode = st.selectbox(
                "Drawing tool:", ("point", "freedraw", "line", "rect", "circle", "transform")
            )
            label_color = (st.sidebar.color_picker("Annotation color: ", "#EA1010") + "77")  # for alpha from 00 to FF
            label = st.text_input("Name your Object", "Default")

            stroke_width = st.sidebar.slider("Stroke width: ", 1, 25, 3)
            if drawing_mode == 'point':
                point_display_radius = st.sidebar.slider("Point display radius: ", 1, 25, 3)
            

            realtime_update = st.sidebar.checkbox("Update in realtime", True)

            if 'count' not in st.session_state:
                st.session_state.count = 0
            # Create a button which will increment the counter
            increment = st.button('Next Image')
            if increment:
                st.session_state.count += 1
                if st.session_state.count == images_counter:
                    st.session_state.count = 0
            # A button to decrement the counter
            decrement = st.button('Previous Image')
            if decrement:
                st.session_state.count -= 1
                if st.session_state.count < 0:
                    st.session_state.count = 0
            st.write('Image number ', st.session_state.count+1)
            imageid = st.session_state.count               
            image_to_label = imported_images[imageid]
            _image = Image.open(image_to_label) 
        if _image:
            width_image, height_image = _image.size
                
            # Create a canvas component
            canvas_result = st_canvas(
                fill_color=label_color,
                stroke_width=stroke_width,
                background_image=_image if imported_images[imageid] else None,
                update_streamlit=realtime_update,
                height=height_image,
                width=width_image,
                drawing_mode="transform" if st.checkbox("Click for edition mode. Double click on label to delete.", False) else drawing_mode,
                point_display_radius=point_display_radius if drawing_mode == 'point' else 0,
                key="annotation_canvas",
            )

            # Do something interesting with the image data and paths
            #if canvas_result.image_data is not None:
                #st.image(canvas_result.image_data)
            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")
                annotations = st.dataframe(objects)
                print(objects)
                print("Filename: ", image_to_label.name)
1 Like

How is it possible to reset the canvas while changing the image displayed as well?

I am trying to use it as a form of segmentation tool but I can’t manage to reset the canvas even if I reset the initial_drawing as follows:

            canvas_answer = st_canvas(
                fill_color="rgba(255, 255, 255, 1.0)",
                stroke_width=2,
                stroke_color="#fc0000",
                background_color="#ffffff",
                background_image=Image.open(tiled_buildings[building_index]),
                update_streamlit=True,
                initial_drawing=None
                if st.session_state["none_initial_drawing"]
                else st.session_state["initial_drawing"],
                drawing_mode="point",
                point_display_radius=2,
                display_toolbar=False,
                key="canvas",
            )
            st.session_state["initial_drawing"] = canvas_answer.json_data
            st.session_state["none_initial_drawing"] = False

Where the st.session_state[“none_initial_drawing”] is set to True during the saving of the segmentation. But I can’t even reach that point since it seems that the canvas_answer.json_data seems to be lagging by one step behind the most recent drawing resulting in a continuous ping pong between the two states.

Is there any way of safely resetting the canvas state?

Thank you very much!

1 Like

hi :wave:!

i’m using this amazing component on my app, but when loading different size image the canvas does no update the size.

I thought i was updating it with the lines 153 and 154, but apparently it is not working.

Any suggestion on how to solve this?

thanks!:smile:

1 Like

Hi @angelicaba23,

If you’re loading a new image into the canvas, i think you’ll need to change the key argument too to reinitialize it :wink:

Fanilo

1 Like



Hi @andfanilo

I’m having issues where the width and height of a drawn element does not change when using the transform tool. The x, y, width and height are set correctly when the rectangle is drawn for the first time, but using the transform tool only results in the x and y values to be updated. The width and height remains the same. Not sure if its an issue on my side. Could you help me out with this?

Thank you!

1 Like