Drawable canvas

I am using an uploaded image in the sidebar. What I have found is that locally, this works fine if I put the drawable-canvas page as the main page. It does not work at all if it’s on any other page.

The second thing I found is that even with the drawable-canvas on the main page, if I deploy to app engine it ends up in an endless cycle of reloading the image and trying to load the drawable-canvas.

I will try to create a minimal example to post.

1 Like

Hello @andfanilo and @Taylor_Hutyra
I am facing the same issue. I added a snippet at Streamlit Discussions and at Github

I have a multipage app with the main entry as main.py and a number of pages titled [01_calibrate.py, 02_label.py, …etc] all located in a pages subfolder placed with the main.py file in an app folder. When I put the above referenced snippet in main.py it works. When I put it in any of the pages, the background_color works (it is initialized to black), but when I load a background_image, the image is not shown and a white background is shown instead (can’t tell if it is a white background or a transparent one resetting the background_color, but it doesn’t relate much, since the problem is that the background_image is not shown).

I appreciate your help.

1 Like

Hmmm that’s odd, I admit I’ve never tried putting a component on a page, I need to try this

Concerning Google App Engine, there’s an issue with components with GCP apparently: [Google Cloud Run] Hasn't received its "streamlit:componentReady" warning on custom modules · Issue #5494 · streamlit/streamlit · GitHub I’m trying to follow. Do you have more details on how you’re deploying on app engine? Unfortunately this is not a deployment I’m very familiar with :confused:

Have a nice day,
Fanilo

I hope we can have a quick fix for the background image… It is driving me nuts :grin:
Thanks @andfanilo

@andfanilo and @Taylor_Hutyra
As you would see on Github version 0.9.0 is working fine with loading the background image. Seems like 0.9.2 messed it up a bit.

Hello @andfanilo and all;
I am building a multipage app that does some stuff. In one page, the user selects image(s) and then I use canvas as an annotation tool for the image. I use canvas 0.9.0 for local work, and it works perfectly. But when I deploy the app to streamlit cloud (using latest canvas “0.9.2” to resolve the background image being opened on cloud), I face a very strange error. The error is variables (not related to canvas) stored in st.session_state do not exist!

Here is a code snippet:

df_columns = ['class_name','truncation','occlusion','alpha','bbox_tl_x','bbox_tl_y','bbox_br_x','bbox_br_y','height','width','length','loc_x','loc_y','loc_z','rot_y']
entry = {'img':'', 'labels': pd.DataFrame(columns=df_columns)}

#defining st.session_state variables
if 'df_anns' not in st.session_state.keys():
    st.session_state.df_anns = []
if 'df_ix' not in st.session_state.keys():
    st.session_state.df_ix = -1
if 'tmp_ds_file_set' not in st.session_state.keys():
    st.session_state.tmp_ds_file_set =set()
if 'tmp_cam_dev_img_id' not in st.session_state.keys():
    st.session_state.tmp_cam_dev_img_id =''

#Input method for user to either use camera or upload files
ip_method = st.radio('Input Method',('Camera','Files'))
if ip_method == 'Camera':
    img_stream = st.camera_input('Capture an image')
    if img_stream is not None:
        if img_stream.id != st.session_state.tmp_cam_dev_img_id:
            img_bytes = img_stream.getvalue()
            cv_img = cv2.imdecode(np.frombuffer(img_bytes, np.uint8), cv2.IMREAD_COLOR)
            new_entry = entry.copy()
            new_entry['img']=cv_img
            st.session_state.df_anns.append(new_entry)
            st.session_state.df_ix += 1
            st.session_state.tmp_cam_dev_img_id = img_stream.id
elif ip_method == 'Files':
    img_streams = st.file_uploader('Select Image', type=['png','jpeg','jpg'], key='dset_fu', accept_multiple_files=True)
    new_file_names = [i.name for i in img_streams]
    new_file_set = set(new_file_names)
    diff_file_set = new_file_set.difference(st.session_state.tmp_ds_file_set)
    for img_stream in img_streams:
        if img_stream.name in diff_file_set:
            img_bytes = img_stream.getvalue()
            cv_img = cv2.imdecode(np.frombuffer(img_bytes, np.uint8), cv2.IMREAD_COLOR)
            new_entry = entry.copy()
            new_entry['img']=cv_img
            st.session_state.df_anns.append(new_entry)
            st.session_state.df_ix += 1
            st.session_state.tmp_ds_file_set.add(img_stream.name)

