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.

Overview

Streamlit - Drawable Canvas

Streamlit component which provides a sketching canvas using Fabric.js.

Streamlit App

PyPI PyPI - Downloads

Buy Me A Coffee

Features

  • Draw freely, lines, circles, boxes and polygons on the canvas, with options on stroke & fill
  • Rotate, skew, scale, move any object of the canvas on demand
  • Select a background color or image to draw on
  • Get image data and every drawn object properties back to Streamlit !
  • Choose to fetch back data in realtime or on demand with a button
  • Undo, Redo or Delete canvas contents
  • Save canvas data as JSON to reuse for another session

Installation

pip install streamlit-drawable-canvas

Example Usage

Copy this code snippet:

import pandas as pd
from PIL import Image
import streamlit as st
from streamlit_drawable_canvas import st_canvas

# 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")
bg_image = st.sidebar.file_uploader("Background image:", type=["png", "jpg"])
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
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,
    height=150,
    drawing_mode=drawing_mode,
    key="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")
    st.dataframe(objects)

You will find more detailed examples on the demo app.

API

st_canvas(
    fill_color: str
    stroke_width: int
    stroke_color: str
    background_color: str
    background_image: Image
    update_streamlit: bool
    height: int
    width: int
    drawing_mode: str
    initial_drawing: dict
    display_toolbar: bool
    key: str
)
  • fill_color : Color of fill for Rect in CSS color property. Defaults to "#eee".
  • stroke_width : Width of drawing brush in CSS color property. Defaults to 20.
  • stroke_color : Color of drawing brush in hex. Defaults to "black".
  • background_color : Color of canvas background in CSS color property. Defaults to "" which is transparent. Overriden by background_image. Changing background_color will reset the drawing.
  • background_image : Pillow Image to display behind canvas. Automatically resized to canvas dimensions. Being behind the canvas, it is not sent back to Streamlit on mouse event. Overrides background_color. Changes to this will reset canvas contents.
  • update_streamlit : Whenever True, send canvas data to Streamlit when object/selection is updated or mouse up.
  • height : Height of canvas in pixels. Defaults to 400.
  • width : Width of canvas in pixels. Defaults to 600.
  • drawing_mode : Enable free drawing when "freedraw", object manipulation when "transform", otherwise create new objects with "line", "rect", "circle" and "polygon". Defaults to "freedraw".
    • On "polygon" mode, double-clicking will remove the latest point and right-clicking will close the polygon.
  • initial_drawing : Initialize canvas with drawings from here. Should be the json_data output from other canvas. Beware: if you try to import a drawing from a bigger/smaller canvas, no rescaling is done in the canvas and the import could fail.
  • display_toolbar : If False, don't display the undo/redo/delete toolbar.

Example:

import streamlit as st
from streamlit_drawable_canvas import st_canvas

canvas_result = st_canvas()
st_canvas(initial_drawing=canvas_result.json_data)
  • display_toolbar : Display the undo/redo/reset toolbar.
  • key : An optional string to use as the unique key for the widget. Assign a key so the component is not remount every time the script is rerun.

Development

Install

  • JS side
cd frontend
npm install
  • Python side
conda create -n streamlit-drawable-canvas python=3.7
conda activate streamlit-drawable-canvas
pip install -e .

Run

Both webpack dev server and Streamlit should run at the same time.

  • JS side
cd frontend
npm run start
  • Python side
streamlit run app.py

Cypress integration tests

  • Install Cypress: cd e2e; npm i or npx install cypress (with --force if cache problem)
  • Start Streamlit frontend server: cd streamlit_drawable_canvas/frontend; npm run start
  • Start Streamlit test script: streamlit run e2e/app_to_test.py
  • Start Cypress app: cd e2e; npm run cypress:open

References

