() => { 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 += `
`; } 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(); }