#Adjust image dimensions to show in canvas
img_org_w = st.session_state.df_anns[st.session_state.df_ix]['img'].shape[1] if st.session_state.df_ix >= 0 else 0
img_org_h = st.session_state.df_anns[st.session_state.df_ix]['img'].shape[0] if st.session_state.df_ix >= 0 else 0
cvs_w = 700
cvs_h = int(img_org_h * (cvs_w/img_org_w)) if st.session_state.df_ix >= 0 else 400
scale_w = img_org_w / cvs_w if st.session_state.df_ix >= 0 else 1
scale_h = img_org_h / cvs_h if st.session_state.df_ix >= 0 else 1
scale_img = cv2.resize(st.session_state.df_anns[st.session_state.df_ix]['img'][:,:,::-1], (cvs_w,cvs_h)) if st.session_state.df_ix >= 0 else None

#convert image to buffer to allow using PIL.Image.open
if st.session_state.df_ix >= 0:
    x = Image.fromarray(scale_img)
    img_obj = BytesIO()
    x.save(img_obj,format='png')
    img_obj.seek(0)
else:
    img_obj=None

canvas_result = st_canvas(fill_color='rgba(0,165,255,0.3)', stroke_width=3, stroke_color='#000000', background_color='#eee',
                            background_image=Image.open(img_obj) if img_obj else None, update_streamlit=True, height=cvs_h, width=cvs_w,
                            drawing_mode='rect', point_display_radius=0, key='canvas'+(str(st.session_state.df_ix) if st.session_state.df_ix>=0 else ''))

This is the error I get from streamlit cloud:

Traceback (most recent call last):

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/state/session_state_proxy.py", line 118, in __getattr__

    return self[key]

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/state/session_state_proxy.py", line 89, in __getitem__

    return get_session_state()[key]

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/state/safe_session_state.py", line 108, in __getitem__

    raise KeyError(key)

KeyError: 'df_ix'


During handling of the above exception, another exception occurred:


Traceback (most recent call last):

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 563, in _run_script

    exec(code, module.__dict__)

  File "/app/monocular_measurement_3d/pages/02_Label_Dataset.py", line 90, in <module>

    if st.session_state.df_ix >= 0:

  File "/home/appuser/venv/lib/python3.9/site-packages/streamlit/runtime/state/session_state_proxy.py", line 120, in __getattr__

    raise AttributeError(_missing_attr_error_message(key))

To make it even more challenging, if I st.write(st.session_state) All the variables, including the df_ix are printed as expected!

Any clues??
Thanks

Your code seems to work for me after adding the missing imports. Certainly no KeyError at all, as expected.

Thanks Goyo for your reply.
Is it working for you on streamlit share? or locally?
For me, locally is working fine, but my problem is with streamlit share.
The full file is at Monocular_Measurement_3D/02_Label_Dataset.py at main · hanisalah/Monocular_Measurement_3D · GitHub
It might not be exactly as the code snippet I shared earlier, as I am actively trying to resolve :smiley:

Both. The most common causes of things not working the same here and there are: different code, different streamlit, different python versions, different versions of the dependencies.

Thanks @Goyo for telling me.
Although it leaves me a bit clueless about what I should do.
Would you care to have a look at the full github repo? May be it is something with my requirements.txt file?
It is my first trial with streamlit, so I would really appreciate your help.

Further testing reveals that if I select multiple images in the file uploader, the key error is not displayed, yet the background image is still not shown.
If i select a single image in the file uploader (although I am defining accept_multiple_files=True), the key error is displayed and the background image is still not shown.
Both of the above scenarios appear only with streamlit share. When I run the app locally, the app is working fine.

I agree with @Goyo that differences could lie in different streamlit, different python versions, different versions of dependencies. But I have very little control on any of those in the case of deployment on streamlit share, let alone that the code as is works with @Goyo but not with me while we both should be running the same thing on streamlit share!

Any clues to this magic!! :smiley:

