lmz radames commited on
Commit
9769f61
1 Parent(s): 351b503

pose-update (#2)

Browse files

- add pose model (92dc53a37b584fa311fcb5fad589ef012ddf2ca3)


Co-authored-by: Radamés Ajna <[email protected]>

Files changed (6) hide show
  1. build/m.d.ts +20 -1
  2. build/m.js +92 -134
  3. build/m_bg.wasm +2 -2
  4. build/m_bg.wasm.d.ts +3 -1
  5. index.html +109 -20
  6. yoloWorker.js +7 -2
build/m.d.ts CHANGED
@@ -17,6 +17,23 @@ export class Model {
17
  */
18
  run(image: Uint8Array, conf_threshold: number, iou_threshold: number): string;
19
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
22
 
@@ -25,12 +42,14 @@ export interface InitOutput {
25
  readonly __wbg_model_free: (a: number) => void;
26
  readonly model_new: (a: number, b: number, c: number, d: number, e: number) => void;
27
  readonly model_run: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
 
 
 
28
  readonly main: (a: number, b: number) => number;
29
  readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
30
  readonly __wbindgen_malloc: (a: number, b: number) => number;
31
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
32
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
33
- readonly __wbindgen_exn_store: (a: number) => void;
34
  readonly __wbindgen_start: () => void;
35
  }
36
 
 
17
  */
18
  run(image: Uint8Array, conf_threshold: number, iou_threshold: number): string;
19
  }
20
+ /**
21
+ */
22
+ export class ModelPose {
23
+ free(): void;
24
+ /**
25
+ * @param {Uint8Array} data
26
+ * @param {string} model_size
27
+ */
28
+ constructor(data: Uint8Array, model_size: string);
29
+ /**
30
+ * @param {Uint8Array} image
31
+ * @param {number} conf_threshold
32
+ * @param {number} iou_threshold
33
+ * @returns {string}
34
+ */
35
+ run(image: Uint8Array, conf_threshold: number, iou_threshold: number): string;
36
+ }
37
 
38
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
39
 
 
42
  readonly __wbg_model_free: (a: number) => void;
43
  readonly model_new: (a: number, b: number, c: number, d: number, e: number) => void;
44
  readonly model_run: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
45
+ readonly __wbg_modelpose_free: (a: number) => void;
46
+ readonly modelpose_new: (a: number, b: number, c: number, d: number, e: number) => void;
47
+ readonly modelpose_run: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
48
  readonly main: (a: number, b: number) => number;
49
  readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
50
  readonly __wbindgen_malloc: (a: number, b: number) => number;
51
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
52
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
 
53
  readonly __wbindgen_start: () => void;
54
  }
55
 
build/m.js CHANGED
@@ -33,20 +33,6 @@ function addHeapObject(obj) {
33
  return idx;
34
  }
35
 
36
- function getObject(idx) { return heap[idx]; }
37
-
38
- function dropObject(idx) {
39
- if (idx < 132) return;
40
- heap[idx] = heap_next;
41
- heap_next = idx;
42
- }
43
-
44
- function takeObject(idx) {
45
- const ret = getObject(idx);
46
- dropObject(idx);
47
- return ret;
48
- }
49
-
50
  let WASM_VECTOR_LEN = 0;
51
 
52
  function passArray8ToWasm0(arg, malloc) {
@@ -118,12 +104,18 @@ function getInt32Memory0() {
118
  return cachedInt32Memory0;
119
  }
120
 
121
- function handleError(f, args) {
122
- try {
123
- return f.apply(this, args);
124
- } catch (e) {
125
- wasm.__wbindgen_exn_store(addHeapObject(e));
126
- }
 
 
 
 
 
 
127
  }
128
  /**
129
  */
@@ -204,6 +196,85 @@ export class Model {
204
  }
205
  }
