tempest-react-sdk 0.69.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +1 -1
  2. package/dist/vision/core/canvas.cjs +1 -1
  3. package/dist/vision/core/canvas.cjs.map +1 -1
  4. package/dist/vision/core/canvas.js +37 -13
  5. package/dist/vision/core/canvas.js.map +1 -1
  6. package/dist/vision/core/dtypes.cjs +2 -0
  7. package/dist/vision/core/dtypes.cjs.map +1 -0
  8. package/dist/vision/core/dtypes.js +33 -0
  9. package/dist/vision/core/dtypes.js.map +1 -0
  10. package/dist/vision/core/graph.cjs +1 -1
  11. package/dist/vision/core/graph.cjs.map +1 -1
  12. package/dist/vision/core/graph.js +10 -2
  13. package/dist/vision/core/graph.js.map +1 -1
  14. package/dist/vision/core/metadata.cjs +2 -2
  15. package/dist/vision/core/metadata.cjs.map +1 -1
  16. package/dist/vision/core/metadata.js +103 -13
  17. package/dist/vision/core/metadata.js.map +1 -1
  18. package/dist/vision/core/providers.cjs +1 -1
  19. package/dist/vision/core/providers.cjs.map +1 -1
  20. package/dist/vision/core/providers.js +9 -6
  21. package/dist/vision/core/providers.js.map +1 -1
  22. package/dist/vision/core/session.cjs +1 -1
  23. package/dist/vision/core/session.cjs.map +1 -1
  24. package/dist/vision/core/session.js +115 -27
  25. package/dist/vision/core/session.js.map +1 -1
  26. package/dist/vision/fusion.cjs +1 -1
  27. package/dist/vision/fusion.cjs.map +1 -1
  28. package/dist/vision/fusion.js +28 -23
  29. package/dist/vision/fusion.js.map +1 -1
  30. package/dist/vision/index.cjs +1 -1
  31. package/dist/vision/index.cjs.map +1 -1
  32. package/dist/vision/index.js +20 -19
  33. package/dist/vision/index.js.map +1 -1
  34. package/dist/vision/io/image.cjs +1 -1
  35. package/dist/vision/io/image.cjs.map +1 -1
  36. package/dist/vision/io/image.js +55 -20
  37. package/dist/vision/io/image.js.map +1 -1
  38. package/dist/vision/postprocess/detection.cjs +1 -1
  39. package/dist/vision/postprocess/detection.cjs.map +1 -1
  40. package/dist/vision/postprocess/detection.js +42 -51
  41. package/dist/vision/postprocess/detection.js.map +1 -1
  42. package/dist/vision/postprocess/segmentation.cjs +1 -1
  43. package/dist/vision/postprocess/segmentation.cjs.map +1 -1
  44. package/dist/vision/postprocess/segmentation.js +45 -31
  45. package/dist/vision/postprocess/segmentation.js.map +1 -1
  46. package/dist/vision/preprocess/pipeline.cjs +1 -1
  47. package/dist/vision/preprocess/pipeline.cjs.map +1 -1
  48. package/dist/vision/preprocess/pipeline.js +24 -25
  49. package/dist/vision/preprocess/pipeline.js.map +1 -1
  50. package/dist/vision/tasks/base.cjs +1 -1
  51. package/dist/vision/tasks/base.cjs.map +1 -1
  52. package/dist/vision/tasks/base.js +22 -7
  53. package/dist/vision/tasks/base.js.map +1 -1
  54. package/dist/vision/tasks/classifier.cjs +1 -1
  55. package/dist/vision/tasks/classifier.cjs.map +1 -1
  56. package/dist/vision/tasks/classifier.js +64 -55
  57. package/dist/vision/tasks/classifier.js.map +1 -1
  58. package/dist/vision/tasks/detectClassify.cjs +1 -1
  59. package/dist/vision/tasks/detectClassify.cjs.map +1 -1
  60. package/dist/vision/tasks/detectClassify.js +80 -69
  61. package/dist/vision/tasks/detectClassify.js.map +1 -1
  62. package/dist/vision/tasks/detector.cjs +1 -1
  63. package/dist/vision/tasks/detector.cjs.map +1 -1
  64. package/dist/vision/tasks/detector.js +74 -68
  65. package/dist/vision/tasks/detector.js.map +1 -1
  66. package/dist/vision/tasks/segmenter.cjs +1 -1
  67. package/dist/vision/tasks/segmenter.cjs.map +1 -1
  68. package/dist/vision/tasks/segmenter.js +85 -62
  69. package/dist/vision/tasks/segmenter.js.map +1 -1
  70. package/dist/vision/types.cjs +1 -1
  71. package/dist/vision/types.cjs.map +1 -1
  72. package/dist/vision/types.js +24 -4
  73. package/dist/vision/types.js.map +1 -1
  74. package/dist/vision.cjs +1 -1
  75. package/dist/vision.d.ts +1153 -777
  76. package/dist/vision.js +22 -21
  77. package/package.json +1 -1
@@ -1,17 +1,33 @@
1
1
  import { ImageLoadError as e } from "../core/exceptions.js";
2
2
  import { RGBImage as t } from "../types.js";
3
- import { createCanvas as n, get2DContext as r, imageDataToRGB as i } from "../core/canvas.js";
3
+ import { createCanvas as n, get2DContext as r, imageDataToRGBChecked as i } from "../core/canvas.js";
4
4
  //#region src/vision/io/image.ts
