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
This repo contains source code and materials for the TEmporally COherent GAN SIGGRAPH project.

TecoGAN This repository contains source code and materials for the TecoGAN project, i.e. code for a TEmporally COherent GAN for video super-resolution

Nils Thuerey 5.2k Jan 02, 2023
A machine learning library for spiking neural networks. Supports training with both torch and jax pipelines, and deployment to neuromorphic hardware.

Rockpool Rockpool is a Python package for developing signal processing applications with spiking neural networks. Rockpool allows you to build network

SynSense 21 Dec 14, 2022
[AAAI 2021] MVFNet: Multi-View Fusion Network for Efficient Video Recognition

MVFNet: Multi-View Fusion Network for Efficient Video Recognition (AAAI 2021) Overview We release the code of the MVFNet (Multi-View Fusion Network).

Wenhao Wu 114 Nov 27, 2022
Losslandscapetaxonomy - Taxonomizing local versus global structure in neural network loss landscapes

Taxonomizing local versus global structure in neural network loss landscapes Int

Yaoqing Yang 8 Dec 30, 2022
Easy to use Audio Tagging in PyTorch

Audio Classification, Tagging & Sound Event Detection in PyTorch Progress: Fine-tune on audio classification Fine-tune on audio tagging Fine-tune on s

sithu3 15 Dec 22, 2022
FinGAT: A Financial Graph Attention Networkto Recommend Top-K Profitable Stocks

FinGAT: A Financial Graph Attention Networkto Recommend Top-K Profitable Stocks This is our implementation for the paper: FinGAT: A Financial Graph At

Yu-Che Tsai 64 Dec 13, 2022
Traditional deepdream with VQGAN+CLIP and optical flow. Ready to use in Google Colab

VQGAN-CLIP-Video cat.mp4 policeman.mp4 schoolboy.mp4 forsenBOG.mp4

23 Oct 26, 2022
Large dataset storage format for Pytorch

H5Record Large dataset ( 100G, = 1T) storage format for Pytorch (wip) Support python 3 pip install h5record Why? Writing large dataset is still a

theblackcat102 43 Oct 22, 2022
Towards Representation Learning for Atmospheric Dynamics (AtmoDist)

Towards Representation Learning for Atmospheric Dynamics (AtmoDist) The prediction of future climate scenarios under anthropogenic forcing is critical

Sebastian Hoffmann 4 Dec 15, 2022
Offical code for the paper: "Growing 3D Artefacts and Functional Machines with Neural Cellular Automata" https://arxiv.org/abs/2103.08737

Growing 3D Artefacts and Functional Machines with Neural Cellular Automata Video of more results: https://www.youtube.com/watch?v=-EzztzKoPeo Requirem

Robotics Evolution and Art Lab 51 Jan 01, 2023
Detectron2-FC a fast construction platform of neural network algorithm based on detectron2

What is Detectron2-FC Detectron2-FC a fast construction platform of neural network algorithm based on detectron2. We have been working hard in two dir

董晋宗 9 Jun 06, 2022
This repository contains the code for the paper 'PARM: Paragraph Aggregation Retrieval Model for Dense Document-to-Document Retrieval' published at ECIR'22.

Paragraph Aggregation Retrieval Model (PARM) for Dense Document-to-Document Retrieval This repository contains the code for the paper PARM: A Paragrap

Sophia Althammer 33 Aug 26, 2022
A more easy-to-use implementation of KPConv

A more easy-to-use implementation of KPConv This repo contains a more easy-to-use implementation of KPConv based on PyTorch. Introduction KPConv is a

Zheng Qin 35 Dec 14, 2022
Implementation of the paper "Generating Symbolic Reasoning Problems with Transformer GANs"

Generating Symbolic Reasoning Problems with Transformer GANs This is the implementation of the paper Generating Symbolic Reasoning Problems with Trans

Reactive Systems Group 1 Apr 18, 2022
MDETR: Modulated Detection for End-to-End Multi-Modal Understanding

MDETR: Modulated Detection for End-to-End Multi-Modal Understanding Website • Colab • Paper This repository contains code and links to pre-trained mod

Aishwarya Kamath 770 Dec 28, 2022
Library of various Few-Shot Learning frameworks for text classification

FewShotText This repository contains code for the paper A Neural Few-Shot Text Classification Reality Check Environment setup # Create environment pyt

Thomas Dopierre 47 Jan 03, 2023
AAI supports interdisciplinary research to help better understand human, animal, and artificial cognition.

AnimalAI 3 AAI supports interdisciplinary research to help better understand human, animal, and artificial cognition. It aims to support AI research t

Matthew Crosby 58 Dec 12, 2022
一个目标检测的通用框架(不需要cuda编译),支持Yolo全系列(v2~v5)、EfficientDet、RetinaNet、Cascade-RCNN等SOTA网络。

一个目标检测的通用框架(不需要cuda编译),支持Yolo全系列(v2~v5)、EfficientDet、RetinaNet、Cascade-RCNN等SOTA网络。

Haoyu Xu 203 Jan 03, 2023
Overview of architecture and implementation of TEDS-Net, as described in MICCAI 2021: "TEDS-Net: Enforcing Diffeomorphisms in Spatial Transformers to Guarantee TopologyPreservation in Segmentations"

TEDS-Net Overview of architecture and implementation of TEDS-Net, as described in MICCAI 2021: "TEDS-Net: Enforcing Diffeomorphisms in Spatial Transfo

Madeleine K Wyburd 14 Jan 04, 2023
git《Learning Pairwise Inter-Plane Relations for Piecewise Planar Reconstruction》(ECCV 2020) GitHub:

Learning Pairwise Inter-Plane Relations for Piecewise Planar Reconstruction Code for the ECCV 2020 paper by Yiming Qian and Yasutaka Furukawa Getting

37 Dec 04, 2022