206
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  async function __wbg_load(module, imports) {
209
  if (typeof Response === 'function' && module instanceof Response) {
@@ -243,125 +314,12 @@ function __wbg_get_imports() {
243
  const ret = new Error(getStringFromWasm0(arg0, arg1));
244
  return addHeapObject(ret);
245
  };
246
- imports.wbg.__wbg_log_20252ed396e16790 = function(arg0, arg1) {
247
  console.log(getStringFromWasm0(arg0, arg1));
248
  };
249
- imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
250
- takeObject(arg0);
251
- };
252
- imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
253
- const ret = getStringFromWasm0(arg0, arg1);
254
- return addHeapObject(ret);
255
- };
256
- imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
257
- const ret = getObject(arg0);
258
- return addHeapObject(ret);
259
- };
260
- imports.wbg.__wbg_crypto_c48a774b022d20ac = function(arg0) {
261
- const ret = getObject(arg0).crypto;
262
- return addHeapObject(ret);
263
- };
264
- imports.wbg.__wbindgen_is_object = function(arg0) {
265
- const val = getObject(arg0);
266
- const ret = typeof(val) === 'object' && val !== null;
267
- return ret;
268
- };
269
- imports.wbg.__wbg_process_298734cf255a885d = function(arg0) {
270
- const ret = getObject(arg0).process;
271
- return addHeapObject(ret);
272
- };
273
- imports.wbg.__wbg_versions_e2e78e134e3e5d01 = function(arg0) {
274
- const ret = getObject(arg0).versions;
275
- return addHeapObject(ret);
276
- };
277
- imports.wbg.__wbg_node_1cd7a5d853dbea79 = function(arg0) {
278
- const ret = getObject(arg0).node;
279
- return addHeapObject(ret);
280
- };
281
- imports.wbg.__wbindgen_is_string = function(arg0) {
282
- const ret = typeof(getObject(arg0)) === 'string';
283
- return ret;
284
- };
285
- imports.wbg.__wbg_msCrypto_bcb970640f50a1e8 = function(arg0) {
286
- const ret = getObject(arg0).msCrypto;
287
- return addHeapObject(ret);
288
- };
289
- imports.wbg.__wbg_require_8f08ceecec0f4fee = function() { return handleError(function () {
290
- const ret = module.require;
291
- return addHeapObject(ret);
292
- }, arguments) };
293
- imports.wbg.__wbindgen_is_function = function(arg0) {
294
- const ret = typeof(getObject(arg0)) === 'function';
295
- return ret;
296
- };
297
- imports.wbg.__wbg_getRandomValues_37fa2ca9e4e07fab = function() { return handleError(function (arg0, arg1) {
298
- getObject(arg0).getRandomValues(getObject(arg1));
299
- }, arguments) };
300
- imports.wbg.__wbg_randomFillSync_dc1e9a60c158336d = function() { return handleError(function (arg0, arg1) {
301
- getObject(arg0).randomFillSync(takeObject(arg1));
302
- }, arguments) };
303
- imports.wbg.__wbg_newnoargs_581967eacc0e2604 = function(arg0, arg1) {
304
- const ret = new Function(getStringFromWasm0(arg0, arg1));
305
- return addHeapObject(ret);
306
- };
307
- imports.wbg.__wbg_call_cb65541d95d71282 = function() { return handleError(function (arg0, arg1) {
308
- const ret = getObject(arg0).call(getObject(arg1));
309
- return addHeapObject(ret);
310
- }, arguments) };
311
- imports.wbg.__wbg_self_1ff1d729e9aae938 = function() { return handleError(function () {
312
- const ret = self.self;
313
- return addHeapObject(ret);
314
- }, arguments) };
315
- imports.wbg.__wbg_window_5f4faef6c12b79ec = function() { return handleError(function () {
316
- const ret = window.window;
317
- return addHeapObject(ret);
318
- }, arguments) };
319
- imports.wbg.__wbg_globalThis_1d39714405582d3c = function() { return handleError(function () {
320
- const ret = globalThis.globalThis;
321
- return addHeapObject(ret);
322
- }, arguments) };
323
- imports.wbg.__wbg_global_651f05c6a0944d1c = function() { return handleError(function () {
324
- const ret = global.global;
325
- return addHeapObject(ret);
326
- }, arguments) };
327
- imports.wbg.__wbindgen_is_undefined = function(arg0) {
328
- const ret = getObject(arg0) === undefined;
329
- return ret;
330
- };
331
- imports.wbg.__wbg_call_01734de55d61e11d = function() { return handleError(function (arg0, arg1, arg2) {
332
- const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
333
- return addHeapObject(ret);
334
- }, arguments) };
335
- imports.wbg.__wbg_buffer_085ec1f694018c4f = function(arg0) {
336
- const ret = getObject(arg0).buffer;
337
- return addHeapObject(ret);
338
- };
339
- imports.wbg.__wbg_newwithbyteoffsetandlength_6da8e527659b86aa = function(arg0, arg1, arg2) {
340
- const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
341
- return addHeapObject(ret);
342
- };
343
- imports.wbg.__wbg_new_8125e318e6245eed = function(arg0) {
344
- const ret = new Uint8Array(getObject(arg0));
345
- return addHeapObject(ret);
346
- };
347
- imports.wbg.__wbg_set_5cf90238115182c3 = function(arg0, arg1, arg2) {
348
- getObject(arg0).set(getObject(arg1), arg2 >>> 0);
349
- };
350
- imports.wbg.__wbg_newwithlength_e5d69174d6984cd7 = function(arg0) {
351
- const ret = new Uint8Array(arg0 >>> 0);
352
- return addHeapObject(ret);
353
- };
354
- imports.wbg.__wbg_subarray_13db269f57aa838d = function(arg0, arg1, arg2) {
355
- const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0);
356
- return addHeapObject(ret);
357
- };
358
  imports.wbg.__wbindgen_throw = function(arg0, arg1) {
359
  throw new Error(getStringFromWasm0(arg0, arg1));
360
  };
