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
All supplementary material used by me while TA-ing CS3244: Machine Learning

CS3244-Tutorial-Material All supplementary material used by me while TA-ing CS3244: Machine Learning at NUS School of Computing. What is this? I teach

Rishabh Anand 18 Sep 23, 2022
Multimodal Descriptions of Social Concepts: Automatic Modeling and Detection of (Highly Abstract) Social Concepts evoked by Art Images

MUSCO - Multimodal Descriptions of Social Concepts Automatic Modeling of (Highly Abstract) Social Concepts evoked by Art Images This project aims to i

0 Aug 22, 2021
SeqAttack: a framework for adversarial attacks on token classification models

A framework for adversarial attacks against token classification models

Walter 23 Nov 25, 2022
AoT is a system for automatically generating off-target test harness by using build information.

AoT: Auto off-Target Automatically generating off-target test harness by using build information. Brought to you by the Mobile Security Team at Samsun

Samsung 10 Oct 19, 2022
Enigma-Plus - Python based Enigma machine simulator with some extra features

Enigma-Plus Python based Enigma machine simulator with some extra features Examp

1 Jan 05, 2022
The Medical Detection Toolkit contains 2D + 3D implementations of prevalent object detectors such as Mask R-CNN, Retina Net, Retina U-Net, as well as a training and inference framework focused on dealing with medical images.

The Medical Detection Toolkit contains 2D + 3D implementations of prevalent object detectors such as Mask R-CNN, Retina Net, Retina U-Net, as well as a training and inference framework focused on dea

MIC-DKFZ 1.2k Jan 04, 2023
Code for the Image similarity challenge.

ISC 2021 This repository contains code for the Image Similarity Challenge 2021. Getting started The docs subdirectory has step-by-step instructions on

Facebook Research 173 Dec 12, 2022
Implementation of hyperparameter optimization/tuning methods for machine learning & deep learning models

Hyperparameter Optimization of Machine Learning Algorithms This code provides a hyper-parameter optimization implementation for machine learning algor

Li Yang 1.1k Dec 19, 2022
The official implementation of VAENAR-TTS, a VAE based non-autoregressive TTS model.

VAENAR-TTS This repo contains code accompanying the paper "VAENAR-TTS: Variational Auto-Encoder based Non-AutoRegressive Text-to-Speech Synthesis". Sa

THUHCSI 138 Oct 28, 2022
Iranian Cars Detection using Yolov5s, PyTorch

Iranian Cars Detection using Yolov5 Train 1- git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt 2- Dataset ../

Nahid Ebrahimian 22 Dec 05, 2022
A python library for time-series smoothing and outlier detection in a vectorized way.

tsmoothie A python library for time-series smoothing and outlier detection in a vectorized way. Overview tsmoothie computes, in a fast and efficient w

Marco Cerliani 517 Dec 28, 2022
CVPR 2022 "Online Convolutional Re-parameterization"

OREPA: Online Convolutional Re-parameterization This repo is the PyTorch implementation of our paper to appear in CVPR2022 on "Online Convolutional Re

Mu Hu 121 Dec 21, 2022
BC3407-Group-5-Project - BC3407 Group Project With Python

BC3407-Group-5-Project As the world struggles to contain the ever-changing varia

1 Jan 26, 2022
Official implementation for "Low-light Image Enhancement via Breaking Down the Darkness"

Low-light Image Enhancement via Breaking Down the Darkness by Qiming Hu, Xiaojie Guo. 1. Dependencies Python3 PyTorch=1.0 OpenCV-Python, TensorboardX

Qiming Hu 30 Jan 01, 2023
Totally Versatile Miscellanea for Pytorch

Totally Versatile Miscellania for PyTorch Thomas Viehmann [email protected] Thi

Thomas Viehmann 428 Dec 28, 2022
This repository is based on Ultralytics/yolov5, with adjustments to enable polygon prediction boxes.

Polygon-Yolov5 This repository is based on Ultralytics/yolov5, with adjustments to enable polygon prediction boxes. Section I. Description The codes a

xinzelee 226 Jan 05, 2023
VarCLR: Variable Semantic Representation Pre-training via Contrastive Learning

    VarCLR: Variable Representation Pre-training via Contrastive Learning New: Paper accepted by ICSE 2022. Preprint at arXiv! This repository contain

squaresLab 32 Oct 24, 2022
Fully-automated scripts for collecting AI-related papers

AI-Paper-collector Fully-automated scripts for collecting AI-related papers List of Conferences to crawel ACL: 21-19 (including findings) EMNLP: 21-19

Gordon Lee 776 Jan 08, 2023
Exploring Versatile Prior for Human Motion via Motion Frequency Guidance (3DV2021)

Exploring Versatile Prior for Human Motion via Motion Frequency Guidance This is the codebase for video-based human motion reconstruction in human-mot

Jiachen Xu 5 Jul 14, 2022
Deep Learning tutorials in jupyter notebooks.

DeepSchool.io Sign up here for Udemy Course on Machine Learning (Use code DEEPSCHOOL-MARCH to get 85% off course). Goals Make Deep Learning easier (mi

Sachin Abeywardana 1.8k Dec 28, 2022