picture-annotation.py
Python script, ASCII text executable
1from pyscript import document 2from pyodide.ffi import create_proxy 3 4document.getElementById("shape-options").style.display = "flex" 5 6image = document.getElementById("annotation-image") 7zone = document.getElementById("annotation-zone") 8 9 10shape_type = "" 11 12 13def switch_shape(event): 14global shape_type 15shape = event.currentTarget.id 16shape_type = shape 17print("Shape is now of type:", shape) 18 19 20vertical_ruler = document.getElementById("annotation-ruler-vertical") 21horizontal_ruler = document.getElementById("annotation-ruler-horizontal") 22vertical_ruler_2 = document.getElementById("annotation-ruler-vertical-secondary") 23horizontal_ruler_2 = document.getElementById("annotation-ruler-horizontal-secondary") 24helper_message = document.getElementById("annotation-helper-message") 25 26helper_message.innerText = "Select a shape type then click on the image to begin defining it" 27 28 29def follow_cursor(event): 30rect = zone.getBoundingClientRect() 31x = event.clientX - rect.left 32y = event.clientY - rect.top 33vertical_ruler.style.left = str(x) + "px" 34horizontal_ruler.style.top = str(y) + "px" 35 36 37bbox_pos = None 38 39 40def make_bbox(event): 41global new_shape, bbox_pos 42zone_rect = zone.getBoundingClientRect() 43 44if bbox_pos is None: 45bbox_pos = [(event.clientX - zone_rect.left) / zone_rect.width, 46(event.clientY - zone_rect.top) / zone_rect.height] 47vertical_ruler_2.style.left = str(bbox_pos[0] * 100) + "%" 48horizontal_ruler_2.style.top = str(bbox_pos[1] * 100) + "%" 49vertical_ruler_2.style.display = "block" 50horizontal_ruler_2.style.display = "block" 51 52else: 53x0, y0 = bbox_pos 54x1 = (event.clientX - zone_rect.left) / zone_rect.width 55y1 = (event.clientY - zone_rect.top) / zone_rect.height 56 57rectangle = document.createElementNS("http://www.w3.org/2000/svg", "rect") 58minx = min(x0, x1) 59miny = min(y0, y1) 60maxx = max(x0, x1) 61maxy = max(y0, y1) 62 63rectangle.setAttribute("x", str(minx * image.naturalWidth)) 64rectangle.setAttribute("y", str(miny * image.naturalHeight)) 65rectangle.setAttribute("width", str((maxx - minx) * image.naturalWidth)) 66rectangle.setAttribute("height", str((maxy - miny) * image.naturalHeight)) 67rectangle.setAttribute("fill", "none") 68rectangle.classList.add("shape-bbox") 69 70new_shape.appendChild(rectangle) 71zone.appendChild(new_shape) 72vertical_ruler.style.display = "none" 73horizontal_ruler.style.display = "none" 74vertical_ruler_2.style.display = "none" 75horizontal_ruler_2.style.display = "none" 76document.body.style.cursor = "auto" 77zone.removeEventListener("click", create_proxy(make_bbox)) 78 79return 80 81 82zone.addEventListener( 83"mousemove", 84create_proxy(follow_cursor) 85) 86 87new_shape = None 88 89def open_shape(event): 90global last_click_position, new_shape 91print("Creating a new shape of type:", shape_type) 92new_shape = document.createElementNS("http://www.w3.org/2000/svg", "svg") 93new_shape.setAttribute("width", "100%") 94new_shape.setAttribute("height", "100%") 95zone_rect = zone.getBoundingClientRect() 96new_shape.setAttribute("viewBox", f"0 0 {image.naturalWidth} {image.naturalHeight}") 97 98if shape_type == "shape-bbox": 99bbox_pos = None 100zone.addEventListener( 101"click", 102create_proxy(make_bbox) 103) 104vertical_ruler.style.display = "block" 105horizontal_ruler.style.display = "block" 106document.body.style.cursor = "crosshair" 107 108 109for button in list(document.getElementById("shape-selector").children): 110button.addEventListener( 111"click", 112create_proxy(switch_shape) 113) 114print("Shape", button.id, "is available") 115 116 117 118zone.addEventListener( 119"click", 120create_proxy(open_shape) 121) 122