<gh_stars>0
import json

import dash
import imageio
import numpy as np
import skimage
from dash import html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from dash_canvas import DashCanvas
from dash_canvas.utils import array_to_data_url
from scipy import ndimage
from skimage import draw, morphology

H, W = (280, 280)

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        html.H5(
            "Press down the left mouse button and draw inside the canvas",
            style={"text-align": "center"},
        ),
        html.Div(
            DashCanvas(
                id="mycanvas",
                height=H,
                width=W,
                # filename="wb.png",
                lineWidth=20,
                lineColor="black",
                hide_buttons=["zoom", "pan", "line", "rectangle", "select"],
            ),
            style={
                "width": "49%",
                "height": "400",
                "display": "inline-block",
            },
        ),
        html.Div(
            html.Img(id="myimage", width=300),
            style={
                "width": "49%",
                "display": "inline-block",
            },
        ),
    ]
)


def _indices_of_path(path, scale=1):
    """
    Retrieve pixel indices (integer values).
    Parameters
    ----------
    path: SVG-like path formatted as JSON string
        The path is formatted like
        ['M', x0, y0],
        ['Q', xc1, yc1, xe1, ye1],
        ['Q', xc2, yc2, xe2, ye2],
        ...
        ['L', xn, yn]
        where (xc, yc) are for control points and (xe, ye) for end points.
    Notes
    -----
    I took a weight of 1 and it seems fine from visual inspection.
    """
    rr, cc = [], []
    for (Q1, Q2) in zip(path[:-2], path[1:-1]):
        # int(round()) is for Python 2 compatibility
        inds = draw.bezier_curve(
            int(round(Q1[-1] / scale)),
            int(round(Q1[-2] / scale)),
            int(round(Q2[2] / scale)),
            int(round(Q2[1] / scale)),
            int(round(Q2[4] / scale)),
            int(round(Q2[3] / scale)),
            1,
        )
        rr += list(inds[0])
        cc += list(inds[1])
    return rr, cc


def parse_jsonstring(string, shape=None, scale=1):
    """
    Takne from https://github.com/plotly/dash-canvas/blob/master/dash_canvas/utils/parse_json.py
    Parse JSON string to draw the path saved by react-sketch.
    Up to now only path objects are processed (created with Pencil tool).
    Parameters
    ----------
    data : str
        JSON string of data
    shape: tuple, optional
        shape of returned image.
    Returns
    -------
    mask: ndarray of bools
        binary array where the painted regions are one.
    """
    if shape is None:
        shape = (500, 500)
    mask = np.zeros(shape, dtype=np.bool)
    try:
        data = json.loads(string)
    except:
        return mask
    scale = 1
    for obj in data["objects"]:
        if obj["type"] == "image":
            scale = obj["scaleX"]
        elif obj["type"] == "path":
            scale_obj = obj["scaleX"]
            inds = _indices_of_path(obj["path"], scale=scale / scale_obj)
            radius = round(obj["strokeWidth"] / 2.0 / scale)
            mask_tmp = np.zeros(shape, dtype=np.bool)
            ys = np.array(inds[0]).astype(int)
            xs = np.array(inds[1]).astype(int)
            valid_ys = np.logical_and(0 <= ys, ys < shape[0])
            valid_xs = np.logical_and(0 <= xs, xs < shape[1])
            valid_ind = np.logical_and(valid_ys, valid_xs)
            ys = ys[valid_ind]
            xs = xs[valid_ind]
            mask_tmp[ys, xs] = 1
            mask_tmp = ndimage.binary_dilation(mask_tmp, morphology.disk(radius))
            mask += mask_tmp
    return mask


@app.callback(Output("myimage", "src"), Input("mycanvas", "json_data"))
def update_data(string):
    if string:
        mask = skimage.transform.resize(
            parse_jsonstring(string, (H, W)),
            (28, 28),
            anti_aliasing=False,
        )
    else:
        raise PreventUpdate
    return array_to_data_url((255 * mask).astype(np.uint8))


if __name__ == "__main__":
    app.run_server(debug=True)

import json

import dash
import imageio
import numpy as np
import skimage
from dash import html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from dash_canvas import DashCanvas
from dash_canvas.utils import array_to_data_url
from scipy import ndimage
from skimage import draw, morphology

