roundabout,
created on Sunday, 6 October 2024, 08:45:53 (1728204353),
received on Saturday, 12 October 2024, 14:56:18 (1728744978)
Author identity: vlad <vlad.muntoiu@gmail.com>
353ff97496f285d486cc5bfac67a91c6799ea27f
static/picture-annotation.py
@@ -1,6 +1,5 @@
from pyscript import documentfrom pyodide.ffi import create_proxyfrom pyodide.http import pyfetchfrom pyscript import document, fetch as pyfetch from pyscript.ffi import create_proxyimport asyncio import json
@@ -69,40 +68,39 @@ def list_shapes():
json_shapes = [] for shape in shapes: shape_dict = {} match shape.tagName:case "rect":shape_dict["type"] = "bbox"shape_dict["shape"] = {"x": float(shape.getAttribute("x")) / image.naturalWidth,"y": float(shape.getAttribute("y")) / image.naturalHeight,"w": float(shape.getAttribute("width")) / image.naturalWidth,"h": float(shape.getAttribute("height")) / image.naturalHeight}case "polygon" | "polyline":if shape.tagName == "polygon":shape_dict["type"] = "polygon"elif shape.tagName == "polyline":shape_dict["type"] = "polyline"points = shape.getAttribute("points").split(" ")json_points = []for point in points:x, y = point.split(",")x, y = float(x), float(y)json_points.append({"x": x / image.naturalWidth,"y": y / image.naturalHeight})shape_dict["shape"] = json_pointscase "circle" if shape.classList.contains("shape-point"):shape_dict["type"] = "point"shape_dict["shape"] = {"x": float(shape.getAttribute("cx")) / image.naturalWidth,"y": float(shape.getAttribute("cy")) / image.naturalHeight}case _:continueif shape.tagName == "rect": shape_dict["type"] = "bbox" shape_dict["shape"] = { "x": float(shape.getAttribute("x")) / image.naturalWidth, "y": float(shape.getAttribute("y")) / image.naturalHeight, "w": float(shape.getAttribute("width")) / image.naturalWidth, "h": float(shape.getAttribute("height")) / image.naturalHeight } elif shape.tagName == "polygon" or shape.tagName == "polyline": if shape.tagName == "polygon": shape_dict["type"] = "polygon" elif shape.tagName == "polyline": shape_dict["type"] = "polyline" points = shape.getAttribute("points").split(" ") json_points = [] for point in points: x, y = point.split(",") x, y = float(x), float(y) json_points.append({ "x": x / image.naturalWidth, "y": y / image.naturalHeight }) shape_dict["shape"] = json_points elif shape.tagName == "circle" and shape.classList.contains("shape-point"): shape_dict["type"] = "point" shape_dict["shape"] = { "x": float(shape.getAttribute("cx")) / image.naturalWidth, "y": float(shape.getAttribute("cy")) / image.naturalHeight } else: continueshape_dict["object"] = shape.getAttribute("data-object-type") json_shapes.append(shape_dict)
@@ -340,13 +338,12 @@ def switch_shape(event):
image.addEventListener("click", unselect_shape_proxy) helper_message.innerText = "Click on a shape to select" # Cancel the current shape creation match shape_type:case "shape-bbox":cancel_bbox(None)case "shape-polygon":cancel_polygon(None)case "shape-polyline":cancel_polygon(None)if shape_type == "shape-bbox": cancel_bbox(None) elif shape_type == "shape-polygon": cancel_polygon(None) elif shape_type == "shape-polyline": cancel_polygon(None)else: # Remove event listeners for selection for shape in document.getElementsByClassName("shape"):
@@ -514,75 +511,72 @@ def open_shape(event):
return print("Creating a new shape of type:", shape_type) match shape_type:case "shape-bbox":helper_message.innerText = ("Define the first point at the intersection of the lines ""by clicking on the image, or click the cross to cancel")if shape_type == "shape-bbox": helper_message.innerText = ("Define the first point at the intersection of the lines " "by clicking on the image, or click the cross to cancel") cancel_button.addEventListener("click", cancel_bbox_proxy) document.addEventListener("keydown", cancel_bbox_proxy) cancel_button.style.display = "block" bbox_pos = None zone.addEventListener("click", make_bbox_proxy) vertical_ruler.style.display = "block" horizontal_ruler.style.display = "block" zone.style.cursor = "crosshair" elif shape_type == "shape-polygon" or shape_type == "shape-polyline": if shape_type == "shape-polygon": helper_message.innerText = ("Click on the image to define the points of the polygon, " "press escape to cancel, enter to close, or backspace to " "remove the last point") elif shape_type == "shape-polyline": helper_message.innerText = ("Click on the image to define the points of the polyline, " "press escape to cancel, enter to finish, or backspace to " "remove the last point") if not polygon_points and not new_shape: new_shape = make_shape_container() zone_rect = zone.getBoundingClientRect()cancel_button.addEventListener("click", cancel_bbox_proxy)document.addEventListener("keydown", cancel_bbox_proxy)if not polygon_points and int(new_shape.children.length) == 0: zone.addEventListener("click", make_polygon_proxy) document.addEventListener("keydown", close_polygon_proxy) document.addEventListener("keydown", cancel_polygon_proxy) document.addEventListener("keydown", backspace_polygon_proxy) cancel_button.addEventListener("click", cancel_polygon_proxy)cancel_button.style.display = "block" bbox_pos = Nonezone.addEventListener("click", make_bbox_proxy)vertical_ruler.style.display = "block"horizontal_ruler.style.display = "block"confirm_button.addEventListener("click", close_polygon_proxy) confirm_button.style.display = "block" backspace_button.addEventListener("click", backspace_polygon_proxy) backspace_button.style.display = "block" if shape_type == "shape-polygon": polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon") polygon.classList.add("shape-polygon") elif shape_type == "shape-polyline": polygon = document.createElementNS("http://www.w3.org/2000/svg", "polyline") polygon.classList.add("shape-polyline") polygon.setAttribute("fill", "none") polygon.setAttribute("data-object-type", "") polygon.classList.add("shape") new_shape.appendChild(polygon) zone.appendChild(new_shape)zone.style.cursor = "crosshair" case "shape-polygon" | "shape-polyline":match shape_type:case "shape-polygon":helper_message.innerText = ("Click on the image to define the points of the polygon, ""press escape to cancel, enter to close, or backspace to ""remove the last point")case "shape-polyline":helper_message.innerText = ("Click on the image to define the points of the polyline, ""press escape to cancel, enter to finish, or backspace to ""remove the last point")if not polygon_points and not new_shape:new_shape = make_shape_container()zone_rect = zone.getBoundingClientRect()if not polygon_points and int(len(new_shape.children)) == 0:zone.addEventListener("click", make_polygon_proxy)document.addEventListener("keydown", close_polygon_proxy)document.addEventListener("keydown", cancel_polygon_proxy)document.addEventListener("keydown", backspace_polygon_proxy)cancel_button.addEventListener("click", cancel_polygon_proxy)cancel_button.style.display = "block"confirm_button.addEventListener("click", close_polygon_proxy)confirm_button.style.display = "block"backspace_button.addEventListener("click", backspace_polygon_proxy)backspace_button.style.display = "block"match shape_type:case "shape-polygon":polygon = document.createElementNS("http://www.w3.org/2000/svg", "polygon")polygon.classList.add("shape-polygon")case "shape-polyline":polygon = document.createElementNS("http://www.w3.org/2000/svg", "polyline")polygon.classList.add("shape-polyline")polygon.setAttribute("fill", "none")polygon.setAttribute("data-object-type", "")polygon.classList.add("shape")new_shape.appendChild(polygon)zone.appendChild(new_shape)zone.style.cursor = "crosshair"case "shape-point":point = document.createElementNS("http://www.w3.org/2000/svg", "circle")zone_rect = zone.getBoundingClientRect()point.setAttribute("cx", str((event.clientX - zone_rect.left) / zone_rect.width * image.naturalWidth))point.setAttribute("cy", str((event.clientY - zone_rect.top) / zone_rect.height * image.naturalHeight))point.setAttribute("r", "0")point.classList.add("shape-point")point.classList.add("shape")point.setAttribute("data-object-type", "")elif shape_type == "shape-point": point = document.createElementNS("http://www.w3.org/2000/svg", "circle") zone_rect = zone.getBoundingClientRect() point.setAttribute("cx", str((event.clientX - zone_rect.left) / zone_rect.width * image.naturalWidth)) point.setAttribute("cy", str((event.clientY - zone_rect.top) / zone_rect.height * image.naturalHeight)) point.setAttribute("r", "0") point.classList.add("shape-point") point.classList.add("shape") point.setAttribute("data-object-type", "")new_shape = make_shape_container()zone_rect = zone.getBoundingClientRect()new_shape = make_shape_container() zone_rect = zone.getBoundingClientRect()new_shape.appendChild(point)zone.appendChild(new_shape)new_shape.appendChild(point) zone.appendChild(new_shape)new_shape = Nonenew_shape = None
@@ -594,7 +588,5 @@ zone.addEventListener("mousemove", follow_cursor_proxy)
zone.addEventListener("click", create_proxy(open_shape)) # Load existing annotations, if any async def load_existing():put_shapes(await load_shapes())load_existing()put_shapes(await load_shapes()) print("Ready!")
templates/picture-annotation.html
@@ -66,5 +66,5 @@
</x-vbox> </x-frame> <input type="hidden" id="resource-id" name="resource-id" value="{{ resource.id }}"> <script type="py" src="/static/picture-annotation.py"></script>{% endblock %}<script type="mpy" src="/static/picture-annotation.py"></script> {% endblock %}