File size: 5,315 Bytes
19bb843
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 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;
    }
});