H, W = (280, 280)

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        html.H5(
            "Press down the left mouse button and draw inside the canvas",
            style={"text-align": "center"},
        ),
        html.Div(
            DashCanvas(
                id="mycanvas",
                height=H,
                width=W,
                # filename="wb.png",
                lineWidth=20,
                lineColor="black",
                hide_buttons=["zoom", "pan", "line", "rectangle", "select"],
            ),
            style={
                "width": "49%",
                "height": "400",
                "display": "inline-block",
            },
        ),
        html.Div(
            html.Img(id="myimage", width=300),
            style={
                "width": "49%",
                "display": "inline-block",
            },
        ),
    ]
)


def _indices_of_path(path, scale=1):
    """
    Retrieve pixel indices (integer values).
    Parameters
    ----------
    path: SVG-like path formatted as JSON string
        The path is formatted like
        ['M', x0, y0],
        ['Q', xc1, yc1, xe1, ye1],
        ['Q', xc2, yc2, xe2, ye2],
        ...
        ['L', xn, yn]
        where (xc, yc) are for control points and (xe, ye) for end points.
    Notes
    -----
    I took a weight of 1 and it seems fine from visual inspection.
    """
    rr, cc = [], []
    for (Q1, Q2) in zip(path[:-2], path[1:-1]):
        # int(round()) is for Python 2 compatibility
        inds = draw.bezier_curve(
            int(round(Q1[-1] / scale)),
            int(round(Q1[-2] / scale)),
            int(round(Q2[2] / scale)),
            int(round(Q2[1] / scale)),
            int(round(Q2[4] / scale)),
            int(round(Q2[3] / scale)),
            1,
        )
        rr += list(inds[0])
        cc += list(inds[1])
    return rr, cc


def parse_jsonstring(string, shape=None, scale=1):
    """
    Takne from https://github.com/plotly/dash-canvas/blob/master/dash_canvas/utils/parse_json.py
    Parse JSON string to draw the path saved by react-sketch.
    Up to now only path objects are processed (created with Pencil tool).
    Parameters
    ----------
    data : str
        JSON string of data
    shape: tuple, optional
        shape of returned image.
    Returns
    -------
    mask: ndarray of bools
        binary array where the painted regions are one.
    """
    if shape is None:
        shape = (500, 500)
    mask = np.zeros(shape, dtype=np.bool)
    try:
        data = json.loads(string)
    except:
        return mask
    scale = 1
    for obj in data["objects"]:
        if obj["type"] == "image":
            scale = obj["scaleX"]
        elif obj["type"] == "path":
            scale_obj = obj["scaleX"]
            inds = _indices_of_path(obj["path"], scale=scale / scale_obj)
            radius = round(obj["strokeWidth"] / 2.0 / scale)
            mask_tmp = np.zeros(shape, dtype=np.bool)
            ys = np.array(inds[0]).astype(int)
            xs = np.array(inds[1]).astype(int)
            valid_ys = np.logical_and(0 <= ys, ys < shape[0])
            valid_xs = np.logical_and(0 <= xs, xs < shape[1])
            valid_ind = np.logical_and(valid_ys, valid_xs)
            ys = ys[valid_ind]
            xs = xs[valid_ind]
            mask_tmp[ys, xs] = 1
            mask_tmp = ndimage.binary_dilation(mask_tmp, morphology.disk(radius))
            mask += mask_tmp
    return mask


@app.callback(Output("myimage", "src"), Input("mycanvas", "json_data"))
def update_data(string):
    if string:
        mask = skimage.transform.resize(
            parse_jsonstring(string, (H, W)),
            (28, 28),
            anti_aliasing=False,
        )
    else:
        raise PreventUpdate
    return array_to_data_url((255 * mask).astype(np.uint8))


if __name__ == "__main__":
    app.run_server(debug=True)
# filename="wb.png", Retrieve pixel indices (integer values).     Parameters     ----------     path: SVG-like path formatted as JSON string         The path is formatted like         ['M', x0, y0],         ['Q', xc1, yc1, xe1, ye1],         ['Q', xc2, yc2, xe2, ye2],         ...         ['L', xn, yn]         where (xc, yc) are for control points and (xe, ye) for end points.     Notes     -----     I took a weight of 1 and it seems fine from visual inspection. # int(round()) is for Python 2 compatibility Takne from https://github.com/plotly/dash-canvas/blob/master/dash_canvas/utils/parse_json.py     Parse JSON string to draw the path saved by react-sketch.     Up to now only path objects are processed (created with Pencil tool).     Parameters     ----------     data : str         JSON string of data     shape: tuple, optional         shape of returned image.     Returns     -------     mask: ndarray of bools         binary array where the painted regions are one.