361
- imports.wbg.__wbindgen_memory = function() {
362
- const ret = wasm.memory;
363
- return addHeapObject(ret);
364
- };
365
 
366
  return imports;
367
  }
 
33
  return idx;
34
  }
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  let WASM_VECTOR_LEN = 0;
37
 
38
  function passArray8ToWasm0(arg, malloc) {
 
104
  return cachedInt32Memory0;
105
  }
106
 
107
+ function getObject(idx) { return heap[idx]; }
108
+
109
+ function dropObject(idx) {
110
+ if (idx < 132) return;
111
+ heap[idx] = heap_next;
112
+ heap_next = idx;
113
+ }
114
+
115
+ function takeObject(idx) {
116
+ const ret = getObject(idx);
117
+ dropObject(idx);
118
+ return ret;
119
  }
120
  /**
121
  */
 
196
  }
197
  }
198
  }
199
+ /**
200
+ */
201
+ export class ModelPose {
202
+
203
+ static __wrap(ptr) {
204
+ ptr = ptr >>> 0;
205
+ const obj = Object.create(ModelPose.prototype);
206
+ obj.__wbg_ptr = ptr;
207
+
208
+ return obj;
209
+ }
210
+
211
+ __destroy_into_raw() {
212
+ const ptr = this.__wbg_ptr;
213
+ this.__wbg_ptr = 0;
214
+
215
+ return ptr;
216
+ }
217
+
218
+ free() {
219
+ const ptr = this.__destroy_into_raw();
220
+ wasm.__wbg_modelpose_free(ptr);
221
+ }
222
+ /**
223
+ * @param {Uint8Array} data
224
+ * @param {string} model_size
225
+ */
226
+ constructor(data, model_size) {
227
+ try {
228
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
229
+ const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
230
+ const len0 = WASM_VECTOR_LEN;
231
+ const ptr1 = passStringToWasm0(model_size, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
232
+ const len1 = WASM_VECTOR_LEN;
233
+ wasm.modelpose_new(retptr, ptr0, len0, ptr1, len1);
234
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
235
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
236
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
237
+ if (r2) {
238
+ throw takeObject(r1);
239
+ }
240
+ return ModelPose.__wrap(r0);
241
+ } finally {
242
+ wasm.__wbindgen_add_to_stack_pointer(16);
243
+ }
244
+ }
245
+ /**
246
+ * @param {Uint8Array} image
247
+ * @param {number} conf_threshold
248
+ * @param {number} iou_threshold
249
+ * @returns {string}
250
+ */
251
+ run(image, conf_threshold, iou_threshold) {
252
+ let deferred3_0;
253
+ let deferred3_1;
254
+ try {
255
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
256
+ const ptr0 = passArray8ToWasm0(image, wasm.__wbindgen_malloc);
257
+ const len0 = WASM_VECTOR_LEN;
258
+ wasm.modelpose_run(retptr, this.__wbg_ptr, ptr0, len0, conf_threshold, iou_threshold);
259
+ var r0 = getInt32Memory0()[retptr / 4 + 0];
260
+ var r1 = getInt32Memory0()[retptr / 4 + 1];
261
+ var r2 = getInt32Memory0()[retptr / 4 + 2];
262
+ var r3 = getInt32Memory0()[retptr / 4 + 3];
263
+ var ptr2 = r0;
264
+ var len2 = r1;
265
+ if (r3) {
266
+ ptr2 = 0; len2 = 0;
267
+ throw takeObject(r2);
268
+ }
269
+ deferred3_0 = ptr2;
270
+ deferred3_1 = len2;
271
+ return getStringFromWasm0(ptr2, len2);
272
+ } finally {
273
+ wasm.__wbindgen_add_to_stack_pointer(16);
274
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
275
+ }
276
+ }
277
+ }
278
 
279
  async function __wbg_load(module, imports) {
280
  if (typeof Response === 'function' && module instanceof Response) {
 
314
  const ret = new Error(getStringFromWasm0(arg0, arg1));
315
  return addHeapObject(ret);
316
  };
317
+ imports.wbg.__wbg_log_598ccd735a33342c = function(arg0, arg1) {
318
  console.log(getStringFromWasm0(arg0, arg1));
319
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  imports.wbg.__wbindgen_throw = function(arg0, arg1) {
321
  throw new Error(getStringFromWasm0(arg0, arg1));
322
  };
 
 
 
 
323
 
324
  return imports;
325
  }
build/m_bg.wasm CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:755ae31980152f83b923e4f4b5e7f2e501c264539d0271f0455fdb6cf38b16c0
3
- size 1624018
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4ab25e3b74019ed2f5635491492ec52403ce009a36813e7829a4ed802ef2f637
3
+ size 1577889
build/m_bg.wasm.d.ts CHANGED
@@ -4,10 +4,12 @@ export const memory: WebAssembly.Memory;
4
  export function __wbg_model_free(a: number): void;
5
  export function model_new(a: number, b: number, c: number, d: number, e: number): void;
6
  export function model_run(a: number, b: number, c: number, d: number, e: number, f: number): void;
 
 
 
7
  export function main(a: number, b: number): number;
8
  export function __wbindgen_add_to_stack_pointer(a: number): number;
9
  export function __wbindgen_malloc(a: number, b: number): number;
10
  export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
11
  export function __wbindgen_free(a: number, b: number, c: number): void;
12
- export function __wbindgen_exn_store(a: number): void;
13
  export function __wbindgen_start(): void;
 
4
  export function __wbg_model_free(a: number): void;
5
  export function model_new(a: number, b: number, c: number, d: number, e: number): void;
6
  export function model_run(a: number, b: number, c: number, d: number, e: number, f: number): void;
7
+ export function __wbg_modelpose_free(a: number): void;
8
+ export function modelpose_new(a: number, b: number, c: number, d: number, e: number): void;
9
+ export function modelpose_run(a: number, b: number, c: number, d: number, e: number, f: number): void;
10
  export function main(a: number, b: number): number;
11
  export function __wbindgen_add_to_stack_pointer(a: number): number;
12
  export function __wbindgen_malloc(a: number, b: number): number;
13
  export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
14
  export function __wbindgen_free(a: number, b: number, c: number): void;
 
15
  export function __wbindgen_start(): void;
index.html CHANGED
@@ -54,8 +54,50 @@
54
  model_size: "x",
55
  url: "yolov8x.safetensors",
56
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  };
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  // init web worker
60
  const yoloWorker = new Worker("./yoloWorker.js", { type: "module" });
61
 
@@ -202,17 +244,28 @@
202
  ctx.fillStyle = "#0dff9a";
203
  const fontSize = 14 * scale;
204
  ctx.font = `${fontSize}px sans-serif`;
205
- for (const [label, bbox] of output) {
206
- const [x, y, w, h] = [
207
- bbox.xmin,
208
- bbox.ymin,
209
- bbox.xmax - bbox.xmin,
210
- bbox.ymax - bbox.ymin,
211
- ];
212
-
213
- const confidence = bbox.confidence;
 
 
 
 
 
 
 
 
 
 
 
214
 
215
- const text = `${label} ${confidence.toFixed(2)}`;
216
  const width = ctx.measureText(text).width;
217
  ctx.fillStyle = "#3c8566";
218
  ctx.fillRect(x - 2, y - fontSize, width + 4, fontSize);
@@ -220,6 +273,28 @@
220
 
221
  ctx.strokeRect(x, y, w, h);
222
  ctx.fillText(text, x, y - 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
  });
225
 
@@ -229,12 +304,12 @@
229
  button.disabled = true;
230
  button.classList.add("bg-blue-700");
231
  button.classList.remove("bg-blue-950");
232
- button.textContent = "Detecting...";
233
  } else if (statusMessage === "complete") {
234
  button.disabled = false;
235
  button.classList.add("bg-blue-950");
236
  button.classList.remove("bg-blue-700");
237
- button.textContent = "Detect Objects";
238
  document.querySelector("#share-btn").hidden = false;
239
  }
