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
[ICML'21] Estimate the accuracy of the classifier in various environments through self-supervision

What Does Rotation Prediction Tell Us about Classifier Accuracy under Varying Testing Environments? [Paper] [ICML'21 Project] PyTorch Implementation T

24 Oct 26, 2022
Meta-TTS: Meta-Learning for Few-shot SpeakerAdaptive Text-to-Speech

Meta-TTS: Meta-Learning for Few-shot SpeakerAdaptive Text-to-Speech This repository is the official implementation of "Meta-TTS: Meta-Learning for Few

Sung-Feng Huang 128 Dec 25, 2022
Pytorch implemenation of Stochastic Multi-Label Image-to-image Translation (SMIT)

SMIT: Stochastic Multi-Label Image-to-image Translation This repository provides a PyTorch implementation of SMIT. SMIT can stochastically translate a

Biomedical Computer Vision Group @ Uniandes 37 Mar 01, 2022
A general framework for inferring CNNs efficiently. Reduce the inference latency of MobileNet-V3 by 1.3x on an iPhone XS Max without sacrificing accuracy.

GFNet-Pytorch (NeurIPS 2020) This repo contains the official code and pre-trained models for the glance and focus network (GFNet). Glance and Focus: a

Rainforest Wang 169 Oct 28, 2022
A Python Package for Convex Regression and Frontier Estimation

pyStoNED pyStoNED is a Python package that provides functions for estimating multivariate convex regression, convex quantile regression, convex expect

Sheng Dai 17 Jan 08, 2023
Self-Supervised Pre-Training for Transformer-Based Person Re-Identification

Self-Supervised Pre-Training for Transformer-Based Person Re-Identification [pdf] The official repository for Self-Supervised Pre-Training for Transfo

Hao Luo 116 Jan 04, 2023
Code for paper "Extract, Denoise and Enforce: Evaluating and Improving Concept Preservation for Text-to-Text Generation" EMNLP 2021

The repo provides the code for paper "Extract, Denoise and Enforce: Evaluating and Improving Concept Preservation for Text-to-Text Generation" EMNLP 2

Yuning Mao 18 May 24, 2022
Code for our paper "MG-GAN: A Multi-Generator Model Preventing Out-of-Distribution Samples in Pedestrian Trajectory Prediction" published at ICCV 2021.

MG-GAN: A Multi-Generator Model Preventing Out-of-Distribution Samples in Pedestrian Trajectory Prediction This repository contains the code for the p

Sven 30 Jan 05, 2023
Faune proche - Retrieval of Faune-France data near a google maps location

faune_proche Récupération des données de Faune-France près d'un lieu google maps

4 Feb 15, 2022
an implementation of 3D Ken Burns Effect from a Single Image using PyTorch

3d-ken-burns This is a reference implementation of 3D Ken Burns Effect from a Single Image [1] using PyTorch. Given a single input image, it animates

Simon Niklaus 1.4k Dec 28, 2022
Real life contra a deep learning project built using mediapipe and openc

real-life-contra Description A python script that translates the body movement into in game control. Welcome to all new real life contra a deep learni

Programminghut 7 Jan 26, 2022
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
Everything you need to know about NumPy( Creating Arrays, Indexing, Math,Statistics,Reshaping).

Everything you need to know about NumPy( Creating Arrays, Indexing, Math,Statistics,Reshaping).

1 Feb 14, 2022
Semi-Supervised Signed Clustering Graph Neural Network (and Implementation of Some Spectral Methods)

SSSNET SSSNET: Semi-Supervised Signed Network Clustering For details, please read our paper. Environment Setup Overview The project has been tested on

Yixuan He 9 Nov 24, 2022
Deep learning model for EEG artifact removal

DeepSeparator Introduction Electroencephalogram (EEG) recordings are often contaminated with artifacts. Various methods have been developed to elimina

23 Dec 21, 2022
Class-Balanced Loss Based on Effective Number of Samples. CVPR 2019

Class-Balanced Loss Based on Effective Number of Samples Tensorflow code for the paper: Class-Balanced Loss Based on Effective Number of Samples Yin C

Yin Cui 546 Jan 08, 2023
The codes I made while I practiced various TensorFlow examples

TensorFlow_Exercises The codes I made while I practiced various TensorFlow examples About the codes I didn't create these codes by myself, but re-crea

Terry Taewoong Um 614 Dec 08, 2022
A NSFW content filter.

Project_Nfilter A NSFW content filter. With a motive of minimizing the spreads and leakage of NSFW contents on internet and access to others devices ,

1 Jan 20, 2022
Code for IntraQ, PyTorch implementation of our paper under review

IntraQ: Learning Synthetic Images with Intra-Class Heterogeneity for Zero-Shot Network Quantization paper Requirements Python = 3.7.10 Pytorch == 1.7

1 Nov 19, 2021
Simulation of moving particles under microscopic imaging

Simulation of moving particles under microscopic imaging Install scipy numpy scikit-image tiffile Run python simulation.py Read result https://imagej

Zehao Wang 2 Dec 14, 2021