Comments
  • Feature request: tag annotations

    Feature request: tag annotations

    I am annotation regions of interest (ROI) and displaying them in a dataframe for a text extraction app. I would like to auto generate an incrementing tag for the regions, e.g. region_1, region_2 etc and have this displayed inside the annotation and also in the dataframe, so that people can associate the data. Is this currently possible, or a feature request? Cheers and great work!

    image

    opened by robmarkcole 12
  • st_canvas displays cropped background image

    st_canvas displays cropped background image

    Hi! Thanks for the super cool package. As the title says st_canvas displays a cropped background image even though the heightand width of the canvas are set acc. to the image dimensions.

    Here's my code:

        canvas_result = st_canvas(
                stroke_color='#fff',
                stroke_width=stroke_width,
                background_color='#000',
                background_image=image.copy(),
                height = image.size[1],
                width = image.size[0],
                update_streamlit=realtime_update,
                drawing_mode = drawing_mode,
                key = "canvas",
                )
    
    

    Am I doing something wrong?

    opened by hello-fri-end 10
  • Straight line segments

    Straight line segments

    Hi I was wondering if you can point me to where I can adjust code to only produce straight line segments.

    I'm currently going through the repo myself, but I'd appreciate any advice on this!

    Best, Theodore.

    enhancement 
    opened by TheodoreGalanos 9
  • Format of canvas image_data

    Format of canvas image_data

    To further proceed with the canvas result, image_data, I need to know its type to work with opencv. Can you suggest a way that will work for opencv functions as well as displaying the image with st.image()

    opened by deepika2502 8
  • Add minSize to circle.ts and rect.ts to prevent drawing unintentionally tiny shapes

    Add minSize to circle.ts and rect.ts to prevent drawing unintentionally tiny shapes

    I found it might be more reasonable to limit the circle and the rectangle to their minimal size by the width of stroke width. Without the limitation, I have been occasionally added some "very tiny" circles or rectangles on the canvas and didn't aware it.

    I have intermediate Python experience but nothing about web apps nor typescript (very beginner understanding about JS). Therefore I tried to modify the code by guessing the rules from the original code base, and there are probably some bad programming styles or practices.

    Also this is my first PR. I've tried to get rid of the unwanted files (i.e., the folder named test_app/) but got no luck. In short, only circle.ts and rect.ts are relevant in this PR. If there are anything I can study from please also let me know.

    opened by hiankun 7
  • Export canvas as png

    Export canvas as png

    I'm working on a front end for a neural search framework to take a user's drawing and match it with the closest looking Pokemon: https://github.com/alexcg1/jina-streamlit-frontend

    My goal is to have the user draw on the canvas, then click button to export that drawing into a base64 encoded png, which I then pass to Jina via REST API.

    How can I take the np.ndarray generated by the canvas and convert that to a png? I've been trying a few things so far, but all I get is a blank transparent png in the canvas dimensions.

    I'm working on the code in the draw branch: https://github.com/alexcg1/jina-streamlit-frontend/tree/draw

    Thanks for putting together a cool project. I can't wait to get it working!

    opened by alexcg1 7
  • Change Uncompress ImageData to DataURL

    Change Uncompress ImageData to DataURL

    I reduce sending data from ImageData to DataURL. It's very compact and faster to transfer information when the canvas is large.

    for example, I reduce from 7MB to 74kB with the same canvas size

    image
    opened by kapong 6
  • Freedraw brush transparency

    Freedraw brush transparency

    One more feature request; is it possible to be able to alter the transparency of the brush colour when in freedraw mode? Currently, when I alter the hex value to be semi-transparent, the brush colour is always fully opaque. This feature would be particularly useful when we want to identify two overlapping paths in an image.

    opened by jonnyevans3210 6
  • Add Polypath

    Add Polypath

    This code is trying to add polygon/polyline function as mentioned in issue #10 .

    What I have done is to use left clicks to add points and right click to complete the polygon.

    The polygon was drawn by creating a string which was formatted as a fabric.Path. (My reference is Introduction to Fabric.js)

    Because I have no idea about how to control the fabric.Path, so finally my workaround is to create temporary line segments to show the polygon's strokes, and temporary polygons to "update" during the drawing process. All of the temporary objects will be removed when the polygon is completed.

    The path json data shown in the test app is in the form of [['M', 176, 109], ['L', 293, 26], ['L', 356, 75], ['L', 300, 106], ['L', 293, 75], ['z']], for example.

    opened by hiankun 6
  • Fix: correctly handle image URLs

    Fix: correctly handle image URLs

    This PR fixes an issue where image URLs are not correctly handled when a streamlit app is not exposed on the root of the domain. I think it fixes https://github.com/andfanilo/streamlit-drawable-canvas/issues/83

    Basically, we always pass a relative URL on the Python side, then we reconstruct the full URL on the frontend. This works both in development and in deployment, so it removes the need for these lines

    opened by andreaferretti 5
  • Calculate path length for line and freedraw?

    Calculate path length for line and freedraw?

    Hi!

    Great streamlit component! I want to calculate the path length (ie: total distance in pixels) for the freedraw and line tool. For the line tool, it's pretty straightforward by applying the Pythagorean theorem using the width and height provided. But I'm having a little trouble figuring out the best/most efficient way to do this for the freedraw tool.

    Any advice for be greatly appreciated. Thanks!

    Jethro

    opened by JCCKwong 5
  • Canvas doesn't reset when I upload a new image

    Canvas doesn't reset when I upload a new image

    Hi, I am using the streamlit canvas element to build a demo app for a deep learning project and ran into a couple of issues. I've provided the app flow below for additional context.

    1. User uploads an image (the image is read, resized and displayed on the canvas)
    2. Bounding boxes for three objects are drawn by the user using the "rect" drawing mode. (The bounding box values are displayed as a data frame)
    3. Once done, the user clicks on submit to pass the image and bounding box coordinates to the DL model for inference
    4. The model returns a torch.Tensor. Which is converted to a numpy array and displayed using an image element.

    When I upload a new image for testing, the bounding boxes draw for the pervious image are superimposed on the new image. Also, the data frame displayed for the previous image, is also displayed below the new image. I also tried to delete all the entries in the st.sesstion_state, but still does't work.

    I have provide the code below (please ignore the model_run function call) I am not sure what I am doing wrong, any help on this would be much appreciated.

    def main():
        about()
        main_app()
    
    
    def clear_session():
        for key in st.session_state.keys():
            del st.session_state[key]
    
    def main_app():
        drawing_mode = "rect"
        fill_color = "#00000000"
        stroke_width = st.sidebar.slider("Strok width", min_value=1, max_value=3, value=2)
        stroke_color = st.sidebar.color_picker("Stroke color hex code: ", "#FFFB00")
        uploaded_file = st.sidebar.file_uploader("Background image", type=["jpg", "jpeg"])
        realtime_update = st.sidebar.checkbox("Update in real time", True)
        if uploaded_file is not None:
            image = Image.open(uploaded_file)
            image = image.resize((512, 512))
        else:
            image = None
        #create a canvas element
        st.text("Uploaded image")
        canvas_result = st_canvas(
            fill_color= fill_color,
            stroke_width = stroke_width,
            height=512,
            width=512,
            stroke_color = stroke_color,
            drawing_mode=drawing_mode,
            background_image= image,
            update_streamlit= realtime_update,
            key="main_app",
        )
        btn_placeholder = st.empty()    
        btn_placeholder.button('Submit', disabled=True, key="dummy_btn")
        if canvas_result.json_data is not None:
            df = pd.json_normalize(canvas_result.json_data["objects"])
            if len(df) == 0:
                return
            anno_df = df[["top", "left", "width", "height"]]
            st.dataframe(anno_df)
            if len(anno_df) == 3:
                bbox1, bbox2, bbox3 = anno_df.iloc[0].to_list(), anno_df.iloc[1].to_list(), anno_df.iloc[2].to_list()
                btn_placeholder.button('Submit', disabled=False, key="submit-btn")
    
                   
        st.text("Predicted image")
        pred_map_placeholder = st.empty()
        if "submit-btn" in st.session_state and st.session_state["submit-btn"]:
            with st.spinner('Running model inference.'):
                bbox_list = [bbox1, bbox2, bbox3]
                pred_map = model_run(image, bbox_list)
                count = round(pred_map.sum().item())
                pred_map_placeholder.image(pred_map)
                st.write(f"Count: {count}")
            st.success('Done!')
            st.button("Clear", on_click=clear_session, key="clear-btn")
            
              
    if __name__ == "__main__":
        st.set_page_config(page_title="Sample")
        st.title("Sample")
        st.sidebar.subheader("Configuration")
        main()```
    
    opened by ajkailash 0
  • Zoom in/out

    Zoom in/out

    I would like to have a large canvas, but it may not fit on the screen. Can I zoom in on a specific part of the canvas? Or I would like to be able to scroll horizontally.

    opened by mi-spindel 0
  • Static rectangle streamlit-drawable-canvas

    Static rectangle streamlit-drawable-canvas

    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.    

    opened by Arezki93 0
  • Feature request: add troubleshooting documentation

    Feature request: add troubleshooting documentation

    I feel like the repo lacks some troubleshooting documentation on how to use the JSON data from the canvas.

    In my case, I lost some time wondering why transforming a rectangle did not change its width and heigth before stumbling upon the issue #36.

    I think a troubleshooting documentation, or even a documentation on the fields from the JSON data, could be very useful for this repo!

    opened by julienperichon 1
  • Canvas disappearing shortly after loading

    Canvas disappearing shortly after loading

    I noticed a bug, which seem to have been discussed at #79 and on the forum.

    Context: I'm on a multipage streamlit, and I try to display a canvas with a background image (and sized to this image) in order to draw boxes to create new annotations. Just after displaying the canvas, I try to access the JSON data to display drawn boxes and let the user choose the annotation. I noticed that the canvas often disappears <1 second after loading. I don't observe this issue at each loading, but maybe close to 50% of the loadings.

    I tried 2 methods (I did not see that some people retrograded to 0.7.0 at that time):

    • I waited for 1 second just after the canvas loading with time.sleep(1). It's obviously not a good fix, but it resolved the issue in most cases
    • I changed the update_streamlit parameter to False. ~~The problem completely disappeared!~~

    ~~Therefore, it seems that it is linked to some issue during the loading of the canvas' state.~~

    Here's my configuration: python==3.7.12 streamlit==1.12.2 streamlit-drawable-canvas==0.9.0

    Hope this helps!

    EDIT: Seems like changing the update_streamlit parameter to False was not sufficient, even though it appears the problem is less frequent.

    opened by julienperichon 0
Releases(v0.8.0)
  • v0.8.0(Dec 12, 2021)

    • New polygon drawing mode:
      • left-click will add point
      • right click will close polygon
      • double click will remove latest point
    • the Bin button in the toolbar which deletes the canvas content will now empty the history and send back to Streamlit a blank state, even if update_streamlit is set to False.
    • Right-click fires the send canvas data back to Streamlit event for all tools (not only the polygon) even if update_streamlit is set to False.
    Source code(tar.gz)
    Source code(zip)
Owner
Fanilo Andrianasolo
Data Science & BI Specialist @worldline. @streamlit Creator/Moderator. Starting my Content Creator journey
Fanilo Andrianasolo
Like ThreeJS but for Python and based on wgpu

pygfx A render engine, inspired by ThreeJS, but for Python and targeting Vulkan/Metal/DX12 (via wgpu). Introduction This is a Python render engine bui

139 Jan 07, 2023
Knowledgeable Prompt-tuning: Incorporating Knowledge into Prompt Verbalizer for Text Classification

Knowledgeable Prompt-tuning: Incorporating Knowledge into Prompt Verbalizer for Text Classification

DingDing 143 Jan 01, 2023
A Python implementation of global optimization with gaussian processes.

Bayesian Optimization Pure Python implementation of bayesian global optimization with gaussian processes. PyPI (pip): $ pip install bayesian-optimizat

fernando 6.5k Jan 02, 2023
Official PyTorch implementation of Retrieve in Style: Unsupervised Facial Feature Transfer and Retrieval.

Retrieve in Style: Unsupervised Facial Feature Transfer and Retrieval PyTorch This is the PyTorch implementation of Retrieve in Style: Unsupervised Fa

60 Oct 12, 2022
Implementation for the paper 'YOLO-ReT: Towards High Accuracy Real-time Object Detection on Edge GPUs'

YOLO-ReT This is the original implementation of the paper: YOLO-ReT: Towards High Accuracy Real-time Object Detection on Edge GPUs. Prakhar Ganesh, Ya

69 Oct 19, 2022
Tensorflow python implementation of "Learning High Fidelity Depths of Dressed Humans by Watching Social Media Dance Videos"

Learning High Fidelity Depths of Dressed Humans by Watching Social Media Dance Videos This repository is the official tensorflow python implementation

Yasamin Jafarian 287 Jan 06, 2023
Source code and dataset for ACL2021 paper: "ERICA: Improving Entity and Relation Understanding for Pre-trained Language Models via Contrastive Learning".

ERICA Source code and dataset for ACL2021 paper: "ERICA: Improving Entity and Relation Understanding for Pre-trained Language Models via Contrastive L

THUNLP 75 Nov 02, 2022
CoReD: Generalizing Fake Media Detection with Continual Representation using Distillation (ACMMM'21 Oral Paper)

CoReD: Generalizing Fake Media Detection with Continual Representation using Distillation (ACMMM'21 Oral Paper) (Accepted for oral presentation at ACM

Minha Kim 1 Nov 12, 2021
FMA: A Dataset For Music Analysis

FMA: A Dataset For Music Analysis Michaël Defferrard, Kirell Benzi, Pierre Vandergheynst, Xavier Bresson. International Society for Music Information

Michaël Defferrard 1.8k Dec 29, 2022
This is Unofficial Repo. Lips Don't Lie: A Generalisable and Robust Approach to Face Forgery Detection (CVPR 2021)

Lips Don't Lie: A Generalisable and Robust Approach to Face Forgery Detection This is a PyTorch implementation of the LipForensics paper. This is an U

Minha Kim 2 May 11, 2022
PyTorch code for the ICCV'21 paper: "Always Be Dreaming: A New Approach for Class-Incremental Learning"

Always Be Dreaming: A New Approach for Data-Free Class-Incremental Learning PyTorch code for the ICCV 2021 paper: Always Be Dreaming: A New Approach f

49 Dec 21, 2022
Practical tutorials and labs for TensorFlow used by Nvidia, FFN, CNN, RNN, Kaggle, AE

TensorFlow Tutorial - used by Nvidia Learn TensorFlow from scratch by examples and visualizations with interactive jupyter notebooks. Learn to compete

Alexander R Johansen 1.9k Dec 19, 2022
ICLR21 Tent: Fully Test-Time Adaptation by Entropy Minimization

⛺️ Tent: Fully Test-Time Adaptation by Entropy Minimization This is the official project repository for Tent: Fully-Test Time Adaptation by Entropy Mi

Dequan Wang 204 Dec 25, 2022
Official implementation of "Synthetic Temporal Anomaly Guided End-to-End Video Anomaly Detection" (ICCV Workshops 2021: RSL-CV).

Official PyTorch implementation of "Synthetic Temporal Anomaly Guided End-to-End Video Anomaly Detection" This is the implementation of the paper "Syn

Marcella Astrid 11 Oct 07, 2022
In this project, we'll be making our own screen recorder in Python using some libraries.

Screen Recorder in Python Project Description: In this project, we'll be making our own screen recorder in Python using some libraries. Requirements:

Hassan Shahzad 4 Jan 24, 2022
Camview - A CLI-tool used to stream CCTV online footage based on URL params

CamView A CLI-tool used to stream CCTV online footage based on URL params Get St

Finn Lancaster 54 Dec 09, 2022
CS583: Deep Learning

CS583: Deep Learning

Shusen Wang 2.6k Dec 30, 2022
ALL Snow Removed: Single Image Desnowing Algorithm Using Hierarchical Dual-tree Complex Wavelet Representation and Contradict Channel Loss (HDCWNet)

ALL Snow Removed: Single Image Desnowing Algorithm Using Hierarchical Dual-tree Complex Wavelet Representation and Contradict Channel Loss (HDCWNet) (

Wei-Ting Chen 49 Dec 27, 2022
Code for the paper: Sketch Your Own GAN

Sketch Your Own GAN Project | Paper | Youtube Our method takes in one or a few hand-drawn sketches and customizes an off-the-shelf GAN to match the in

677 Dec 28, 2022