240
  }
@@ -250,27 +325,31 @@
250
  </script>
251
  </head>
252
  <body class="container max-w-4xl mx-auto p-4">
253
- <main class="grid grid-cols-1 gap-8">
 
254
  <div>
255
  <h1 class="text-5xl font-bold">Candle YOLOv8</h1>
256
  <h2 class="text-2xl font-bold">Rust/WASM Demo</h2>
257
  <p class="max-w-lg">
258
- Running an object detection model in the browser using rust/wasm with
259
- an image. This demo uses the
260
  <a
261
  href="https://huggingface.co/lmz/candle-yolo-v8"
262
  target="_blank"
263
  class="underline hover:text-blue-500 hover:no-underline"
264
  >
265
- Candle YOLOv8
266
  </a>
267
- models to detect objects in images and WASM runtime built with
268
  <a
269
  href="https://github.com/huggingface/candle/"
270
  target="_blank"
271
  class="underline hover:text-blue-500 hover:no-underline"
272
- >Candle
273
- </a>
 
 
 
274
  </p>
275
  </div>
276
 
@@ -285,6 +364,12 @@
285
  <option value="yolov8m">yolov8m (51.9 MB)</option>
286
  <option value="yolov8l">yolov8l (87.5 MB)</option>
287
  <option value="yolov8x">yolov8x (137 MB)</option>
 
 
 
 
 
 