5
- async function a(n) {
6
- if (n instanceof t) return n;
7
- if (typeof ImageData < "u" && n instanceof ImageData) return i(n);
8
- if (typeof n == "string") return o(n);
9
- if (typeof Blob < "u" && n instanceof Blob) return s(n);
10
- if (typeof HTMLImageElement < "u" && n instanceof HTMLImageElement) return await c(n), l(n, n.naturalWidth, n.naturalHeight);
11
- if (typeof HTMLCanvasElement < "u" && n instanceof HTMLCanvasElement || typeof OffscreenCanvas < "u" && n instanceof OffscreenCanvas || typeof ImageBitmap < "u" && n instanceof ImageBitmap) return l(n, n.width, n.height);
5
+ async function a(e) {
6
+ return (await o(e)).image;
7
+ }
8
+ async function o(n) {
9
+ if (n instanceof t) return {
10
+ image: n,
11
+ canvas: null,
12
+ owned: !1
13
+ };
14
+ if (typeof ImageData < "u" && n instanceof ImageData) return {
15
+ image: i(n).image,
16
+ canvas: null,
17
+ owned: !0
18
+ };
19
+ if (typeof n == "string") return s(n);
20
+ if (typeof Blob < "u" && n instanceof Blob) return c(n);
21
+ if (typeof HTMLImageElement < "u" && n instanceof HTMLImageElement) return await l(n), f(n, n.naturalWidth, n.naturalHeight);
22
+ if (typeof HTMLVideoElement < "u" && n instanceof HTMLVideoElement) {
23
+ if (n.readyState < 2) throw new e("HTMLVideoElement has no current frame yet (readyState < HAVE_CURRENT_DATA). Wait for its 'loadeddata' event before predicting on it.");
24
+ return f(n, n.videoWidth, n.videoHeight, d(n));
25
+ }
26
+ if (typeof HTMLCanvasElement < "u" && n instanceof HTMLCanvasElement || typeof OffscreenCanvas < "u" && n instanceof OffscreenCanvas || typeof ImageBitmap < "u" && n instanceof ImageBitmap) return f(n, n.width, n.height);
27
+ if (typeof VideoFrame < "u" && n instanceof VideoFrame) return f(n, n.displayWidth, n.displayHeight, n.format !== null && u.has(n.format));
12
28
  throw new e(`Unsupported image source type: ${Object.prototype.toString.call(n)}`);
13
29
  }
14
- async function o(t) {
30
+ async function s(t) {
15
31
  let n;
16
32
  try {
17
33
  n = await fetch(t);
@@ -19,9 +35,9 @@ async function o(t) {
19
35
  throw new e(`Failed to fetch image from ${t}: ${n.message}`, { cause: n });
20
36
  }
21
37
  if (!n.ok) throw new e(`Failed to fetch image from ${t}: HTTP ${n.status} ${n.statusText}`);
22
- return s(await n.blob());
38
+ return c(await n.blob());
23
39
  }
24
- async function s(t) {
40
+ async function c(t) {
25
41
  let n;
26
42
  try {
27
43
  n = await createImageBitmap(t);
@@ -29,12 +45,12 @@ async function s(t) {
29
45
  throw new e(`Failed to decode image blob: ${t.message}`, { cause: t });
30
46
  }
31
47
  try {
32
- return l(n, n.width, n.height);
48
+ return f(n, n.width, n.height, t.type === "image/jpeg");
33
49
  } finally {
34
50
  n.close();
35
51
  }
36
52
  }
37
- function c(t) {
53
+ function l(t) {
38
54
  return t.complete && t.naturalWidth > 0 ? Promise.resolve() : new Promise((n, r) => {
39
55
  let i = () => {
40
56
  o(), n();
@@ -46,14 +62,33 @@ function c(t) {
46
62
  t.addEventListener("load", i, { once: !0 }), t.addEventListener("error", a, { once: !0 });
47
63
  });
48
64
  }
49
- function l(t, a, o) {
50
- if (a === 0 || o === 0) throw new e(`Cannot load image with zero dimension (${a}x${o}).`);
51
- let s = n(a, o), c = r(s);
52
- c.drawImage(t, 0, 0);
53
- let l = c.getImageData(0, 0, a, o);
54
- return i(l);
65
+ var u = /* @__PURE__ */ new Set([
66
+ "I420",
67
+ "I422",
68
+ "I444",
69
+ "NV12",
70
+ "RGBX",
71
+ "BGRX"
72
+ ]);
73
+ function d(e) {
74
+ return typeof MediaStream < "u" && e.srcObject instanceof MediaStream;
75
+ }
76
+ function f(a, o, s, c = !1) {
77
+ if (o === 0 || s === 0) throw new e(`Cannot load image with zero dimension (${o}x${s}).`);
78
+ let l = n(o, s), u = r(l);
79
+ if (u.drawImage(a, 0, 0, o, s), c) return {
80
+ image: t.deferred(o, s, () => i(u.getImageData(0, 0, o, s)).image.data),
81
+ canvas: l,
82
+ owned: !0
83
+ };
84
+ let { image: d, opaque: f } = i(u.getImageData(0, 0, o, s));
85
+ return {
86
+ image: d,
87
+ canvas: f ? l : null,
88
+ owned: !0
89
+ };
55
90
  }
56
91
  //#endregion
57
- export { a as loadImage };
92
+ export { a as loadImage, o as loadImageSource };
58
93
 
59
94
  //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","names":[],"sources":["../../../src/vision/io/image.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Image loading utilities — convert any supported source into the canonical\n * {@link RGBImage} format.\n *\n * Browser-only: uses `fetch`, `createImageBitmap`, and a Canvas 2D context to\n * decode arbitrary inputs into a tightly-packed `Uint8Array` of HWC RGB pixels.\n */\n\nimport { createCanvas, get2DContext, imageDataToRGB } from \"../core/canvas\";\nimport { ImageLoadError } from \"../core/exceptions\";\nimport { RGBImage } from \"../types\";\n\n/** Anything {@link loadImage} accepts as an image input. */\nexport type ImageInput =\n | string\n | Blob\n | HTMLImageElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | ImageData\n | RGBImage;\n\n/**\n * Load an image from any supported source into a HWC uint8 RGB array.\n *\n * @throws {@link ImageLoadError} if the source cannot be decoded or has an unsupported shape.\n */\nexport async function loadImage(source: ImageInput): Promise<RGBImage> {\n if (source instanceof RGBImage) {\n return source;\n }\n\n if (typeof ImageData !== \"undefined\" && source instanceof ImageData) {\n return imageDataToRGB(source);\n }\n\n if (typeof source === \"string\") {\n return loadFromUrl(source);\n }\n\n if (typeof Blob !== \"undefined\" && source instanceof Blob) {\n return loadFromBlob(source);\n }\n\n if (typeof HTMLImageElement !== \"undefined\" && source instanceof HTMLImageElement) {\n await waitForImageElement(source);\n return drawableToRGB(source, source.naturalWidth, source.naturalHeight);\n }\n\n if (typeof HTMLCanvasElement !== \"undefined\" && source instanceof HTMLCanvasElement) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n if (typeof OffscreenCanvas !== \"undefined\" && source instanceof OffscreenCanvas) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n if (typeof ImageBitmap !== \"undefined\" && source instanceof ImageBitmap) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n throw new ImageLoadError(\n `Unsupported image source type: ${Object.prototype.toString.call(source)}`,\n );\n}\n\nasync function loadFromUrl(url: string): Promise<RGBImage> {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (err) {\n throw new ImageLoadError(`Failed to fetch image from ${url}: ${(err as Error).message}`, {\n cause: err,\n });\n }\n if (!response.ok) {\n throw new ImageLoadError(\n `Failed to fetch image from ${url}: HTTP ${response.status} ${response.statusText}`,\n );\n }\n const blob = await response.blob();\n return loadFromBlob(blob);\n}\n\nasync function loadFromBlob(blob: Blob): Promise<RGBImage> {\n let bitmap: ImageBitmap;\n try {\n bitmap = await createImageBitmap(blob);\n } catch (err) {\n throw new ImageLoadError(`Failed to decode image blob: ${(err as Error).message}`, {\n cause: err,\n });\n }\n try {\n return drawableToRGB(bitmap, bitmap.width, bitmap.height);\n } finally {\n bitmap.close();\n }\n}\n\nfunction waitForImageElement(img: HTMLImageElement): Promise<void> {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n const onLoad = (): void => {\n cleanup();\n resolve();\n };\n const onError = (): void => {\n cleanup();\n reject(new ImageLoadError(\"Failed to load HTMLImageElement (load event errored)\"));\n };\n const cleanup = (): void => {\n img.removeEventListener(\"load\", onLoad);\n img.removeEventListener(\"error\", onError);\n };\n img.addEventListener(\"load\", onLoad, { once: true });\n img.addEventListener(\"error\", onError, { once: true });\n });\n}\n\nfunction drawableToRGB(drawable: CanvasImageSource, width: number, height: number): RGBImage {\n if (width === 0 || height === 0) {\n throw new ImageLoadError(`Cannot load image with zero dimension (${width}x${height}).`);\n }\n const canvas = createCanvas(width, height);\n const ctx = get2DContext(canvas);\n ctx.drawImage(drawable, 0, 0);\n const imageData = ctx.getImageData(0, 0, width, height);\n return imageDataToRGB(imageData);\n}\n"],"mappings":";;;;AA6BA,eAAsB,EAAU,GAAuC;CACnE,IAAI,aAAkB,GAClB,OAAO;CAGX,IAAI,OAAO,YAAc,OAAe,aAAkB,WACtD,OAAO,EAAe,CAAM;CAGhC,IAAI,OAAO,KAAW,UAClB,OAAO,EAAY,CAAM;CAG7B,IAAI,OAAO,OAAS,OAAe,aAAkB,MACjD,OAAO,EAAa,CAAM;CAG9B,IAAI,OAAO,mBAAqB,OAAe,aAAkB,kBAE7D,OADA,MAAM,EAAoB,CAAM,GACzB,EAAc,GAAQ,EAAO,cAAc,EAAO,aAAa;CAW1E,IARI,OAAO,oBAAsB,OAAe,aAAkB,qBAI9D,OAAO,kBAAoB,OAAe,aAAkB,mBAI5D,OAAO,cAAgB,OAAe,aAAkB,aACxD,OAAO,EAAc,GAAQ,EAAO,OAAO,EAAO,MAAM;CAG5D,MAAM,IAAI,EACN,kCAAkC,OAAO,UAAU,SAAS,KAAK,CAAM,GAC3E;AACJ;AAEA,eAAe,EAAY,GAAgC;CACvD,IAAI;CACJ,IAAI;EACA,IAAW,MAAM,MAAM,CAAG;CAC9B,SAAS,GAAK;EACV,MAAM,IAAI,EAAe,8BAA8B,EAAI,IAAK,EAAc,WAAW,EACrF,OAAO,EACX,CAAC;CACL;CACA,IAAI,CAAC,EAAS,IACV,MAAM,IAAI,EACN,8BAA8B,EAAI,SAAS,EAAS,OAAO,GAAG,EAAS,YAC3E;CAGJ,OAAO,EAAa,MADD,EAAS,KAAK,CACT;AAC5B;AAEA,eAAe,EAAa,GAA+B;CACvD,IAAI;CACJ,IAAI;EACA,IAAS,MAAM,kBAAkB,CAAI;CACzC,SAAS,GAAK;EACV,MAAM,IAAI,EAAe,gCAAiC,EAAc,WAAW,EAC/E,OAAO,EACX,CAAC;CACL;CACA,IAAI;EACA,OAAO,EAAc,GAAQ,EAAO,OAAO,EAAO,MAAM;CAC5D,UAAU;EACN,EAAO,MAAM;CACjB;AACJ;AAEA,SAAS,EAAoB,GAAsC;CAI/D,OAHI,EAAI,YAAY,EAAI,eAAe,IAC5B,QAAQ,QAAQ,IAEpB,IAAI,SAAe,GAAS,MAAW;EAC1C,IAAM,UAAqB;GAEvB,AADA,EAAQ,GACR,EAAQ;EACZ,GACM,UAAsB;GAExB,AADA,EAAQ,GACR,EAAO,IAAI,EAAe,sDAAsD,CAAC;EACrF,GACM,UAAsB;GAExB,AADA,EAAI,oBAAoB,QAAQ,CAAM,GACtC,EAAI,oBAAoB,SAAS,CAAO;EAC5C;EAEA,AADA,EAAI,iBAAiB,QAAQ,GAAQ,EAAE,MAAM,GAAK,CAAC,GACnD,EAAI,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC;CACzD,CAAC;AACL;AAEA,SAAS,EAAc,GAA6B,GAAe,GAA0B;CACzF,IAAI,MAAU,KAAK,MAAW,GAC1B,MAAM,IAAI,EAAe,0CAA0C,EAAM,GAAG,EAAO,GAAG;CAE1F,IAAM,IAAS,EAAa,GAAO,CAAM,GACnC,IAAM,EAAa,CAAM;CAC/B,EAAI,UAAU,GAAU,GAAG,CAAC;CAC5B,IAAM,IAAY,EAAI,aAAa,GAAG,GAAG,GAAO,CAAM;CACtD,OAAO,EAAe,CAAS;AACnC"}
1
+ {"version":3,"file":"image.js","names":[],"sources":["../../../src/vision/io/image.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Image loading utilities — convert any supported source into the canonical\n * {@link RGBImage} format.\n *\n * Browser-only: uses `fetch`, `createImageBitmap`, and a Canvas 2D context to\n * decode arbitrary inputs into a tightly-packed `Uint8Array` of HWC RGB pixels.\n */\n\nimport { createCanvas, get2DContext, imageDataToRGBChecked } from \"../core/canvas\";\nimport { ImageLoadError } from \"../core/exceptions\";\nimport { RGBImage } from \"../types\";\n\n/** Anything {@link loadImage} accepts as an image input. */\nexport type ImageInput =\n | string\n | Blob\n | HTMLImageElement\n | HTMLVideoElement\n | HTMLCanvasElement\n | OffscreenCanvas\n | ImageBitmap\n | VideoFrame\n | ImageData\n | RGBImage;\n\n/**\n * A decoded input, plus what the tasks can reuse from decoding it.\n *\n * Internal to the SDK: the tasks need more than {@link loadImage} returns, and\n * the extra fields are about avoiding work, not about the image itself.\n */\nexport interface LoadedImage {\n /** The input as the canonical HWC RGB image. */\n readonly image: RGBImage;\n /**\n * A full-resolution canvas holding exactly {@link image}'s pixels, or `null`.\n *\n * Decoding any drawable already paints it onto a canvas to read it back.\n * Handing that canvas to a preprocessing pipeline lets it `drawImage` the\n * frame straight into the model input, instead of rebuilding RGBA from\n * {@link image} and `putImageData`-ing it onto a canvas of its own — two\n * full-resolution passes and an allocation the size of the frame. Only set\n * when every pixel was opaque: a translucent pixel composites differently\n * from the RGB that dropping its alpha kept.\n */\n readonly canvas: CanvasImageSource | null;\n /**\n * Whether {@link image}'s buffer was allocated here rather than passed in.\n *\n * An `RGBImage` the caller handed over is theirs, and a video loop may well\n * refill the same buffer every frame. Anything derived from it lazily would\n * then read a later frame, so lazy crops are only taken on owned buffers.\n */\n readonly owned: boolean;\n}\n\n/**\n * Load an image from any supported source into a HWC uint8 RGB array.\n *\n * A video element must have its current frame available (`readyState >= 2`);\n * the frame showing at the time of the call is the one decoded. A `VideoFrame`\n * is read but not closed — it belongs to the caller.\n *\n * @throws {@link ImageLoadError} if the source cannot be decoded or has an unsupported shape.\n */\nexport async function loadImage(source: ImageInput): Promise<RGBImage> {\n return (await loadImageSource(source)).image;\n}\n\n/**\n * Decode an input like {@link loadImage}, keeping what preprocessing can reuse.\n *\n * @param source Any {@link ImageInput}.\n * @returns The image, the canvas it was painted on (when reusable), and\n * whether the SDK owns the pixel buffer.\n * @throws {@link ImageLoadError} if the source cannot be decoded.\n */\nexport async function loadImageSource(source: ImageInput): Promise<LoadedImage> {\n if (source instanceof RGBImage) {\n return { image: source, canvas: null, owned: false };\n }\n\n if (typeof ImageData !== \"undefined\" && source instanceof ImageData) {\n return { image: imageDataToRGBChecked(source).image, canvas: null, owned: true };\n }\n\n if (typeof source === \"string\") {\n return loadFromUrl(source);\n }\n\n if (typeof Blob !== \"undefined\" && source instanceof Blob) {\n return loadFromBlob(source);\n }\n\n if (typeof HTMLImageElement !== \"undefined\" && source instanceof HTMLImageElement) {\n await waitForImageElement(source);\n return drawableToRGB(source, source.naturalWidth, source.naturalHeight);\n }\n\n if (typeof HTMLVideoElement !== \"undefined\" && source instanceof HTMLVideoElement) {\n if (source.readyState < 2) {\n throw new ImageLoadError(\n \"HTMLVideoElement has no current frame yet (readyState < HAVE_CURRENT_DATA). \" +\n \"Wait for its 'loadeddata' event before predicting on it.\",\n );\n }\n return drawableToRGB(source, source.videoWidth, source.videoHeight, isLiveStream(source));\n }\n\n if (typeof HTMLCanvasElement !== \"undefined\" && source instanceof HTMLCanvasElement) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n if (typeof OffscreenCanvas !== \"undefined\" && source instanceof OffscreenCanvas) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n if (typeof ImageBitmap !== \"undefined\" && source instanceof ImageBitmap) {\n return drawableToRGB(source, source.width, source.height);\n }\n\n if (typeof VideoFrame !== \"undefined\" && source instanceof VideoFrame) {\n return drawableToRGB(\n source,\n source.displayWidth,\n source.displayHeight,\n source.format !== null && OPAQUE_FRAME_FORMATS.has(source.format),\n );\n }\n\n throw new ImageLoadError(\n `Unsupported image source type: ${Object.prototype.toString.call(source)}`,\n );\n}\n\nasync function loadFromUrl(url: string): Promise<LoadedImage> {\n let response: Response;\n try {\n response = await fetch(url);\n } catch (err) {\n throw new ImageLoadError(`Failed to fetch image from ${url}: ${(err as Error).message}`, {\n cause: err,\n });\n }\n if (!response.ok) {\n throw new ImageLoadError(\n `Failed to fetch image from ${url}: HTTP ${response.status} ${response.statusText}`,\n );\n }\n const blob = await response.blob();\n return loadFromBlob(blob);\n}\n\nasync function loadFromBlob(blob: Blob): Promise<LoadedImage> {\n let bitmap: ImageBitmap;\n try {\n bitmap = await createImageBitmap(blob);\n } catch (err) {\n throw new ImageLoadError(`Failed to decode image blob: ${(err as Error).message}`, {\n cause: err,\n });\n }\n try {\n return drawableToRGB(bitmap, bitmap.width, bitmap.height, blob.type === \"image/jpeg\");\n } finally {\n bitmap.close();\n }\n}\n\nfunction waitForImageElement(img: HTMLImageElement): Promise<void> {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n const onLoad = (): void => {\n cleanup();\n resolve();\n };\n const onError = (): void => {\n cleanup();\n reject(new ImageLoadError(\"Failed to load HTMLImageElement (load event errored)\"));\n };\n const cleanup = (): void => {\n img.removeEventListener(\"load\", onLoad);\n img.removeEventListener(\"error\", onError);\n };\n img.addEventListener(\"load\", onLoad, { once: true });\n img.addEventListener(\"error\", onError, { once: true });\n });\n}\n\n/**\n * `VideoFrame` pixel formats that carry no alpha channel.\n *\n * The `A`-suffixed planar formats and `RGBA`/`BGRA` do; a frame in one of\n * these never has a translucent pixel.\n */\nconst OPAQUE_FRAME_FORMATS: ReadonlySet<string> = new Set([\n \"I420\",\n \"I422\",\n \"I444\",\n \"NV12\",\n \"RGBX\",\n \"BGRX\",\n]);\n\n/**\n * Whether a video element is playing a live capture (camera, screen).\n *\n * `MediaStream` video tracks carry no alpha, so every frame is opaque. A video\n * playing a file may not be — VP9 with alpha exists — so it gets no such\n * promise.\n *\n * @param video The element.\n */\nfunction isLiveStream(video: HTMLVideoElement): boolean {\n return typeof MediaStream !== \"undefined\" && video.srcObject instanceof MediaStream;\n}\n\n/**\n * Paint a drawable onto a fresh canvas and read it back as RGB.\n *\n * When the source is known to be opaque without looking — a JPEG, a camera\n * frame, an alpha-less `VideoFrame` — the read-back is deferred: the image's\n * pixels come out of the canvas the first time something reads `data`. The\n * canvas is this call's own, so it still holds the frame of this moment\n * however late that read happens. Preprocessing draws from the canvas and\n * never reads `data`, so a loop that only consumes boxes skips the\n * full-resolution `getImageData` altogether.\n *\n * Any other source is read back now, because whether its canvas may stand in\n * for the RGB image depends on every pixel being opaque, and only the read\n * tells.\n *\n * @param drawable The source to decode.\n * @param width Its intrinsic width.\n * @param height Its intrinsic height.\n * @param knownOpaque Whether the source cannot contain a translucent pixel.\n * @returns The decoded image, with the canvas attached when it is opaque.\n * @throws {@link ImageLoadError} for a zero-sized source.\n */\nfunction drawableToRGB(\n drawable: CanvasImageSource,\n width: number,\n height: number,\n knownOpaque: boolean = false,\n): LoadedImage {\n if (width === 0 || height === 0) {\n throw new ImageLoadError(`Cannot load image with zero dimension (${width}x${height}).`);\n }\n const canvas = createCanvas(width, height);\n const ctx = get2DContext(canvas);\n ctx.drawImage(drawable, 0, 0, width, height);\n if (knownOpaque) {\n const image = RGBImage.deferred(\n width,\n height,\n () => imageDataToRGBChecked(ctx.getImageData(0, 0, width, height)).image.data,\n );\n return { image, canvas, owned: true };\n }\n const { image, opaque } = imageDataToRGBChecked(ctx.getImageData(0, 0, width, height));\n return { image, canvas: opaque ? canvas : null, owned: true };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../types.cjs");function t(e,t,n){let r=t.length;if(r===0)return new Int32Array;let i=new Float32Array(r);for(let t=0;t<r;t++){let n=e[t*4],r=e[t*4+1],a=e[t*4+2],o=e[t*4+3];i[t]=Math.max(0,a-n)*Math.max(0,o-r)}let a=Array(r);for(let e=0;e<r;e++)a[e]=e;a.sort((e,n)=>t[n]-t[e]||e-n);let o=new Uint8Array(r),s=[];for(let t=0;t<a.length;t++){let r=a[t];if(o[r])continue;s.push(r);let c=e[r*4],l=e[r*4+1],u=e[r*4+2],d=e[r*4+3],f=i[r];for(let r=t+1;r<a.length;r++){let t=a[r];if(o[t])continue;let s=e[t*4],p=e[t*4+1],m=e[t*4+2],h=e[t*4+3],g=Math.max(c,s),_=Math.max(l,p),v=Math.min(u,m),y=Math.min(d,h),b=Math.max(0,v-g)*Math.max(0,y-_),x=f+i[t]-b;(x>0?b/x:0)>n&&(o[t]=1)}}return Int32Array.from(s)}function n(e,n,r,i){if(n.length===0)return new Int32Array;let a=new Map;for(let e=0;e<r.length;e++){let t=r[e],n=a.get(t);n===void 0?a.set(t,[e]):n.push(e)}let o=[];for(let r of a.values()){let a=r.length,s=new Float32Array(a*4),c=new Float32Array(a);for(let t=0;t<a;t++){let i=r[t];s[t*4]=e[i*4],s[t*4+1]=e[i*4+1],s[t*4+2]=e[i*4+2],s[t*4+3]=e[i*4+3],c[t]=n[i]}let l=t(s,c,i);for(let e=0;e<l.length;e++)o.push(r[l[e]])}return o.sort((e,t)=>n[t]-n[e]||e-t),Int32Array.from(o)}function r(e,t,r){let a=t;if(a.length===3){if(a[0]!==1)throw Error(`decodeYoloAnchors: expected batch size 1, got ${a[0]}.`);a=[a[1],a[2]]}if(a.length!==2)throw Error(`decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(t)}.`);let o=a[0],s=a[1],{numClasses:c,originalWidth:l,originalHeight:u,padLeft:d,padTop:f,scale:p,confThreshold:m,iouThreshold:h,maxDetections:g}=r;if(c<1||c+4>o)throw Error(`decodeYoloAnchors: invalid numClasses=${c} for channels=${o}.`);if(e.length!==o*s)throw Error(`decodeYoloAnchors: data length ${e.length} does not match channels*numAnchors=${o*s}.`);let _=[];for(let t=0;t<s;t++){let n=0,r=-1/0;for(let i=0;i<c;i++){let a=e[(4+i)*s+t];a!==void 0&&a>r&&(r=a,n=i)}if(r<m)continue;let i=e[t],a=e[s+t],o=e[2*s+t],h=e[3*s+t],g=i-o/2,v=a-h/2,y=i+o/2,b=a+h/2;g=(g-d)/p,v=(v-f)/p,y=(y-d)/p,b=(b-f)/p,g=Math.max(0,Math.min(l,g)),v=Math.max(0,Math.min(u,v)),y=Math.max(0,Math.min(l,y)),b=Math.max(0,Math.min(u,b)),_.push({anchorIdx:t,x1:g,y1:v,x2:y,y2:b,classId:n,confidence:r})}if(_.length===0)return i();let v=new Float32Array(_.length*4),y=new Float32Array(_.length),b=new Int32Array(_.length);for(let e=0;e<_.length;e++){let t=_[e];v[e*4]=t.x1,v[e*4+1]=t.y1,v[e*4+2]=t.x2,v[e*4+3]=t.y2,y[e]=t.confidence,b[e]=t.classId}let x=n(v,y,b,h);if(x.length===0)return i();let S=Array.from(x).slice(0,g),C=S.length,w=new Int32Array(C),T=new Float32Array(C*4),E=new Int32Array(C),D=new Float32Array(C);for(let e=0;e<C;e++){let t=_[S[e]];w[e]=t.anchorIdx,T[e*4]=t.x1,T[e*4+1]=t.y1,T[e*4+2]=t.x2,T[e*4+3]=t.y2,E[e]=t.classId,D[e]=t.confidence}return{anchorIndices:w,boxesXyxy:T,classIds:E,confidences:D}}function i(){return{anchorIndices:new Int32Array,boxesXyxy:new Float32Array,classIds:new Int32Array,confidences:new Float32Array}}function a(t,n,i){let a=n.length===3?n[1]:n[0];if(a===void 0||a<5)throw Error(`decodeYolo: invalid output channel count ${a} (expected >= 5).`);let o=r(t,n,{numClasses:a-4,...i}),s=[];for(let t=0;t<o.classIds.length;t++)s.push({bbox:new e.BoundingBox(o.boxesXyxy[t*4],o.boxesXyxy[t*4+1],o.boxesXyxy[t*4+2],o.boxesXyxy[t*4+3]),classId:o.classIds[t],confidence:o.confidences[t]});return s}exports.batchedNms=n,exports.decodeYolo=a,exports.decodeYoloAnchors=r,exports.nms=t;
1
+ const e=require("../types.cjs");function t(e,t,n){let r=t.length;if(r===0)return new Int32Array;let i=Array(r);for(let e=0;e<r;e++)i[e]=e;i.sort((e,n)=>t[n]-t[e]||e-n);let a=new Float32Array(r*4),o=new Float32Array(r);for(let t=0;t<r;t++){let n=i[t]*4,r=e[n],s=e[n+1],c=e[n+2],l=e[n+3];a[t*4]=r,a[t*4+1]=s,a[t*4+2]=c,a[t*4+3]=l,o[t]=Math.max(0,c-r)*Math.max(0,l-s)}let s=n>=0,c=new Uint8Array(r),l=[];for(let e=0;e<r;e++){if(c[e])continue;l.push(i[e]);let t=a[e*4],u=a[e*4+1],d=a[e*4+2],f=a[e*4+3],p=o[e];for(let i=e+1;i<r;i++){if(c[i])continue;let e=a[i*4],r=a[i*4+1],l=a[i*4+2],m=a[i*4+3],h=Math.max(0,Math.min(d,l)-Math.max(t,e));if(h===0&&s)continue;let g=h*Math.max(0,Math.min(f,m)-Math.max(u,r)),_=p+o[i]-g;(_>0?g/_:0)>n&&(c[i]=1)}}return Int32Array.from(l)}function n(e,n,r,i){if(n.length===0)return new Int32Array;let a=new Map;for(let e=0;e<r.length;e++){let t=r[e],n=a.get(t);n===void 0?a.set(t,[e]):n.push(e)}let o=[];for(let r of a.values()){let a=r.length,s=new Float32Array(a*4),c=new Float32Array(a);for(let t=0;t<a;t++){let i=r[t];s[t*4]=e[i*4],s[t*4+1]=e[i*4+1],s[t*4+2]=e[i*4+2],s[t*4+3]=e[i*4+3],c[t]=n[i]}let l=t(s,c,i);for(let e=0;e<l.length;e++)o.push(r[l[e]])}return o.sort((e,t)=>n[t]-n[e]||e-t),Int32Array.from(o)}function r(e,t,r){let a=t;if(a.length===3){if(a[0]!==1)throw Error(`decodeYoloAnchors: expected batch size 1, got ${a[0]}.`);a=[a[1],a[2]]}if(a.length!==2)throw Error(`decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(t)}.`);let o=a[0],s=a[1],{numClasses:c,originalWidth:l,originalHeight:u,padLeft:d,padTop:f,scale:p,confThreshold:m,iouThreshold:h,maxDetections:g}=r;if(c<1||c+4>o)throw Error(`decodeYoloAnchors: invalid numClasses=${c} for channels=${o}.`);if(e.length!==o*s)throw Error(`decodeYoloAnchors: data length ${e.length} does not match channels*numAnchors=${o*s}.`);let _=new Uint8Array(s),v=(4+c)*s;for(let t=4*s;t<v;t+=s)for(let n=0;n<s;n++)e[t+n]>=m&&(_[n]=1);let y=0,b=new Int32Array(s);for(let e=0;e<s;e++)_[e]===1&&(b[y++]=e);if(y===0)return i();let x=new Float32Array(y*4),S=new Float32Array(y),C=new Int32Array(y);for(let t=0;t<y;t++){let n=b[t],r=e[n],i=e[s+n],a=e[2*s+n],o=e[3*s+n],m=(r-a/2-d)/p,h=(i-o/2-f)/p,g=(r+a/2-d)/p,_=(i+o/2-f)/p;x[t*4]=Math.max(0,Math.min(l,m)),x[t*4+1]=Math.max(0,Math.min(u,h)),x[t*4+2]=Math.max(0,Math.min(l,g)),x[t*4+3]=Math.max(0,Math.min(u,_));let v=-1/0,y=0;for(let t=0,r=4*s+n;t<c;t++,r+=s){let n=e[r];n>v&&(v=n,y=t)}S[t]=v,C[t]=y}let w=n(x,S,C,h);if(w.length===0)return i();let T=Math.min(w.length,g),E=new Int32Array(T),D=new Float32Array(T*4),O=new Int32Array(T),k=new Float32Array(T);for(let e=0;e<T;e++){let t=w[e];E[e]=b[t],D[e*4]=x[t*4],D[e*4+1]=x[t*4+1],D[e*4+2]=x[t*4+2],D[e*4+3]=x[t*4+3],O[e]=C[t],k[e]=S[t]}return{anchorIndices:E,boxesXyxy:D,classIds:O,confidences:k}}function i(){return{anchorIndices:new Int32Array,boxesXyxy:new Float32Array,classIds:new Int32Array,confidences:new Float32Array}}function a(t,n,i){let a=n.length===3?n[1]:n[0];if(a===void 0||a<5)throw Error(`decodeYolo: invalid output channel count ${a} (expected >= 5).`);let o=r(t,n,{numClasses:a-4,...i}),s=[];for(let t=0;t<o.classIds.length;t++)s.push({bbox:new e.BoundingBox(o.boxesXyxy[t*4],o.boxesXyxy[t*4+1],o.boxesXyxy[t*4+2],o.boxesXyxy[t*4+3]),classId:o.classIds[t],confidence:o.confidences[t]});return s}exports.batchedNms=n,exports.decodeYolo=a,exports.decodeYoloAnchors=r,exports.nms=t;
2
2
  //# sourceMappingURL=detection.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"detection.cjs","names":[],"sources":["../../../src/vision/postprocess/detection.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Detection head postprocessing: anchor-free YOLO decoding + non-maximum suppression.\n *\n * The shared {@link decodeYoloAnchors} helper does the per-anchor work that\n * is identical for plain detection and segmentation (transpose, xywh→xyxy,\n * letterbox unmap, per-class NMS, sort & cap). {@link decodeYolo} is a thin\n * wrapper around it; the segmentation module ({@link ./segmentation.js})\n * calls the helper directly so it can also recover the per-anchor mask\n * coefficients.\n *\n * Works for any YOLO export with the post-v8 anchor-free head:\n * **YOLOv8 / v9 / v10 / v11 / v12** detect heads, all of which share the\n * `[1, 4 + nc, N]` output layout.\n */\n\nimport { BoundingBox } from \"../types\";\n\n/**\n * Greedy non-maximum suppression on axis-aligned bounding boxes.\n *\n * Mirrors `torchvision.ops.nms` (keeps boxes with the highest score, drops\n * any subsequent box whose IoU exceeds the threshold).\n *\n * @param boxes Flat array of length `4 * N` in xyxy order: `[x1,y1,x2,y2, ...]`.\n * @param scores Detection score per box, length `N`.\n * @param iouThreshold Boxes with IoU above this threshold relative to a kept box are suppressed.\n * @returns Indices of kept boxes, in descending score order. Boxes tied on\n * score are visited lowest-index first, so the survivor of a tie is\n * deterministic and matches both `torchvision` and the Python SDK.\n */\nexport function nms(boxes: Float32Array, scores: Float32Array, iouThreshold: number): Int32Array {\n const n = scores.length;\n if (n === 0) return new Int32Array(0);\n\n const areas = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n const x1 = boxes[i * 4] as number;\n const y1 = boxes[i * 4 + 1] as number;\n const x2 = boxes[i * 4 + 2] as number;\n const y2 = boxes[i * 4 + 3] as number;\n areas[i] = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);\n }\n\n const order = new Array<number>(n);\n for (let i = 0; i < n; i++) order[i] = i;\n order.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n\n const suppressed = new Uint8Array(n);\n const keep: number[] = [];\n\n for (let oi = 0; oi < order.length; oi++) {\n const i = order[oi] as number;\n if (suppressed[i]) continue;\n keep.push(i);\n\n const ax1 = boxes[i * 4] as number;\n const ay1 = boxes[i * 4 + 1] as number;\n const ax2 = boxes[i * 4 + 2] as number;\n const ay2 = boxes[i * 4 + 3] as number;\n const ai = areas[i] as number;\n\n for (let oj = oi + 1; oj < order.length; oj++) {\n const j = order[oj] as number;\n if (suppressed[j]) continue;\n\n const bx1 = boxes[j * 4] as number;\n const by1 = boxes[j * 4 + 1] as number;\n const bx2 = boxes[j * 4 + 2] as number;\n const by2 = boxes[j * 4 + 3] as number;\n\n const ix1 = Math.max(ax1, bx1);\n const iy1 = Math.max(ay1, by1);\n const ix2 = Math.min(ax2, bx2);\n const iy2 = Math.min(ay2, by2);\n const iw = Math.max(0, ix2 - ix1);\n const ih = Math.max(0, iy2 - iy1);\n const inter = iw * ih;\n const union = ai + (areas[j] as number) - inter;\n const iou = union > 0 ? inter / union : 0;\n if (iou > iouThreshold) suppressed[j] = 1;\n }\n }\n\n return Int32Array.from(keep);\n}\n\n/**\n * Per-class NMS — boxes are suppressed only by other boxes of the same class.\n *\n * Mirrors `torchvision.ops.batched_nms`.\n *\n * @param boxes Flat array of length `4 * N` in xyxy order.\n * @param scores Detection score per box, length `N`.\n * @param idxs Class index per box, length `N`. Boxes with different `idxs`\n * never suppress each other.\n * @param iouThreshold IoU threshold for suppression within a class.\n * @returns Indices of kept boxes, sorted by descending score across all\n * classes. Survivors from different classes that are tied on score are\n * ordered lowest-index first — an explicit tie-break, because the order the\n * per-class loop emits them in is an implementation detail (here, `Map`\n * insertion order; in Python, sorted class order).\n */\nexport function batchedNms(\n boxes: Float32Array,\n scores: Float32Array,\n idxs: Int32Array,\n iouThreshold: number,\n): Int32Array {\n if (scores.length === 0) return new Int32Array(0);\n\n const byClass = new Map<number, number[]>();\n for (let i = 0; i < idxs.length; i++) {\n const c = idxs[i] as number;\n const list = byClass.get(c);\n if (list === undefined) byClass.set(c, [i]);\n else list.push(i);\n }\n\n const keep: number[] = [];\n for (const indices of byClass.values()) {\n const m = indices.length;\n const subBoxes = new Float32Array(m * 4);\n const subScores = new Float32Array(m);\n for (let k = 0; k < m; k++) {\n const i = indices[k] as number;\n subBoxes[k * 4] = boxes[i * 4] as number;\n subBoxes[k * 4 + 1] = boxes[i * 4 + 1] as number;\n subBoxes[k * 4 + 2] = boxes[i * 4 + 2] as number;\n subBoxes[k * 4 + 3] = boxes[i * 4 + 3] as number;\n subScores[k] = scores[i] as number;\n }\n const subKeep = nms(subBoxes, subScores, iouThreshold);\n for (let k = 0; k < subKeep.length; k++) {\n keep.push(indices[subKeep[k] as number] as number);\n }\n }\n\n keep.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n return Int32Array.from(keep);\n}\n\nexport interface DecodeYoloAnchorsOptions {\n /** Number of class-score channels following the 4 box channels. */\n readonly numClasses: number;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedAnchors {\n /** Indices into the original `numAnchors` axis, in descending confidence order. */\n readonly anchorIndices: Int32Array;\n /** `[k, 4]` boxes in original-image pixel coords, flat row-major xyxy. */\n readonly boxesXyxy: Float32Array;\n /** Predicted class id per survivor. */\n readonly classIds: Int32Array;\n /** Confidence per survivor. */\n readonly confidences: Float32Array;\n}\n\n/**\n * Shared YOLO per-anchor decode used by both detection and segmentation\n * (v8 / v9 / v10 / v11 / v12).\n *\n * Only the first `4 + numClasses` channels are read; later channels (e.g.\n * mask coefficients) are ignored — callers can fetch them via the returned\n * {@link DecodedAnchors.anchorIndices}.\n *\n * @param data Flat per-anchor output, length `channels * numAnchors`.\n * @param dims Dims as reported by ORT, e.g. `[1, 84, 8400]` (det) or\n * `[1, 116, 8400]` (seg). The leading batch dim must be 1.\n */\nexport function decodeYoloAnchors(\n data: Float32Array,\n dims: readonly number[],\n options: DecodeYoloAnchorsOptions,\n): DecodedAnchors {\n let normalized = dims;\n if (normalized.length === 3) {\n if (normalized[0] !== 1) {\n throw new Error(`decodeYoloAnchors: expected batch size 1, got ${normalized[0]}.`);\n }\n normalized = [normalized[1] as number, normalized[2] as number];\n }\n if (normalized.length !== 2) {\n throw new Error(\n `decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(dims)}.`,\n );\n }\n const channels = normalized[0] as number;\n const numAnchors = normalized[1] as number;\n\n const {\n numClasses,\n originalWidth,\n originalHeight,\n padLeft,\n padTop,\n scale,\n confThreshold,\n iouThreshold,\n maxDetections,\n } = options;\n\n if (numClasses < 1 || numClasses + 4 > channels) {\n throw new Error(\n `decodeYoloAnchors: invalid numClasses=${numClasses} for channels=${channels}.`,\n );\n }\n if (data.length !== channels * numAnchors) {\n throw new Error(\n `decodeYoloAnchors: data length ${data.length} does not match channels*numAnchors=${channels * numAnchors}.`,\n );\n }\n\n type Candidate = {\n anchorIdx: number;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n classId: number;\n confidence: number;\n };\n const candidates: Candidate[] = [];\n\n for (let a = 0; a < numAnchors; a++) {\n let bestCls = 0;\n let bestScore = -Infinity;\n for (let c = 0; c < numClasses; c++) {\n const s = data[(4 + c) * numAnchors + a];\n if (s !== undefined && s > bestScore) {\n bestScore = s;\n bestCls = c;\n }\n }\n if (bestScore < confThreshold) continue;\n\n const cx = data[a] as number;\n const cy = data[numAnchors + a] as number;\n const w = data[2 * numAnchors + a] as number;\n const h = data[3 * numAnchors + a] as number;\n\n let x1 = cx - w / 2;\n let y1 = cy - h / 2;\n let x2 = cx + w / 2;\n let y2 = cy + h / 2;\n\n x1 = (x1 - padLeft) / scale;\n y1 = (y1 - padTop) / scale;\n x2 = (x2 - padLeft) / scale;\n y2 = (y2 - padTop) / scale;\n\n x1 = Math.max(0, Math.min(originalWidth, x1));\n y1 = Math.max(0, Math.min(originalHeight, y1));\n x2 = Math.max(0, Math.min(originalWidth, x2));\n y2 = Math.max(0, Math.min(originalHeight, y2));\n\n candidates.push({ anchorIdx: a, x1, y1, x2, y2, classId: bestCls, confidence: bestScore });\n }\n\n if (candidates.length === 0) return emptyDecoded();\n\n // Build flat arrays then delegate to batchedNms — same algorithm as before\n // but funnelled through the public per-class NMS helper.\n const flatBoxes = new Float32Array(candidates.length * 4);\n const scoresArr = new Float32Array(candidates.length);\n const idxsArr = new Int32Array(candidates.length);\n for (let i = 0; i < candidates.length; i++) {\n const c = candidates[i] as Candidate;\n flatBoxes[i * 4] = c.x1;\n flatBoxes[i * 4 + 1] = c.y1;\n flatBoxes[i * 4 + 2] = c.x2;\n flatBoxes[i * 4 + 3] = c.y2;\n scoresArr[i] = c.confidence;\n idxsArr[i] = c.classId;\n }\n const kept = batchedNms(flatBoxes, scoresArr, idxsArr, iouThreshold);\n if (kept.length === 0) return emptyDecoded();\n\n const limited = Array.from(kept).slice(0, maxDetections);\n const k = limited.length;\n const anchorIndices = new Int32Array(k);\n const boxesXyxy = new Float32Array(k * 4);\n const classIds = new Int32Array(k);\n const confidences = new Float32Array(k);\n for (let i = 0; i < k; i++) {\n const c = candidates[limited[i] as number] as Candidate;\n anchorIndices[i] = c.anchorIdx;\n boxesXyxy[i * 4] = c.x1;\n boxesXyxy[i * 4 + 1] = c.y1;\n boxesXyxy[i * 4 + 2] = c.x2;\n boxesXyxy[i * 4 + 3] = c.y2;\n classIds[i] = c.classId;\n confidences[i] = c.confidence;\n }\n return { anchorIndices, boxesXyxy, classIds, confidences };\n}\n\nfunction emptyDecoded(): DecodedAnchors {\n return {\n anchorIndices: new Int32Array(0),\n boxesXyxy: new Float32Array(0),\n classIds: new Int32Array(0),\n confidences: new Float32Array(0),\n };\n}\n\nexport interface DecodeYoloOptions {\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedDetection {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n}\n\n/**\n * Decode an anchor-free YOLO detection output into a list of detections.\n *\n * Works for **YOLOv8 / v9 / v10 / v11 / v12** detect heads.\n *\n * Expected raw shape: `[1, 4 + numClasses, N]`. `numClasses` is inferred\n * from the channel count.\n */\nexport function decodeYolo(\n output: Float32Array,\n outputDims: readonly number[],\n options: DecodeYoloOptions,\n): DecodedDetection[] {\n const channels = outputDims.length === 3 ? outputDims[1] : outputDims[0];\n if (channels === undefined || channels < 5) {\n throw new Error(`decodeYolo: invalid output channel count ${channels} (expected >= 5).`);\n }\n const numClasses = channels - 4;\n\n const decoded = decodeYoloAnchors(output, outputDims, {\n numClasses,\n ...options,\n });\n\n const results: DecodedDetection[] = [];\n for (let i = 0; i < decoded.classIds.length; i++) {\n results.push({\n bbox: new BoundingBox(\n decoded.boxesXyxy[i * 4] as number,\n decoded.boxesXyxy[i * 4 + 1] as number,\n decoded.boxesXyxy[i * 4 + 2] as number,\n decoded.boxesXyxy[i * 4 + 3] as number,\n ),\n classId: decoded.classIds[i] as number,\n confidence: decoded.confidences[i] as number,\n });\n }\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"detection.cjs","names":[],"sources":["../../../src/vision/postprocess/detection.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Detection head postprocessing: anchor-free YOLO decoding + non-maximum suppression.\n *\n * The shared {@link decodeYoloAnchors} helper does the per-anchor work that\n * is identical for plain detection and segmentation (transpose, xywh→xyxy,\n * letterbox unmap, per-class NMS, sort & cap). {@link decodeYolo} is a thin\n * wrapper around it; the segmentation module ({@link ./segmentation.js})\n * calls the helper directly so it can also recover the per-anchor mask\n * coefficients.\n *\n * Works for any YOLO export with the post-v8 anchor-free head:\n * **YOLOv8 / v9 / v10 / v11 / v12** detect heads, all of which share the\n * `[1, 4 + nc, N]` output layout.\n */\n\nimport { BoundingBox } from \"../types\";\n\n/**\n * Greedy non-maximum suppression on axis-aligned bounding boxes.\n *\n * Mirrors `torchvision.ops.nms` (keeps boxes with the highest score, drops\n * any subsequent box whose IoU exceeds the threshold).\n *\n * @param boxes Flat array of length `4 * N` in xyxy order: `[x1,y1,x2,y2, ...]`.\n * @param scores Detection score per box, length `N`.\n * @param iouThreshold Boxes with IoU above this threshold relative to a kept box are suppressed.\n * @returns Indices of kept boxes, in descending score order. Boxes tied on\n * score are visited lowest-index first, so the survivor of a tie is\n * deterministic and matches both `torchvision` and the Python SDK.\n *\n * The boxes are copied once into score order, so the inner loop walks a\n * contiguous `Float32Array` instead of hopping through `order` into the caller's\n * layout. A pair with no horizontal overlap is skipped before the vertical\n * extent is computed: its IoU is `0`, which suppresses nothing for any\n * non-negative threshold — the shortcut is gated on that so a negative\n * threshold keeps the exact semantics.\n */\nexport function nms(boxes: Float32Array, scores: Float32Array, iouThreshold: number): Int32Array {\n const n = scores.length;\n if (n === 0) return new Int32Array(0);\n\n const order = new Array<number>(n);\n for (let i = 0; i < n; i++) order[i] = i;\n order.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n\n const sorted = new Float32Array(n * 4);\n const areas = new Float32Array(n);\n for (let k = 0; k < n; k++) {\n const i = (order[k] as number) * 4;\n const x1 = boxes[i] as number;\n const y1 = boxes[i + 1] as number;\n const x2 = boxes[i + 2] as number;\n const y2 = boxes[i + 3] as number;\n sorted[k * 4] = x1;\n sorted[k * 4 + 1] = y1;\n sorted[k * 4 + 2] = x2;\n sorted[k * 4 + 3] = y2;\n areas[k] = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);\n }\n\n const skipDisjoint = iouThreshold >= 0;\n const suppressed = new Uint8Array(n);\n const keep: number[] = [];\n\n for (let k = 0; k < n; k++) {\n if (suppressed[k]) continue;\n keep.push(order[k] as number);\n\n const ax1 = sorted[k * 4] as number;\n const ay1 = sorted[k * 4 + 1] as number;\n const ax2 = sorted[k * 4 + 2] as number;\n const ay2 = sorted[k * 4 + 3] as number;\n const ak = areas[k] as number;\n\n for (let m = k + 1; m < n; m++) {\n if (suppressed[m]) continue;\n const bx1 = sorted[m * 4] as number;\n const by1 = sorted[m * 4 + 1] as number;\n const bx2 = sorted[m * 4 + 2] as number;\n const by2 = sorted[m * 4 + 3] as number;\n const iw = Math.max(0, Math.min(ax2, bx2) - Math.max(ax1, bx1));\n if (iw === 0 && skipDisjoint) continue;\n const ih = Math.max(0, Math.min(ay2, by2) - Math.max(ay1, by1));\n const inter = iw * ih;\n const union = ak + (areas[m] as number) - inter;\n const iou = union > 0 ? inter / union : 0;\n if (iou > iouThreshold) suppressed[m] = 1;\n }\n }\n\n return Int32Array.from(keep);\n}\n\n/**\n * Per-class NMS — boxes are suppressed only by other boxes of the same class.\n *\n * Mirrors `torchvision.ops.batched_nms`.\n *\n * @param boxes Flat array of length `4 * N` in xyxy order.\n * @param scores Detection score per box, length `N`.\n * @param idxs Class index per box, length `N`. Boxes with different `idxs`\n * never suppress each other.\n * @param iouThreshold IoU threshold for suppression within a class.\n * @returns Indices of kept boxes, sorted by descending score across all\n * classes. Survivors from different classes that are tied on score are\n * ordered lowest-index first — an explicit tie-break, because the order the\n * per-class loop emits them in is an implementation detail (here, `Map`\n * insertion order; in Python, sorted class order).\n */\nexport function batchedNms(\n boxes: Float32Array,\n scores: Float32Array,\n idxs: Int32Array,\n iouThreshold: number,\n): Int32Array {\n if (scores.length === 0) return new Int32Array(0);\n\n const byClass = new Map<number, number[]>();\n for (let i = 0; i < idxs.length; i++) {\n const c = idxs[i] as number;\n const list = byClass.get(c);\n if (list === undefined) byClass.set(c, [i]);\n else list.push(i);\n }\n\n const keep: number[] = [];\n for (const indices of byClass.values()) {\n const m = indices.length;\n const subBoxes = new Float32Array(m * 4);\n const subScores = new Float32Array(m);\n for (let k = 0; k < m; k++) {\n const i = indices[k] as number;\n subBoxes[k * 4] = boxes[i * 4] as number;\n subBoxes[k * 4 + 1] = boxes[i * 4 + 1] as number;\n subBoxes[k * 4 + 2] = boxes[i * 4 + 2] as number;\n subBoxes[k * 4 + 3] = boxes[i * 4 + 3] as number;\n subScores[k] = scores[i] as number;\n }\n const subKeep = nms(subBoxes, subScores, iouThreshold);\n for (let k = 0; k < subKeep.length; k++) {\n keep.push(indices[subKeep[k] as number] as number);\n }\n }\n\n keep.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n return Int32Array.from(keep);\n}\n\nexport interface DecodeYoloAnchorsOptions {\n /** Number of class-score channels following the 4 box channels. */\n readonly numClasses: number;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedAnchors {\n /** Indices into the original `numAnchors` axis, in descending confidence order. */\n readonly anchorIndices: Int32Array;\n /** `[k, 4]` boxes in original-image pixel coords, flat row-major xyxy. */\n readonly boxesXyxy: Float32Array;\n /** Predicted class id per survivor. */\n readonly classIds: Int32Array;\n /** Confidence per survivor. */\n readonly confidences: Float32Array;\n}\n\n/**\n * Shared YOLO per-anchor decode used by both detection and segmentation\n * (v8 / v9 / v10 / v11 / v12).\n *\n * Only the first `4 + numClasses` channels are read; later channels (e.g.\n * mask coefficients) are ignored — callers can fetch them via the returned\n * {@link DecodedAnchors.anchorIndices}.\n *\n * Candidates are found in two passes, mirroring the Python decoder. The first\n * walks each class row contiguously and only *marks* anchors holding some score\n * at or above the threshold — an anchor's best score clears the threshold\n * exactly when one of its scores does, so no running maximum is needed and the\n * hot loop never writes on a typical frame. The anchor-major scan it replaces\n * read `numClasses` values spaced `numAnchors` apart for every one of the 8400\n * anchors and cost ~1 ms even on a frame with no candidates. The winning class\n * is then picked only for the marked anchors — a few hundred, not 8400 — with\n * the same strict `>` that keeps the lowest class id on a tie.\n *\n * @param data Flat per-anchor output, length `channels * numAnchors`.\n * @param dims Dims as reported by ORT, e.g. `[1, 84, 8400]` (det) or\n * `[1, 116, 8400]` (seg). The leading batch dim must be 1.\n */\nexport function decodeYoloAnchors(\n data: Float32Array,\n dims: readonly number[],\n options: DecodeYoloAnchorsOptions,\n): DecodedAnchors {\n let normalized = dims;\n if (normalized.length === 3) {\n if (normalized[0] !== 1) {\n throw new Error(`decodeYoloAnchors: expected batch size 1, got ${normalized[0]}.`);\n }\n normalized = [normalized[1] as number, normalized[2] as number];\n }\n if (normalized.length !== 2) {\n throw new Error(\n `decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(dims)}.`,\n );\n }\n const channels = normalized[0] as number;\n const numAnchors = normalized[1] as number;\n\n const {\n numClasses,\n originalWidth,\n originalHeight,\n padLeft,\n padTop,\n scale,\n confThreshold,\n iouThreshold,\n maxDetections,\n } = options;\n\n if (numClasses < 1 || numClasses + 4 > channels) {\n throw new Error(\n `decodeYoloAnchors: invalid numClasses=${numClasses} for channels=${channels}.`,\n );\n }\n if (data.length !== channels * numAnchors) {\n throw new Error(\n `decodeYoloAnchors: data length ${data.length} does not match channels*numAnchors=${channels * numAnchors}.`,\n );\n }\n\n const hit = new Uint8Array(numAnchors);\n const classEnd = (4 + numClasses) * numAnchors;\n for (let row = 4 * numAnchors; row < classEnd; row += numAnchors) {\n for (let a = 0; a < numAnchors; a++) {\n if ((data[row + a] as number) >= confThreshold) hit[a] = 1;\n }\n }\n\n let count = 0;\n const candidateAnchors = new Int32Array(numAnchors);\n for (let a = 0; a < numAnchors; a++) {\n if (hit[a] === 1) candidateAnchors[count++] = a;\n }\n if (count === 0) return emptyDecoded();\n\n const flatBoxes = new Float32Array(count * 4);\n const scoresArr = new Float32Array(count);\n const idxsArr = new Int32Array(count);\n for (let i = 0; i < count; i++) {\n const a = candidateAnchors[i] as number;\n const cx = data[a] as number;\n const cy = data[numAnchors + a] as number;\n const w = data[2 * numAnchors + a] as number;\n const h = data[3 * numAnchors + a] as number;\n const x1 = (cx - w / 2 - padLeft) / scale;\n const y1 = (cy - h / 2 - padTop) / scale;\n const x2 = (cx + w / 2 - padLeft) / scale;\n const y2 = (cy + h / 2 - padTop) / scale;\n flatBoxes[i * 4] = Math.max(0, Math.min(originalWidth, x1));\n flatBoxes[i * 4 + 1] = Math.max(0, Math.min(originalHeight, y1));\n flatBoxes[i * 4 + 2] = Math.max(0, Math.min(originalWidth, x2));\n flatBoxes[i * 4 + 3] = Math.max(0, Math.min(originalHeight, y2));\n let bestScore = -Infinity;\n let bestClass = 0;\n for (let c = 0, at = 4 * numAnchors + a; c < numClasses; c++, at += numAnchors) {\n const score = data[at] as number;\n if (score > bestScore) {\n bestScore = score;\n bestClass = c;\n }\n }\n scoresArr[i] = bestScore;\n idxsArr[i] = bestClass;\n }\n const kept = batchedNms(flatBoxes, scoresArr, idxsArr, iouThreshold);\n if (kept.length === 0) return emptyDecoded();\n\n const k = Math.min(kept.length, maxDetections);\n const anchorIndices = new Int32Array(k);\n const boxesXyxy = new Float32Array(k * 4);\n const classIds = new Int32Array(k);\n const confidences = new Float32Array(k);\n for (let i = 0; i < k; i++) {\n const j = kept[i] as number;\n anchorIndices[i] = candidateAnchors[j] as number;\n boxesXyxy[i * 4] = flatBoxes[j * 4] as number;\n boxesXyxy[i * 4 + 1] = flatBoxes[j * 4 + 1] as number;\n boxesXyxy[i * 4 + 2] = flatBoxes[j * 4 + 2] as number;\n boxesXyxy[i * 4 + 3] = flatBoxes[j * 4 + 3] as number;\n classIds[i] = idxsArr[j] as number;\n confidences[i] = scoresArr[j] as number;\n }\n return { anchorIndices, boxesXyxy, classIds, confidences };\n}\n\nfunction emptyDecoded(): DecodedAnchors {\n return {\n anchorIndices: new Int32Array(0),\n boxesXyxy: new Float32Array(0),\n classIds: new Int32Array(0),\n confidences: new Float32Array(0),\n };\n}\n\nexport interface DecodeYoloOptions {\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedDetection {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n}\n\n/**\n * Decode an anchor-free YOLO detection output into a list of detections.\n *\n * Works for **YOLOv8 / v9 / v10 / v11 / v12** detect heads.\n *\n * Expected raw shape: `[1, 4 + numClasses, N]`. `numClasses` is inferred\n * from the channel count.\n */\nexport function decodeYolo(\n output: Float32Array,\n outputDims: readonly number[],\n options: DecodeYoloOptions,\n): DecodedDetection[] {\n const channels = outputDims.length === 3 ? outputDims[1] : outputDims[0];\n if (channels === undefined || channels < 5) {\n throw new Error(`decodeYolo: invalid output channel count ${channels} (expected >= 5).`);\n }\n const numClasses = channels - 4;\n\n const decoded = decodeYoloAnchors(output, outputDims, {\n numClasses,\n ...options,\n });\n\n const results: DecodedDetection[] = [];\n for (let i = 0; i < decoded.classIds.length; i++) {\n results.push({\n bbox: new BoundingBox(\n decoded.boxesXyxy[i * 4] as number,\n decoded.boxesXyxy[i * 4 + 1] as number,\n decoded.boxesXyxy[i * 4 + 2] as number,\n decoded.boxesXyxy[i * 4 + 3] as number,\n ),\n classId: decoded.classIds[i] as number,\n confidence: decoded.confidences[i] as number,\n });\n }\n return results;\n}\n"],"mappings":"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"}
@@ -3,28 +3,28 @@ import { BoundingBox as e } from "../types.js";
3
3
  function t(e, t, n) {
4
4
  let r = t.length;
5
5
  if (r === 0) return /* @__PURE__ */ new Int32Array();
6
- let i = new Float32Array(r);
6
+ let i = Array(r);
7
+ for (let e = 0; e < r; e++) i[e] = e;
8
+ i.sort((e, n) => t[n] - t[e] || e - n);
9
+ let a = new Float32Array(r * 4), o = new Float32Array(r);
7
10
  for (let t = 0; t < r; t++) {
8
- let n = e[t * 4], r = e[t * 4 + 1], a = e[t * 4 + 2], o = e[t * 4 + 3];
9
- i[t] = Math.max(0, a - n) * Math.max(0, o - r);
11
+ let n = i[t] * 4, r = e[n], s = e[n + 1], c = e[n + 2], l = e[n + 3];
12
+ a[t * 4] = r, a[t * 4 + 1] = s, a[t * 4 + 2] = c, a[t * 4 + 3] = l, o[t] = Math.max(0, c - r) * Math.max(0, l - s);
10
13
  }
11
- let a = Array(r);
12
- for (let e = 0; e < r; e++) a[e] = e;
13
- a.sort((e, n) => t[n] - t[e] || e - n);
14
- let o = new Uint8Array(r), s = [];
15
- for (let t = 0; t < a.length; t++) {
16
- let r = a[t];
17
- if (o[r]) continue;
18
- s.push(r);
19
- let c = e[r * 4], l = e[r * 4 + 1], u = e[r * 4 + 2], d = e[r * 4 + 3], f = i[r];
20
- for (let r = t + 1; r < a.length; r++) {
21
- let t = a[r];
22
- if (o[t]) continue;
23
- let s = e[t * 4], p = e[t * 4 + 1], m = e[t * 4 + 2], h = e[t * 4 + 3], g = Math.max(c, s), _ = Math.max(l, p), v = Math.min(u, m), y = Math.min(d, h), b = Math.max(0, v - g) * Math.max(0, y - _), x = f + i[t] - b;
24
- (x > 0 ? b / x : 0) > n && (o[t] = 1);
14
+ let s = n >= 0, c = new Uint8Array(r), l = [];
15
+ for (let e = 0; e < r; e++) {
16
+ if (c[e]) continue;
17
+ l.push(i[e]);
18
+ let t = a[e * 4], u = a[e * 4 + 1], d = a[e * 4 + 2], f = a[e * 4 + 3], p = o[e];
19
+ for (let i = e + 1; i < r; i++) {
20
+ if (c[i]) continue;
21
+ let e = a[i * 4], r = a[i * 4 + 1], l = a[i * 4 + 2], m = a[i * 4 + 3], h = Math.max(0, Math.min(d, l) - Math.max(t, e));
22
+ if (h === 0 && s) continue;
23
+ let g = h * Math.max(0, Math.min(f, m) - Math.max(u, r)), _ = p + o[i] - g;
24
+ (_ > 0 ? g / _ : 0) > n && (c[i] = 1);
25
25
  }
26
26
  }
27
- return Int32Array.from(s);
27
+ return Int32Array.from(l);
28
28
  }
29
29
  function n(e, n, r, i) {
30
30
  if (n.length === 0) return /* @__PURE__ */ new Int32Array();
@@ -55,43 +55,34 @@ function r(e, t, r) {
55
55
  let o = a[0], s = a[1], { numClasses: c, originalWidth: l, originalHeight: u, padLeft: d, padTop: f, scale: p, confThreshold: m, iouThreshold: h, maxDetections: g } = r;
56
56
  if (c < 1 || c + 4 > o) throw Error(`decodeYoloAnchors: invalid numClasses=${c} for channels=${o}.`);
57
57
  if (e.length !== o * s) throw Error(`decodeYoloAnchors: data length ${e.length} does not match channels*numAnchors=${o * s}.`);
58
- let _ = [];
59
- for (let t = 0; t < s; t++) {
60
- let n = 0, r = -Infinity;
61
- for (let i = 0; i < c; i++) {
62
- let a = e[(4 + i) * s + t];
63
- a !== void 0 && a > r && (r = a, n = i);
58
+ let _ = new Uint8Array(s), v = (4 + c) * s;
59
+ for (let t = 4 * s; t < v; t += s) for (let n = 0; n < s; n++) e[t + n] >= m && (_[n] = 1);
60
+ let y = 0, b = new Int32Array(s);
61
+ for (let e = 0; e < s; e++) _[e] === 1 && (b[y++] = e);
62
+ if (y === 0) return i();
63
+ let x = new Float32Array(y * 4), S = new Float32Array(y), C = new Int32Array(y);
64
+ for (let t = 0; t < y; t++) {
65
+ let n = b[t], r = e[n], i = e[s + n], a = e[2 * s + n], o = e[3 * s + n], m = (r - a / 2 - d) / p, h = (i - o / 2 - f) / p, g = (r + a / 2 - d) / p, _ = (i + o / 2 - f) / p;
66
+ x[t * 4] = Math.max(0, Math.min(l, m)), x[t * 4 + 1] = Math.max(0, Math.min(u, h)), x[t * 4 + 2] = Math.max(0, Math.min(l, g)), x[t * 4 + 3] = Math.max(0, Math.min(u, _));
67
+ let v = -Infinity, y = 0;
68
+ for (let t = 0, r = 4 * s + n; t < c; t++, r += s) {
69
+ let n = e[r];
70
+ n > v && (v = n, y = t);
64
71
  }
65
- if (r < m) continue;
66
- let i = e[t], a = e[s + t], o = e[2 * s + t], h = e[3 * s + t], g = i - o / 2, v = a - h / 2, y = i + o / 2, b = a + h / 2;
67
- g = (g - d) / p, v = (v - f) / p, y = (y - d) / p, b = (b - f) / p, g = Math.max(0, Math.min(l, g)), v = Math.max(0, Math.min(u, v)), y = Math.max(0, Math.min(l, y)), b = Math.max(0, Math.min(u, b)), _.push({
68
- anchorIdx: t,
69
- x1: g,
70
- y1: v,
71
- x2: y,
72
- y2: b,
73
- classId: n,
74
- confidence: r
75
- });
72
+ S[t] = v, C[t] = y;
76
73
  }
77
- if (_.length === 0) return i();
78
- let v = new Float32Array(_.length * 4), y = new Float32Array(_.length), b = new Int32Array(_.length);
79
- for (let e = 0; e < _.length; e++) {
80
- let t = _[e];
81
- v[e * 4] = t.x1, v[e * 4 + 1] = t.y1, v[e * 4 + 2] = t.x2, v[e * 4 + 3] = t.y2, y[e] = t.confidence, b[e] = t.classId;
82
- }
83
- let x = n(v, y, b, h);
84
- if (x.length === 0) return i();
85
- let S = Array.from(x).slice(0, g), C = S.length, w = new Int32Array(C), T = new Float32Array(C * 4), E = new Int32Array(C), D = new Float32Array(C);
86
- for (let e = 0; e < C; e++) {
87
- let t = _[S[e]];
88
- w[e] = t.anchorIdx, T[e * 4] = t.x1, T[e * 4 + 1] = t.y1, T[e * 4 + 2] = t.x2, T[e * 4 + 3] = t.y2, E[e] = t.classId, D[e] = t.confidence;
74
+ let w = n(x, S, C, h);
75
+ if (w.length === 0) return i();
76
+ let T = Math.min(w.length, g), E = new Int32Array(T), D = new Float32Array(T * 4), O = new Int32Array(T), k = new Float32Array(T);
77
+ for (let e = 0; e < T; e++) {
78
+ let t = w[e];
79
+ E[e] = b[t], D[e * 4] = x[t * 4], D[e * 4 + 1] = x[t * 4 + 1], D[e * 4 + 2] = x[t * 4 + 2], D[e * 4 + 3] = x[t * 4 + 3], O[e] = C[t], k[e] = S[t];
89
80
  }
90
81
  return {
91
- anchorIndices: w,
92
- boxesXyxy: T,
93
- classIds: E,
94
- confidences: D
82
+ anchorIndices: E,
83
+ boxesXyxy: D,
84
+ classIds: O,
85
+ confidences: k
95
86
  };
96
87
  }
97
88
  function i() {
@@ -1 +1 @@
1
- {"version":3,"file":"detection.js","names":[],"sources":["../../../src/vision/postprocess/detection.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Detection head postprocessing: anchor-free YOLO decoding + non-maximum suppression.\n *\n * The shared {@link decodeYoloAnchors} helper does the per-anchor work that\n * is identical for plain detection and segmentation (transpose, xywh→xyxy,\n * letterbox unmap, per-class NMS, sort & cap). {@link decodeYolo} is a thin\n * wrapper around it; the segmentation module ({@link ./segmentation.js})\n * calls the helper directly so it can also recover the per-anchor mask\n * coefficients.\n *\n * Works for any YOLO export with the post-v8 anchor-free head:\n * **YOLOv8 / v9 / v10 / v11 / v12** detect heads, all of which share the\n * `[1, 4 + nc, N]` output layout.\n */\n\nimport { BoundingBox } from \"../types\";\n\n/**\n * Greedy non-maximum suppression on axis-aligned bounding boxes.\n *\n * Mirrors `torchvision.ops.nms` (keeps boxes with the highest score, drops\n * any subsequent box whose IoU exceeds the threshold).\n *\n * @param boxes Flat array of length `4 * N` in xyxy order: `[x1,y1,x2,y2, ...]`.\n * @param scores Detection score per box, length `N`.\n * @param iouThreshold Boxes with IoU above this threshold relative to a kept box are suppressed.\n * @returns Indices of kept boxes, in descending score order. Boxes tied on\n * score are visited lowest-index first, so the survivor of a tie is\n * deterministic and matches both `torchvision` and the Python SDK.\n */\nexport function nms(boxes: Float32Array, scores: Float32Array, iouThreshold: number): Int32Array {\n const n = scores.length;\n if (n === 0) return new Int32Array(0);\n\n const areas = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n const x1 = boxes[i * 4] as number;\n const y1 = boxes[i * 4 + 1] as number;\n const x2 = boxes[i * 4 + 2] as number;\n const y2 = boxes[i * 4 + 3] as number;\n areas[i] = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);\n }\n\n const order = new Array<number>(n);\n for (let i = 0; i < n; i++) order[i] = i;\n order.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n\n const suppressed = new Uint8Array(n);\n const keep: number[] = [];\n\n for (let oi = 0; oi < order.length; oi++) {\n const i = order[oi] as number;\n if (suppressed[i]) continue;\n keep.push(i);\n\n const ax1 = boxes[i * 4] as number;\n const ay1 = boxes[i * 4 + 1] as number;\n const ax2 = boxes[i * 4 + 2] as number;\n const ay2 = boxes[i * 4 + 3] as number;\n const ai = areas[i] as number;\n\n for (let oj = oi + 1; oj < order.length; oj++) {\n const j = order[oj] as number;\n if (suppressed[j]) continue;\n\n const bx1 = boxes[j * 4] as number;\n const by1 = boxes[j * 4 + 1] as number;\n const bx2 = boxes[j * 4 + 2] as number;\n const by2 = boxes[j * 4 + 3] as number;\n\n const ix1 = Math.max(ax1, bx1);\n const iy1 = Math.max(ay1, by1);\n const ix2 = Math.min(ax2, bx2);\n const iy2 = Math.min(ay2, by2);\n const iw = Math.max(0, ix2 - ix1);\n const ih = Math.max(0, iy2 - iy1);\n const inter = iw * ih;\n const union = ai + (areas[j] as number) - inter;\n const iou = union > 0 ? inter / union : 0;\n if (iou > iouThreshold) suppressed[j] = 1;\n }\n }\n\n return Int32Array.from(keep);\n}\n\n/**\n * Per-class NMS — boxes are suppressed only by other boxes of the same class.\n *\n * Mirrors `torchvision.ops.batched_nms`.\n *\n * @param boxes Flat array of length `4 * N` in xyxy order.\n * @param scores Detection score per box, length `N`.\n * @param idxs Class index per box, length `N`. Boxes with different `idxs`\n * never suppress each other.\n * @param iouThreshold IoU threshold for suppression within a class.\n * @returns Indices of kept boxes, sorted by descending score across all\n * classes. Survivors from different classes that are tied on score are\n * ordered lowest-index first — an explicit tie-break, because the order the\n * per-class loop emits them in is an implementation detail (here, `Map`\n * insertion order; in Python, sorted class order).\n */\nexport function batchedNms(\n boxes: Float32Array,\n scores: Float32Array,\n idxs: Int32Array,\n iouThreshold: number,\n): Int32Array {\n if (scores.length === 0) return new Int32Array(0);\n\n const byClass = new Map<number, number[]>();\n for (let i = 0; i < idxs.length; i++) {\n const c = idxs[i] as number;\n const list = byClass.get(c);\n if (list === undefined) byClass.set(c, [i]);\n else list.push(i);\n }\n\n const keep: number[] = [];\n for (const indices of byClass.values()) {\n const m = indices.length;\n const subBoxes = new Float32Array(m * 4);\n const subScores = new Float32Array(m);\n for (let k = 0; k < m; k++) {\n const i = indices[k] as number;\n subBoxes[k * 4] = boxes[i * 4] as number;\n subBoxes[k * 4 + 1] = boxes[i * 4 + 1] as number;\n subBoxes[k * 4 + 2] = boxes[i * 4 + 2] as number;\n subBoxes[k * 4 + 3] = boxes[i * 4 + 3] as number;\n subScores[k] = scores[i] as number;\n }\n const subKeep = nms(subBoxes, subScores, iouThreshold);\n for (let k = 0; k < subKeep.length; k++) {\n keep.push(indices[subKeep[k] as number] as number);\n }\n }\n\n keep.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n return Int32Array.from(keep);\n}\n\nexport interface DecodeYoloAnchorsOptions {\n /** Number of class-score channels following the 4 box channels. */\n readonly numClasses: number;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedAnchors {\n /** Indices into the original `numAnchors` axis, in descending confidence order. */\n readonly anchorIndices: Int32Array;\n /** `[k, 4]` boxes in original-image pixel coords, flat row-major xyxy. */\n readonly boxesXyxy: Float32Array;\n /** Predicted class id per survivor. */\n readonly classIds: Int32Array;\n /** Confidence per survivor. */\n readonly confidences: Float32Array;\n}\n\n/**\n * Shared YOLO per-anchor decode used by both detection and segmentation\n * (v8 / v9 / v10 / v11 / v12).\n *\n * Only the first `4 + numClasses` channels are read; later channels (e.g.\n * mask coefficients) are ignored — callers can fetch them via the returned\n * {@link DecodedAnchors.anchorIndices}.\n *\n * @param data Flat per-anchor output, length `channels * numAnchors`.\n * @param dims Dims as reported by ORT, e.g. `[1, 84, 8400]` (det) or\n * `[1, 116, 8400]` (seg). The leading batch dim must be 1.\n */\nexport function decodeYoloAnchors(\n data: Float32Array,\n dims: readonly number[],\n options: DecodeYoloAnchorsOptions,\n): DecodedAnchors {\n let normalized = dims;\n if (normalized.length === 3) {\n if (normalized[0] !== 1) {\n throw new Error(`decodeYoloAnchors: expected batch size 1, got ${normalized[0]}.`);\n }\n normalized = [normalized[1] as number, normalized[2] as number];\n }\n if (normalized.length !== 2) {\n throw new Error(\n `decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(dims)}.`,\n );\n }\n const channels = normalized[0] as number;\n const numAnchors = normalized[1] as number;\n\n const {\n numClasses,\n originalWidth,\n originalHeight,\n padLeft,\n padTop,\n scale,\n confThreshold,\n iouThreshold,\n maxDetections,\n } = options;\n\n if (numClasses < 1 || numClasses + 4 > channels) {\n throw new Error(\n `decodeYoloAnchors: invalid numClasses=${numClasses} for channels=${channels}.`,\n );\n }\n if (data.length !== channels * numAnchors) {\n throw new Error(\n `decodeYoloAnchors: data length ${data.length} does not match channels*numAnchors=${channels * numAnchors}.`,\n );\n }\n\n type Candidate = {\n anchorIdx: number;\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n classId: number;\n confidence: number;\n };\n const candidates: Candidate[] = [];\n\n for (let a = 0; a < numAnchors; a++) {\n let bestCls = 0;\n let bestScore = -Infinity;\n for (let c = 0; c < numClasses; c++) {\n const s = data[(4 + c) * numAnchors + a];\n if (s !== undefined && s > bestScore) {\n bestScore = s;\n bestCls = c;\n }\n }\n if (bestScore < confThreshold) continue;\n\n const cx = data[a] as number;\n const cy = data[numAnchors + a] as number;\n const w = data[2 * numAnchors + a] as number;\n const h = data[3 * numAnchors + a] as number;\n\n let x1 = cx - w / 2;\n let y1 = cy - h / 2;\n let x2 = cx + w / 2;\n let y2 = cy + h / 2;\n\n x1 = (x1 - padLeft) / scale;\n y1 = (y1 - padTop) / scale;\n x2 = (x2 - padLeft) / scale;\n y2 = (y2 - padTop) / scale;\n\n x1 = Math.max(0, Math.min(originalWidth, x1));\n y1 = Math.max(0, Math.min(originalHeight, y1));\n x2 = Math.max(0, Math.min(originalWidth, x2));\n y2 = Math.max(0, Math.min(originalHeight, y2));\n\n candidates.push({ anchorIdx: a, x1, y1, x2, y2, classId: bestCls, confidence: bestScore });\n }\n\n if (candidates.length === 0) return emptyDecoded();\n\n // Build flat arrays then delegate to batchedNms — same algorithm as before\n // but funnelled through the public per-class NMS helper.\n const flatBoxes = new Float32Array(candidates.length * 4);\n const scoresArr = new Float32Array(candidates.length);\n const idxsArr = new Int32Array(candidates.length);\n for (let i = 0; i < candidates.length; i++) {\n const c = candidates[i] as Candidate;\n flatBoxes[i * 4] = c.x1;\n flatBoxes[i * 4 + 1] = c.y1;\n flatBoxes[i * 4 + 2] = c.x2;\n flatBoxes[i * 4 + 3] = c.y2;\n scoresArr[i] = c.confidence;\n idxsArr[i] = c.classId;\n }\n const kept = batchedNms(flatBoxes, scoresArr, idxsArr, iouThreshold);\n if (kept.length === 0) return emptyDecoded();\n\n const limited = Array.from(kept).slice(0, maxDetections);\n const k = limited.length;\n const anchorIndices = new Int32Array(k);\n const boxesXyxy = new Float32Array(k * 4);\n const classIds = new Int32Array(k);\n const confidences = new Float32Array(k);\n for (let i = 0; i < k; i++) {\n const c = candidates[limited[i] as number] as Candidate;\n anchorIndices[i] = c.anchorIdx;\n boxesXyxy[i * 4] = c.x1;\n boxesXyxy[i * 4 + 1] = c.y1;\n boxesXyxy[i * 4 + 2] = c.x2;\n boxesXyxy[i * 4 + 3] = c.y2;\n classIds[i] = c.classId;\n confidences[i] = c.confidence;\n }\n return { anchorIndices, boxesXyxy, classIds, confidences };\n}\n\nfunction emptyDecoded(): DecodedAnchors {\n return {\n anchorIndices: new Int32Array(0),\n boxesXyxy: new Float32Array(0),\n classIds: new Int32Array(0),\n confidences: new Float32Array(0),\n };\n}\n\nexport interface DecodeYoloOptions {\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedDetection {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n}\n\n/**\n * Decode an anchor-free YOLO detection output into a list of detections.\n *\n * Works for **YOLOv8 / v9 / v10 / v11 / v12** detect heads.\n *\n * Expected raw shape: `[1, 4 + numClasses, N]`. `numClasses` is inferred\n * from the channel count.\n */\nexport function decodeYolo(\n output: Float32Array,\n outputDims: readonly number[],\n options: DecodeYoloOptions,\n): DecodedDetection[] {\n const channels = outputDims.length === 3 ? outputDims[1] : outputDims[0];\n if (channels === undefined || channels < 5) {\n throw new Error(`decodeYolo: invalid output channel count ${channels} (expected >= 5).`);\n }\n const numClasses = channels - 4;\n\n const decoded = decodeYoloAnchors(output, outputDims, {\n numClasses,\n ...options,\n });\n\n const results: DecodedDetection[] = [];\n for (let i = 0; i < decoded.classIds.length; i++) {\n results.push({\n bbox: new BoundingBox(\n decoded.boxesXyxy[i * 4] as number,\n decoded.boxesXyxy[i * 4 + 1] as number,\n decoded.boxesXyxy[i * 4 + 2] as number,\n decoded.boxesXyxy[i * 4 + 3] as number,\n ),\n classId: decoded.classIds[i] as number,\n confidence: decoded.confidences[i] as number,\n });\n }\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"detection.js","names":[],"sources":["../../../src/vision/postprocess/detection.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Detection head postprocessing: anchor-free YOLO decoding + non-maximum suppression.\n *\n * The shared {@link decodeYoloAnchors} helper does the per-anchor work that\n * is identical for plain detection and segmentation (transpose, xywh→xyxy,\n * letterbox unmap, per-class NMS, sort & cap). {@link decodeYolo} is a thin\n * wrapper around it; the segmentation module ({@link ./segmentation.js})\n * calls the helper directly so it can also recover the per-anchor mask\n * coefficients.\n *\n * Works for any YOLO export with the post-v8 anchor-free head:\n * **YOLOv8 / v9 / v10 / v11 / v12** detect heads, all of which share the\n * `[1, 4 + nc, N]` output layout.\n */\n\nimport { BoundingBox } from \"../types\";\n\n/**\n * Greedy non-maximum suppression on axis-aligned bounding boxes.\n *\n * Mirrors `torchvision.ops.nms` (keeps boxes with the highest score, drops\n * any subsequent box whose IoU exceeds the threshold).\n *\n * @param boxes Flat array of length `4 * N` in xyxy order: `[x1,y1,x2,y2, ...]`.\n * @param scores Detection score per box, length `N`.\n * @param iouThreshold Boxes with IoU above this threshold relative to a kept box are suppressed.\n * @returns Indices of kept boxes, in descending score order. Boxes tied on\n * score are visited lowest-index first, so the survivor of a tie is\n * deterministic and matches both `torchvision` and the Python SDK.\n *\n * The boxes are copied once into score order, so the inner loop walks a\n * contiguous `Float32Array` instead of hopping through `order` into the caller's\n * layout. A pair with no horizontal overlap is skipped before the vertical\n * extent is computed: its IoU is `0`, which suppresses nothing for any\n * non-negative threshold — the shortcut is gated on that so a negative\n * threshold keeps the exact semantics.\n */\nexport function nms(boxes: Float32Array, scores: Float32Array, iouThreshold: number): Int32Array {\n const n = scores.length;\n if (n === 0) return new Int32Array(0);\n\n const order = new Array<number>(n);\n for (let i = 0; i < n; i++) order[i] = i;\n order.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n\n const sorted = new Float32Array(n * 4);\n const areas = new Float32Array(n);\n for (let k = 0; k < n; k++) {\n const i = (order[k] as number) * 4;\n const x1 = boxes[i] as number;\n const y1 = boxes[i + 1] as number;\n const x2 = boxes[i + 2] as number;\n const y2 = boxes[i + 3] as number;\n sorted[k * 4] = x1;\n sorted[k * 4 + 1] = y1;\n sorted[k * 4 + 2] = x2;\n sorted[k * 4 + 3] = y2;\n areas[k] = Math.max(0, x2 - x1) * Math.max(0, y2 - y1);\n }\n\n const skipDisjoint = iouThreshold >= 0;\n const suppressed = new Uint8Array(n);\n const keep: number[] = [];\n\n for (let k = 0; k < n; k++) {\n if (suppressed[k]) continue;\n keep.push(order[k] as number);\n\n const ax1 = sorted[k * 4] as number;\n const ay1 = sorted[k * 4 + 1] as number;\n const ax2 = sorted[k * 4 + 2] as number;\n const ay2 = sorted[k * 4 + 3] as number;\n const ak = areas[k] as number;\n\n for (let m = k + 1; m < n; m++) {\n if (suppressed[m]) continue;\n const bx1 = sorted[m * 4] as number;\n const by1 = sorted[m * 4 + 1] as number;\n const bx2 = sorted[m * 4 + 2] as number;\n const by2 = sorted[m * 4 + 3] as number;\n const iw = Math.max(0, Math.min(ax2, bx2) - Math.max(ax1, bx1));\n if (iw === 0 && skipDisjoint) continue;\n const ih = Math.max(0, Math.min(ay2, by2) - Math.max(ay1, by1));\n const inter = iw * ih;\n const union = ak + (areas[m] as number) - inter;\n const iou = union > 0 ? inter / union : 0;\n if (iou > iouThreshold) suppressed[m] = 1;\n }\n }\n\n return Int32Array.from(keep);\n}\n\n/**\n * Per-class NMS — boxes are suppressed only by other boxes of the same class.\n *\n * Mirrors `torchvision.ops.batched_nms`.\n *\n * @param boxes Flat array of length `4 * N` in xyxy order.\n * @param scores Detection score per box, length `N`.\n * @param idxs Class index per box, length `N`. Boxes with different `idxs`\n * never suppress each other.\n * @param iouThreshold IoU threshold for suppression within a class.\n * @returns Indices of kept boxes, sorted by descending score across all\n * classes. Survivors from different classes that are tied on score are\n * ordered lowest-index first — an explicit tie-break, because the order the\n * per-class loop emits them in is an implementation detail (here, `Map`\n * insertion order; in Python, sorted class order).\n */\nexport function batchedNms(\n boxes: Float32Array,\n scores: Float32Array,\n idxs: Int32Array,\n iouThreshold: number,\n): Int32Array {\n if (scores.length === 0) return new Int32Array(0);\n\n const byClass = new Map<number, number[]>();\n for (let i = 0; i < idxs.length; i++) {\n const c = idxs[i] as number;\n const list = byClass.get(c);\n if (list === undefined) byClass.set(c, [i]);\n else list.push(i);\n }\n\n const keep: number[] = [];\n for (const indices of byClass.values()) {\n const m = indices.length;\n const subBoxes = new Float32Array(m * 4);\n const subScores = new Float32Array(m);\n for (let k = 0; k < m; k++) {\n const i = indices[k] as number;\n subBoxes[k * 4] = boxes[i * 4] as number;\n subBoxes[k * 4 + 1] = boxes[i * 4 + 1] as number;\n subBoxes[k * 4 + 2] = boxes[i * 4 + 2] as number;\n subBoxes[k * 4 + 3] = boxes[i * 4 + 3] as number;\n subScores[k] = scores[i] as number;\n }\n const subKeep = nms(subBoxes, subScores, iouThreshold);\n for (let k = 0; k < subKeep.length; k++) {\n keep.push(indices[subKeep[k] as number] as number);\n }\n }\n\n keep.sort((a, b) => (scores[b] as number) - (scores[a] as number) || a - b);\n return Int32Array.from(keep);\n}\n\nexport interface DecodeYoloAnchorsOptions {\n /** Number of class-score channels following the 4 box channels. */\n readonly numClasses: number;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedAnchors {\n /** Indices into the original `numAnchors` axis, in descending confidence order. */\n readonly anchorIndices: Int32Array;\n /** `[k, 4]` boxes in original-image pixel coords, flat row-major xyxy. */\n readonly boxesXyxy: Float32Array;\n /** Predicted class id per survivor. */\n readonly classIds: Int32Array;\n /** Confidence per survivor. */\n readonly confidences: Float32Array;\n}\n\n/**\n * Shared YOLO per-anchor decode used by both detection and segmentation\n * (v8 / v9 / v10 / v11 / v12).\n *\n * Only the first `4 + numClasses` channels are read; later channels (e.g.\n * mask coefficients) are ignored — callers can fetch them via the returned\n * {@link DecodedAnchors.anchorIndices}.\n *\n * Candidates are found in two passes, mirroring the Python decoder. The first\n * walks each class row contiguously and only *marks* anchors holding some score\n * at or above the threshold — an anchor's best score clears the threshold\n * exactly when one of its scores does, so no running maximum is needed and the\n * hot loop never writes on a typical frame. The anchor-major scan it replaces\n * read `numClasses` values spaced `numAnchors` apart for every one of the 8400\n * anchors and cost ~1 ms even on a frame with no candidates. The winning class\n * is then picked only for the marked anchors — a few hundred, not 8400 — with\n * the same strict `>` that keeps the lowest class id on a tie.\n *\n * @param data Flat per-anchor output, length `channels * numAnchors`.\n * @param dims Dims as reported by ORT, e.g. `[1, 84, 8400]` (det) or\n * `[1, 116, 8400]` (seg). The leading batch dim must be 1.\n */\nexport function decodeYoloAnchors(\n data: Float32Array,\n dims: readonly number[],\n options: DecodeYoloAnchorsOptions,\n): DecodedAnchors {\n let normalized = dims;\n if (normalized.length === 3) {\n if (normalized[0] !== 1) {\n throw new Error(`decodeYoloAnchors: expected batch size 1, got ${normalized[0]}.`);\n }\n normalized = [normalized[1] as number, normalized[2] as number];\n }\n if (normalized.length !== 2) {\n throw new Error(\n `decodeYoloAnchors: expected 2-D output after batch removal, got dims=${JSON.stringify(dims)}.`,\n );\n }\n const channels = normalized[0] as number;\n const numAnchors = normalized[1] as number;\n\n const {\n numClasses,\n originalWidth,\n originalHeight,\n padLeft,\n padTop,\n scale,\n confThreshold,\n iouThreshold,\n maxDetections,\n } = options;\n\n if (numClasses < 1 || numClasses + 4 > channels) {\n throw new Error(\n `decodeYoloAnchors: invalid numClasses=${numClasses} for channels=${channels}.`,\n );\n }\n if (data.length !== channels * numAnchors) {\n throw new Error(\n `decodeYoloAnchors: data length ${data.length} does not match channels*numAnchors=${channels * numAnchors}.`,\n );\n }\n\n const hit = new Uint8Array(numAnchors);\n const classEnd = (4 + numClasses) * numAnchors;\n for (let row = 4 * numAnchors; row < classEnd; row += numAnchors) {\n for (let a = 0; a < numAnchors; a++) {\n if ((data[row + a] as number) >= confThreshold) hit[a] = 1;\n }\n }\n\n let count = 0;\n const candidateAnchors = new Int32Array(numAnchors);\n for (let a = 0; a < numAnchors; a++) {\n if (hit[a] === 1) candidateAnchors[count++] = a;\n }\n if (count === 0) return emptyDecoded();\n\n const flatBoxes = new Float32Array(count * 4);\n const scoresArr = new Float32Array(count);\n const idxsArr = new Int32Array(count);\n for (let i = 0; i < count; i++) {\n const a = candidateAnchors[i] as number;\n const cx = data[a] as number;\n const cy = data[numAnchors + a] as number;\n const w = data[2 * numAnchors + a] as number;\n const h = data[3 * numAnchors + a] as number;\n const x1 = (cx - w / 2 - padLeft) / scale;\n const y1 = (cy - h / 2 - padTop) / scale;\n const x2 = (cx + w / 2 - padLeft) / scale;\n const y2 = (cy + h / 2 - padTop) / scale;\n flatBoxes[i * 4] = Math.max(0, Math.min(originalWidth, x1));\n flatBoxes[i * 4 + 1] = Math.max(0, Math.min(originalHeight, y1));\n flatBoxes[i * 4 + 2] = Math.max(0, Math.min(originalWidth, x2));\n flatBoxes[i * 4 + 3] = Math.max(0, Math.min(originalHeight, y2));\n let bestScore = -Infinity;\n let bestClass = 0;\n for (let c = 0, at = 4 * numAnchors + a; c < numClasses; c++, at += numAnchors) {\n const score = data[at] as number;\n if (score > bestScore) {\n bestScore = score;\n bestClass = c;\n }\n }\n scoresArr[i] = bestScore;\n idxsArr[i] = bestClass;\n }\n const kept = batchedNms(flatBoxes, scoresArr, idxsArr, iouThreshold);\n if (kept.length === 0) return emptyDecoded();\n\n const k = Math.min(kept.length, maxDetections);\n const anchorIndices = new Int32Array(k);\n const boxesXyxy = new Float32Array(k * 4);\n const classIds = new Int32Array(k);\n const confidences = new Float32Array(k);\n for (let i = 0; i < k; i++) {\n const j = kept[i] as number;\n anchorIndices[i] = candidateAnchors[j] as number;\n boxesXyxy[i * 4] = flatBoxes[j * 4] as number;\n boxesXyxy[i * 4 + 1] = flatBoxes[j * 4 + 1] as number;\n boxesXyxy[i * 4 + 2] = flatBoxes[j * 4 + 2] as number;\n boxesXyxy[i * 4 + 3] = flatBoxes[j * 4 + 3] as number;\n classIds[i] = idxsArr[j] as number;\n confidences[i] = scoresArr[j] as number;\n }\n return { anchorIndices, boxesXyxy, classIds, confidences };\n}\n\nfunction emptyDecoded(): DecodedAnchors {\n return {\n anchorIndices: new Int32Array(0),\n boxesXyxy: new Float32Array(0),\n classIds: new Int32Array(0),\n confidences: new Float32Array(0),\n };\n}\n\nexport interface DecodeYoloOptions {\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly padLeft: number;\n readonly padTop: number;\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n}\n\nexport interface DecodedDetection {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n}\n\n/**\n * Decode an anchor-free YOLO detection output into a list of detections.\n *\n * Works for **YOLOv8 / v9 / v10 / v11 / v12** detect heads.\n *\n * Expected raw shape: `[1, 4 + numClasses, N]`. `numClasses` is inferred\n * from the channel count.\n */\nexport function decodeYolo(\n output: Float32Array,\n outputDims: readonly number[],\n options: DecodeYoloOptions,\n): DecodedDetection[] {\n const channels = outputDims.length === 3 ? outputDims[1] : outputDims[0];\n if (channels === undefined || channels < 5) {\n throw new Error(`decodeYolo: invalid output channel count ${channels} (expected >= 5).`);\n }\n const numClasses = channels - 4;\n\n const decoded = decodeYoloAnchors(output, outputDims, {\n numClasses,\n ...options,\n });\n\n const results: DecodedDetection[] = [];\n for (let i = 0; i < decoded.classIds.length; i++) {\n results.push({\n bbox: new BoundingBox(\n decoded.boxesXyxy[i * 4] as number,\n decoded.boxesXyxy[i * 4 + 1] as number,\n decoded.boxesXyxy[i * 4 + 2] as number,\n decoded.boxesXyxy[i * 4 + 3] as number,\n ),\n classId: decoded.classIds[i] as number,\n confidence: decoded.confidences[i] as number,\n });\n }\n return results;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../types.cjs"),t=require("./detection.cjs");function n(n,a,o,s,c){let l=s;if(l.length===4){if(l[0]!==1)throw Error(`decodeYoloSeg: expected batch size 1 in prototypes, got ${l[0]}.`);l=[l[1],l[2],l[3]]}if(l.length!==3)throw Error(`decodeYoloSeg: expected 3-D prototypes after batch removal, got dims=${JSON.stringify(s)}.`);let u=l[0],d=l[1],f=l[2],p=a.length===3?a[1]:a[0],m=a.length===3?a[2]:a[1];if(p===void 0||m===void 0)throw Error(`decodeYoloSeg: cannot read channels/numAnchors from dims=${JSON.stringify(a)}.`);let h=4+c.numClasses+u;if(p!==h)throw Error(`decodeYoloSeg: channels=${p} does not match 4 + numClasses(${c.numClasses}) + numMaskCoefs(${u}) = ${h}.`);if(o.length!==u*d*f)throw Error(`decodeYoloSeg: prototype length ${o.length} does not match dims=${JSON.stringify(s)}.`);let g=t.decodeYoloAnchors(n,a,{numClasses:c.numClasses,originalWidth:c.originalWidth,originalHeight:c.originalHeight,padLeft:c.padLeft,padTop:c.padTop,scale:c.scale,confThreshold:c.confThreshold,iouThreshold:c.iouThreshold,maxDetections:c.maxDetections});if(g.anchorIndices.length===0)return[];let _=c.maskThreshold??.5,v=f/c.inputWidth,y=d/c.inputHeight,b=d*f,x=(4+c.numClasses)*m,S=[];for(let t=0;t<g.anchorIndices.length;t++){let a=g.anchorIndices[t],s=g.boxesXyxy[t*4],l=g.boxesXyxy[t*4+1],p=g.boxesXyxy[t*4+2],h=g.boxesXyxy[t*4+3],C=new e.BoundingBox(s,l,p,h),w=g.classIds[t],T=g.confidences[t],E=Math.max(0,Math.trunc(p)-Math.trunc(s)),D=Math.max(0,Math.trunc(h)-Math.trunc(l));if(E===0||D===0){S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(new Uint8Array,0,0)});continue}let O=s*c.scale+c.padLeft,k=l*c.scale+c.padTop,A=p*c.scale+c.padLeft,j=h*c.scale+c.padTop,M=Math.max(0,Math.floor(O*v)),N=Math.max(0,Math.floor(k*y)),P=Math.min(f,Math.ceil(A*v)),F=Math.min(d,Math.ceil(j*y));if(P<=M||F<=N){S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(new Uint8Array(E*D),E,D)});continue}let I=P-M,L=F-N,R=new Float32Array(I*L);for(let e=0;e<L;e++){let t=N+e;for(let i=0;i<I;i++){let s=M+i,c=0;for(let e=0;e<u;e++){let r=n[x+e*m+a],i=o[e*b+t*f+s];c+=r*i}R[e*I+i]=r(c)}}let z=i(R,I,L,E,D),B=new Uint8Array(E*D);for(let e=0;e<B.length;e++)B[e]=z[e]>=_?255:0;S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(B,E,D)})}return S}function r(e){if(e>=0)return 1/(1+Math.exp(-e));let t=Math.exp(e);return t/(1+t)}function i(e,t,n,r,i){let a=new Float32Array(r*i);if(r===0||i===0||t===0||n===0)return a;if(r===t&&i===n)return a.set(e),a;let o=t/r,s=n/i;for(let c=0;c<i;c++){let i=(c+.5)*s-.5,l=Math.max(0,Math.floor(i)),u=Math.min(n-1,l+1),d=Math.max(0,Math.min(1,i-l));for(let n=0;n<r;n++){let i=(n+.5)*o-.5,s=Math.max(0,Math.floor(i)),f=Math.min(t-1,s+1),p=Math.max(0,Math.min(1,i-s)),m=e[l*t+s],h=e[l*t+f],g=e[u*t+s],_=e[u*t+f],v=m*(1-p)+h*p,y=g*(1-p)+_*p;a[c*r+n]=v*(1-d)+y*d}}return a}exports.decodeYoloSeg=n;
1
+ const e=require("../types.cjs"),t=require("./detection.cjs");function n(n,r,o,s,c){let l=s;if(l.length===4){if(l[0]!==1)throw Error(`decodeYoloSeg: expected batch size 1 in prototypes, got ${l[0]}.`);l=[l[1],l[2],l[3]]}if(l.length!==3)throw Error(`decodeYoloSeg: expected 3-D prototypes after batch removal, got dims=${JSON.stringify(s)}.`);let u=l[0],d=l[1],f=l[2],p=r.length===3?r[1]:r[0],m=r.length===3?r[2]:r[1];if(p===void 0||m===void 0)throw Error(`decodeYoloSeg: cannot read channels/numAnchors from dims=${JSON.stringify(r)}.`);let h=4+c.numClasses+u;if(p!==h)throw Error(`decodeYoloSeg: channels=${p} does not match 4 + numClasses(${c.numClasses}) + numMaskCoefs(${u}) = ${h}.`);if(o.length!==u*d*f)throw Error(`decodeYoloSeg: prototype length ${o.length} does not match dims=${JSON.stringify(s)}.`);let g=t.decodeYoloAnchors(n,r,{numClasses:c.numClasses,originalWidth:c.originalWidth,originalHeight:c.originalHeight,padLeft:c.padLeft,padTop:c.padTop,scale:c.scale,confThreshold:c.confThreshold,iouThreshold:c.iouThreshold,maxDetections:c.maxDetections});if(g.anchorIndices.length===0)return[];let _=c.maskThreshold??.5,v=f/c.inputWidth,y=d/c.inputHeight,b=d*f,x=(4+c.numClasses)*m,S=[];for(let t=0;t<g.anchorIndices.length;t++){let r=g.anchorIndices[t],s=g.boxesXyxy[t*4],l=g.boxesXyxy[t*4+1],p=g.boxesXyxy[t*4+2],h=g.boxesXyxy[t*4+3],C=new e.BoundingBox(s,l,p,h),w=g.classIds[t],T=g.confidences[t],E=Math.max(0,Math.trunc(p)-Math.trunc(s)),D=Math.max(0,Math.trunc(h)-Math.trunc(l));if(E===0||D===0){S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(new Uint8Array,0,0)});continue}let O=s*c.scale+c.padLeft,k=l*c.scale+c.padTop,A=p*c.scale+c.padLeft,j=h*c.scale+c.padTop,M=Math.max(0,Math.floor(O*v)),N=Math.max(0,Math.floor(k*y)),P=Math.min(f,Math.ceil(A*v)),F=Math.min(d,Math.ceil(j*y));if(P<=M||F<=N){S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(new Uint8Array(E*D),E,D)});continue}let I=P-M,L=F-N,R=a(i(n,o,x+r,m,u,f,b,M,N,I,L),I,L,E,D,_);S.push({bbox:C,classId:w,confidence:T,mask:new e.Mask(R,E,D)})}return S}function r(e){if(e>=0)return 1/(1+Math.exp(-e));let t=Math.exp(e);return t/(1+t)}function i(e,t,n,i,a,o,s,c,l,u,d){let f=new Float64Array(u*d);for(let r=0;r<a;r++){let a=e[n+r*i],p=r*s+c;for(let e=0;e<d;e++){let n=p+(l+e)*o,r=e*u;for(let e=0;e<u;e++)f[r+e]=f[r+e]+a*t[n+e]}}let p=new Float32Array(f.length);for(let e=0;e<f.length;e++)p[e]=r(f[e]);return p}function a(e,t,n,r,i,a){let o=new Uint8Array(r*i);if(r===t&&i===n){for(let t=0;t<o.length;t++)o[t]=e[t]>=a?255:0;return o}let s=t/r,c=n/i,l=new Int32Array(r),u=new Int32Array(r),d=new Float64Array(r);for(let e=0;e<r;e++){let n=(e+.5)*s-.5,r=Math.max(0,Math.floor(n));l[e]=r,u[e]=Math.min(t-1,r+1),d[e]=Math.max(0,Math.min(1,n-r))}let f=new Float64Array(n*r);for(let i=0;i<n;i++){let n=i*t,a=i*r;for(let t=0;t<r;t++){let r=d[t];f[a+t]=e[n+l[t]]*(1-r)+e[n+u[t]]*r}}for(let e=0;e<i;e++){let t=(e+.5)*c-.5,i=Math.max(0,Math.floor(t)),s=Math.min(n-1,i+1),l=Math.max(0,Math.min(1,t-i)),u=i*r,d=s*r,p=e*r;for(let e=0;e<r;e++){let t=f[u+e]*(1-l)+f[d+e]*l;o[p+e]=Math.fround(t)>=a?255:0}}return o}exports.decodeYoloSeg=n;
2
2
  //# sourceMappingURL=segmentation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"segmentation.cjs","names":[],"sources":["../../../src/vision/postprocess/segmentation.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Segmentation head postprocessing: YOLO instance-segmentation decoding.\n *\n * Compatible with YOLOv8-seg / YOLOv11-seg exports (and any later seg head\n * sharing the layout). The model produces two output tensors:\n *\n * - `output0` of shape `(1, 4 + numClasses + numMaskCoefs, numAnchors)` — the\n * same per-anchor predictions as plain YOLO detection plus an extra\n * `numMaskCoefs` (typically 32) channels of mask coefficients.\n * - `output1` of shape `(1, numMaskCoefs, maskH, maskW)` — a set of \"prototype\"\n * masks shared across all anchors.\n *\n * The per-anchor decode (xywh→xyxy, undo letterbox, per-class NMS, sort & cap)\n * is delegated to {@link decodeYoloAnchors}; this module only handles the\n * mask-specific work: matmul against prototypes, sigmoid, bilinear resize and\n * thresholding.\n */\n\nimport { decodeYoloAnchors } from \"./detection\";\nimport { BoundingBox, Mask } from \"../types\";\n\nexport interface DecodeYoloSegOptions {\n readonly numClasses: number;\n /** Model input `[width, height]` (post-letterbox). */\n readonly inputWidth: number;\n readonly inputHeight: number;\n /** Original image `[width, height]`. */\n readonly originalWidth: number;\n readonly originalHeight: number;\n /** Letterbox horizontal padding in input-tensor pixels. */\n readonly padLeft: number;\n /** Letterbox vertical padding in input-tensor pixels. */\n readonly padTop: number;\n /** Letterbox scale factor. */\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n /** Probability cutoff applied to the soft mask. Defaults to `0.5`. */\n readonly maskThreshold?: number;\n}\n\nexport interface DecodedSegmentation {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n /** Binary mask cropped to `bbox`. Width/height match `bbox.asIntXyxy()` extents. */\n readonly mask: Mask;\n}\n\n/**\n * Decode YOLO segmentation raw outputs into a list of segmented instances.\n *\n * Compatible with YOLOv8-seg / YOLOv11-seg.\n *\n * @param perAnchorData Flat `output0`, length `(4 + numClasses + numMaskCoefs) * numAnchors`.\n * @param perAnchorDims Dims as reported by ORT, e.g. `[1, 116, 8400]`.\n * @param prototypeData Flat `output1`, length `numMaskCoefs * maskH * maskW`.\n * @param prototypeDims Dims as reported by ORT, e.g. `[1, 32, 160, 160]`.\n */\nexport function decodeYoloSeg(\n perAnchorData: Float32Array,\n perAnchorDims: readonly number[],\n prototypeData: Float32Array,\n prototypeDims: readonly number[],\n options: DecodeYoloSegOptions,\n): DecodedSegmentation[] {\n // Strip batch dim from prototypes; perAnchor batch is validated by the helper.\n let pDims = prototypeDims;\n if (pDims.length === 4) {\n if (pDims[0] !== 1) {\n throw new Error(`decodeYoloSeg: expected batch size 1 in prototypes, got ${pDims[0]}.`);\n }\n pDims = [pDims[1] as number, pDims[2] as number, pDims[3] as number];\n }\n if (pDims.length !== 3) {\n throw new Error(\n `decodeYoloSeg: expected 3-D prototypes after batch removal, got dims=${JSON.stringify(prototypeDims)}.`,\n );\n }\n const numMaskCoefs = pDims[0] as number;\n const maskH = pDims[1] as number;\n const maskW = pDims[2] as number;\n\n const channels = perAnchorDims.length === 3 ? perAnchorDims[1] : perAnchorDims[0];\n const numAnchors = perAnchorDims.length === 3 ? perAnchorDims[2] : perAnchorDims[1];\n if (channels === undefined || numAnchors === undefined) {\n throw new Error(\n `decodeYoloSeg: cannot read channels/numAnchors from dims=${JSON.stringify(perAnchorDims)}.`,\n );\n }\n\n const expectedChannels = 4 + options.numClasses + numMaskCoefs;\n if (channels !== expectedChannels) {\n throw new Error(\n `decodeYoloSeg: channels=${channels} does not match 4 + numClasses(${options.numClasses}) + numMaskCoefs(${numMaskCoefs}) = ${expectedChannels}.`,\n );\n }\n if (prototypeData.length !== numMaskCoefs * maskH * maskW) {\n throw new Error(\n `decodeYoloSeg: prototype length ${prototypeData.length} does not match dims=${JSON.stringify(prototypeDims)}.`,\n );\n }\n\n const decoded = decodeYoloAnchors(perAnchorData, perAnchorDims, {\n numClasses: options.numClasses,\n originalWidth: options.originalWidth,\n originalHeight: options.originalHeight,\n padLeft: options.padLeft,\n padTop: options.padTop,\n scale: options.scale,\n confThreshold: options.confThreshold,\n iouThreshold: options.iouThreshold,\n maxDetections: options.maxDetections,\n });\n\n if (decoded.anchorIndices.length === 0) return [];\n\n const maskThreshold = options.maskThreshold ?? 0.5;\n const scaleX = maskW / options.inputWidth;\n const scaleY = maskH / options.inputHeight;\n const protoPlane = maskH * maskW;\n const coefBase = (4 + options.numClasses) * numAnchors;\n\n const results: DecodedSegmentation[] = [];\n\n for (let i = 0; i < decoded.anchorIndices.length; i++) {\n const a = decoded.anchorIndices[i] as number;\n const x1 = decoded.boxesXyxy[i * 4] as number;\n const y1 = decoded.boxesXyxy[i * 4 + 1] as number;\n const x2 = decoded.boxesXyxy[i * 4 + 2] as number;\n const y2 = decoded.boxesXyxy[i * 4 + 3] as number;\n const bbox = new BoundingBox(x1, y1, x2, y2);\n const classId = decoded.classIds[i] as number;\n const confidence = decoded.confidences[i] as number;\n\n const bboxW = Math.max(0, Math.trunc(x2) - Math.trunc(x1));\n const bboxH = Math.max(0, Math.trunc(y2) - Math.trunc(y1));\n\n if (bboxW === 0 || bboxH === 0) {\n results.push({ bbox, classId, confidence, mask: new Mask(new Uint8Array(0), 0, 0) });\n continue;\n }\n\n // Bbox in input-tensor coords, then in low-res mask coords.\n const ibx1 = x1 * options.scale + options.padLeft;\n const iby1 = y1 * options.scale + options.padTop;\n const ibx2 = x2 * options.scale + options.padLeft;\n const iby2 = y2 * options.scale + options.padTop;\n\n const mbx1 = Math.max(0, Math.floor(ibx1 * scaleX));\n const mby1 = Math.max(0, Math.floor(iby1 * scaleY));\n const mbx2 = Math.min(maskW, Math.ceil(ibx2 * scaleX));\n const mby2 = Math.min(maskH, Math.ceil(iby2 * scaleY));\n\n if (mbx2 <= mbx1 || mby2 <= mby1) {\n results.push({\n bbox,\n classId,\n confidence,\n mask: new Mask(new Uint8Array(bboxW * bboxH), bboxW, bboxH),\n });\n continue;\n }\n\n // Compute soft mask only within the prototype region under this bbox.\n const cropW = mbx2 - mbx1;\n const cropH = mby2 - mby1;\n const softCrop = new Float32Array(cropW * cropH);\n for (let y = 0; y < cropH; y++) {\n const py = mby1 + y;\n for (let x = 0; x < cropW; x++) {\n const px = mbx1 + x;\n let sum = 0;\n for (let kk = 0; kk < numMaskCoefs; kk++) {\n const coef = perAnchorData[coefBase + kk * numAnchors + a];\n const proto = prototypeData[kk * protoPlane + py * maskW + px];\n sum += coef * proto;\n }\n softCrop[y * cropW + x] = sigmoid(sum);\n }\n }\n\n const resized = resizeBilinear(softCrop, cropW, cropH, bboxW, bboxH);\n const binary = new Uint8Array(bboxW * bboxH);\n for (let j = 0; j < binary.length; j++) {\n binary[j] = resized[j] >= maskThreshold ? 255 : 0;\n }\n\n results.push({ bbox, classId, confidence, mask: new Mask(binary, bboxW, bboxH) });\n }\n\n return results;\n}\n\nfunction sigmoid(x: number): number {\n if (x >= 0) {\n return 1 / (1 + Math.exp(-x));\n }\n const e = Math.exp(x);\n return e / (1 + e);\n}\n\nfunction resizeBilinear(\n src: Float32Array,\n srcWidth: number,\n srcHeight: number,\n targetWidth: number,\n targetHeight: number,\n): Float32Array {\n const out = new Float32Array(targetWidth * targetHeight);\n if (targetWidth === 0 || targetHeight === 0 || srcWidth === 0 || srcHeight === 0) {\n return out;\n }\n if (targetWidth === srcWidth && targetHeight === srcHeight) {\n out.set(src);\n return out;\n }\n const sx = srcWidth / targetWidth;\n const sy = srcHeight / targetHeight;\n for (let y = 0; y < targetHeight; y++) {\n const yy = (y + 0.5) * sy - 0.5;\n const y0 = Math.max(0, Math.floor(yy));\n const y1 = Math.min(srcHeight - 1, y0 + 1);\n const wy = Math.max(0, Math.min(1, yy - y0));\n for (let x = 0; x < targetWidth; x++) {\n const xx = (x + 0.5) * sx - 0.5;\n const x0 = Math.max(0, Math.floor(xx));\n const x1 = Math.min(srcWidth - 1, x0 + 1);\n const wx = Math.max(0, Math.min(1, xx - x0));\n const v00 = src[y0 * srcWidth + x0];\n const v01 = src[y0 * srcWidth + x1];\n const v10 = src[y1 * srcWidth + x0];\n const v11 = src[y1 * srcWidth + x1];\n const top = v00 * (1 - wx) + v01 * wx;\n const bot = v10 * (1 - wx) + v11 * wx;\n out[y * targetWidth + x] = top * (1 - wy) + bot * wy;\n }\n }\n return out;\n}\n"],"mappings":"6DA6DA,SAAgB,EACZ,EACA,EACA,EACA,EACA,EACqB,CAErB,IAAI,EAAQ,EACZ,GAAI,EAAM,SAAW,EAAG,CACpB,GAAI,EAAM,KAAO,EACb,MAAU,MAAM,2DAA2D,EAAM,GAAG,EAAE,EAE1F,EAAQ,CAAC,EAAM,GAAc,EAAM,GAAc,EAAM,EAAY,CACvE,CACA,GAAI,EAAM,SAAW,EACjB,MAAU,MACN,wEAAwE,KAAK,UAAU,CAAa,EAAE,EAC1G,EAEJ,IAAM,EAAe,EAAM,GACrB,EAAQ,EAAM,GACd,EAAQ,EAAM,GAEd,EAAW,EAAc,SAAW,EAAI,EAAc,GAAK,EAAc,GACzE,EAAa,EAAc,SAAW,EAAI,EAAc,GAAK,EAAc,GACjF,GAAI,IAAa,IAAA,IAAa,IAAe,IAAA,GACzC,MAAU,MACN,4DAA4D,KAAK,UAAU,CAAa,EAAE,EAC9F,EAGJ,IAAM,EAAmB,EAAI,EAAQ,WAAa,EAClD,GAAI,IAAa,EACb,MAAU,MACN,2BAA2B,EAAS,iCAAiC,EAAQ,WAAW,mBAAmB,EAAa,MAAM,EAAiB,EACnJ,EAEJ,GAAI,EAAc,SAAW,EAAe,EAAQ,EAChD,MAAU,MACN,mCAAmC,EAAc,OAAO,uBAAuB,KAAK,UAAU,CAAa,EAAE,EACjH,EAGJ,IAAM,EAAU,EAAA,kBAAkB,EAAe,EAAe,CAC5D,WAAY,EAAQ,WACpB,cAAe,EAAQ,cACvB,eAAgB,EAAQ,eACxB,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,MAAO,EAAQ,MACf,cAAe,EAAQ,cACvB,aAAc,EAAQ,aACtB,cAAe,EAAQ,aAC3B,CAAC,EAED,GAAI,EAAQ,cAAc,SAAW,EAAG,MAAO,CAAC,EAEhD,IAAM,EAAgB,EAAQ,eAAiB,GACzC,EAAS,EAAQ,EAAQ,WACzB,EAAS,EAAQ,EAAQ,YACzB,EAAa,EAAQ,EACrB,GAAY,EAAI,EAAQ,YAAc,EAEtC,EAAiC,CAAC,EAExC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,cAAc,OAAQ,IAAK,CACnD,IAAM,EAAI,EAAQ,cAAc,GAC1B,EAAK,EAAQ,UAAU,EAAI,GAC3B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAO,IAAI,EAAA,YAAY,EAAI,EAAI,EAAI,CAAE,EACrC,EAAU,EAAQ,SAAS,GAC3B,EAAa,EAAQ,YAAY,GAEjC,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,EAAI,KAAK,MAAM,CAAE,CAAC,EACnD,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,EAAI,KAAK,MAAM,CAAE,CAAC,EAEzD,GAAI,IAAU,GAAK,IAAU,EAAG,CAC5B,EAAQ,KAAK,CAAE,OAAM,UAAS,aAAY,KAAM,IAAI,EAAA,KAAK,IAAI,WAAe,EAAG,CAAC,CAAE,CAAC,EACnF,QACJ,CAGA,IAAM,EAAO,EAAK,EAAQ,MAAQ,EAAQ,QACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,OACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,QACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,OAEpC,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,CAAM,CAAC,EAC5C,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,CAAM,CAAC,EAC5C,EAAO,KAAK,IAAI,EAAO,KAAK,KAAK,EAAO,CAAM,CAAC,EAC/C,EAAO,KAAK,IAAI,EAAO,KAAK,KAAK,EAAO,CAAM,CAAC,EAErD,GAAI,GAAQ,GAAQ,GAAQ,EAAM,CAC9B,EAAQ,KAAK,CACT,OACA,UACA,aACA,KAAM,IAAI,EAAA,KAAK,IAAI,WAAW,EAAQ,CAAK,EAAG,EAAO,CAAK,CAC9D,CAAC,EACD,QACJ,CAGA,IAAM,EAAQ,EAAO,EACf,EAAQ,EAAO,EACf,EAAW,IAAI,aAAa,EAAQ,CAAK,EAC/C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC5B,IAAM,EAAK,EAAO,EAClB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC5B,IAAM,EAAK,EAAO,EACd,EAAM,EACV,IAAK,IAAI,EAAK,EAAG,EAAK,EAAc,IAAM,CACtC,IAAM,EAAO,EAAc,EAAW,EAAK,EAAa,GAClD,EAAQ,EAAc,EAAK,EAAa,EAAK,EAAQ,GAC3D,GAAO,EAAO,CAClB,CACA,EAAS,EAAI,EAAQ,GAAK,EAAQ,CAAG,CACzC,CACJ,CAEA,IAAM,EAAU,EAAe,EAAU,EAAO,EAAO,EAAO,CAAK,EAC7D,EAAS,IAAI,WAAW,EAAQ,CAAK,EAC3C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAC/B,EAAO,GAAK,EAAQ,IAAM,EAAgB,IAAM,EAGpD,EAAQ,KAAK,CAAE,OAAM,UAAS,aAAY,KAAM,IAAI,EAAA,KAAK,EAAQ,EAAO,CAAK,CAAE,CAAC,CACpF,CAEA,OAAO,CACX,CAEA,SAAS,EAAQ,EAAmB,CAChC,GAAI,GAAK,EACL,MAAO,IAAK,EAAI,KAAK,IAAI,CAAC,CAAC,GAE/B,IAAM,EAAI,KAAK,IAAI,CAAC,EACpB,OAAO,GAAK,EAAI,EACpB,CAEA,SAAS,EACL,EACA,EACA,EACA,EACA,EACY,CACZ,IAAM,EAAM,IAAI,aAAa,EAAc,CAAY,EACvD,GAAI,IAAgB,GAAK,IAAiB,GAAK,IAAa,GAAK,IAAc,EAC3E,OAAO,EAEX,GAAI,IAAgB,GAAY,IAAiB,EAE7C,OADA,EAAI,IAAI,CAAG,EACJ,EAEX,IAAM,EAAK,EAAW,EAChB,EAAK,EAAY,EACvB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAc,IAAK,CACnC,IAAM,GAAM,EAAI,IAAO,EAAK,GACtB,EAAK,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EAC/B,EAAK,KAAK,IAAI,EAAY,EAAG,EAAK,CAAC,EACnC,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAK,CAAE,CAAC,EAC3C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAAK,CAClC,IAAM,GAAM,EAAI,IAAO,EAAK,GACtB,EAAK,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EAC/B,EAAK,KAAK,IAAI,EAAW,EAAG,EAAK,CAAC,EAClC,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAK,CAAE,CAAC,EACrC,EAAM,EAAI,EAAK,EAAW,GAC1B,EAAM,EAAI,EAAK,EAAW,GAC1B,EAAM,EAAI,EAAK,EAAW,GAC1B,EAAM,EAAI,EAAK,EAAW,GAC1B,EAAM,GAAO,EAAI,GAAM,EAAM,EAC7B,EAAM,GAAO,EAAI,GAAM,EAAM,EACnC,EAAI,EAAI,EAAc,GAAK,GAAO,EAAI,GAAM,EAAM,CACtD,CACJ,CACA,OAAO,CACX"}
1
+ {"version":3,"file":"segmentation.cjs","names":[],"sources":["../../../src/vision/postprocess/segmentation.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Segmentation head postprocessing: YOLO instance-segmentation decoding.\n *\n * Compatible with YOLOv8-seg / YOLOv11-seg exports (and any later seg head\n * sharing the layout). The model produces two output tensors:\n *\n * - `output0` of shape `(1, 4 + numClasses + numMaskCoefs, numAnchors)` — the\n * same per-anchor predictions as plain YOLO detection plus an extra\n * `numMaskCoefs` (typically 32) channels of mask coefficients.\n * - `output1` of shape `(1, numMaskCoefs, maskH, maskW)` — a set of \"prototype\"\n * masks shared across all anchors.\n *\n * The per-anchor decode (xywh→xyxy, undo letterbox, per-class NMS, sort & cap)\n * is delegated to {@link decodeYoloAnchors}; this module only handles the\n * mask-specific work: matmul against prototypes, sigmoid, bilinear resize and\n * thresholding.\n */\n\nimport { decodeYoloAnchors } from \"./detection\";\nimport { BoundingBox, Mask } from \"../types\";\n\nexport interface DecodeYoloSegOptions {\n readonly numClasses: number;\n /** Model input `[width, height]` (post-letterbox). */\n readonly inputWidth: number;\n readonly inputHeight: number;\n /** Original image `[width, height]`. */\n readonly originalWidth: number;\n readonly originalHeight: number;\n /** Letterbox horizontal padding in input-tensor pixels. */\n readonly padLeft: number;\n /** Letterbox vertical padding in input-tensor pixels. */\n readonly padTop: number;\n /** Letterbox scale factor. */\n readonly scale: number;\n readonly confThreshold: number;\n readonly iouThreshold: number;\n readonly maxDetections: number;\n /** Probability cutoff applied to the soft mask. Defaults to `0.5`. */\n readonly maskThreshold?: number;\n}\n\nexport interface DecodedSegmentation {\n readonly bbox: BoundingBox;\n readonly classId: number;\n readonly confidence: number;\n /** Binary mask cropped to `bbox`. Width/height match `bbox.asIntXyxy()` extents. */\n readonly mask: Mask;\n}\n\n/**\n * Decode YOLO segmentation raw outputs into a list of segmented instances.\n *\n * Compatible with YOLOv8-seg / YOLOv11-seg.\n *\n * The prototypes' batch axis is stripped here; the per-anchor batch axis is\n * validated by {@link decodeYoloAnchors}. Each instance's box is mapped back\n * into input-tensor coordinates and from there into the low-resolution\n * prototype grid, and only the prototype pixels under it are assembled.\n *\n * @param perAnchorData Flat `output0`, length `(4 + numClasses + numMaskCoefs) * numAnchors`.\n * @param perAnchorDims Dims as reported by ORT, e.g. `[1, 116, 8400]`.\n * @param prototypeData Flat `output1`, length `numMaskCoefs * maskH * maskW`.\n * @param prototypeDims Dims as reported by ORT, e.g. `[1, 32, 160, 160]`.\n */\nexport function decodeYoloSeg(\n perAnchorData: Float32Array,\n perAnchorDims: readonly number[],\n prototypeData: Float32Array,\n prototypeDims: readonly number[],\n options: DecodeYoloSegOptions,\n): DecodedSegmentation[] {\n let pDims = prototypeDims;\n if (pDims.length === 4) {\n if (pDims[0] !== 1) {\n throw new Error(`decodeYoloSeg: expected batch size 1 in prototypes, got ${pDims[0]}.`);\n }\n pDims = [pDims[1] as number, pDims[2] as number, pDims[3] as number];\n }\n if (pDims.length !== 3) {\n throw new Error(\n `decodeYoloSeg: expected 3-D prototypes after batch removal, got dims=${JSON.stringify(prototypeDims)}.`,\n );\n }\n const numMaskCoefs = pDims[0] as number;\n const maskH = pDims[1] as number;\n const maskW = pDims[2] as number;\n\n const channels = perAnchorDims.length === 3 ? perAnchorDims[1] : perAnchorDims[0];\n const numAnchors = perAnchorDims.length === 3 ? perAnchorDims[2] : perAnchorDims[1];\n if (channels === undefined || numAnchors === undefined) {\n throw new Error(\n `decodeYoloSeg: cannot read channels/numAnchors from dims=${JSON.stringify(perAnchorDims)}.`,\n );\n }\n\n const expectedChannels = 4 + options.numClasses + numMaskCoefs;\n if (channels !== expectedChannels) {\n throw new Error(\n `decodeYoloSeg: channels=${channels} does not match 4 + numClasses(${options.numClasses}) + numMaskCoefs(${numMaskCoefs}) = ${expectedChannels}.`,\n );\n }\n if (prototypeData.length !== numMaskCoefs * maskH * maskW) {\n throw new Error(\n `decodeYoloSeg: prototype length ${prototypeData.length} does not match dims=${JSON.stringify(prototypeDims)}.`,\n );\n }\n\n const decoded = decodeYoloAnchors(perAnchorData, perAnchorDims, {\n numClasses: options.numClasses,\n originalWidth: options.originalWidth,\n originalHeight: options.originalHeight,\n padLeft: options.padLeft,\n padTop: options.padTop,\n scale: options.scale,\n confThreshold: options.confThreshold,\n iouThreshold: options.iouThreshold,\n maxDetections: options.maxDetections,\n });\n\n if (decoded.anchorIndices.length === 0) return [];\n\n const maskThreshold = options.maskThreshold ?? 0.5;\n const scaleX = maskW / options.inputWidth;\n const scaleY = maskH / options.inputHeight;\n const protoPlane = maskH * maskW;\n const coefBase = (4 + options.numClasses) * numAnchors;\n\n const results: DecodedSegmentation[] = [];\n\n for (let i = 0; i < decoded.anchorIndices.length; i++) {\n const a = decoded.anchorIndices[i] as number;\n const x1 = decoded.boxesXyxy[i * 4] as number;\n const y1 = decoded.boxesXyxy[i * 4 + 1] as number;\n const x2 = decoded.boxesXyxy[i * 4 + 2] as number;\n const y2 = decoded.boxesXyxy[i * 4 + 3] as number;\n const bbox = new BoundingBox(x1, y1, x2, y2);\n const classId = decoded.classIds[i] as number;\n const confidence = decoded.confidences[i] as number;\n\n const bboxW = Math.max(0, Math.trunc(x2) - Math.trunc(x1));\n const bboxH = Math.max(0, Math.trunc(y2) - Math.trunc(y1));\n\n if (bboxW === 0 || bboxH === 0) {\n results.push({ bbox, classId, confidence, mask: new Mask(new Uint8Array(0), 0, 0) });\n continue;\n }\n\n const ibx1 = x1 * options.scale + options.padLeft;\n const iby1 = y1 * options.scale + options.padTop;\n const ibx2 = x2 * options.scale + options.padLeft;\n const iby2 = y2 * options.scale + options.padTop;\n\n const mbx1 = Math.max(0, Math.floor(ibx1 * scaleX));\n const mby1 = Math.max(0, Math.floor(iby1 * scaleY));\n const mbx2 = Math.min(maskW, Math.ceil(ibx2 * scaleX));\n const mby2 = Math.min(maskH, Math.ceil(iby2 * scaleY));\n\n if (mbx2 <= mbx1 || mby2 <= mby1) {\n results.push({\n bbox,\n classId,\n confidence,\n mask: new Mask(new Uint8Array(bboxW * bboxH), bboxW, bboxH),\n });\n continue;\n }\n\n const cropW = mbx2 - mbx1;\n const cropH = mby2 - mby1;\n const softCrop = softMaskCrop(\n perAnchorData,\n prototypeData,\n coefBase + a,\n numAnchors,\n numMaskCoefs,\n maskW,\n protoPlane,\n mbx1,\n mby1,\n cropW,\n cropH,\n );\n const binary = resizeBilinearThreshold(softCrop, cropW, cropH, bboxW, bboxH, maskThreshold);\n\n results.push({ bbox, classId, confidence, mask: new Mask(binary, bboxW, bboxH) });\n }\n\n return results;\n}\n\nfunction sigmoid(x: number): number {\n if (x >= 0) {\n return 1 / (1 + Math.exp(-x));\n }\n const e = Math.exp(x);\n return e / (1 + e);\n}\n\n/**\n * Assemble one instance's soft mask over the prototype region under its box.\n *\n * The loop runs coefficient-major: for each of the `numMaskCoefs` prototypes it\n * walks the crop row by row, so every read of `prototypeData` is contiguous.\n * The pixel-major order it replaces jumped a whole prototype plane (25 600\n * floats for a 160×160 head) between consecutive reads, touching a fresh cache\n * line on every multiply. The accumulator is a `Float64Array` so each pixel's\n * sum is still built in double precision and in the same coefficient order as a\n * scalar `let sum = 0` — the result is bit-identical, only the memory walk\n * changed.\n *\n * @param perAnchorData Flat `output0`.\n * @param prototypeData Flat `output1`.\n * @param coefOffset Index of this anchor's first mask coefficient.\n * @param numAnchors Stride between consecutive coefficients of one anchor.\n * @param numMaskCoefs Number of prototypes.\n * @param maskW Prototype width.\n * @param protoPlane Prototype plane size, `maskW * maskH`.\n * @param left First prototype column of the crop.\n * @param top First prototype row of the crop.\n * @param cropW Crop width in prototype pixels.\n * @param cropH Crop height in prototype pixels.\n * @returns The sigmoid of the mask logits, `cropW * cropH`, row-major.\n */\nfunction softMaskCrop(\n perAnchorData: Float32Array,\n prototypeData: Float32Array,\n coefOffset: number,\n numAnchors: number,\n numMaskCoefs: number,\n maskW: number,\n protoPlane: number,\n left: number,\n top: number,\n cropW: number,\n cropH: number,\n): Float32Array {\n const acc = new Float64Array(cropW * cropH);\n for (let kk = 0; kk < numMaskCoefs; kk++) {\n const coef = perAnchorData[coefOffset + kk * numAnchors] as number;\n const planeBase = kk * protoPlane + left;\n for (let y = 0; y < cropH; y++) {\n const src = planeBase + (top + y) * maskW;\n const dst = y * cropW;\n for (let x = 0; x < cropW; x++) {\n acc[dst + x] = (acc[dst + x] as number) + coef * (prototypeData[src + x] as number);\n }\n }\n }\n const soft = new Float32Array(acc.length);\n for (let j = 0; j < acc.length; j++) soft[j] = sigmoid(acc[j] as number);\n return soft;\n}\n\n/**\n * Bilinearly resize a soft mask and threshold it in the same pass.\n *\n * Half-pixel-centre sampling, matching `cv2.resize(..., INTER_LINEAR)` and the\n * Python SDK. The interpolation is separable and runs that way, like the\n * Python `_resize_bilinear`: first along x over the few source rows, into a\n * `Float64Array` so every horizontal blend keeps its exact double value, then\n * along y between two of those rows. Each output pixel costs two reads and one\n * blend instead of four reads and three, with the same values as blending all\n * four directly. The thresholded byte is written directly instead of\n * materialising a `Float32Array` of the resized mask first.\n * Each interpolated value is rounded to float32 with `Math.fround` before the\n * comparison, because that is the value the intermediate array used to hold —\n * comparing the unrounded double would flip pixels sitting exactly on the\n * threshold.\n *\n * @param src Soft mask, `srcWidth * srcHeight`, row-major.\n * @param srcWidth Source width.\n * @param srcHeight Source height.\n * @param targetWidth Output width, at least 1.\n * @param targetHeight Output height, at least 1.\n * @param threshold Probability at or above which a pixel is foreground.\n * @returns `255` for foreground and `0` for background, `targetWidth * targetHeight`.\n */\nfunction resizeBilinearThreshold(\n src: Float32Array,\n srcWidth: number,\n srcHeight: number,\n targetWidth: number,\n targetHeight: number,\n threshold: number,\n): Uint8Array {\n const out = new Uint8Array(targetWidth * targetHeight);\n if (targetWidth === srcWidth && targetHeight === srcHeight) {\n for (let j = 0; j < out.length; j++) out[j] = (src[j] as number) >= threshold ? 255 : 0;\n return out;\n }\n const sx = srcWidth / targetWidth;\n const sy = srcHeight / targetHeight;\n const x0s = new Int32Array(targetWidth);\n const x1s = new Int32Array(targetWidth);\n const wxs = new Float64Array(targetWidth);\n for (let x = 0; x < targetWidth; x++) {\n const xx = (x + 0.5) * sx - 0.5;\n const x0 = Math.max(0, Math.floor(xx));\n x0s[x] = x0;\n x1s[x] = Math.min(srcWidth - 1, x0 + 1);\n wxs[x] = Math.max(0, Math.min(1, xx - x0));\n }\n const across = new Float64Array(srcHeight * targetWidth);\n for (let r = 0; r < srcHeight; r++) {\n const row = r * srcWidth;\n const dst = r * targetWidth;\n for (let x = 0; x < targetWidth; x++) {\n const wx = wxs[x] as number;\n across[dst + x] =\n (src[row + (x0s[x] as number)] as number) * (1 - wx) +\n (src[row + (x1s[x] as number)] as number) * wx;\n }\n }\n for (let y = 0; y < targetHeight; y++) {\n const yy = (y + 0.5) * sy - 0.5;\n const y0 = Math.max(0, Math.floor(yy));\n const y1 = Math.min(srcHeight - 1, y0 + 1);\n const wy = Math.max(0, Math.min(1, yy - y0));\n const top = y0 * targetWidth;\n const bot = y1 * targetWidth;\n const dst = y * targetWidth;\n for (let x = 0; x < targetWidth; x++) {\n const value = (across[top + x] as number) * (1 - wy) + (across[bot + x] as number) * wy;\n out[dst + x] = Math.fround(value) >= threshold ? 255 : 0;\n }\n }\n return out;\n}\n"],"mappings":"6DAkEA,SAAgB,EACZ,EACA,EACA,EACA,EACA,EACqB,CACrB,IAAI,EAAQ,EACZ,GAAI,EAAM,SAAW,EAAG,CACpB,GAAI,EAAM,KAAO,EACb,MAAU,MAAM,2DAA2D,EAAM,GAAG,EAAE,EAE1F,EAAQ,CAAC,EAAM,GAAc,EAAM,GAAc,EAAM,EAAY,CACvE,CACA,GAAI,EAAM,SAAW,EACjB,MAAU,MACN,wEAAwE,KAAK,UAAU,CAAa,EAAE,EAC1G,EAEJ,IAAM,EAAe,EAAM,GACrB,EAAQ,EAAM,GACd,EAAQ,EAAM,GAEd,EAAW,EAAc,SAAW,EAAI,EAAc,GAAK,EAAc,GACzE,EAAa,EAAc,SAAW,EAAI,EAAc,GAAK,EAAc,GACjF,GAAI,IAAa,IAAA,IAAa,IAAe,IAAA,GACzC,MAAU,MACN,4DAA4D,KAAK,UAAU,CAAa,EAAE,EAC9F,EAGJ,IAAM,EAAmB,EAAI,EAAQ,WAAa,EAClD,GAAI,IAAa,EACb,MAAU,MACN,2BAA2B,EAAS,iCAAiC,EAAQ,WAAW,mBAAmB,EAAa,MAAM,EAAiB,EACnJ,EAEJ,GAAI,EAAc,SAAW,EAAe,EAAQ,EAChD,MAAU,MACN,mCAAmC,EAAc,OAAO,uBAAuB,KAAK,UAAU,CAAa,EAAE,EACjH,EAGJ,IAAM,EAAU,EAAA,kBAAkB,EAAe,EAAe,CAC5D,WAAY,EAAQ,WACpB,cAAe,EAAQ,cACvB,eAAgB,EAAQ,eACxB,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,MAAO,EAAQ,MACf,cAAe,EAAQ,cACvB,aAAc,EAAQ,aACtB,cAAe,EAAQ,aAC3B,CAAC,EAED,GAAI,EAAQ,cAAc,SAAW,EAAG,MAAO,CAAC,EAEhD,IAAM,EAAgB,EAAQ,eAAiB,GACzC,EAAS,EAAQ,EAAQ,WACzB,EAAS,EAAQ,EAAQ,YACzB,EAAa,EAAQ,EACrB,GAAY,EAAI,EAAQ,YAAc,EAEtC,EAAiC,CAAC,EAExC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,cAAc,OAAQ,IAAK,CACnD,IAAM,EAAI,EAAQ,cAAc,GAC1B,EAAK,EAAQ,UAAU,EAAI,GAC3B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAK,EAAQ,UAAU,EAAI,EAAI,GAC/B,EAAO,IAAI,EAAA,YAAY,EAAI,EAAI,EAAI,CAAE,EACrC,EAAU,EAAQ,SAAS,GAC3B,EAAa,EAAQ,YAAY,GAEjC,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,EAAI,KAAK,MAAM,CAAE,CAAC,EACnD,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,EAAI,KAAK,MAAM,CAAE,CAAC,EAEzD,GAAI,IAAU,GAAK,IAAU,EAAG,CAC5B,EAAQ,KAAK,CAAE,OAAM,UAAS,aAAY,KAAM,IAAI,EAAA,KAAK,IAAI,WAAe,EAAG,CAAC,CAAE,CAAC,EACnF,QACJ,CAEA,IAAM,EAAO,EAAK,EAAQ,MAAQ,EAAQ,QACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,OACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,QACpC,EAAO,EAAK,EAAQ,MAAQ,EAAQ,OAEpC,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,CAAM,CAAC,EAC5C,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAO,CAAM,CAAC,EAC5C,EAAO,KAAK,IAAI,EAAO,KAAK,KAAK,EAAO,CAAM,CAAC,EAC/C,EAAO,KAAK,IAAI,EAAO,KAAK,KAAK,EAAO,CAAM,CAAC,EAErD,GAAI,GAAQ,GAAQ,GAAQ,EAAM,CAC9B,EAAQ,KAAK,CACT,OACA,UACA,aACA,KAAM,IAAI,EAAA,KAAK,IAAI,WAAW,EAAQ,CAAK,EAAG,EAAO,CAAK,CAC9D,CAAC,EACD,QACJ,CAEA,IAAM,EAAQ,EAAO,EACf,EAAQ,EAAO,EAcf,EAAS,EAbE,EACb,EACA,EACA,EAAW,EACX,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CAEmC,EAAU,EAAO,EAAO,EAAO,EAAO,CAAa,EAE1F,EAAQ,KAAK,CAAE,OAAM,UAAS,aAAY,KAAM,IAAI,EAAA,KAAK,EAAQ,EAAO,CAAK,CAAE,CAAC,CACpF,CAEA,OAAO,CACX,CAEA,SAAS,EAAQ,EAAmB,CAChC,GAAI,GAAK,EACL,MAAO,IAAK,EAAI,KAAK,IAAI,CAAC,CAAC,GAE/B,IAAM,EAAI,KAAK,IAAI,CAAC,EACpB,OAAO,GAAK,EAAI,EACpB,CA2BA,SAAS,EACL,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACY,CACZ,IAAM,EAAM,IAAI,aAAa,EAAQ,CAAK,EAC1C,IAAK,IAAI,EAAK,EAAG,EAAK,EAAc,IAAM,CACtC,IAAM,EAAO,EAAc,EAAa,EAAK,GACvC,EAAY,EAAK,EAAa,EACpC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IAAK,CAC5B,IAAM,EAAM,GAAa,EAAM,GAAK,EAC9B,EAAM,EAAI,EAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,IACvB,EAAI,EAAM,GAAM,EAAI,EAAM,GAAgB,EAAQ,EAAc,EAAM,EAE9E,CACJ,CACA,IAAM,EAAO,IAAI,aAAa,EAAI,MAAM,EACxC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAQ,IAAK,EAAK,GAAK,EAAQ,EAAI,EAAY,EACvE,OAAO,CACX,CA0BA,SAAS,EACL,EACA,EACA,EACA,EACA,EACA,EACU,CACV,IAAM,EAAM,IAAI,WAAW,EAAc,CAAY,EACrD,GAAI,IAAgB,GAAY,IAAiB,EAAW,CACxD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAQ,IAAK,EAAI,GAAM,EAAI,IAAiB,EAAY,IAAM,EACtF,OAAO,CACX,CACA,IAAM,EAAK,EAAW,EAChB,EAAK,EAAY,EACjB,EAAM,IAAI,WAAW,CAAW,EAChC,EAAM,IAAI,WAAW,CAAW,EAChC,EAAM,IAAI,aAAa,CAAW,EACxC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAAK,CAClC,IAAM,GAAM,EAAI,IAAO,EAAK,GACtB,EAAK,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EACrC,EAAI,GAAK,EACT,EAAI,GAAK,KAAK,IAAI,EAAW,EAAG,EAAK,CAAC,EACtC,EAAI,GAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAK,CAAE,CAAC,CAC7C,CACA,IAAM,EAAS,IAAI,aAAa,EAAY,CAAW,EACvD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAW,IAAK,CAChC,IAAM,EAAM,EAAI,EACV,EAAM,EAAI,EAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAAK,CAClC,IAAM,EAAK,EAAI,GACf,EAAO,EAAM,GACR,EAAI,EAAO,EAAI,KAA6B,EAAI,GAChD,EAAI,EAAO,EAAI,IAA4B,CACpD,CACJ,CACA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAc,IAAK,CACnC,IAAM,GAAM,EAAI,IAAO,EAAK,GACtB,EAAK,KAAK,IAAI,EAAG,KAAK,MAAM,CAAE,CAAC,EAC/B,EAAK,KAAK,IAAI,EAAY,EAAG,EAAK,CAAC,EACnC,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,EAAK,CAAE,CAAC,EACrC,EAAM,EAAK,EACX,EAAM,EAAK,EACX,EAAM,EAAI,EAChB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAa,IAAK,CAClC,IAAM,EAAS,EAAO,EAAM,IAAiB,EAAI,GAAO,EAAO,EAAM,GAAgB,EACrF,EAAI,EAAM,GAAK,KAAK,OAAO,CAAK,GAAK,EAAY,IAAM,CAC3D,CACJ,CACA,OAAO,CACX"}