Spaces:
Paused
Paused
| <!-- CONFIG_HASH: 699e5e95747aae42d452fa7b4d557076_51f6085f563f5d680c0377ce351f17b0 --> | |
| <!-- Generated annotation layout file --> | |
| <!-- This file was automatically generated based on the annotation schemes in your config --> | |
| <!-- You can customize this file to modify the layout of your annotation interface --> | |
| <!-- Changes to this file will be preserved across server restarts --> | |
| <div class="annotation_schema"> | |
| <form id="object_detection" class="annotation-form image-annotation" action="javascript:void(0)"> | |
| <fieldset schema="object_detection"> | |
| <legend>Draw boxes around objects you see in the image. Use the polygon tool for irregular shapes.</legend> | |
| <!-- Image Annotation Container --> | |
| <div class="image-annotation-container" data-schema="object_detection" data-ai-enabled="false"> | |
| <!-- Toolbar --> | |
| <div class="image-annotation-toolbar"> | |
| <!-- Tool buttons --> | |
| <div class="tool-group"> | |
| <span class="tool-group-label">Tools:</span> | |
| <button type="button" class="tool-btn" data-tool="bbox" title="Bounding Box (B)">□ Box</button> | |
| <button type="button" class="tool-btn" data-tool="polygon" title="Polygon (P)">⬡ Polygon</button> | |
| </div> | |
| <!-- Label selector --> | |
| <div class="label-group"> | |
| <span class="tool-group-label">Label:</span> | |
| <button type="button" class="label-btn" data-label="person" data-color="#FF6B6B" title="person (1)" style="--label-color: #FF6B6B;"><span class="label-color-dot" style="background-color: #FF6B6B;"></span>person</button> | |
| <button type="button" class="label-btn" data-label="vehicle" data-color="#4ECDC4" title="vehicle (2)" style="--label-color: #4ECDC4;"><span class="label-color-dot" style="background-color: #4ECDC4;"></span>vehicle</button> | |
| <button type="button" class="label-btn" data-label="animal" data-color="#45B7D1" title="animal (3)" style="--label-color: #45B7D1;"><span class="label-color-dot" style="background-color: #45B7D1;"></span>animal</button> | |
| <button type="button" class="label-btn" data-label="object" data-color="#96CEB4" title="object (4)" style="--label-color: #96CEB4;"><span class="label-color-dot" style="background-color: #96CEB4;"></span>object</button> | |
| </div> | |
| <!-- Zoom controls --> | |
| <div class="zoom-group"> | |
| <button type="button" class="zoom-btn" data-action="zoom-in" title="Zoom In (+)">+</button> | |
| <button type="button" class="zoom-btn" data-action="zoom-out" title="Zoom Out (-)">-</button> | |
| <button type="button" class="zoom-btn" data-action="zoom-fit" title="Fit to View (0)">Fit</button> | |
| <button type="button" class="zoom-btn" data-action="zoom-reset" title="Reset Zoom">100%</button> | |
| </div> | |
| <!-- Edit controls --> | |
| <div class="edit-group"> | |
| <button type="button" class="edit-btn" data-action="undo" title="Undo (Ctrl+Z)">Undo</button> | |
| <button type="button" class="edit-btn" data-action="redo" title="Redo (Ctrl+Shift+Z)">Redo</button> | |
| <button type="button" class="edit-btn delete-btn" data-action="delete" title="Delete Selected (Del)">Delete</button> | |
| </div> | |
| <!-- Brush size control (shown when brush/eraser selected) --> | |
| <div class="brush-size-group" style="display: none;"> | |
| <span class="tool-group-label">Size:</span> | |
| <input type="range" class="brush-size-slider" min="1" max="100" value="20" title="Brush Size"> | |
| <span class="brush-size-value">20</span> | |
| </div> | |
| <!-- Annotation count --> | |
| <div class="count-group"> | |
| <span class="annotation-count">Annotations: <span class="count-value">0</span></span> | |
| </div> | |
| </div> | |
| <!-- Canvas wrapper --> | |
| <div class="canvas-wrapper"> | |
| <canvas id="canvas-object_detection" class="annotation-canvas"></canvas> | |
| <!-- Mask canvas for segmentation (overlaid on top) --> | |
| <canvas id="mask-canvas-object_detection" class="mask-canvas" style="display: none;"></canvas> | |
| </div> | |
| <!-- Hidden input for storing annotation data --> | |
| <input type="hidden" | |
| name="object_detection" | |
| id="input-object_detection" | |
| class="annotation-data-input" | |
| value=""> | |
| <!-- Hidden input for storing mask data (RLE encoded) --> | |
| <input type="hidden" | |
| name="object_detection_masks" | |
| id="mask-input-object_detection" | |
| class="mask-data-input" | |
| value=""> | |
| </div> | |
| <!-- Initialize the annotation manager --> | |
| <script> | |
| (function() { | |
| // Wait for DOM and dependencies | |
| function initWhenReady() { | |
| if (typeof ImageAnnotationManager === 'undefined') { | |
| setTimeout(initWhenReady, 100); | |
| return; | |
| } | |
| var container = document.querySelector('.image-annotation-container[data-schema="object_detection"]'); | |
| if (!container) return; | |
| var config = {"schemaName": "object_detection", "tools": ["bbox", "polygon"], "labels": [{"name": "person", "color": "#FF6B6B", "key_value": "1"}, {"name": "vehicle", "color": "#4ECDC4", "key_value": "2"}, {"name": "animal", "color": "#45B7D1", "key_value": "3"}, {"name": "object", "color": "#96CEB4", "key_value": "4"}], "zoomEnabled": true, "panEnabled": true, "minAnnotations": 1, "maxAnnotations": null, "freeformBrushSize": 5, "freeformSimplify": true, "brushSize": 20, "eraserSize": 20, "maskOpacity": 0.5, "aiSupport": false, "aiFeatures": {}, "sourceField": ""}; | |
| var canvasId = 'canvas-object_detection'; | |
| var inputId = 'input-object_detection'; | |
| // Get image URL from the instance display | |
| // Priority: 1) data-image-url on #text-content, 2) img src, 3) text content itself | |
| var instanceContainer = document.getElementById('instance-text'); | |
| console.log('[ImageAnnotation] Instance container:', instanceContainer); | |
| var textContent = instanceContainer ? instanceContainer.querySelector('#text-content') : null; | |
| console.log('[ImageAnnotation] Text content element:', textContent); | |
| var imageUrl = null; | |
| // Method 1: Check data-image-url attribute (set by template when has_image_annotation=true) | |
| if (textContent) { | |
| imageUrl = textContent.getAttribute('data-image-url'); | |
| if (imageUrl) { | |
| console.log('[ImageAnnotation] Found URL from data-image-url:', imageUrl); | |
| } | |
| } | |
| // Method 2: Fallback to looking for an img element with data-source-url or src | |
| if (!imageUrl && instanceContainer) { | |
| var imgElement = instanceContainer.querySelector('img'); | |
| if (imgElement) { | |
| imageUrl = imgElement.getAttribute('data-source-url') || imgElement.src; | |
| console.log('[ImageAnnotation] Found URL from img element:', imageUrl); | |
| } | |
| } | |
| // Method 3: If text content looks like a URL, use it directly | |
| if (!imageUrl && textContent) { | |
| var textVal = textContent.textContent.trim(); | |
| if (textVal.match(/^https?:\/\/.+\.(jpg|jpeg|png|gif|webp|svg)/i)) { | |
| imageUrl = textVal; | |
| console.log('[ImageAnnotation] Found URL from text content:', imageUrl); | |
| } | |
| } | |
| // Method 4: Check source_field in instance data (via display fields) | |
| if (!imageUrl && config.sourceField) { | |
| var displayFields = document.querySelectorAll('[data-field-key="' + config.sourceField + '"]'); | |
| displayFields.forEach(function(field) { | |
| var url = field.getAttribute('data-source-url') || field.getAttribute('src'); | |
| if (url && !imageUrl) { | |
| imageUrl = url; | |
| console.log('[ImageAnnotation] Found URL from source_field display:', imageUrl); | |
| } | |
| }); | |
| } | |
| if (!imageUrl) { | |
| console.error('[ImageAnnotation] No image URL found! Check that the instance data contains an image URL.'); | |
| } | |
| // Initialize manager | |
| console.log('Initializing ImageAnnotationManager with canvas:', canvasId); | |
| var manager = new ImageAnnotationManager(canvasId, inputId, config); | |
| // Store reference on container | |
| container.annotationManager = manager; | |
| // Load image if available | |
| if (imageUrl) { | |
| console.log('Calling loadImage with:', imageUrl); | |
| manager.loadImage(imageUrl); | |
| } else { | |
| console.warn('No image URL found for annotation'); | |
| } | |
| // Wire up toolbar buttons | |
| container.querySelectorAll('.tool-btn').forEach(function(btn) { | |
| btn.addEventListener('click', function() { | |
| var tool = this.dataset.tool; | |
| manager.setTool(tool); | |
| container.querySelectorAll('.tool-btn').forEach(function(b) { | |
| b.classList.remove('active'); | |
| }); | |
| this.classList.add('active'); | |
| // Show/hide brush size control for brush/eraser tools | |
| var brushSizeGroup = container.querySelector('.brush-size-group'); | |
| if (brushSizeGroup) { | |
| brushSizeGroup.style.display = (tool === 'brush' || tool === 'eraser') ? 'flex' : 'none'; | |
| } | |
| }); | |
| }); | |
| // Wire up brush size slider | |
| var brushSlider = container.querySelector('.brush-size-slider'); | |
| var brushSizeValue = container.querySelector('.brush-size-value'); | |
| if (brushSlider) { | |
| brushSlider.addEventListener('input', function() { | |
| var size = parseInt(this.value); | |
| if (brushSizeValue) brushSizeValue.textContent = size; | |
| manager.setBrushSize(size); | |
| }); | |
| } | |
| container.querySelectorAll('.label-btn').forEach(function(btn) { | |
| btn.addEventListener('click', function() { | |
| var label = this.dataset.label; | |
| var color = this.dataset.color; | |
| manager.setLabel(label, color); | |
| container.querySelectorAll('.label-btn').forEach(function(b) { | |
| b.classList.remove('active'); | |
| }); | |
| this.classList.add('active'); | |
| }); | |
| }); | |
| container.querySelectorAll('.zoom-btn').forEach(function(btn) { | |
| btn.addEventListener('click', function() { | |
| var action = this.dataset.action; | |
| if (action === 'zoom-in') manager.zoom(1.2); | |
| else if (action === 'zoom-out') manager.zoom(0.8); | |
| else if (action === 'zoom-fit') manager.zoomFit(); | |
| else if (action === 'zoom-reset') manager.zoomReset(); | |
| }); | |
| }); | |
| container.querySelectorAll('.edit-btn').forEach(function(btn) { | |
| btn.addEventListener('click', function() { | |
| var action = this.dataset.action; | |
| if (action === 'undo') manager.undo(); | |
| else if (action === 'redo') manager.redo(); | |
| else if (action === 'delete') manager.deleteSelected(); | |
| }); | |
| }); | |
| // Set default tool and label | |
| var firstToolBtn = container.querySelector('.tool-btn'); | |
| if (firstToolBtn) firstToolBtn.click(); | |
| var firstLabelBtn = container.querySelector('.label-btn'); | |
| if (firstLabelBtn) firstLabelBtn.click(); | |
| // Update count display | |
| manager.onAnnotationChange = function(count) { | |
| var countEl = container.querySelector('.count-value'); | |
| if (countEl) countEl.textContent = count; | |
| }; | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initWhenReady); | |
| } else { | |
| initWhenReady(); | |
| } | |
| })(); | |
| </script> | |
| </fieldset> | |
| </form> | |
| <form id="image_quality" class="annotation-form radio shadcn-radio-container" action="javascript:void(0)" data-annotation-id="1" data-annotation-type="radio" data-schema-name="image_quality" data-grid-columns="1"> | |
| <fieldset schema="image_quality"> | |
| <legend class="shadcn-radio-title">How clear is this image?</legend> | |
| <div class="shadcn-radio-options"> | |
| <div class="shadcn-radio-option"> | |
| <input class="image_quality shadcn-radio-input annotation-input" | |
| type="radio" | |
| id="image_quality_Very clear_radio" | |
| name="image_quality" | |
| value="Very clear" | |
| selection_constraint="single" | |
| schema="image_quality" | |
| label_name="Very clear" | |
| onclick="onlyOne(this);registerAnnotation(this);" | |
| validation="" | |
| data-key="c"> | |
| <label for="image_quality_Very clear_radio" class="shadcn-radio-label" >Very Clear <span class="keybinding-badge">C</span></label> | |
| </div> | |
| <div class="shadcn-radio-option"> | |
| <input class="image_quality shadcn-radio-input annotation-input" | |
| type="radio" | |
| id="image_quality_Somewhat clear_radio" | |
| name="image_quality" | |
| value="Somewhat clear" | |
| selection_constraint="single" | |
| schema="image_quality" | |
| label_name="Somewhat clear" | |
| onclick="onlyOne(this);registerAnnotation(this);" | |
| validation="" | |
| data-key="s"> | |
| <label for="image_quality_Somewhat clear_radio" class="shadcn-radio-label" >Somewhat Clear <span class="keybinding-badge">S</span></label> | |
| </div> | |
| <div class="shadcn-radio-option"> | |
| <input class="image_quality shadcn-radio-input annotation-input" | |
| type="radio" | |
| id="image_quality_Blurry_radio" | |
| name="image_quality" | |
| value="Blurry" | |
| selection_constraint="single" | |
| schema="image_quality" | |
| label_name="Blurry" | |
| onclick="onlyOne(this);registerAnnotation(this);" | |
| validation="" | |
| data-key="b"> | |
| <label for="image_quality_Blurry_radio" class="shadcn-radio-label" >Blurry <span class="keybinding-badge">B</span></label> | |
| </div> | |
| </div></fieldset></form> | |
| </div> | |