288
  </select>
289
  </div>
290
  <!-- drag and drop area -->
@@ -358,6 +443,10 @@
358
  src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/candle/examples/bike.jpeg"
359
  class="cursor-pointer w-24 h-24 object-cover"
360
  />
 
 
 
 
361
  </div>
362
  </div>
363
  <div>
@@ -406,7 +495,7 @@
406
  disabled
407
  class="bg-blue-950 hover:bg-blue-700 text-white font-normal py-2 px-4 rounded disabled:opacity-75 disabled:hover:bg-blue-950"
408
  >
409
- Detect Objects
410
  </button>
411
  </div>
412
  </main>
 
54
  model_size: "x",
55
  url: "yolov8x.safetensors",
56
  },
57
+ yolov8n_pose: {
58
+ model_size: "n",
59
+ url: "yolov8n-pose.safetensors",
60
+ },
61
+ yolov8s_pose: {
62
+ model_size: "s",
63
+ url: "yolov8s-pose.safetensors",
64
+ },
65
+ yolov8m_pose: {
66
+ model_size: "m",
67
+ url: "yolov8m-pose.safetensors",
68
+ },
69
+ yolov8l_pose: {
70
+ model_size: "l",
71
+ url: "yolov8l-pose.safetensors",
72
+ },
73
+ yolov8x_pose: {
74
+ model_size: "x",
75
+ url: "yolov8x-pose.safetensors",
76
+ },
77
  };
