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
GAN encoders in PyTorch that could match PGGAN, StyleGAN v1/v2, and BigGAN. Code also integrates the implementation of these GANs.

MTV-TSA: Adaptable GAN Encoders for Image Reconstruction via Multi-type Latent Vectors with Two-scale Attentions. This is the official code release fo

owl 37 Dec 24, 2022
OCR Streamlit App is used to extract text from images using python's easyocr, pytorch and streamlit packages

OCR-Streamlit-App OCR Streamlit App is used to extract text from images using python's easyocr, pytorch and streamlit packages OCR app gets an image a

Siva Prakash 5 Apr 5, 2022
This project aim to create multi-label classification annotation tool to boost annotation speed and make it more easier.

This project aim to create multi-label classification annotation tool to boost annotation speed and make it more easier.

null 4 Aug 2, 2022
sequitur is a library that lets you create and train an autoencoder for sequential data in just two lines of code

sequitur sequitur is a library that lets you create and train an autoencoder for sequential data in just two lines of code. It implements three differ

Jonathan Shobrook 305 Dec 21, 2022
A Planar RGB-D SLAM which utilizes Manhattan World structure to provide optimal camera pose trajectory while also providing a sparse reconstruction containing points, lines and planes, and a dense surfel-based reconstruction.

ManhattanSLAM Authors: Raza Yunus, Yanyan Li and Federico Tombari ManhattanSLAM is a real-time SLAM library for RGB-D cameras that computes the camera

null 117 Dec 28, 2022
An image base contains 490 images for learning (400 cars and 90 boats), and another 21 images for testingAn image base contains 490 images for learning (400 cars and 90 boats), and another 21 images for testing

SVM Données Une base d’images contient 490 images pour l’apprentissage (400 voitures et 90 bateaux), et encore 21 images pour fait des tests. Prétrait

Achraf Rahouti 3 Nov 30, 2021
Train neural network for semantic segmentation (deep lab V3) with pytorch in less then 50 lines of code

Train neural network for semantic segmentation (deep lab V3) with pytorch in 50 lines of code Train net semantic segmentation net using Trans10K datas

null 17 Dec 19, 2022
Draw like Bob Ross using the power of Neural Networks (With PyTorch)!

Draw like Bob Ross using the power of Neural Networks! (+ Pytorch) Learning Process Visualization Getting started Install dependecies Requires python3

Kendrick Tan 116 Mar 7, 2022
Streamlit App For Product Analysis - Streamlit App For Product Analysis

Streamlit_App_For_Product_Analysis Здравствуйте! Перед вами дашборд, позволяющий

Grigory Sirotkin 1 Jan 10, 2022
Using Streamlit to host a multi-page tool with model specs and classification metrics, while also accepting user input values for prediction.

Predicitng_viability Using Streamlit to host a multi-page tool with model specs and classification metrics, while also accepting user input values for

Gopalika Sharma 1 Nov 8, 2021
A complete end-to-end demonstration in which we collect training data in Unity and use that data to train a deep neural network to predict the pose of a cube. This model is then deployed in a simulated robotic pick-and-place task.

Object Pose Estimation Demo This tutorial will go through the steps necessary to perform pose estimation with a UR3 robotic arm in Unity. You’ll gain

Unity Technologies 187 Dec 24, 2022
HeatNet is a python package that provides tools to build, train and evaluate neural networks designed to predict extreme heat wave events globally on daily to subseasonal timescales.

HeatNet HeatNet is a python package that provides tools to build, train and evaluate neural networks designed to predict extreme heat wave events glob

Google Research 6 Jul 7, 2022
High performance Cross-platform Inference-engine, you could run Anakin on x86-cpu,arm, nv-gpu, amd-gpu,bitmain and cambricon devices.

Anakin2.0 Welcome to the Anakin GitHub. Anakin is a cross-platform, high-performance inference engine, which is originally developed by Baidu engineer

null 514 Dec 28, 2022
Compute execution plan: A DAG representation of work that you want to get done. Individual nodes of the DAG could be simple python or shell tasks or complex deeply nested parallel branches or embedded DAGs themselves.

Hello from magnus Magnus provides four capabilities for data teams: Compute execution plan: A DAG representation of work that you want to get done. In

null 12 Feb 8, 2022
PyKale is a PyTorch library for multimodal learning and transfer learning as well as deep learning and dimensionality reduction on graphs, images, texts, and videos

PyKale is a PyTorch library for multimodal learning and transfer learning as well as deep learning and dimensionality reduction on graphs, images, texts, and videos. By adopting a unified pipeline-based API design, PyKale enforces standardization and minimalism, via reusing existing resources, reducing repetitions and redundancy, and recycling learning models across areas.

PyKale 370 Dec 27, 2022
This tool uses Deep Learning to help you draw and write with your hand and webcam.

This tool uses Deep Learning to help you draw and write with your hand and webcam. A Deep Learning model is used to try to predict whether you want to have 'pencil up' or 'pencil down'.

lmagne 169 Dec 10, 2022