andfanilo / streamlit-drawable-canvas

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.
https://drawable-canvas.streamlit.app/
MIT License
576 stars 88 forks source link

Calculate path length for line and freedraw? #53

Closed JCCKwong closed 3 years ago

JCCKwong commented 3 years ago

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

andfanilo commented 3 years ago

Hey @JCCKwong !

That's a super interesting question, let me dig into it. It seems there are some Python SVG libs that could help:

Let me check and come back to you (and you find before me, hope to read your solution!)

Fanilo

andfanilo commented 3 years ago

So it is absolutely possible with svgpathtools.

I've added the code to the demo, here's the snippet I used:

from streamlit_drawable_canvas import st_canvas
from svgpathtools import parse_path

canvas_result = st_canvas(...)

if (
    canvas_result.json_data is not None
    and len(canvas_result.json_data["objects"]) != 0
):
    df = pd.json_normalize(canvas_result.json_data["objects"])
    paths = df["path"].tolist()  # get list of paths from dataframe
    for ind, path in enumerate(paths):  
        path = parse_path(" ".join([str(e) for line in path for e in line]))  # concat each path into its SVG string representation and parse it
        st.write(f"Path {ind} has length {path.length():.3f} pixels")
JCCKwong commented 3 years ago

This is perfect! Many thanks to you and the Streamlit team for making it so easy to develop these high quality apps. Looking forward to sharing my next clinical app in the future!

JCCKwong commented 3 years ago

Hi @andfanilo , sorry to revisit this issue.

I implemented the code described above.

if (canvas_result.json_data is not None and len(canvas_result.json_data["objects"]) != 0 ):
     st.dataframe(pd.json_normalize(canvas_result.json_data["objects"]))
     df = pd.json_normalize(canvas_result.json_data["objects"])
     paths = df["path"].tolist()

     for _, path in enumerate(paths):
         path2 = parse_path(" ".join([str(e) for line in path for e in line]))
         st.write(path2.length())
         direct_dist = math.sqrt((df["width"][0]) ** 2 + (df["height"][0]) ** 2)
         tort = path2.length() / direct_dist
         st.write("Tortuosity: ", tort)

However, I get a TypeError. Any advice would be amazing! image

andfanilo commented 3 years ago

Hmmm that's odd, can try to add a st.write(path) to check the value and see if it's a nested list or another type of object?