78
 
79
+ const COCO_PERSON_SKELETON = [
80
+ [4, 0], // head
81
+ [3, 0],
82
+ [16, 14], // left lower leg
83
+ [14, 12], // left upper leg
84
+ [6, 12], // left torso
85
+ [6, 5], // top torso
86
+ [6, 8], // upper arm
87
+ [8, 10], // lower arm
88
+ [1, 2], // head
89
+ [1, 3], // right head
90
+ [2, 4], // left head
91
+ [3, 5], // right neck
92
+ [4, 6], // left neck
93
+ [5, 7], // right upper arm
94
+ [7, 9], // right lower arm
95
+ [5, 11], // right torso
96
+ [11, 12], // bottom torso
97
+ [11, 13], // right upper leg
98
+ [13, 15], // right lower leg
99
+ ];
100
+
101
  // init web worker
102
  const yoloWorker = new Worker("./yoloWorker.js", { type: "module" });
103
 
 
244
  ctx.fillStyle = "#0dff9a";
245
  const fontSize = 14 * scale;
246
  ctx.font = `${fontSize}px sans-serif`;
247
+ for (const detection of output) {
248
+ // check keypoint for pose model data
249
+ let xmin, xmax, ymin, ymax, label, confidence, keypoints;
250
+ if ("keypoints" in detection) {
251
+ xmin = detection.xmin;
252
+ xmax = detection.xmax;
253
+ ymin = detection.ymin;
254
+ ymax = detection.ymax;
255
+ confidence = detection.confidence;
256
+ keypoints = detection.keypoints;
257
+ } else {
258
+ const [_label, bbox] = detection;
259
+ label = _label;
260
+ xmin = bbox.xmin;
261
+ xmax = bbox.xmax;
262
+ ymin = bbox.ymin;
263
+ ymax = bbox.ymax;
264
+ confidence = bbox.confidence;
265
+ }
266
+ const [x, y, w, h] = [xmin, ymin, xmax - xmin, ymax - ymin];
267
 
268
+ const text = `${label ? label + " " : ""}${confidence.toFixed(2)}`;
269
  const width = ctx.measureText(text).width;
270
  ctx.fillStyle = "#3c8566";
271
  ctx.fillRect(x - 2, y - fontSize, width + 4, fontSize);
 
273
 
274
  ctx.strokeRect(x, y, w, h);
275
  ctx.fillText(text, x, y - 2);
276
+ if (keypoints) {
277
+ ctx.save();
278
+ ctx.fillStyle = "magenta";
279
+ ctx.strokeStyle = "yellow";
280
+
281
+ for (const keypoint of keypoints) {
282
+ const { x, y } = keypoint;
283
+ ctx.beginPath();
284
+ ctx.arc(x, y, 3, 0, 2 * Math.PI);
285
+ ctx.fill();
286
+ }
287
+ ctx.beginPath();
288
+ for (const [xid, yid] of COCO_PERSON_SKELETON) {
289
+ //draw line between skeleton keypoitns
290
+ if (keypoints[xid] && keypoints[yid]) {
291
+ ctx.moveTo(keypoints[xid].x, keypoints[xid].y);
292
+ ctx.lineTo(keypoints[yid].x, keypoints[yid].y);
293
+ }
294
+ }
295
+ ctx.stroke();
296
+ ctx.restore();
297
+ }
298
  }
299
  });
300
 
 
304
  button.disabled = true;
305
  button.classList.add("bg-blue-700");
306
  button.classList.remove("bg-blue-950");
307
+ button.textContent = "Predicting...";
308
  } else if (statusMessage === "complete") {
309
  button.disabled = false;
310
  button.classList.add("bg-blue-950");
311
  button.classList.remove("bg-blue-700");
312
+ button.textContent = "Predict";
313
  document.querySelector("#share-btn").hidden = false;
314
  }
315
  }
 
325
  </script>
326
  </head>
327
  <body class="container max-w-4xl mx-auto p-4">
328
+ <main class="grid grid-cols-1 gap-8 relative">
329
+ <span class="absolute text-5xl -ml-[1em]"> 🕯️ </span>
330
  <div>
331
  <h1 class="text-5xl font-bold">Candle YOLOv8</h1>
332
  <h2 class="text-2xl font-bold">Rust/WASM Demo</h2>
333
  <p class="max-w-lg">
334
+ This demo showcases object detection and pose estimation models in
335
+ your browser using Rust/WASM. It utilizes
336
  <a
337
  href="https://huggingface.co/lmz/candle-yolo-v8"
338
  target="_blank"
339
  class="underline hover:text-blue-500 hover:no-underline"
340
  >
341
+ safetensor's YOLOv8 models
342
  </a>
343
+ and a WASM runtime built with
344
  <a
345
  href="https://github.com/huggingface/candle/"
346
  target="_blank"
347
  class="underline hover:text-blue-500 hover:no-underline"
348
+ >Candle </a
349
+ >.
350
+ </p>
351
+ <p>
352
+ To run pose estimation, select a yolo pose model from the dropdown
353
  </p>
354
  </div>
355
 
 
364
  <option value="yolov8m">yolov8m (51.9 MB)</option>
365
  <option value="yolov8l">yolov8l (87.5 MB)</option>
366
  <option value="yolov8x">yolov8x (137 MB)</option>
367
+ <!-- Pose models -->
368
+ <option value="yolov8n_pose">yolov8n_pose (6.65 MB)</option>
369
+ <option value="yolov8s_pose">yolov8s_pose (23.3 MB)</option>
370
+ <option value="yolov8m_pose">yolov8m_pose (53 MB)</option>
371
+ <option value="yolov8l_pose">yolov8l_pose (89.1 MB)</option>
372
+ <option value="yolov8x_pose">yolov8x_pose (139 MB)</option>
373
  </select>
374
  </div>
375
  <!-- drag and drop area -->
 
443
  src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/candle/examples/bike.jpeg"
444
  class="cursor-pointer w-24 h-24 object-cover"
445
  />
446
+ <img
447
+ src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/candle/examples/000000000077.jpg"
448
+ class="cursor-pointer w-24 h-24 object-cover"
449
+ />
450
  </div>
451
  </div>
452
  <div>
 
495
  disabled
496
  class="bg-blue-950 hover:bg-blue-700 text-white font-normal py-2 px-4 rounded disabled:opacity-75 disabled:hover:bg-blue-950"
497
  >
498
+ Predict
499
  </button>
500
  </div>
501
  </main>
yoloWorker.js CHANGED
@@ -1,5 +1,5 @@
1
  //load the candle yolo wasm module
2
- import init, { Model } from "./build/m.js";
3
 
4
  class Yolo {
5
  static instance = {};
@@ -14,7 +14,12 @@ class Yolo {
14
  const modelRes = await fetch(modelURL);
15
  const yoloArrayBuffer = await modelRes.arrayBuffer();
16
  const weightsArrayU8 = new Uint8Array(yoloArrayBuffer);
17
- this.instance[modelID] = new Model(weightsArrayU8, modelSize);
 
 
 
 
 
18
  } else {
19
  self.postMessage({ status: "model already loaded" });
20
  }
 
1
  //load the candle yolo wasm module
2
+ import init, { Model, ModelPose } from "./build/m.js";
3
 
4
  class Yolo {
5
  static instance = {};
 
14
  const modelRes = await fetch(modelURL);
15
  const yoloArrayBuffer = await modelRes.arrayBuffer();
16
  const weightsArrayU8 = new Uint8Array(yoloArrayBuffer);
17
+ if (/pose/.test(modelID)) {
18
+ // if pose model, use ModelPose
19
+ this.instance[modelID] = new ModelPose(weightsArrayU8, modelSize);
20
+ } else {
21
+ this.instance[modelID] = new Model(weightsArrayU8, modelSize);
22
+ }
23
  } else {
24
  self.postMessage({ status: "model already loaded" });
25
  }