Xenova HF Staff commited on
Commit
1f97e39
·
verified ·
1 Parent(s): 2176e6e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +207 -19
index.html CHANGED
@@ -1,19 +1,207 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>RF-DETR WebGPU</title>
7
+ <link rel="stylesheet" href="style.css" />
8
+ </head>
9
+ <body>
10
+
11
+ <h1>RF-DETR WebGPU</h1>
12
+ <div class="subtitle">
13
+ Real-Time Detection Transformers<br>
14
+ running 100% locally in your browser.
15
+ </div>
16
+
17
+ <div class="container">
18
+ <div id="status">
19
+ <div class="spinner"></div>
20
+ <div id="status-content">
21
+ <div id="status-text">Initializing...</div>
22
+ <div id="status-sub">Please allow camera access</div>
23
+ </div>
24
+ </div>
25
+ <div id="fps">FPS: 0.0</div>
26
+ <video id="webcam" autoplay playsinline muted></video>
27
+ <canvas id="overlay"></canvas>
28
+ </div>
29
+
30
+ <div class="controls">
31
+ <label class="control-label">
32
+ <span>Threshold</span>
33
+ <input type="range" id="threshold" min="0" max="1" step="0.01" value="0.5">
34
+ <span id="thresh-val">0.50</span>
35
+ </label>
36
+ </div>
37
+
38
+ <footer>
39
+ Powered by <a href="https://github.com/huggingface/transformers.js" target="_blank">Transformers.js v4</a>
40
+ </footer>
41
+
42
+ <script type="module">
43
+ import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@next';
44
+
45
+ const video = document.getElementById('webcam');
46
+ const overlay = document.getElementById('overlay');
47
+ const status = document.getElementById('status');
48
+ const statusText = document.getElementById('status-text');
49
+ const statusSub = document.getElementById('status-sub');
50
+ const fpsElem = document.getElementById('fps');
51
+ const slider = document.getElementById('threshold');
52
+ const sliderVal = document.getElementById('thresh-val');
53
+
54
+ let detector;
55
+ let lastTime = performance.now();
56
+ let threshold = 0.5;
57
+
58
+ const inputCanvas = document.createElement('canvas');
59
+ const inputCtx = inputCanvas.getContext('2d', { willReadFrequently: true });
60
+
61
+ const COLORS = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899'];
62
+
63
+ slider.addEventListener('input', (e) => {
64
+ threshold = parseFloat(e.target.value);
65
+ sliderVal.textContent = threshold.toFixed(2);
66
+ });
67
+
68
+ // Handle high DPI displays
69
+ function resizeOverlay() {
70
+ const width = video.clientWidth;
71
+ const height = video.clientHeight;
72
+ const dpr = window.devicePixelRatio || 1;
73
+
74
+ overlay.width = width * dpr;
75
+ overlay.height = height * dpr;
76
+
77
+ const ctx = overlay.getContext('2d');
78
+ ctx.scale(dpr, dpr);
79
+
80
+ inputCanvas.width = video.videoWidth;
81
+ inputCanvas.height = video.videoHeight;
82
+ }
83
+
84
+ window.addEventListener('resize', resizeOverlay);
85
+
86
+ // 1. Start Camera
87
+ try {
88
+ const stream = await navigator.mediaDevices.getUserMedia({
89
+ video: {
90
+ facingMode: 'environment',
91
+ width: { ideal: 640 },
92
+ height: { ideal: 480 }
93
+ },
94
+ audio: false
95
+ });
96
+
97
+ video.srcObject = stream;
98
+ await new Promise(r => video.onloadedmetadata = r);
99
+ video.play();
100
+ resizeOverlay();
101
+
102
+ } catch (e) {
103
+ statusText.textContent = "Camera Error";
104
+ statusSub.textContent = e.message;
105
+ document.querySelector('.spinner').style.display = 'none';
106
+ throw e;
107
+ }
108
+
109
+ // 2. Load Model
110
+ statusText.textContent = "Loading Model...";
111
+ statusSub.textContent = "Downloading RF-DETR Nano (fp32)";
112
+
113
+ try {
114
+ detector = await pipeline('object-detection', 'onnx-community/rfdetr_nano-ONNX', {
115
+ device: 'webgpu',
116
+ dtype: 'fp32',
117
+ });
118
+
119
+ // 3. Warmup
120
+ statusText.textContent = "Compiling Shaders...";
121
+ statusSub.textContent = "This may take a moment";
122
+
123
+ inputCtx.drawImage(video, 0, 0, inputCanvas.width, inputCanvas.height);
124
+ await detector(inputCanvas, { threshold: 0.5, percentage: true });
125
+
126
+ status.style.opacity = '0';
127
+ setTimeout(() => status.style.display = 'none', 300);
128
+
129
+ } catch (e) {
130
+ statusText.textContent = "Model Error";
131
+ statusSub.textContent = e.message;
132
+ document.querySelector('.spinner').style.display = 'none';
133
+ throw e;
134
+ }
135
+
136
+ // 4. Render Loop
137
+ async function loop() {
138
+ const now = performance.now();
139
+ const dt = now - lastTime;
140
+ lastTime = now;
141
+
142
+ if (dt > 0) {
143
+ fpsElem.textContent = `FPS: ${(1000 / dt).toFixed(1)}`;
144
+ }
145
+
146
+ inputCtx.drawImage(video, 0, 0, inputCanvas.width, inputCanvas.height);
147
+
148
+ const results = await detector(inputCanvas, {
149
+ threshold: threshold,
150
+ percentage: true
151
+ });
152
+ drawResults(results);
153
+
154
+ requestAnimationFrame(loop);
155
+ }
156
+
157
+ function drawResults(results) {
158
+ const ctx = overlay.getContext('2d');
159
+ const w = video.clientWidth;
160
+ const h = video.clientHeight;
161
+
162
+ // Clear canvas
163
+ ctx.save();
164
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
165
+ ctx.clearRect(0, 0, overlay.width, overlay.height);
166
+ ctx.restore();
167
+
168
+ // Set styles common to all results
169
+ ctx.font = '600 13px system-ui';
170
+ ctx.lineWidth = 2.5;
171
+
172
+ results.forEach((res, i) => {
173
+ const { box, label, score } = res;
174
+ const color = COLORS[i % COLORS.length];
175
+
176
+ const x1 = box.xmin * w;
177
+ const y1 = box.ymin * h;
178
+ const width = (box.xmax - box.xmin) * w;
179
+ const height = (box.ymax - box.ymin) * h;
180
+
181
+ // Box
182
+ ctx.strokeStyle = color;
183
+ ctx.beginPath();
184
+ ctx.roundRect(x1, y1, width, height, 6);
185
+ ctx.stroke();
186
+
187
+ // Label
188
+ ctx.fillStyle = color;
189
+ const text = `${label} ${(score*100).toFixed(0)}%`;
190
+ const textMetrics = ctx.measureText(text);
191
+ const textWidth = textMetrics.width;
192
+ const textHeight = 22;
193
+
194
+ ctx.beginPath();
195
+ ctx.roundRect(x1, y1 - textHeight - 4, textWidth + 12, textHeight, 4);
196
+ ctx.fill();
197
+
198
+ ctx.fillStyle = 'white';
199
+ ctx.fillText(text, x1 + 6, y1 - 9);
200
+ });
201
+ }
202
+
203
+ requestAnimationFrame(loop);
204
+
205
+ </script>
206
+ </body>
207
+ </html>