roundabout,
created on Tuesday, 3 September 2024, 13:24:32 (1725369872),
received on Wednesday, 4 September 2024, 09:03:53 (1725440633)
Author identity: vlad <vlad.muntoiu@gmail.com>
eb02773a8b58365bdf220f029b24a2e8508ada27
static/picture-annotation.py
@@ -11,6 +11,8 @@ confirm_button = document.getElementById("annotation-confirm")
cancel_button = document.getElementById("annotation-cancel") backspace_button = document.getElementById("annotation-backspace") delete_button = document.getElementById("annotation-delete") previous_button = document.getElementById("annotation-previous") next_button = document.getElementById("annotation-next")object_list = document.getElementById("object-types")
@@ -18,6 +20,8 @@ confirm_button.style.display = "none"
cancel_button.style.display = "none" backspace_button.style.display = "none" delete_button.style.display = "none" previous_button.style.display = "none" next_button.style.display = "none"shape_type = "" bbox_pos = None new_shape = None
@@ -48,12 +52,23 @@ def change_object_type(event):
change_object_type_proxy = create_proxy(change_object_type) async def focus_shape(selected_shape):async def focus_shape(shape): global selected_shape if shape_type != "select": return if selected_shape is not None: selected_shape.classList.remove("selected") selected_shape = shapeselected_shape.classList.add("selected") objects = await get_all_objects() delete_button.style.display = "block" next_button.style.display = "block" previous_button.style.display = "block"object_list.innerHTML = ""
@@ -86,17 +101,61 @@ async def focus_shape(selected_shape):
async def select_shape(event): await focus_shape(event.target) async def next_shape(event):global selected_shape if shape_type != "select":if selected_shape is None: return selected_svg = selected_shape.parentNode while selected_svg is not None: next_sibling = selected_svg.nextElementSibling if next_sibling and next_sibling.classList.contains("shape-container"): selected_svg = next_sibling break elif next_sibling is None: # If no more siblings, loop back to the first child selected_svg = selected_svg.parentNode.firstElementChild while selected_svg is not None and not selected_svg.classList.contains( "shape-container"): selected_svg = selected_svg.nextElementSibling break else: selected_svg = next_sibling if selected_svg: shape = selected_svg.firstElementChild await focus_shape(shape) async def previous_shape(event): global selected_shape if selected_shape is None:return if selected_shape is not None:selected_shape.classList.remove("selected")print("Selected shape:", event.target)selected_svg = selected_shape.parentNodeselected_shape = event.targetwhile selected_svg is not None: next_sibling = selected_svg.previousElementSibling if next_sibling and next_sibling.classList.contains("shape-container"): selected_svg = next_sibling break elif next_sibling is None: # If no more siblings, loop back to the last child selected_svg = selected_svg.parentNode.lastElementChild while selected_svg is not None and not selected_svg.classList.contains( "shape-container"): selected_svg = selected_svg.previousElementSibling break else: selected_svg = next_siblingawait focus_shape(selected_shape)if selected_svg: shape = selected_svg.firstElementChild await focus_shape(shape)def unselect_shape(event):
@@ -108,6 +167,8 @@ def unselect_shape(event):
object_list.innerHTML = "" delete_button.style.display = "none" next_button.style.display = "none" previous_button.style.display = "none"def delete_shape(event):
@@ -119,14 +180,19 @@ def delete_shape(event):
selected_shape = None object_list.innerHTML = "" delete_button.style.display = "none" next_button.style.display = "none" previous_button.style.display = "none"select_shape_proxy = create_proxy(select_shape) unselect_shape_proxy = create_proxy(unselect_shape) delete_shape_proxy = create_proxy(delete_shape) next_shape_proxy = create_proxy(next_shape) previous_shape_proxy = create_proxy(previous_shape)delete_button.addEventListener("click", delete_shape_proxy) next_button.addEventListener("click", next_shape_proxy) previous_button.addEventListener("click", previous_shape_proxy)# These are functions usable in JS cancel_bbox_proxy = create_proxy(lambda event: cancel_bbox(event))
@@ -149,6 +215,12 @@ def switch_shape(event):
shape.addEventListener("click", select_shape_proxy) 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)else: # Remove event listeners for selection for shape in document.getElementsByClassName("shape"):
@@ -158,6 +230,7 @@ def switch_shape(event):
helper_message.innerText = "Select a shape type then click on the image to begin defining it" print("Shape is now of type:", shape) vertical_ruler = document.getElementById("annotation-ruler-vertical") horizontal_ruler = document.getElementById("annotation-ruler-horizontal") vertical_ruler_2 = document.getElementById("annotation-ruler-vertical-secondary")
@@ -191,6 +264,7 @@ def cancel_bbox(event):
cancel_button.style.display = "none" helper_message.innerText = "Select a shape type then click on the image to begin defining it" def make_bbox(event): global new_shape, bbox_pos zone_rect = zone.getBoundingClientRect()
@@ -237,6 +311,7 @@ def make_bbox(event):
polygon_points = [] def make_polygon(event): global new_shape, polygon_points
@@ -249,7 +324,9 @@ def make_polygon(event):
(event.clientY - zone_rect.top) / zone_rect.height)) # Update the polygon polygon.setAttribute("points", " ".join([f"{point[0] * image.naturalWidth},{point[1] * image.naturalHeight}" for point in polygon_points]))polygon.setAttribute("points", " ".join( [f"{point[0] * image.naturalWidth},{point[1] * image.naturalHeight}" for point in polygon_points]))def reset_polygon():
@@ -290,7 +367,9 @@ def backspace_polygon(event):
return polygon_points.pop() polygon = new_shape.children[0] polygon.setAttribute("points", " ".join([f"{point[0] * image.naturalWidth},{point[1] * image.naturalHeight}" for point in polygon_points]))polygon.setAttribute("points", " ".join( [f"{point[0] * image.naturalWidth},{point[1] * image.naturalHeight}" for point in polygon_points]))close_polygon_proxy = create_proxy(close_polygon)
@@ -353,6 +432,5 @@ for button in list(document.getElementById("shape-selector").children):
button.addEventListener("click", create_proxy(switch_shape)) print("Shape", button.id, "is available") zone.addEventListener("mousemove", follow_cursor_proxy) zone.addEventListener("click", create_proxy(open_shape))
templates/picture-annotation.html
@@ -54,6 +54,12 @@
<button class="button-flat" title="delete shape" id="annotation-delete"> <iconify-icon icon="mdi:delete"></iconify-icon> </button> <button class="button-flat" title="select previous shape" id="annotation-previous"> <iconify-icon icon="mdi:chevron-left"></iconify-icon> </button> <button class="button-flat" title="select next shape" id="annotation-next"> <iconify-icon icon="mdi:chevron-right"></iconify-icon> </button></x-buttonbox> <x-vbox id="object-types" style="--gap-box: 0.25rem; --padding-box: 1rem;"> </x-vbox>