Spaces:
Running
Running
| // const URL = "https://teachablemachine.withgoogle.com/models/uYk-wIQy6/"; | |
| const URL = "https://teachablemachine.withgoogle.com/models/DTgj24KYa/"; | |
| let model, webcam, ctx, labelContainer, maxPredictions; | |
| let isInitialized = false; | |
| // Wait for libraries to load | |
| function waitForLibraries() { | |
| return new Promise((resolve) => { | |
| const checkLibraries = () => { | |
| if (typeof tmPose !== 'undefined' && typeof tf !== 'undefined') { | |
| resolve(); | |
| } else { | |
| setTimeout(checkLibraries, 100); | |
| } | |
| }; | |
| checkLibraries(); | |
| }); | |
| } | |
| async function init() { | |
| if (isInitialized) return; | |
| // First, wait for libraries to load | |
| await waitForLibraries(); | |
| const startBtn = document.getElementById('startBtn'); | |
| const statusIndicator = document.getElementById('statusIndicator'); | |
| const loadingSpinner = document.getElementById('loadingSpinner'); | |
| // Display loading status | |
| startBtn.innerHTML = 'π Loading...'; | |
| startBtn.disabled = true; | |
| loadingSpinner.style.display = 'block'; | |
| try { | |
| const modelURL = URL + "model.json"; | |
| const metadataURL = URL + "metadata.json"; | |
| model = await tmPose.load(modelURL, metadataURL); | |
| maxPredictions = model.getTotalClasses(); | |
| const size = 300; | |
| const flip = true; | |
| webcam = new tmPose.Webcam(size, size, flip); | |
| await webcam.setup(); | |
| await webcam.play(); | |
| window.requestAnimationFrame(loop); | |
| const canvas = document.getElementById("canvas"); | |
| canvas.width = size; | |
| canvas.height = size; | |
| ctx = canvas.getContext("2d"); | |
| labelContainer = document.getElementById("label-container"); | |
| labelContainer.innerHTML = ''; | |
| for (let i = 0; i < maxPredictions; i++) { | |
| const div = document.createElement("div"); | |
| div.className = "prediction-item"; | |
| div.innerHTML = '<div class="prediction-text">Loading...</div>'; | |
| labelContainer.appendChild(div); | |
| } | |
| // Update UI status | |
| startBtn.innerHTML = 'β Detecting...'; | |
| statusIndicator.classList.add('active'); | |
| loadingSpinner.style.display = 'none'; | |
| isInitialized = true; | |
| } catch (error) { | |
| console.error('Initialization failed:', error); | |
| startBtn.innerHTML = 'β Load Failed, Retry'; | |
| startBtn.disabled = false; | |
| loadingSpinner.style.display = 'none'; | |
| } | |
| } | |
| async function loop(timestamp) { | |
| if (webcam) { | |
| webcam.update(); | |
| await predict(); | |
| window.requestAnimationFrame(loop); | |
| } | |
| } | |
| async function predict() { | |
| try { | |
| const { pose, posenetOutput } = await model.estimatePose(webcam.canvas); | |
| const prediction = await model.predict(posenetOutput); | |
| for (let i = 0; i < maxPredictions; i++) { | |
| const probability = (prediction[i].probability * 100).toFixed(1); | |
| const className = prediction[i].className; | |
| // Add emojis and better formatting | |
| let emoji = 'π'; | |
| if (probability > 80) emoji = 'π₯'; | |
| else if (probability > 60) emoji = 'β'; | |
| else if (probability > 40) emoji = 'π'; | |
| const predictionText = `${emoji} ${className}: ${probability}%`; | |
| const predictionElement = labelContainer.childNodes[i]; | |
| if (predictionElement) { | |
| predictionElement.querySelector('.prediction-text').innerHTML = predictionText; | |
| // Add dynamic styling | |
| const confidence = parseFloat(probability); | |
| if (confidence > 50) { | |
| predictionElement.style.borderLeftColor = '#44ff44'; | |
| predictionElement.style.background = 'rgba(68, 255, 68, 0.1)'; | |
| } else if (confidence > 30) { | |
| predictionElement.style.borderLeftColor = '#ffd700'; | |
| predictionElement.style.background = 'rgba(255, 215, 0, 0.1)'; | |
| } else { | |
| predictionElement.style.borderLeftColor = '#ff6b35'; | |
| predictionElement.style.background = 'rgba(255, 107, 53, 0.1)'; | |
| } | |
| } | |
| } | |
| drawPose(pose); | |
| } catch (error) { | |
| console.error('Prediction error:', error); | |
| } | |
| } | |
| function drawPose(pose) { | |
| if (webcam && webcam.canvas && ctx) { | |
| ctx.drawImage(webcam.canvas, 0, 0); | |
| if (pose) { | |
| const minPartConfidence = 0.5; | |
| tmPose.drawKeypoints(pose.keypoints, minPartConfidence, ctx); | |
| tmPose.drawSkeleton(pose.keypoints, minPartConfidence, ctx); | |
| } | |
| } | |
| } | |
| // Check if libraries are loaded after page load | |
| window.addEventListener('load', async function() { | |
| try { | |
| await waitForLibraries(); | |
| console.log('π NBA Pose Detector Ready!'); | |
| // Display ready status | |
| const startBtn = document.getElementById('startBtn'); | |
| startBtn.innerHTML = 'π Start Detection'; | |
| startBtn.style.opacity = '1'; | |
| } catch (error) { | |
| console.error('Library load failed:', error); | |
| const startBtn = document.getElementById('startBtn'); | |
| startBtn.innerHTML = 'β Load Failed'; | |
| startBtn.disabled = true; | |
| } | |
| }); |