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; | |
} | |
}); |