Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Smart Distance Estimation</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script> | |
| <style> | |
| .camera-feed { | |
| position: relative; | |
| width: 100%; | |
| height: 0; | |
| padding-bottom: 75%; /* 4:3 aspect ratio */ | |
| background-color: #f0f0f0; | |
| border-radius: 0.5rem; | |
| overflow: hidden; | |
| } | |
| #canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .measurement-line { | |
| stroke: #3b82f6; | |
| stroke-width: 2; | |
| stroke-dasharray: 5; | |
| } | |
| .dimension-text { | |
| font-size: 12px; | |
| fill: #3b82f6; | |
| font-weight: bold; | |
| background-color: rgba(255, 255, 255, 0.7); | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| } | |
| .object-label { | |
| font-size: 10px; | |
| fill: white; | |
| background-color: rgba(59, 130, 246, 0.9); | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| } | |
| .depth-map { | |
| position: relative; | |
| width: 100%; | |
| height: 0; | |
| padding-bottom: 75%; | |
| background-color: #1e293b; | |
| border-radius: 0.5rem; | |
| overflow: hidden; | |
| } | |
| .toggle-container { | |
| position: relative; | |
| display: inline-block; | |
| width: 60px; | |
| height: 30px; | |
| } | |
| .toggle-checkbox { | |
| opacity: 0; | |
| width: 0; | |
| height: 0; | |
| } | |
| .toggle-slider { | |
| position: absolute; | |
| cursor: pointer; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: #ccc; | |
| transition: .4s; | |
| border-radius: 34px; | |
| } | |
| .toggle-slider:before { | |
| position: absolute; | |
| content: ""; | |
| height: 22px; | |
| width: 22px; | |
| left: 4px; | |
| bottom: 4px; | |
| background-color: white; | |
| transition: .4s; | |
| border-radius: 50%; | |
| } | |
| .toggle-checkbox:checked + .toggle-slider { | |
| background-color: #3b82f6; | |
| } | |
| .toggle-checkbox:checked + .toggle-slider:before { | |
| transform: translateX(30px); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <header class="mb-8"> | |
| <h1 class="text-3xl font-bold text-gray-800 mb-2">Smart Distance Estimation</h1> | |
| <p class="text-gray-600">Measure real-world distances between objects using your smartphone's camera and LiDAR</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8"> | |
| <!-- Camera Feed Section --> | |
| <div class="bg-white rounded-lg shadow-md p-4"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Camera Feed</h2> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center"> | |
| <span class="mr-2 text-sm text-gray-600">LiDAR</span> | |
| <label class="toggle-container"> | |
| <input type="checkbox" id="lidarToggle" class="toggle-checkbox" checked> | |
| <span class="toggle-slider"></span> | |
| </label> | |
| </div> | |
| <button id="captureBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md text-sm"> | |
| Capture Image | |
| </button> | |
| </div> | |
| </div> | |
| <div class="camera-feed"> | |
| <video id="video" autoplay playsinline class="w-full h-full object-cover" style="display: none;"></video> | |
| <canvas id="canvas" width="640" height="480"></canvas> | |
| </div> | |
| <div class="mt-4 flex justify-center"> | |
| <button id="startCameraBtn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-md text-sm mr-2"> | |
| Start Camera | |
| </button> | |
| <button id="stopCameraBtn" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm"> | |
| Stop Camera | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Depth Map Section --> | |
| <div class="bg-white rounded-lg shadow-md p-4"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-700">Depth Estimation</h2> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center"> | |
| <span class="mr-2 text-sm text-gray-600">Auto-detect</span> | |
| <label class="toggle-container"> | |
| <input type="checkbox" id="autoDetectToggle" class="toggle-checkbox" checked> | |
| <span class="toggle-slider"></span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="depth-map"> | |
| <canvas id="depthCanvas" width="640" height="480"></canvas> | |
| </div> | |
| <div class="mt-4"> | |
| <div class="flex justify-between mb-2"> | |
| <span class="text-sm text-gray-600">Near</span> | |
| <span class="text-sm text-gray-600">Far</span> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> | |
| <div class="bg-gradient-to-r from-blue-500 via-purple-500 to-red-500 h-2.5 rounded-full" style="width: 100%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Measurement Results --> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-4">Measurement Results</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <h3 class="text-sm font-medium text-gray-500 mb-1">Horizontal Distance</h3> | |
| <p class="text-2xl font-bold text-gray-800"> | |
| <span id="horizontalDistance">0.00</span> <span class="text-sm">meters</span> | |
| </p> | |
| </div> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <h3 class="text-sm font-medium text-gray-500 mb-1">Vertical Distance</h3> | |
| <p class="text-2xl font-bold text-gray-800"> | |
| <span id="verticalDistance">0.00</span> <span class="text-sm">meters</span> | |
| </p> | |
| </div> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <h3 class="text-sm font-medium text-gray-500 mb-1">Depth Distance</h3> | |
| <p class="text-2xl font-bold text-gray-800"> | |
| <span id="depthDistance">0.00</span> <span class="text-sm">meters</span> | |
| </p> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <h3 class="text-lg font-medium text-gray-700 mb-2">Detected Objects</h3> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Object</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Width (m)</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Height (m)</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Distance (m)</th> | |
| </tr> | |
| </thead> | |
| <tbody id="objectTableBody" class="bg-white divide-y divide-gray-200"> | |
| <!-- Objects will be added here dynamically --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controls Section --> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-4">Measurement Controls</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Measurement Mode</h3> | |
| <div class="space-y-3"> | |
| <div class="flex items-center"> | |
| <input id="singleMeasurement" name="measurementMode" type="radio" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="singleMeasurement" class="ml-2 block text-sm text-gray-700">Single Measurement</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="multiMeasurement" name="measurementMode" type="radio" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="multiMeasurement" class="ml-2 block text-sm text-gray-700">Multi Measurement</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="autoMeasurement" name="measurementMode" type="radio" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="autoMeasurement" class="ml-2 block text-sm text-gray-700">Auto Detect Objects</label> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Reference Points</h3> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center"> | |
| <input id="floorCeiling" name="referencePoints" type="radio" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="floorCeiling" class="ml-2 block text-sm text-gray-700">Floor/Ceiling</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="wallEdge" name="referencePoints" type="radio" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300"> | |
| <label for="wallEdge" class="ml-2 block text-sm text-gray-700">Wall Edge</label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-medium text-gray-700 mb-3">Settings</h3> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="unitSelect" class="block text-sm font-medium text-gray-700">Measurement Unit</label> | |
| <select id="unitSelect" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"> | |
| <option value="meters">Meters</option> | |
| <option value="feet">Feet</option> | |
| <option value="inches">Inches</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="confidenceThreshold" class="block text-sm font-medium text-gray-700">Confidence Threshold</label> | |
| <input type="range" id="confidenceThreshold" min="0" max="100" value="70" class="mt-1 w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"> | |
| <div class="flex justify-between text-xs text-gray-500"> | |
| <span>0%</span> | |
| <span id="confidenceValue">70%</span> | |
| <span>100%</span> | |
| </div> | |
| </div> | |
| <div class="flex items-center"> | |
| <input id="showDepthMap" type="checkbox" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"> | |
| <label for="showDepthMap" class="ml-2 block text-sm text-gray-700">Show Depth Map</label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const video = document.getElementById('video'); | |
| const canvas = document.getElementById('canvas'); | |
| const depthCanvas = document.getElementById('depthCanvas'); | |
| const startCameraBtn = document.getElementById('startCameraBtn'); | |
| const stopCameraBtn = document.getElementById('stopCameraBtn'); | |
| const captureBtn = document.getElementById('captureBtn'); | |
| const horizontalDistanceEl = document.getElementById('horizontalDistance'); | |
| const verticalDistanceEl = document.getElementById('verticalDistance'); | |
| const depthDistanceEl = document.getElementById('depthDistance'); | |
| const objectTableBody = document.getElementById('objectTableBody'); | |
| const confidenceThreshold = document.getElementById('confidenceThreshold'); | |
| const confidenceValue = document.getElementById('confidenceValue'); | |
| const lidarToggle = document.getElementById('lidarToggle'); | |
| const autoDetectToggle = document.getElementById('autoDetectToggle'); | |
| const showDepthMap = document.getElementById('showDepthMap'); | |
| // Canvas context | |
| const ctx = canvas.getContext('2d'); | |
| const depthCtx = depthCanvas.getContext('2d'); | |
| // State variables | |
| let isCameraOn = false; | |
| let measurements = []; | |
| let selectedPoints = []; | |
| let detectedObjects = []; | |
| // Event Listeners | |
| startCameraBtn.addEventListener('click', startCamera); | |
| stopCameraBtn.addEventListener('click', stopCamera); | |
| captureBtn.addEventListener('click', captureImage); | |
| canvas.addEventListener('click', handleCanvasClick); | |
| confidenceThreshold.addEventListener('input', updateConfidenceValue); | |
| showDepthMap.addEventListener('change', toggleDepthMap); | |
| // Initialize | |
| function init() { | |
| // Set up initial UI state | |
| stopCameraBtn.disabled = true; | |
| captureBtn.disabled = true; | |
| // Mock data for demonstration | |
| setTimeout(() => { | |
| mockDetectObjects(); | |
| }, 1000); | |
| } | |
| // Camera functions | |
| async function startCamera() { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { | |
| facingMode: 'environment', | |
| width: { ideal: 1280 }, | |
| height: { ideal: 720 } | |
| } | |
| }); | |
| video.srcObject = stream; | |
| video.style.display = 'block'; | |
| isCameraOn = true; | |
| startCameraBtn.disabled = true; | |
| stopCameraBtn.disabled = false; | |
| captureBtn.disabled = false; | |
| // Start processing frames | |
| processVideo(); | |
| } catch (err) { | |
| console.error("Error accessing camera:", err); | |
| alert("Could not access the camera. Please ensure you've granted camera permissions."); | |
| } | |
| } | |
| function stopCamera() { | |
| const stream = video.srcObject; | |
| if (stream) { | |
| const tracks = stream.getTracks(); | |
| tracks.forEach(track => track.stop()); | |
| video.srcObject = null; | |
| video.style.display = 'none'; | |
| } | |
| isCameraOn = false; | |
| startCameraBtn.disabled = false; | |
| stopCameraBtn.disabled = true; | |
| captureBtn.disabled = true; | |
| } | |
| function processVideo() { | |
| if (!isCameraOn) return; | |
| // Draw video frame to canvas | |
| ctx.drawImage(video, 0, 0, canvas.width, canvas.height); | |
| // Simulate object detection (in a real app, this would call your backend) | |
| if (autoDetectToggle.checked) { | |
| simulateObjectDetection(); | |
| } | |
| // Draw measurements | |
| drawMeasurements(); | |
| // Continue processing | |
| requestAnimationFrame(processVideo); | |
| } | |
| function captureImage() { | |
| // In a real app, this would send the image to your backend for processing | |
| alert("Image captured! In a real implementation, this would be sent to the backend for processing."); | |
| // For demo purposes, simulate detection | |
| simulateObjectDetection(); | |
| } | |
| // Measurement functions | |
| function handleCanvasClick(event) { | |
| if (!isCameraOn) return; | |
| const rect = canvas.getBoundingClientRect(); | |
| const x = event.clientX - rect.left; | |
| const y = event.clientY - rect.top; | |
| selectedPoints.push({ x, y }); | |
| if (selectedPoints.length === 2) { | |
| // Calculate distance between points (in a real app, this would use your backend) | |
| const distance = calculateDistance(selectedPoints[0], selectedPoints[1]); | |
| measurements.push({ | |
| points: [...selectedPoints], | |
| distance: distance, | |
| type: 'manual' | |
| }); | |
| selectedPoints = []; | |
| updateMeasurementsUI(); | |
| } | |
| drawMeasurements(); | |
| } | |
| function calculateDistance(point1, point2) { | |
| // Simple pixel distance (in a real app, this would convert to real-world units) | |
| const dx = point2.x - point1.x; | |
| const dy = point2.y - point1.y; | |
| return Math.sqrt(dx * dx + dy * dy) / 100; // Simplified conversion | |
| } | |
| function drawMeasurements() { | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw video frame | |
| if (isCameraOn) { | |
| ctx.drawImage(video, 0, 0, canvas.width, canvas.height); | |
| } | |
| // Draw selected points | |
| selectedPoints.forEach(point => { | |
| ctx.beginPath(); | |
| ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI); | |
| ctx.fillStyle = '#3b82f6'; | |
| ctx.fill(); | |
| }); | |
| // Draw measurements | |
| measurements.forEach(measurement => { | |
| const [point1, point2] = measurement.points; | |
| // Draw line | |
| ctx.beginPath(); | |
| ctx.moveTo(point1.x, point1.y); | |
| ctx.lineTo(point2.x, point2.y); | |
| ctx.strokeStyle = '#3b82f6'; | |
| ctx.lineWidth = 2; | |
| ctx.setLineDash([5, 5]); | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Draw distance text | |
| const midX = (point1.x + point2.x) / 2; | |
| const midY = (point1.y + point2.y) / 2; | |
| ctx.font = '12px Arial'; | |
| ctx.fillStyle = '#3b82f6'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(`${measurement.distance.toFixed(2)} m`, midX, midY - 10); | |
| }); | |
| // Draw detected objects | |
| detectedObjects.forEach(obj => { | |
| // Draw bounding box | |
| ctx.strokeStyle = '#10b981'; | |
| ctx.lineWidth = 2; | |
| ctx.strokeRect(obj.x, obj.y, obj.width, obj.height); | |
| // Draw label | |
| ctx.fillStyle = 'rgba(16, 185, 129, 0.8)'; | |
| ctx.fillRect(obj.x, obj.y - 20, ctx.measureText(`${obj.label} (${obj.confidence}%)`).width + 10, 20); | |
| ctx.fillStyle = 'white'; | |
| ctx.font = '12px Arial'; | |
| ctx.textAlign = 'left'; | |
| ctx.fillText(`${obj.label} (${obj.confidence}%)`, obj.x + 5, obj.y - 5); | |
| // Draw center point | |
| ctx.beginPath(); | |
| ctx.arc(obj.x + obj.width/2, obj.y + obj.height/2, 3, 0, 2 * Math.PI); | |
| ctx.fillStyle = '#10b981'; | |
| ctx.fill(); | |
| }); | |
| } | |
| function updateMeasurementsUI() { | |
| if (measurements.length > 0) { | |
| const lastMeasurement = measurements[measurements.length - 1]; | |
| const dx = Math.abs(lastMeasurement.points[1].x - lastMeasurement.points[0].x); | |
| const dy = Math.abs(lastMeasurement.points[1].y - lastMeasurement.points[0].y); | |
| // Determine if measurement is more horizontal or vertical | |
| if (dx > dy) { | |
| horizontalDistanceEl.textContent = lastMeasurement.distance.toFixed(2); | |
| } else { | |
| verticalDistanceEl.textContent = lastMeasurement.distance.toFixed(2); | |
| } | |
| } | |
| } | |
| // Object detection simulation | |
| function simulateObjectDetection() { | |
| // Clear previous detections | |
| detectedObjects = []; | |
| // Generate mock objects | |
| const mockObjects = [ | |
| { label: 'Chair', confidence: 85, x: 150, y: 200, width: 80, height: 120 }, | |
| { label: 'Table', confidence: 92, x: 300, y: 180, width: 150, height: 90 }, | |
| { label: 'Window', confidence: 78, x: 100, y: 50, width: 200, height: 150 } | |
| ]; | |
| // Filter by confidence threshold | |
| const threshold = parseInt(confidenceThreshold.value) / 100; | |
| detectedObjects = mockObjects.filter(obj => obj.confidence / 100 >= threshold); | |
| // Update object table | |
| updateObjectTable(); | |
| // Simulate depth map | |
| simulateDepthMap(); | |
| } | |
| function mockDetectObjects() { | |
| // For demo when camera is off | |
| simulateObjectDetection(); | |
| drawMeasurements(); | |
| } | |
| function updateObjectTable() { | |
| // Clear table | |
| objectTableBody.innerHTML = ''; | |
| // Add rows for each detected object | |
| detectedObjects.forEach(obj => { | |
| // Calculate approximate real-world dimensions (simplified) | |
| const width = (obj.width / 100).toFixed(2); | |
| const height = (obj.height / 100).toFixed(2); | |
| const distance = (Math.sqrt(obj.x*obj.x + obj.y*obj.y) / 200).toFixed(2); | |
| const row = document.createElement('tr'); | |
| row.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${obj.label}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${width}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${height}</td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${distance}</td> | |
| `; | |
| objectTableBody.appendChild(row); | |
| }); | |
| } | |
| // Depth map simulation | |
| function simulateDepthMap() { | |
| // Create gradient for depth effect | |
| const gradient = depthCtx.createLinearGradient(0, 0, depthCanvas.width, depthCanvas.height); | |
| gradient.addColorStop(0, '#0000ff'); // Near (blue) | |
| gradient.addColorStop(0.5, '#ff00ff'); // Medium (purple) | |
| gradient.addColorStop(1, '#ff0000'); // Far (red) | |
| depthCtx.fillStyle = gradient; | |
| depthCtx.fillRect(0, 0, depthCanvas.width, depthCanvas.height); | |
| // Add noise for realism | |
| const imageData = depthCtx.getImageData(0, 0, depthCanvas.width, depthCanvas.height); | |
| const data = imageData.data; | |
| for (let i = 0; i < data.length; i += 4) { | |
| // Add some noise | |
| const noise = Math.random() * 30 - 15; | |
| data[i] = Math.min(255, Math.max(0, data[i] + noise)); | |
| data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + noise)); | |
| data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + noise)); | |
| } | |
| depthCtx.putImageData(imageData, 0, 0); | |
| // Add detected objects to depth map | |
| detectedObjects.forEach(obj => { | |
| depthCtx.strokeStyle = 'white'; | |
| depthCtx.lineWidth = 2; | |
| depthCtx.strokeRect(obj.x, obj.y, obj.width, obj.height); | |
| // Add depth text | |
| const depthValue = (Math.sqrt(obj.x*obj.x + obj.y*obj.y) / 200).toFixed(2); | |
| depthCtx.fillStyle = 'white'; | |
| depthCtx.font = '10px Arial'; | |
| depthCtx.textAlign = 'center'; | |
| depthCtx.fillText(`${depthValue}m`, obj.x + obj.width/2, obj.y + obj.height/2); | |
| }); | |
| // Update depth distance display | |
| if (detectedObjects.length > 0) { | |
| const avgDepth = detectedObjects.reduce((sum, obj) => { | |
| return sum + (Math.sqrt(obj.x*obj.x + obj.y*obj.y) / 200); | |
| }, 0) / detectedObjects.length; | |
| depthDistanceEl.textContent = avgDepth.toFixed(2); | |
| } | |
| } | |
| // UI functions | |
| function updateConfidenceValue() { | |
| confidenceValue.textContent = `${confidenceThreshold.value}%`; | |
| // If auto-detect is on, update detection | |
| if (autoDetectToggle.checked && (isCameraOn || measurements.length > 0)) { | |
| simulateObjectDetection(); | |
| drawMeasurements(); | |
| } | |
| } | |
| function toggleDepthMap() { | |
| depthCanvas.style.display = showDepthMap.checked ? 'block' : 'none'; | |
| } | |
| // Initialize the app | |
| init(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=designfailure/xydistance" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |