Uploading an image with pyscript and converting it into lines

127 views Asked by At

I made a html-site to upload an image and convert it into lines for a plotter. I use linedraw modified by evildmp. Unfortunately after uploading nothing happens. It should show me a "lines"-version (svg) of my image.

Therefor I created a function imagetosvg that returns a svg-file. I am new in pyscript and not able to find the problem.


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>File System Examples</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
    <br />
    <label for="myfile">Select a file:</label>
    <input type="file" id="myfile" name="myfile">
    <div id="output"></div> 
    <br />
    <br />
    <p>Image:</p>
    <img id="image">

    <py-env>
        - numpy
        - Pillow
    </py-env>

    <py-script src="main.py">
    </py-script>
        
  </body>
</html>

main.py:

from random import *
import math
import argparse
import json
import time
import asyncio
import js
from js import document
from pyodide import create_proxy

from PIL import Image, ImageDraw, ImageOps

# file settings
export_path = "images/out.svg"
svg_folder = "images/"
json_folder = "images/"

# CV
no_cv = False

try:
    import numpy as np
    import cv2
except:
    print("Cannot import numpy/openCV. Switching to NO_CV mode.")
    no_cv = True



async def read_complete(event):
    # event is ProgressEvent
    # console.log('read_complete')
    print("ich habs")
    imageFile = event.target.result
    image = document.getElementById("image")
    image.src = image_to_svg(imageFile, draw_contours=2, draw_hatch=16)

async def process_file(x):
    fileList = document.getElementById('myfile').files

    for f in fileList:
        # reader is a pyodide.JsProxy
        reader = js.FileReader.new()

        # Create a Python proxy for the callback function
        onload_event = create_proxy(read_complete)

        reader.onload = onload_event

        reader.readAsDataURL(f)

    return

def setup():
    # Create a Python proxy for the callback function
    file_event = create_proxy(process_file)

    # Set the listener to the callback
    e = document.getElementById("myfile")
    e.addEventListener("change", file_event, False)

setup()


# -------------- output functions --------------


#My Own
def image_to_svg(image_filename, resolution=1024, draw_contours=False, repeat_contours=1, draw_hatch=False, repeat_hatch=1):
    lines = vectorise(image_filename, resolution, draw_contours, repeat_contours, draw_hatch, repeat_hatch)
    svg_data = makesvg(lines)
    return svg_data




def image_to_json(
    image_filename,
    resolution=1024,
    draw_contours=False,
    repeat_contours=1,
    draw_hatch=False,
    repeat_hatch=1,
):

    lines = vectorise(
        image_filename,
        resolution,
        draw_contours,
        repeat_contours,
        draw_hatch,
        repeat_hatch,
    )

    filename = json_folder + image_filename + ".json"
    lines_to_file(lines, filename)


def makesvg(lines):
    print("Generating svg file...")
    width = math.ceil(max([max([p[0] * 0.5 for p in l]) for l in lines]))
    height = math.ceil(max([max([p[1] * 0.5 for p in l]) for l in lines]))
    out = '<svg xmlns="http://www.w3.org/2000/svg" height="%spx" width="%spx" version="1.1">' % (
        height,
        width,
    )

    for l in lines:
        l = ",".join([str(p[0] * 0.5) + "," + str(p[1] * 0.5) for p in l])
        out += '<polyline points="' + l + '" stroke="black" stroke-width="1" fill="none" />\n'
    out += "</svg>"
    return out

... see https://raw.githubusercontent.com/evildmp/BrachioGraph/master/linedraw.py
2

There are 2 answers

1
Jeff Conrad On

Now I changed the code and the image is converted into lines but I don't know how to show my svg-image after saving it.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>File System Examples</title>
    <link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
    <script defer src="https://pyscript.net/alpha/pyscript.js"></script>
  </head>
  <body>
  <div style="border:2px inset #AAA;cursor:text;height:120px;overflow:auto;width:600px; resize:both">
  <div id="content">
  </div>
  <py-env>
      - Pillow
  </py-env>
  <label for="Upload a PNG image"></label><input type="file" id="file-upload-pillow">
  <div id="output_upload_pillow"></div>
  <py-script>
from js import document, console, Uint8Array, window, File
from pyodide import create_proxy
import asyncio
import io
import math
import argparse

from PIL import Image, ImageFilter, ImageOps

# CV
no_cv = False

try:
    import numpy as np
    import cv2
except:
    print("Cannot import numpy/openCV. Switching to NO_CV mode.")
    no_cv = True

async def _upload_change_and_show(e):
    #Get the first file from upload
    file_list = e.target.files
    first_item = file_list.item(0)

    #Get the data from the files arrayBuffer as an array of unsigned bytes
    array_buf = Uint8Array.new(await first_item.arrayBuffer())

    #BytesIO wants a bytes-like object, so convert to bytearray first
    bytes_list = bytearray(array_buf)
    my_bytes = io.BytesIO(bytes_list) 

    #Create PIL image from np array
    my_image = Image.open(my_bytes)

    #Log some of the image data for testing
    console.log(f"{my_image.format= } {my_image.width= } {my_image.height= }")

    # Now that we have the image loaded with PIL, we can use all the tools it makes available. 
    my_image = my_image.convert("L")

    my_image = ImageOps.autocontrast(my_image, 5, preserve_tone=True)

    resolution=1024
    draw_contours=2
    repeat_contours=1
    draw_hatch=16
    repeat_hatch=1

    lines = []

    if draw_contours and repeat_contours:
        contours = getcontours(resize_image(my_image, resolution, draw_contours), draw_contours)
        contours = sortlines(contours)
        contours = join_lines(contours)
        for r in range(repeat_contours):
            lines += contours

    if draw_hatch and repeat_hatch:
        hatches = hatch(resize_image(my_image, resolution), line_spacing=draw_hatch)
        hatches = sortlines(hatches)
        hatches = join_lines(hatches)
        for r in range(repeat_hatch):
            lines += hatches

    segments = 0
    for line in lines:
        segments = segments + len(line) - 1
    print(len(lines), "lines,", segments, "segments.")

    f = open("plotter.svg", "w")
    f.write(makesvg(lines))
    f.close()

    #Convert Pillow object array back into File type that createObjectURL will take
    my_stream = io.BytesIO()
    my_image.save(my_stream, format="PNG")

    #Create a JS File object with our data and the proper mime type
    image_file = File.new([Uint8Array.new(my_stream.getvalue())], "new_image_file.png", {type: "image/svg"})

    #Create new tag and insert into page
    new_image = document.createElement('img')
    new_image.src = window.URL.createObjectURL(image_file)
    document.getElementById("output_upload_pillow").appendChild(new_image)

###Linedraw Functions

def makesvg(lines):
    print("generating svg file...")
    out = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
    for l in lines:
        l = ",".join([str(p[0]*0.5)+","+str(p[1]*0.5) for p in l])
        out += '<polyline points="'+l+'" stroke="black" stroke-width="2" fill="none" />\n'
    out += '</svg>'
    return out

#Other linedraw-functions...
0
Jeff Glass On

Following your expanded answer - if you have your SVG as a string of HTML, you can set the innerHTML property of a DOM element to that source:

<div id="dest"></div>
<py-script>
    import js

    svg_as_string = """<svg>Your data here</dvg>"""

    dest_elem = js.document.getElementById("dest")
    dest_elem.innerHTML = svg_as_string
</py-script>