Spaces:
Runtime error
Runtime error
| () => { | |
| window.canvas = document.getElementById('canvas'); | |
| window.ctx = canvas.getContext('2d'); | |
| window.rects = []; | |
| window.mouseX; | |
| window.mouseY; | |
| window.closeEnough = 10; | |
| window.keys = {}; | |
| window.hover = false; | |
| window.TL = 0; | |
| window.TR = 1; | |
| window.BL = 2; | |
| window.BR = 3; | |
| window.frame_index = 0; | |
| window.init = () => { | |
| window.frames = window.annotation_info; | |
| console.log(window.frames); | |
| window.frame_index = 0; | |
| document.removeEventListener('keydown', keydown); | |
| document.removeEventListener('keyup', keyup); | |
| document.addEventListener('keydown', keydown); | |
| document.addEventListener('keyup', keyup); | |
| show_frame(); | |
| } | |
| window.reset_annotation = () => { | |
| window.frames = JSON.parse(document.getElementById("annotation_info").innerHTML); | |
| show_frame(); | |
| } | |
| window.prev_frame = () => { | |
| window.frame_index = Math.max(window.frame_index - 1, 0); | |
| show_frame(); | |
| } | |
| window.next_frame = () => { | |
| window.frame_index = Math.min(window.frame_index + 1, window.frames.length - 1); | |
| show_frame(); | |
| } | |
| window.show_frame = () => { | |
| window.frame = window.frames[window.frame_index]; | |
| // Load annotation from frame | |
| const annotations = frame['annotations']; | |
| window.annotations = annotations; | |
| console.log(window.annotations) | |
| window.dragging = false; | |
| // Load frame image | |
| const frame_img = frame['base64']; | |
| document.getElementById("annotation_img").src = "data:image/jpeg;base64," + frame_img; | |
| // Draw function is called by this element using the onloaded callback | |
| document.getElementById("annotation_frame_nbr").innerHTML = "Frame " + window.frame_index + "/" + window.frames.length; | |
| } | |
| // DRAW FUNCTIONS | |
| window.draw = () => { | |
| draw_canvas(); | |
| draw_input_fields(); | |
| // Mark if frame is edited | |
| document.getElementById("annotation_edited").style.display = (frame.edited) ? "block" : "none"; | |
| } | |
| window.draw_canvas = () => { | |
| ctx = window.ctx; | |
| canvas = window.canvas; | |
| canvas.width = document.getElementById("annotation_img").width; | |
| canvas.height = document.getElementById("annotation_img").height; | |
| canvas.style = "" | |
| annotations = window.annotations; | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| ctx.drawImage(document.getElementById("annotation_img"), 0, 0); | |
| for (const annotation of annotations) { | |
| //ctx.globalAlpha = annotation.conf | |
| const rect = annotation.bbox; | |
| ctx.strokeStyle = color_from_id(annotation.id); | |
| ctx.strokeRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); | |
| ctx.font = "15px Arial"; | |
| ctx.fillStyle = color_from_id(annotation.id); | |
| ctx.textAlign = "right"; | |
| ctx.fillText(annotation.id, rect.right, rect.top - 3); | |
| } | |
| if (hover && !dragging) { | |
| annotation = hover.annotation; | |
| rect = annotation.bbox; | |
| handles = [ | |
| [rect.left, rect.top], | |
| [rect.right, rect.top], | |
| [rect.left, rect.bottom], | |
| [rect.right, rect.bottom] | |
| ]; | |
| handle = handles[hover.corner]; | |
| ctx.fillStyle = color_from_id(annotation.id); | |
| ctx.beginPath(); | |
| s = 6; | |
| ctx.rect(handle[0]-s/2, handle[1]-s/2, s, s); | |
| ctx.fill(); | |
| } | |
| } | |
| window.draw_input_fields = () => { | |
| label_style = "style='width: calc(16% - 6px); display: inline-block; text-align:center; font-weight: bold;'"; | |
| input_style_base = "style='width: calc(16% - 6px); display: inline-block; padding: 5px;'"; | |
| input_style_selected = "style='width: calc(16% - 6px); display: inline-block; padding: 5px; border-width: 3px; border-color: orange; border-radius: 5px;'"; | |
| html = "" | |
| for (const annotation of window.annotations) { | |
| input_style = (window.hover && annotation === window.hover.annotation) ? input_style_selected : input_style_base; | |
| html += ` | |
| <div style='margin: 0 0 20px 10px'> | |
| <div> | |
| <label ${label_style}>${"id"}</label> | |
| <label ${label_style}>${"left"}</label> | |
| <label ${label_style}>${"top"}</label> | |
| <label ${label_style}>${"right"}</label> | |
| <label ${label_style}>${"bottom"}</label> | |
| <label ${label_style}>${"conf"}</label> | |
| </div> | |
| <div style='height:40px'> | |
| <input ${input_style} type='text' value='${annotation.id}'> | |
| <input ${input_style} type='text' value='${Math.round(annotation.bbox.left)}'> | |
| <input ${input_style} type='text' value='${Math.round(annotation.bbox.top)}'> | |
| <input ${input_style} type='text' value='${Math.round(annotation.bbox.right)}'> | |
| <input ${input_style} type='text' value='${Math.round(annotation.bbox.bottom)}'> | |
| <input ${input_style} type='text' value='${annotation.conf}'> | |
| </div> | |
| </div>`; | |
| } | |
| document.getElementById("annotation_display").innerHTML = html; | |
| } | |
| color_from_id = (id) => { | |
| //hue = Math.floor((number * 137.508 + 60) % 360) | |
| power = Math.pow(2, Math.ceil(Math.log2(id))); | |
| hue = (2*id - power - 1) / power; | |
| return 'hsl(' + Math.floor(hue*359) + ', 100%, 50%)' | |
| } | |
| // KEY EVENTS | |
| window.keyup = (e) => { | |
| delete keys[e.key.toLowerCase()]; | |
| } | |
| window.keydown = (e) => { | |
| console.log(e.key.toLowerCase()) | |
| keys[e.key.toLowerCase()] = true; | |
| // if pressing x, delete hovered annotation | |
| if (keys['x'] && window.hover) delete_annotation(window.hover.annotation); | |
| if (keys['arrowright'] || keys['d']) next_frame(); | |
| if (keys['arrowleft'] || keys['a']) prev_frame(); | |
| } | |
| // MOUSE EVENTS | |
| window.mouse_down = (e) => { | |
| update_mouse(e); | |
| // If holding 'n', create new annotation | |
| if (keys['n']) return create_annotation(); | |
| // else, start dragging hovered object | |
| window.dragging = false; | |
| if (window.hover) { | |
| window.dragging = { | |
| annotation: window.hover.annotation, | |
| corner: window.hover.corner | |
| } | |
| } | |
| console.log(dragging) | |
| window.draw() | |
| } | |
| window.mouse_up = () => { | |
| console.log("mouseUp") | |
| window.dragging = false; | |
| } | |
| window.mouse_move = (e) => { | |
| ctx = window.ctx; | |
| canvas = window.canvas; | |
| dragging = window.dragging; | |
| update_mouse(e); | |
| if (!dragging) return window.draw(); | |
| annotation = dragging.annotation; | |
| rect = annotation.bbox; | |
| corner = dragging.corner; | |
| mouse = [mouseX, mouseY]; | |
| if (corner == window.TL) { | |
| [rect.left, rect.top] = mouse; | |
| } else if (corner == window.TR) { | |
| [rect.right, rect.top] = mouse; | |
| } else if (corner == window.BL) { | |
| [rect.left, rect.bottom] = mouse; | |
| } else if (corner == window.BR) { | |
| [rect.right, rect.bottom] = mouse; | |
| } | |
| // If left > right we have swapped sides, switch to horizontally opposite corner | |
| if (rect.left > rect.right) { | |
| [rect.left, rect.right] = [rect.right, rect.left]; | |
| if (corner == window.TL) corner = window.TR; | |
| else if (corner == window.TR) corner = window.TL; | |
| else if (corner == window.BL) corner = window.BR; | |
| else if (corner == window.BR) corner = window.BL; | |
| } | |
| //If top > bottom we have swapped sides, switch to vertically opposite corner | |
| if (rect.top > rect.bottom) { | |
| [rect.top, rect.bottom] = [rect.bottom, rect.top]; | |
| if (corner == window.TL) corner = window.BL; | |
| else if (corner == window.BL) corner = window.TL; | |
| else if (corner == window.TR) corner = window.BR; | |
| else if (corner == window.BR) corner = window.TR; | |
| } | |
| mark_frame_as_edited(); | |
| if (dragging.corner !== corner) console.log(dragging.corner + " -> " + corner); | |
| dragging.corner = corner; | |
| window.draw(); | |
| } | |
| update_mouse = (e) => { | |
| bodyRect = document.body.getBoundingClientRect(); | |
| canvasRect = e.target.getBoundingClientRect(); | |
| offset_x = canvasRect.left - bodyRect.left; | |
| offset_y = canvasRect.top - bodyRect.top; | |
| mouseX = e.pageX - offset_x; | |
| mouseY = e.pageY - offset_y; | |
| function sqDistance(x, y) { | |
| dx = mouseX - x; | |
| dy = mouseY - y; | |
| return dx*dx + dy*dy; | |
| } | |
| window.hover = false; | |
| threshold = 100; | |
| for (const annotation of annotations) { | |
| rect = annotation.bbox; | |
| square_dists = [ | |
| sqDistance(rect.left, rect.top), | |
| sqDistance(rect.right, rect.top), | |
| sqDistance(rect.left, rect.bottom), | |
| sqDistance(rect.right, rect.bottom) | |
| ] | |
| min_dist = Math.min(...square_dists); | |
| if (min_dist > threshold) continue; | |
| threshold = min_dist; | |
| corner = square_dists.indexOf(min_dist); | |
| window.hover = { corner, annotation } | |
| } | |
| } | |
| // ANNOTATION UPDATES | |
| mark_frame_as_edited = () => { | |
| frame.edited = true; | |
| } | |
| create_annotation = () => { | |
| new_annotation = { | |
| bbox: { | |
| left: mouseX, | |
| top: mouseY, | |
| right: mouseX, | |
| bottom: mouseY | |
| }, | |
| color: "rgb(255, 0, 0)", | |
| id: 1, | |
| conf: 1 | |
| }; | |
| annotations.push(new_annotation); | |
| window.dragging = { | |
| annotation: new_annotation, | |
| corner: window.BL | |
| } | |
| mark_frame_as_edited(); | |
| window.draw() | |
| } | |
| delete_annotation = (annotation) => { | |
| window.annotations = window.annotations.filter(function (a) { | |
| return a !== annotation; | |
| }); | |
| window.dragging = false; | |
| window.hover = false; | |
| mark_frame_as_edited(); | |
| window.draw(); | |
| } | |
| window.init(); | |
| } |