I forked your repo at this commit:

I was able to replicate the error and this is what I found:

  1. The error shows up in the logs but not in the app. I didn’t think that could even happen so I might have missed it in my previous attempt.

  2. I made the error go away by just writing a string before calling st_canvas().

I find this really weird and I don’t know what to make of it, other that there seems to be something wrong on the Streamlit side.

@Goyo
Thanks for the diligent efforts.
I tried inserting a random string as you were saying, but the error keeps popping and the background images are not shown.
What is strange that although we are using the same source, we are not having the same results!

Hello,
I’m new to Streamlit.
I want to create a static ROI in streamlit, for this, I created a Json file like (saved_state.json) with a rectangle type.
I used initial_drawing to display my rectangle in the application.

The problem is that this rectangle is dynamic, is there any way to make this rectangle static (user can’t change its size, he can just change its position) ?.

Thanks in advance for the answer.

Dear @andfanilo and dear all;
Any clues to this mystery? background image is not showing on streamlit share. I have tried the following:

  1. pinning streamlit version I used while developing (1.12.2).
  2. used the latest streamlit version (1.15)
  3. Passing the background image using Image.fromarray()
  4. Passing the background image using Image.open()
  5. Reorder my imports arbitrary
  6. Move my session_state definitions from the page .py to my main app .py

All the above give the same behavior on streamlit share: The background image is not displayed, and I get errors about my session_state variables after trying to display an image in drawable-canvas.

While on local, using version 0.9.0 for drawable-canvas, everything works as a charm.

I would really appreciate your help.
Thanks

Hey ,
I would like to ask that can we change the background_image later on in a function . If so , please tell me .

@andfanilo
Can I do image recognition with streamlit canvas
I am unable to do it please help!

@andfanilo
Hi, could you explain what the 4 numbers mean in “path”?

@andfanilo
Thanks again for the awesome library.

I have a naive question: after loading a saved state, how do we make the object editable (i.e. transform the saved state object). Thank you.

hey, I want to use the polygon to draw on the image but the user gives me the number of the polygons he wants, so how can I control the number of the drawing on the canvas, and how to get all the points of the polygon closed.
I was using a rect but the polygon I don’t know how

    with col2:
       st.image("Assets/image.jpg",use_column_width=True)

       checked = st.checkbox('Select Zones')
       if max_rects != 0:
        if checked:

                 

            canvas_result = st_canvas(
                fill_color="rgba(255, 165, 0, 0.3)",
                stroke_width=2,
                stroke_color="#000000",
                background_color="#ffffff",
                background_image=Image.open("Assets/image.jpg"),
                update_streamlit=True,
                width=901,
                height=428,
                drawing_mode="rect",
                point_display_radius=1 if "rect" == 'point' else 0,
                key="full_app",
            )

            rect_coords = []
            rect_counter = 0
            if canvas_result.json_data is not None:
                for shape in canvas_result.json_data["objects"]:
                    if shape["type"] == "rect":
                        x1, y1, x2, y2 = shape["left"], shape["top"], shape["left"] + shape["width"], shape["top"] + shape["height"]
                        rect_coords.append([(x1, y1), (x2, y2)])
                        rect_counter += 1
                        if rect_counter >= max_rects:
                            canvas_result.disabled = True
                            break

            if len(rect_coords) > 0:
                data = {"Zone": [], "Top-Right": [], "Bottom-Left": []}
                for i, coords in enumerate(rect_coords):
                    if i >= max_rects:
                        break
                    data["Zone"].append(f"Zone {i+1}")
                    data["Top-Right"].append(coords[1])
                    data["Bottom-Left"].append(coords[0])
                df = pd.DataFrame(data)
                if right_button_clicked:
                    if len(rect_coords) <= max_rects:
                       
                        st.write(df)
                    else:
                        st.error(f"Only {max_rects} rectangles allowed. Please remove some rectangles.")
                        canvas_result.disabled = True
                if left_button_clicked:
                    canvas_result.json_data["objects"] = [obj for obj in canvas_result.json_data["objects"] if obj["type"] != "rect"]
                    canvas_result.disabled = False
            else:
                st.warning("Please draw some rectangles on the canvas.")```
[Uploading: image.png...]()