@dejadex/web 0.0.0-stage → 0.1.1
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.
- package/CHANGELOG.md +35 -0
- package/README.md +222 -2
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/geometry/homography.d.ts +75 -0
- package/dist/geometry/homography.d.ts.map +1 -0
- package/dist/geometry/homography.js +146 -0
- package/dist/geometry/homography.js.map +1 -0
- package/dist/identify/assets.d.ts +39 -0
- package/dist/identify/assets.d.ts.map +1 -0
- package/dist/identify/assets.js +167 -0
- package/dist/identify/assets.js.map +1 -0
- package/dist/identify/decode.d.ts +6 -0
- package/dist/identify/decode.d.ts.map +1 -0
- package/dist/identify/decode.js +53 -0
- package/dist/identify/decode.js.map +1 -0
- package/dist/identify/engine.d.ts +32 -0
- package/dist/identify/engine.d.ts.map +1 -0
- package/dist/identify/engine.js +127 -0
- package/dist/identify/engine.js.map +1 -0
- package/dist/identify/identifier.d.ts +91 -0
- package/dist/identify/identifier.d.ts.map +1 -0
- package/dist/identify/identifier.js +332 -0
- package/dist/identify/identifier.js.map +1 -0
- package/dist/identify/manifest.d.ts +60 -0
- package/dist/identify/manifest.d.ts.map +1 -0
- package/dist/identify/manifest.js +211 -0
- package/dist/identify/manifest.js.map +1 -0
- package/dist/identify/preprocess.d.ts +52 -0
- package/dist/identify/preprocess.d.ts.map +1 -0
- package/dist/identify/preprocess.js +195 -0
- package/dist/identify/preprocess.js.map +1 -0
- package/dist/identify/router.d.ts +60 -0
- package/dist/identify/router.d.ts.map +1 -0
- package/dist/identify/router.js +83 -0
- package/dist/identify/router.js.map +1 -0
- package/dist/identify/runtime.d.ts +69 -0
- package/dist/identify/runtime.d.ts.map +1 -0
- package/dist/identify/runtime.js +140 -0
- package/dist/identify/runtime.js.map +1 -0
- package/dist/identify/search.d.ts +35 -0
- package/dist/identify/search.d.ts.map +1 -0
- package/dist/identify/search.js +99 -0
- package/dist/identify/search.js.map +1 -0
- package/dist/identify/server.d.ts +60 -0
- package/dist/identify/server.d.ts.map +1 -0
- package/dist/identify/server.js +149 -0
- package/dist/identify/server.js.map +1 -0
- package/dist/identify/shard.d.ts +31 -0
- package/dist/identify/shard.d.ts.map +1 -0
- package/dist/identify/shard.js +146 -0
- package/dist/identify/shard.js.map +1 -0
- package/dist/identify/types.d.ts +107 -0
- package/dist/identify/types.d.ts.map +1 -0
- package/dist/identify/types.js +7 -0
- package/dist/identify/types.js.map +1 -0
- package/dist/identify/worker.d.ts +2 -0
- package/dist/identify/worker.d.ts.map +1 -0
- package/dist/identify/worker.js +13 -0
- package/dist/identify/worker.js.map +1 -0
- package/dist/identify/workerProtocol.d.ts +54 -0
- package/dist/identify/workerProtocol.d.ts.map +1 -0
- package/dist/identify/workerProtocol.js +113 -0
- package/dist/identify/workerProtocol.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lease/verify.d.ts +69 -0
- package/dist/lease/verify.d.ts.map +1 -0
- package/dist/lease/verify.js +235 -0
- package/dist/lease/verify.js.map +1 -0
- package/dist/scanner.d.ts +10 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +58 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/meter.d.ts +83 -0
- package/dist/usage/meter.d.ts.map +1 -0
- package/dist/usage/meter.js +111 -0
- package/dist/usage/meter.js.map +1 -0
- package/dist/usage/store.d.ts +40 -0
- package/dist/usage/store.d.ts.map +1 -0
- package/dist/usage/store.js +248 -0
- package/dist/usage/store.js.map +1 -0
- package/package.json +64 -4
- package/src/errors.ts +24 -0
- package/src/geometry/homography.ts +178 -0
- package/src/identify/assets.ts +189 -0
- package/src/identify/decode.ts +54 -0
- package/src/identify/engine.ts +167 -0
- package/src/identify/identifier.ts +431 -0
- package/src/identify/manifest.ts +240 -0
- package/src/identify/preprocess.ts +227 -0
- package/src/identify/router.ts +115 -0
- package/src/identify/runtime.ts +197 -0
- package/src/identify/search.ts +115 -0
- package/src/identify/server.ts +167 -0
- package/src/identify/shard.ts +154 -0
- package/src/identify/types.ts +122 -0
- package/src/identify/worker.ts +13 -0
- package/src/identify/workerProtocol.ts +136 -0
- package/src/index.ts +71 -0
- package/src/lease/verify.ts +252 -0
- package/src/scanner.ts +56 -0
- package/src/types.ts +196 -0
- package/src/usage/meter.ts +165 -0
- package/src/usage/store.ts +282 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crop → model input, identical to the Python reference that embedded the
|
|
3
|
+
* gallery (`ml/scripts/phase0_zero_shot.py`, `preprocess`):
|
|
4
|
+
*
|
|
5
|
+
* RGB → stretch-resize to width × height with PIL's BILINEAR filter
|
|
6
|
+
* (antialiased when shrinking) → x / 255 → (x − mean) / std → CHW float32.
|
|
7
|
+
*
|
|
8
|
+
* The resize is a port of Pillow's `Resample.c` for 8-bit images, fixed-point
|
|
9
|
+
* coefficients and all: a horizontal pass into an 8-bit intermediate, then a
|
|
10
|
+
* vertical one. A canvas `drawImage` would resample with whatever filter the
|
|
11
|
+
* browser picks, which differs between browsers and from the gallery's.
|
|
12
|
+
*
|
|
13
|
+
* Pure functions on typed arrays: they run the same in a Worker, on the main
|
|
14
|
+
* thread and in Node (the parity tests).
|
|
15
|
+
*/
|
|
16
|
+
/** Interleaved 8-bit pixels, row-major, top row first. */
|
|
17
|
+
export interface PixelImage {
|
|
18
|
+
readonly width: number;
|
|
19
|
+
readonly height: number;
|
|
20
|
+
/** 3 (RGB) or 4 (RGBA, as `ImageData` has; alpha is ignored). */
|
|
21
|
+
readonly channels: 3 | 4;
|
|
22
|
+
readonly data: Uint8Array | Uint8ClampedArray;
|
|
23
|
+
}
|
|
24
|
+
export interface Normalisation {
|
|
25
|
+
readonly mean: readonly [number, number, number];
|
|
26
|
+
readonly std: readonly [number, number, number];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resize to `outWidth × outHeight` RGB, bit-exact with Pillow's
|
|
30
|
+
* `Image.resize((w, h), Image.Resampling.BILINEAR)` on an RGB image (no box,
|
|
31
|
+
* no `reducing_gap`). Returns 3-channel pixels.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resizeBilinear(src: PixelImage, outWidth: number, outHeight: number): PixelImage;
|
|
34
|
+
/** The image turned 180°. Pillow's bilinear resize commutes with it, so the SDK turns the small resized crop. */
|
|
35
|
+
export declare function rotate180(img: PixelImage): PixelImage;
|
|
36
|
+
/**
|
|
37
|
+
* Writes the normalised CHW tensor of an RGB image into `out` at `offset`
|
|
38
|
+
* (in floats). Float32 arithmetic step by step, as numpy does it, so the
|
|
39
|
+
* values are the reference's to the last bit.
|
|
40
|
+
*/
|
|
41
|
+
export declare function writeTensor(img: PixelImage, norm: Normalisation, out: Float32Array, offset?: number): void;
|
|
42
|
+
/** 0 and/or 180: the orientations each crop is embedded at. */
|
|
43
|
+
export type Orientation = 0 | 180;
|
|
44
|
+
/**
|
|
45
|
+
* One crop → `orientations.length` consecutive CHW tensors in `out` at
|
|
46
|
+
* `offset`, ready for an NCHW batch.
|
|
47
|
+
*/
|
|
48
|
+
export declare function preprocessCrop(src: PixelImage, size: {
|
|
49
|
+
readonly width: number;
|
|
50
|
+
readonly height: number;
|
|
51
|
+
}, norm: Normalisation, orientations: readonly Orientation[], out: Float32Array, offset?: number): void;
|
|
52
|
+
//# sourceMappingURL=preprocess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preprocess.d.ts","sourceRoot":"","sources":["../../src/identify/preprocess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAA;CAC9C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChD,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAChD;AA2DD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAmE/F;AAaD,iHAAiH;AACjH,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CASrD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,SAAI,GAAG,IAAI,CAerG;AAED,+DAA+D;AAC/D,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,GAAG,CAAA;AAEjC;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,UAAU,EACf,IAAI,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzD,IAAI,EAAE,aAAa,EACnB,YAAY,EAAE,SAAS,WAAW,EAAE,EACpC,GAAG,EAAE,YAAY,EACjB,MAAM,SAAI,GACT,IAAI,CAMN"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crop → model input, identical to the Python reference that embedded the
|
|
3
|
+
* gallery (`ml/scripts/phase0_zero_shot.py`, `preprocess`):
|
|
4
|
+
*
|
|
5
|
+
* RGB → stretch-resize to width × height with PIL's BILINEAR filter
|
|
6
|
+
* (antialiased when shrinking) → x / 255 → (x − mean) / std → CHW float32.
|
|
7
|
+
*
|
|
8
|
+
* The resize is a port of Pillow's `Resample.c` for 8-bit images, fixed-point
|
|
9
|
+
* coefficients and all: a horizontal pass into an 8-bit intermediate, then a
|
|
10
|
+
* vertical one. A canvas `drawImage` would resample with whatever filter the
|
|
11
|
+
* browser picks, which differs between browsers and from the gallery's.
|
|
12
|
+
*
|
|
13
|
+
* Pure functions on typed arrays: they run the same in a Worker, on the main
|
|
14
|
+
* thread and in Node (the parity tests).
|
|
15
|
+
*/
|
|
16
|
+
/** Pillow: `#define PRECISION_BITS (32 - 8 - 2)`. */
|
|
17
|
+
const PRECISION_BITS = 22;
|
|
18
|
+
const ONE_HALF = 1 << (PRECISION_BITS - 1);
|
|
19
|
+
const CLIP_MAX = (1 << PRECISION_BITS) << 8;
|
|
20
|
+
const bilinear = (x) => {
|
|
21
|
+
const a = x < 0 ? -x : x;
|
|
22
|
+
return a < 1 ? 1 - a : 0;
|
|
23
|
+
};
|
|
24
|
+
/** Pillow's `precompute_coeffs` + `normalize_coeffs_8bpc`, for the bilinear filter (support 1). */
|
|
25
|
+
function precomputeCoeffs(inSize, outSize) {
|
|
26
|
+
const scale = inSize / outSize;
|
|
27
|
+
const filterscale = scale < 1 ? 1 : scale;
|
|
28
|
+
const support = 1 * filterscale;
|
|
29
|
+
const ksize = Math.ceil(support) * 2 + 1;
|
|
30
|
+
const bounds = new Int32Array(outSize * 2);
|
|
31
|
+
const pre = new Float64Array(outSize * ksize);
|
|
32
|
+
const ss = 1 / filterscale;
|
|
33
|
+
for (let xx = 0; xx < outSize; xx++) {
|
|
34
|
+
const center = (xx + 0.5) * scale;
|
|
35
|
+
// C's (int) truncates toward zero.
|
|
36
|
+
let xmin = Math.trunc(center - support + 0.5);
|
|
37
|
+
if (xmin < 0)
|
|
38
|
+
xmin = 0;
|
|
39
|
+
let xmax = Math.trunc(center + support + 0.5);
|
|
40
|
+
if (xmax > inSize)
|
|
41
|
+
xmax = inSize;
|
|
42
|
+
xmax -= xmin;
|
|
43
|
+
let ww = 0;
|
|
44
|
+
const base = xx * ksize;
|
|
45
|
+
for (let x = 0; x < xmax; x++) {
|
|
46
|
+
const w = bilinear((x + xmin - center + 0.5) * ss);
|
|
47
|
+
pre[base + x] = w;
|
|
48
|
+
ww += w;
|
|
49
|
+
}
|
|
50
|
+
if (ww !== 0)
|
|
51
|
+
for (let x = 0; x < xmax; x++)
|
|
52
|
+
pre[base + x] = pre[base + x] / ww;
|
|
53
|
+
bounds[xx * 2] = xmin;
|
|
54
|
+
bounds[xx * 2 + 1] = xmax;
|
|
55
|
+
}
|
|
56
|
+
const kk = new Int32Array(pre.length);
|
|
57
|
+
const one = 1 << PRECISION_BITS;
|
|
58
|
+
for (let i = 0; i < pre.length; i++) {
|
|
59
|
+
const v = pre[i];
|
|
60
|
+
kk[i] = v < 0 ? Math.trunc(-0.5 + v * one) : Math.trunc(0.5 + v * one);
|
|
61
|
+
}
|
|
62
|
+
return { ksize, bounds, kk };
|
|
63
|
+
}
|
|
64
|
+
const clip8 = (v) => (v >= CLIP_MAX ? 255 : v <= 0 ? 0 : v >> PRECISION_BITS);
|
|
65
|
+
/**
|
|
66
|
+
* Resize to `outWidth × outHeight` RGB, bit-exact with Pillow's
|
|
67
|
+
* `Image.resize((w, h), Image.Resampling.BILINEAR)` on an RGB image (no box,
|
|
68
|
+
* no `reducing_gap`). Returns 3-channel pixels.
|
|
69
|
+
*/
|
|
70
|
+
export function resizeBilinear(src, outWidth, outHeight) {
|
|
71
|
+
const { width: inW, height: inH, channels: c, data } = src;
|
|
72
|
+
if (!Number.isInteger(outWidth) || !Number.isInteger(outHeight) || outWidth <= 0 || outHeight <= 0) {
|
|
73
|
+
throw new RangeError(`bad output size ${outWidth}x${outHeight}`);
|
|
74
|
+
}
|
|
75
|
+
if (data.length < inW * inH * c)
|
|
76
|
+
throw new RangeError('pixel buffer is shorter than width × height × channels');
|
|
77
|
+
// Horizontal pass, over only the rows the vertical pass will read.
|
|
78
|
+
const vert = precomputeCoeffs(inH, outHeight);
|
|
79
|
+
const needH = outWidth !== inW;
|
|
80
|
+
const needV = outHeight !== inH;
|
|
81
|
+
let rows;
|
|
82
|
+
let rowOffset = 0;
|
|
83
|
+
let rowsHeight = inH;
|
|
84
|
+
if (needH) {
|
|
85
|
+
const horiz = precomputeCoeffs(inW, outWidth);
|
|
86
|
+
const first = vert.bounds[0];
|
|
87
|
+
const last = vert.bounds[outHeight * 2 - 2] + vert.bounds[outHeight * 2 - 1];
|
|
88
|
+
rowsHeight = needV ? last - first : inH;
|
|
89
|
+
rowOffset = needV ? first : 0;
|
|
90
|
+
rows = new Uint8Array(outWidth * rowsHeight * 3);
|
|
91
|
+
const { ksize, bounds, kk } = horiz;
|
|
92
|
+
for (let y = 0; y < rowsHeight; y++) {
|
|
93
|
+
const inRow = (y + rowOffset) * inW * c;
|
|
94
|
+
const outRow = y * outWidth * 3;
|
|
95
|
+
for (let xx = 0; xx < outWidth; xx++) {
|
|
96
|
+
const xmin = bounds[xx * 2];
|
|
97
|
+
const xmax = bounds[xx * 2 + 1];
|
|
98
|
+
const k = xx * ksize;
|
|
99
|
+
let s0 = ONE_HALF;
|
|
100
|
+
let s1 = ONE_HALF;
|
|
101
|
+
let s2 = ONE_HALF;
|
|
102
|
+
for (let x = 0; x < xmax; x++) {
|
|
103
|
+
const w = kk[k + x];
|
|
104
|
+
const p = inRow + (x + xmin) * c;
|
|
105
|
+
s0 += data[p] * w;
|
|
106
|
+
s1 += data[p + 1] * w;
|
|
107
|
+
s2 += data[p + 2] * w;
|
|
108
|
+
}
|
|
109
|
+
const o = outRow + xx * 3;
|
|
110
|
+
rows[o] = clip8(s0);
|
|
111
|
+
rows[o + 1] = clip8(s1);
|
|
112
|
+
rows[o + 2] = clip8(s2);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
rows = toRgb(src);
|
|
118
|
+
}
|
|
119
|
+
if (!needV)
|
|
120
|
+
return { width: outWidth, height: outHeight, channels: 3, data: rows };
|
|
121
|
+
// Vertical pass. Bounds were computed against the full input height; the
|
|
122
|
+
// intermediate starts at `rowOffset`.
|
|
123
|
+
const out = new Uint8Array(outWidth * outHeight * 3);
|
|
124
|
+
const { ksize, bounds, kk } = vert;
|
|
125
|
+
const stride = outWidth * 3;
|
|
126
|
+
for (let yy = 0; yy < outHeight; yy++) {
|
|
127
|
+
const ymin = bounds[yy * 2] - rowOffset;
|
|
128
|
+
const ymax = bounds[yy * 2 + 1];
|
|
129
|
+
const k = yy * ksize;
|
|
130
|
+
const outRow = yy * stride;
|
|
131
|
+
for (let i = 0; i < stride; i++) {
|
|
132
|
+
let s = ONE_HALF;
|
|
133
|
+
for (let y = 0; y < ymax; y++)
|
|
134
|
+
s += rows[(y + ymin) * stride + i] * kk[k + y];
|
|
135
|
+
out[outRow + i] = clip8(s);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { width: outWidth, height: outHeight, channels: 3, data: out };
|
|
139
|
+
}
|
|
140
|
+
function toRgb(src) {
|
|
141
|
+
const n = src.width * src.height;
|
|
142
|
+
const out = new Uint8Array(n * 3);
|
|
143
|
+
for (let i = 0; i < n; i++) {
|
|
144
|
+
out[i * 3] = src.data[i * src.channels];
|
|
145
|
+
out[i * 3 + 1] = src.data[i * src.channels + 1];
|
|
146
|
+
out[i * 3 + 2] = src.data[i * src.channels + 2];
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
/** The image turned 180°. Pillow's bilinear resize commutes with it, so the SDK turns the small resized crop. */
|
|
151
|
+
export function rotate180(img) {
|
|
152
|
+
const c = img.channels;
|
|
153
|
+
const n = img.width * img.height;
|
|
154
|
+
const out = new Uint8Array(n * c);
|
|
155
|
+
for (let i = 0; i < n; i++) {
|
|
156
|
+
const src = (n - 1 - i) * c;
|
|
157
|
+
for (let k = 0; k < c; k++)
|
|
158
|
+
out[i * c + k] = img.data[src + k];
|
|
159
|
+
}
|
|
160
|
+
return { width: img.width, height: img.height, channels: c, data: out };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Writes the normalised CHW tensor of an RGB image into `out` at `offset`
|
|
164
|
+
* (in floats). Float32 arithmetic step by step, as numpy does it, so the
|
|
165
|
+
* values are the reference's to the last bit.
|
|
166
|
+
*/
|
|
167
|
+
export function writeTensor(img, norm, out, offset = 0) {
|
|
168
|
+
const plane = img.width * img.height;
|
|
169
|
+
if (out.length < offset + plane * 3)
|
|
170
|
+
throw new RangeError('tensor buffer too small');
|
|
171
|
+
const f = Math.fround;
|
|
172
|
+
const inv255 = f(1 / 255);
|
|
173
|
+
const c = img.channels;
|
|
174
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
175
|
+
const mean = f(norm.mean[ch]);
|
|
176
|
+
const std = f(norm.std[ch]);
|
|
177
|
+
const base = offset + ch * plane;
|
|
178
|
+
for (let i = 0; i < plane; i++) {
|
|
179
|
+
const x = f(img.data[i * c + ch] * inv255);
|
|
180
|
+
out[base + i] = f(f(x - mean) / std);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* One crop → `orientations.length` consecutive CHW tensors in `out` at
|
|
186
|
+
* `offset`, ready for an NCHW batch.
|
|
187
|
+
*/
|
|
188
|
+
export function preprocessCrop(src, size, norm, orientations, out, offset = 0) {
|
|
189
|
+
const resized = resizeBilinear(src, size.width, size.height);
|
|
190
|
+
const plane = size.width * size.height * 3;
|
|
191
|
+
orientations.forEach((deg, i) => {
|
|
192
|
+
writeTensor(deg === 180 ? rotate180(resized) : resized, norm, out, offset + i * plane);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=preprocess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preprocess.js","sourceRoot":"","sources":["../../src/identify/preprocess.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an identification runs, and when the server route is skipped. Pure:
|
|
3
|
+
* the clock and every input are passed in, so each rule has its own test.
|
|
4
|
+
*
|
|
5
|
+
* - `local`: the device, always; nothing is ever sent.
|
|
6
|
+
* - `server`: the server first, then the device.
|
|
7
|
+
* - `auto`: the device when it is loaded and fast enough (WebGPU, or a
|
|
8
|
+
* measured per-crop time within the budget); otherwise the server first,
|
|
9
|
+
* then the device. A device whose model is still downloading uses the
|
|
10
|
+
* server meanwhile.
|
|
11
|
+
*
|
|
12
|
+
* The server is skipped, straight to the device, while the browser reports
|
|
13
|
+
* itself offline; for the rest of a lease the server refused (401/403/409);
|
|
14
|
+
* until a 429's `Retry-After`; and for 30 s after three consecutive failures
|
|
15
|
+
* (timeout, network, 5xx, malformed answer).
|
|
16
|
+
*/
|
|
17
|
+
import type { ServerAnswer } from './server.js';
|
|
18
|
+
import type { IdentificationMode } from './types.js';
|
|
19
|
+
/** Phase 0: a mid-range phone without WebGPU took ~326 ms per crop on 4 WASM threads. */
|
|
20
|
+
export declare const DEFAULT_SLOW_DEVICE_BUDGET_MS = 300;
|
|
21
|
+
export declare const FALLBACK_FLOOR_MS = 300;
|
|
22
|
+
export declare const FALLBACK_CAP_MS = 3000;
|
|
23
|
+
/** The server's deadline while the device has nothing to fall back on yet. */
|
|
24
|
+
export declare const DEFAULT_SERVER_TIMEOUT_MS = 8000;
|
|
25
|
+
export declare const BREAKER_FAILURES = 3;
|
|
26
|
+
export declare const BREAKER_PAUSE_MS = 30000;
|
|
27
|
+
export interface DeviceState {
|
|
28
|
+
readonly ready: boolean;
|
|
29
|
+
readonly backend: 'webgpu' | 'wasm' | null;
|
|
30
|
+
/** NaN until measured. */
|
|
31
|
+
readonly perCropMs: number;
|
|
32
|
+
}
|
|
33
|
+
export type Route = 'device' | 'server';
|
|
34
|
+
export declare function chooseRoute(input: {
|
|
35
|
+
readonly mode: IdentificationMode;
|
|
36
|
+
readonly device: DeviceState;
|
|
37
|
+
/** Why the server can't be used now, or null when it can. */
|
|
38
|
+
readonly serverBlocked: string | null;
|
|
39
|
+
readonly slowDeviceBudgetMs: number;
|
|
40
|
+
}): Route;
|
|
41
|
+
/**
|
|
42
|
+
* How long a server attempt may take before the device takes over: the
|
|
43
|
+
* device's own time for the same crops, within [300 ms, 3 s]. With no device
|
|
44
|
+
* engine yet there is nothing faster to fall back on, so the server gets
|
|
45
|
+
* `serverTimeoutMs`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function serverDeadline(device: DeviceState, crops: number, serverTimeoutMs: number): number;
|
|
48
|
+
/** The server route's health, per identifier. */
|
|
49
|
+
export declare class ServerHealth {
|
|
50
|
+
private refusedLease;
|
|
51
|
+
private refusedWhy;
|
|
52
|
+
private pausedUntil;
|
|
53
|
+
private pausedWhy;
|
|
54
|
+
private failures;
|
|
55
|
+
/** Why the server is skipped for `leaseId` at `now`, or null when it may be tried. */
|
|
56
|
+
blocked(leaseId: string | null, now: number, online: boolean): string | null;
|
|
57
|
+
record(answer: ServerAnswer, leaseId: string | null, now: number): void;
|
|
58
|
+
private pause;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/identify/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,MAAM,CAAA;AAChD,eAAO,MAAM,iBAAiB,MAAM,CAAA;AACpC,eAAO,MAAM,eAAe,OAAQ,CAAA;AACpC,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,OAAQ,CAAA;AAC9C,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,gBAAgB,QAAS,CAAA;AAEtC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;IAC1C,0BAA0B;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEvC,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;IACjC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;CACpC,GAAG,KAAK,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAGlG;AAED,iDAAiD;AACjD,qBAAa,YAAY;IACvB,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAI;IACvB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAI;IAEpB,sFAAsF;IACtF,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAO5E,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IA8BvE,OAAO,CAAC,KAAK;CAMd"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Phase 0: a mid-range phone without WebGPU took ~326 ms per crop on 4 WASM threads. */
|
|
2
|
+
export const DEFAULT_SLOW_DEVICE_BUDGET_MS = 300;
|
|
3
|
+
export const FALLBACK_FLOOR_MS = 300;
|
|
4
|
+
export const FALLBACK_CAP_MS = 3_000;
|
|
5
|
+
/** The server's deadline while the device has nothing to fall back on yet. */
|
|
6
|
+
export const DEFAULT_SERVER_TIMEOUT_MS = 8_000;
|
|
7
|
+
export const BREAKER_FAILURES = 3;
|
|
8
|
+
export const BREAKER_PAUSE_MS = 30_000;
|
|
9
|
+
export function chooseRoute(input) {
|
|
10
|
+
if (input.mode === 'local' || input.serverBlocked !== null)
|
|
11
|
+
return 'device';
|
|
12
|
+
if (input.mode === 'server')
|
|
13
|
+
return 'server';
|
|
14
|
+
const { ready, backend, perCropMs } = input.device;
|
|
15
|
+
if (ready && (backend === 'webgpu' || perCropMs <= input.slowDeviceBudgetMs))
|
|
16
|
+
return 'device';
|
|
17
|
+
return 'server';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How long a server attempt may take before the device takes over: the
|
|
21
|
+
* device's own time for the same crops, within [300 ms, 3 s]. With no device
|
|
22
|
+
* engine yet there is nothing faster to fall back on, so the server gets
|
|
23
|
+
* `serverTimeoutMs`.
|
|
24
|
+
*/
|
|
25
|
+
export function serverDeadline(device, crops, serverTimeoutMs) {
|
|
26
|
+
if (!device.ready || !Number.isFinite(device.perCropMs))
|
|
27
|
+
return serverTimeoutMs;
|
|
28
|
+
return Math.min(FALLBACK_CAP_MS, Math.max(FALLBACK_FLOOR_MS, device.perCropMs * crops));
|
|
29
|
+
}
|
|
30
|
+
/** The server route's health, per identifier. */
|
|
31
|
+
export class ServerHealth {
|
|
32
|
+
refusedLease = null;
|
|
33
|
+
refusedWhy = '';
|
|
34
|
+
pausedUntil = 0;
|
|
35
|
+
pausedWhy = '';
|
|
36
|
+
failures = 0;
|
|
37
|
+
/** Why the server is skipped for `leaseId` at `now`, or null when it may be tried. */
|
|
38
|
+
blocked(leaseId, now, online) {
|
|
39
|
+
if (!online)
|
|
40
|
+
return 'offline';
|
|
41
|
+
if (leaseId !== null && leaseId === this.refusedLease)
|
|
42
|
+
return `refused for this lease (${this.refusedWhy})`;
|
|
43
|
+
if (now < this.pausedUntil)
|
|
44
|
+
return `${this.pausedWhy} until ${new Date(this.pausedUntil).toISOString()}`;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
record(answer, leaseId, now) {
|
|
48
|
+
if (answer.ok) {
|
|
49
|
+
this.failures = 0;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
switch (answer.kind) {
|
|
53
|
+
case 'refused':
|
|
54
|
+
this.refusedLease = leaseId;
|
|
55
|
+
this.refusedWhy = answer.detail;
|
|
56
|
+
return;
|
|
57
|
+
case 'rate_limited':
|
|
58
|
+
this.pause(now + (answer.retryAfterMs ?? 10_000), 'rate limited');
|
|
59
|
+
return;
|
|
60
|
+
case 'timeout':
|
|
61
|
+
case 'network':
|
|
62
|
+
case 'server_error':
|
|
63
|
+
case 'malformed':
|
|
64
|
+
this.failures++;
|
|
65
|
+
if (this.failures >= BREAKER_FAILURES) {
|
|
66
|
+
this.failures = 0;
|
|
67
|
+
this.pause(now + BREAKER_PAUSE_MS, `${BREAKER_FAILURES} failures in a row`);
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
case 'aborted':
|
|
71
|
+
case 'bad_request':
|
|
72
|
+
// Neither says anything about the server's health.
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
pause(until, why) {
|
|
77
|
+
if (until > this.pausedUntil) {
|
|
78
|
+
this.pausedUntil = until;
|
|
79
|
+
this.pausedWhy = why;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/identify/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The embedder session: which execution provider, which file, and the call
|
|
3
|
+
* that turns a batch of preprocessed crops into L2-normalised CLS embeddings.
|
|
4
|
+
*
|
|
5
|
+
* Order (web-sdk-pipeline, "Execution providers are tried in a fixed order"):
|
|
6
|
+
* WebGPU through the `onnxruntime-web/webgpu` build when `requestAdapter()`
|
|
7
|
+
* returns an adapter and the model loads on it — the fp16 file, or fp32 when
|
|
8
|
+
* the adapter lacks `shader-f16`; otherwise WASM (SIMD), with threads only when
|
|
9
|
+
* the page is cross-origin isolated (SharedArrayBuffer). int8 and q4f16 files
|
|
10
|
+
* are never listed, so never loaded.
|
|
11
|
+
*/
|
|
12
|
+
import type * as Ort from 'onnxruntime-web';
|
|
13
|
+
import type { Manifest, ManifestModel, ModelPrecision } from './manifest.js';
|
|
14
|
+
export type OrtModule = typeof Ort;
|
|
15
|
+
export type OrtBuild = 'webgpu' | 'wasm';
|
|
16
|
+
export type BackendChoice = 'auto' | 'webgpu' | 'wasm';
|
|
17
|
+
export interface RuntimeOptions {
|
|
18
|
+
/** Force an execution provider. Default `auto` (WebGPU when it loads, else WASM). */
|
|
19
|
+
readonly backend?: BackendChoice;
|
|
20
|
+
/** Model file for WASM. Default `fp16` (half the download; fp32 is faster on WASM). */
|
|
21
|
+
readonly wasmPrecision?: ModelPrecision;
|
|
22
|
+
/** Where onnxruntime-web finds its `.wasm`/`.mjs` files, e.g. `https://cdn.example/ort/`. */
|
|
23
|
+
readonly wasmPaths?: string;
|
|
24
|
+
/** WASM threads when cross-origin isolated. Default min(4, hardwareConcurrency). */
|
|
25
|
+
readonly threads?: number;
|
|
26
|
+
}
|
|
27
|
+
/** Test seams: everything the runtime reads from its environment. */
|
|
28
|
+
export interface RuntimeEnv {
|
|
29
|
+
readonly loadOrt: (build: OrtBuild) => Promise<OrtModule>;
|
|
30
|
+
readonly gpu: GpuLike | undefined;
|
|
31
|
+
readonly crossOriginIsolated: boolean;
|
|
32
|
+
readonly hardwareConcurrency: number;
|
|
33
|
+
}
|
|
34
|
+
export interface GpuLike {
|
|
35
|
+
requestAdapter(options?: {
|
|
36
|
+
powerPreference?: 'low-power' | 'high-performance';
|
|
37
|
+
}): Promise<{
|
|
38
|
+
readonly features: {
|
|
39
|
+
has(name: string): boolean;
|
|
40
|
+
};
|
|
41
|
+
} | null>;
|
|
42
|
+
}
|
|
43
|
+
export interface EmbedBackend {
|
|
44
|
+
readonly backend: OrtBuild;
|
|
45
|
+
readonly precision: ModelPrecision;
|
|
46
|
+
readonly threads: number;
|
|
47
|
+
/** `n` crops' CHW tensors back to back → n × dims, L2-normalised. */
|
|
48
|
+
embed(input: Float32Array, n: number): Promise<Float32Array>;
|
|
49
|
+
release(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export interface GpuProbe {
|
|
52
|
+
readonly adapter: boolean;
|
|
53
|
+
readonly shaderF16: boolean;
|
|
54
|
+
}
|
|
55
|
+
export declare function probeWebGpu(gpu: GpuLike | undefined): Promise<GpuProbe>;
|
|
56
|
+
export interface Plan {
|
|
57
|
+
readonly backend: OrtBuild;
|
|
58
|
+
readonly model: ManifestModel;
|
|
59
|
+
}
|
|
60
|
+
/** The (backend, file) pairs to try, best first. Pure, so every path is testable. */
|
|
61
|
+
export declare function planBackends(probe: GpuProbe, models: readonly ManifestModel[], options?: RuntimeOptions): Plan[];
|
|
62
|
+
export declare function wasmThreads(env: Pick<RuntimeEnv, 'crossOriginIsolated' | 'hardwareConcurrency'>, options?: RuntimeOptions): number;
|
|
63
|
+
export declare function defaultRuntimeEnv(): RuntimeEnv;
|
|
64
|
+
/**
|
|
65
|
+
* Opens a session on the first plan that loads. `modelBytes` fetches (and
|
|
66
|
+
* verifies) a plan's file; it is called only for the plans actually tried.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createEmbedBackend(manifest: Manifest, modelBytes: (model: ManifestModel) => Promise<ArrayBuffer>, options: RuntimeOptions, env: RuntimeEnv): Promise<EmbedBackend>;
|
|
69
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/identify/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAA;AAE3C,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAG5E,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,CAAA;AAClC,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AACxC,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEtD,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAA;IAChC,uFAAuF;IACvF,QAAQ,CAAC,aAAa,CAAC,EAAE,cAAc,CAAA;IACvC,6FAA6F;IAC7F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,SAAS,CAAC,CAAA;IACzD,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;CACrC;AAED,MAAM,WAAW,OAAO;IACtB,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,WAAW,GAAG,kBAAkB,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,QAAQ,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;CACxJ;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,qEAAqE;IACrE,KAAK,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC5D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAID,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkB7E;AAED,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAC9B;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,aAAa,EAAE,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI,EAAE,CAcpH;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,qBAAqB,GAAG,qBAAqB,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,CAItI;AAED,wBAAgB,iBAAiB,IAAI,UAAU,CAS9C;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,WAAW,CAAC,EAC1D,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,YAAY,CAAC,CAevB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ScannerError } from '../errors.js';
|
|
2
|
+
import { l2NormaliseRows } from './search.js';
|
|
3
|
+
const ADAPTER_TIMEOUT_MS = 5_000;
|
|
4
|
+
export async function probeWebGpu(gpu) {
|
|
5
|
+
if (!gpu || typeof gpu.requestAdapter !== 'function')
|
|
6
|
+
return { adapter: false, shaderF16: false };
|
|
7
|
+
let timer;
|
|
8
|
+
try {
|
|
9
|
+
// Bounded: a driver that never answers counts as no adapter.
|
|
10
|
+
const adapter = await Promise.race([
|
|
11
|
+
gpu.requestAdapter({ powerPreference: 'high-performance' }),
|
|
12
|
+
new Promise((resolve) => {
|
|
13
|
+
timer = setTimeout(() => resolve(null), ADAPTER_TIMEOUT_MS);
|
|
14
|
+
}),
|
|
15
|
+
]);
|
|
16
|
+
if (!adapter)
|
|
17
|
+
return { adapter: false, shaderF16: false };
|
|
18
|
+
return { adapter: true, shaderF16: adapter.features.has('shader-f16') };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { adapter: false, shaderF16: false };
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** The (backend, file) pairs to try, best first. Pure, so every path is testable. */
|
|
28
|
+
export function planBackends(probe, models, options = {}) {
|
|
29
|
+
const by = (p) => models.find((m) => m.precision === p);
|
|
30
|
+
const plans = [];
|
|
31
|
+
const choice = options.backend ?? 'auto';
|
|
32
|
+
if (choice !== 'wasm' && probe.adapter) {
|
|
33
|
+
const gpuModel = probe.shaderF16 ? (by('fp16') ?? by('fp32')) : by('fp32');
|
|
34
|
+
if (gpuModel)
|
|
35
|
+
plans.push({ backend: 'webgpu', model: gpuModel });
|
|
36
|
+
}
|
|
37
|
+
if (choice !== 'webgpu') {
|
|
38
|
+
const wanted = options.wasmPrecision ?? 'fp16';
|
|
39
|
+
const wasmModel = by(wanted) ?? by(wanted === 'fp16' ? 'fp32' : 'fp16');
|
|
40
|
+
if (wasmModel)
|
|
41
|
+
plans.push({ backend: 'wasm', model: wasmModel });
|
|
42
|
+
}
|
|
43
|
+
return plans;
|
|
44
|
+
}
|
|
45
|
+
export function wasmThreads(env, options = {}) {
|
|
46
|
+
if (!env.crossOriginIsolated)
|
|
47
|
+
return 1;
|
|
48
|
+
const cap = options.threads ?? 4;
|
|
49
|
+
return Math.max(1, Math.min(cap, env.hardwareConcurrency || 1));
|
|
50
|
+
}
|
|
51
|
+
export function defaultRuntimeEnv() {
|
|
52
|
+
const nav = globalThis.navigator;
|
|
53
|
+
return {
|
|
54
|
+
// Two builds, loaded only when used: the WebGPU one carries the larger WASM.
|
|
55
|
+
loadOrt: (build) => (build === 'webgpu' ? import('onnxruntime-web/webgpu') : import('onnxruntime-web/wasm')),
|
|
56
|
+
gpu: nav?.gpu,
|
|
57
|
+
crossOriginIsolated: globalThis.crossOriginIsolated === true,
|
|
58
|
+
hardwareConcurrency: nav?.hardwareConcurrency ?? 1,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Opens a session on the first plan that loads. `modelBytes` fetches (and
|
|
63
|
+
* verifies) a plan's file; it is called only for the plans actually tried.
|
|
64
|
+
*/
|
|
65
|
+
export async function createEmbedBackend(manifest, modelBytes, options, env) {
|
|
66
|
+
const probe = await probeWebGpu(options.backend === 'wasm' ? undefined : env.gpu);
|
|
67
|
+
const plans = planBackends(probe, manifest.models, options);
|
|
68
|
+
if (plans.length === 0)
|
|
69
|
+
throw new ScannerError('UNSUPPORTED_DEVICE', 'no model file fits this device');
|
|
70
|
+
const failures = [];
|
|
71
|
+
for (const plan of plans) {
|
|
72
|
+
try {
|
|
73
|
+
return await openSession(plan, manifest, await modelBytes(plan.model), options, env);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
// A download or checksum failure is not the device's fault: report it as is.
|
|
77
|
+
if (err instanceof ScannerError && (err.code === 'NETWORK' || err.code === 'MODEL_INTEGRITY'))
|
|
78
|
+
throw err;
|
|
79
|
+
failures.push(`${plan.backend}/${plan.model.precision}: ${err instanceof Error ? err.message : String(err)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw new ScannerError('UNSUPPORTED_DEVICE', `no execution provider loaded the model (${failures.join('; ')})`);
|
|
83
|
+
}
|
|
84
|
+
async function openSession(plan, manifest, bytes, options, env) {
|
|
85
|
+
const ort = await env.loadOrt(plan.backend);
|
|
86
|
+
const threads = plan.backend === 'wasm' ? wasmThreads(env, options) : 1;
|
|
87
|
+
ort.env.wasm.numThreads = threads;
|
|
88
|
+
ort.env.wasm.proxy = false;
|
|
89
|
+
if (options.wasmPaths !== undefined)
|
|
90
|
+
ort.env.wasm.wasmPaths = options.wasmPaths;
|
|
91
|
+
ort.env.logLevel = 'error';
|
|
92
|
+
if (plan.backend === 'webgpu')
|
|
93
|
+
ort.env.webgpu.powerPreference = 'high-performance';
|
|
94
|
+
const session = await ort.InferenceSession.create(new Uint8Array(bytes), {
|
|
95
|
+
executionProviders: [plan.backend],
|
|
96
|
+
graphOptimizationLevel: 'all',
|
|
97
|
+
});
|
|
98
|
+
const { input, output, token } = manifest.embedding;
|
|
99
|
+
if (!session.inputNames.includes(input) || !session.outputNames.includes(output)) {
|
|
100
|
+
await session.release();
|
|
101
|
+
throw new ScannerError('MODEL_INTEGRITY', `model has no ${input} → ${output}`);
|
|
102
|
+
}
|
|
103
|
+
const { width, height } = manifest.preprocessing;
|
|
104
|
+
const dims = manifest.dims;
|
|
105
|
+
const plane = 3 * width * height;
|
|
106
|
+
return {
|
|
107
|
+
backend: plan.backend,
|
|
108
|
+
precision: plan.model.precision,
|
|
109
|
+
threads,
|
|
110
|
+
async embed(data, n) {
|
|
111
|
+
if (data.length < n * plane)
|
|
112
|
+
throw new RangeError('input is shorter than n crops');
|
|
113
|
+
const tensor = new ort.Tensor('float32', data.subarray(0, n * plane), [n, 3, height, width]);
|
|
114
|
+
let hidden;
|
|
115
|
+
try {
|
|
116
|
+
const out = await session.run({ [input]: tensor });
|
|
117
|
+
hidden = out[output];
|
|
118
|
+
if (!hidden)
|
|
119
|
+
throw new ScannerError('MODEL_INTEGRITY', `model returned no ${output}`);
|
|
120
|
+
const [bn, tokens, d] = hidden.dims;
|
|
121
|
+
if (bn !== n || d !== dims || tokens === undefined || token >= tokens) {
|
|
122
|
+
throw new ScannerError('MODEL_INTEGRITY', `model output ${hidden.dims.join('x')} does not match the manifest`);
|
|
123
|
+
}
|
|
124
|
+
const values = (await hidden.getData());
|
|
125
|
+
const result = new Float32Array(n * dims);
|
|
126
|
+
for (let i = 0; i < n; i++) {
|
|
127
|
+
const from = (i * tokens + token) * dims;
|
|
128
|
+
result.set(values.subarray(from, from + dims), i * dims);
|
|
129
|
+
}
|
|
130
|
+
return l2NormaliseRows(result, dims);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
tensor.dispose();
|
|
134
|
+
hidden?.dispose();
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
release: () => session.release(),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/identify/runtime.ts"],"names":[],"mappings":"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"}
|