@mauriciobenjamin700/ort-vision-sdk-web 0.3.0 → 0.5.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.
- package/CHANGELOG.md +104 -0
- package/README.md +9 -0
- package/dist/core/graph.d.ts +81 -0
- package/dist/core/graph.d.ts.map +1 -0
- package/dist/core/graph.js +102 -0
- package/dist/core/graph.js.map +1 -0
- package/dist/core/metadata.d.ts +39 -0
- package/dist/core/metadata.d.ts.map +1 -0
- package/dist/core/metadata.js +233 -0
- package/dist/core/metadata.js.map +1 -0
- package/dist/core/session.d.ts +64 -4
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +98 -11
- package/dist/core/session.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/tasks/classifier.d.ts +29 -6
- package/dist/tasks/classifier.d.ts.map +1 -1
- package/dist/tasks/classifier.js +22 -3
- package/dist/tasks/classifier.js.map +1 -1
- package/dist/tasks/detector.d.ts +15 -1
- package/dist/tasks/detector.d.ts.map +1 -1
- package/dist/tasks/detector.js +19 -3
- package/dist/tasks/detector.js.map +1 -1
- package/dist/tasks/segmenter.d.ts +15 -1
- package/dist/tasks/segmenter.d.ts.map +1 -1
- package/dist/tasks/segmenter.js +19 -3
- package/dist/tasks/segmenter.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,101 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-08-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Tasks read their class names off the model, matching the Python SDK.**
|
|
15
|
+
`labels` is now optional on all three tasks: when omitted, the names the
|
|
16
|
+
exporter baked into the model are used (Ultralytics writes them as `names` in
|
|
17
|
+
the metadata map), and only a model carrying none falls back to the COCO
|
|
18
|
+
preset for detection/segmentation or generated `class_<id>` labels for
|
|
19
|
+
classification. Passing `labels` still wins, for a model whose names are
|
|
20
|
+
wrong or absent.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
const det = await Detector.create("/models/detect.onnx");
|
|
24
|
+
console.log(det.labels); // ["ocular-mucosa"] — from the model, not a preset
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This also fixes a trap: a single-class detector used to **fail** without an
|
|
28
|
+
explicit `labels`, because the 80-name COCO default disagreed with its class
|
|
29
|
+
count.
|
|
30
|
+
|
|
31
|
+
- **`numClasses` is inferred from the declared output shape.** A YOLO head
|
|
32
|
+
declares `(B, 4 + nc, N)` and a classifier `(B, nc)`, so the count no longer
|
|
33
|
+
has to be supplied. Passing it still validates the labels against the model.
|
|
34
|
+
|
|
35
|
+
- **`OrtSession.metadata`** exposes the model's custom metadata map (`names`,
|
|
36
|
+
`task`, `imgsz`, ...). `onnxruntime-web` does not surface that map — unlike
|
|
37
|
+
Python's `custom_metadata_map` — so it is read from the model's own bytes at
|
|
38
|
+
load time by walking `metadata_props` in the ModelProto. A truncated or
|
|
39
|
+
unexpected file yields an empty map instead of an error, and every caller
|
|
40
|
+
falls back to what it was given.
|
|
41
|
+
|
|
42
|
+
- **`OrtSession.outputShapes` / `.outputShape`**, the shapes the graph declares
|
|
43
|
+
for its outputs, with dynamic axes as `null` — the same treatment
|
|
44
|
+
`inputShapes` already got.
|
|
45
|
+
|
|
46
|
+
- **`detectionNumClasses`, `classificationNumClasses`, `readModelMetadata`,
|
|
47
|
+
`modelNames`**: the pure helpers behind the above, exported for anyone
|
|
48
|
+
assembling their own pipeline.
|
|
49
|
+
|
|
50
|
+
### Changed
|
|
51
|
+
|
|
52
|
+
- **A URL model is fetched by the SDK instead of by ORT**, so its bytes are
|
|
53
|
+
available to read metadata from. It is the same single download, and
|
|
54
|
+
`readMetadata: false` on the session options restores the previous path (ORT
|
|
55
|
+
fetches the URL, `metadata` stays empty). A failed fetch falls back to handing
|
|
56
|
+
ORT the URL, so a model that ORT could load still loads.
|
|
57
|
+
|
|
58
|
+
## [0.4.0] - 2026-08-03
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- **Tasks read their input resolution off the model instead of trusting
|
|
63
|
+
configuration.** `Classifier`, `Detector` and `Segmenter` now ask the ONNX
|
|
64
|
+
graph what shape it declares and preprocess to that. The resolution a session
|
|
65
|
+
must be fed at is a property of the export — feeding a 640x640 tensor to a
|
|
66
|
+
graph exported at 224x224 makes ORT abort mid-run with:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
Inference failed: failed to call OrtRun(). ERROR_CODE: 2, ERROR_MESSAGE: Got
|
|
70
|
+
invalid dimensions for input: images for the following indices index: 2
|
|
71
|
+
Got: 640 Expected: 224
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A caller had no way to see that coming: the number lives in the file. So it is
|
|
75
|
+
read from there now.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// An Ultralytics -cls export is 224; nothing to configure, nothing to get wrong
|
|
79
|
+
const clf = await Classifier.create("/models/classify.onnx", { labels: LABELS });
|
|
80
|
+
console.log(clf.inputSize); // [224, 224]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`inputSize` is now a *fallback*, used only when the graph leaves its spatial
|
|
84
|
+
axes dynamic. Passing a size that contradicts a static graph logs a warning
|
|
85
|
+
and the graph wins — honoring the caller there would only turn a fixable
|
|
86
|
+
mismatch into a failed run.
|
|
87
|
+
|
|
88
|
+
- **`inputSize` getter on every task**, so callers can read back the resolution
|
|
89
|
+
inference actually runs at rather than the one they asked for.
|
|
90
|
+
|
|
91
|
+
- **`OrtSession.inputShapes` / `OrtSession.inputShape`** expose what the graph
|
|
92
|
+
declares, with dynamic axes as `null`. Empty when the runtime reports no
|
|
93
|
+
metadata (`onnxruntime-web` older than 1.21).
|
|
94
|
+
|
|
95
|
+
- **`OrtSession.release()`** frees the native session. Needed whenever a session
|
|
96
|
+
is discarded while the page lives on — rebuilding a task at a different input
|
|
97
|
+
size, swapping in a newer model — which previously required reaching into
|
|
98
|
+
`session.raw`.
|
|
99
|
+
|
|
100
|
+
- **`declaredShapesFrom`, `spatialInputSize`, `resolveInputSize`** (plus the
|
|
101
|
+
`DeclaredShape` / `DeclaredDim` types): the pure helpers behind the above,
|
|
102
|
+
exported so callers building their own pipeline can reuse the same precedence
|
|
103
|
+
rules without importing `onnxruntime-web` types themselves.
|
|
104
|
+
|
|
10
105
|
## [0.3.0] - 2026-08-02
|
|
11
106
|
|
|
12
107
|
### Added
|
|
@@ -46,6 +141,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
46
141
|
would break `tempest-react-sdk`, which re-exports all three from its vendored
|
|
47
142
|
copy. The warning now points at `0.4.0`.
|
|
48
143
|
|
|
144
|
+
## [0.2.2] - 2026-05-31
|
|
145
|
+
|
|
146
|
+
### Changed
|
|
147
|
+
|
|
148
|
+
- README now links to the bilingual (PT-BR + EN-US) documentation site on
|
|
149
|
+
GitHub Pages (<https://mauriciobenjamin700.github.io/ort-vision-sdk/>) so the
|
|
150
|
+
npm package page points readers to the full guide and API reference.
|
|
151
|
+
Documentation-only release; no public API changes.
|
|
152
|
+
|
|
49
153
|
## [0.2.1] - 2026-05-03
|
|
50
154
|
|
|
51
155
|
First **published** release on npm. The previous `0.2.0` tag never produced a
|
package/README.md
CHANGED
|
@@ -37,8 +37,17 @@ const result = await clf.predict("/images/dog.jpg", { topK: 5 });
|
|
|
37
37
|
console.log(result.className, result.confidence);
|
|
38
38
|
console.log(result.probabilities);
|
|
39
39
|
// result.image is an RGBImage (HWC RGB Uint8Array) — the original input.
|
|
40
|
+
console.log(clf.inputSize);
|
|
41
|
+
// [224, 224] — read from the .onnx graph, not configured.
|
|
40
42
|
```
|
|
41
43
|
|
|
44
|
+
> **The model decides its input size.** `inputSize` is optional: the resolution
|
|
45
|
+
> the graph declares always wins, because it is the only shape ONNX Runtime will
|
|
46
|
+
> accept. An Ultralytics `-cls` export is 224x224 while a detector is 640x640 —
|
|
47
|
+
> get it wrong and ORT aborts with `Got invalid dimensions for input: images`.
|
|
48
|
+
> Read it back with `task.inputSize`, or inspect `session.inputShape`. See
|
|
49
|
+
> [O modelo manda / The model decides](https://mauriciobenjamin700.github.io/ort-vision-sdk/en/guia/modelo/).
|
|
50
|
+
|
|
42
51
|
### Object detection
|
|
43
52
|
|
|
44
53
|
```typescript
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the ONNX graph itself says about its inputs.
|
|
3
|
+
*
|
|
4
|
+
* The resolution a session must be fed at is a property of the exported model,
|
|
5
|
+
* not of the configuration around it. Feeding a 640x640 tensor to a graph
|
|
6
|
+
* exported at 224x224 makes ORT abort the run with
|
|
7
|
+
* `Got invalid dimensions for input: images ... Got: 640 Expected: 224`, and the
|
|
8
|
+
* caller has no way to see that coming from the outside — the number lives in
|
|
9
|
+
* the file. So the SDK reads it from the graph and treats any configured size as
|
|
10
|
+
* a fallback for when the graph leaves it open.
|
|
11
|
+
*/
|
|
12
|
+
import type * as ort from "onnxruntime-web";
|
|
13
|
+
/**
|
|
14
|
+
* One declared dimension: a number when the graph pins it, `null` when the
|
|
15
|
+
* dimension is symbolic (dynamic).
|
|
16
|
+
*/
|
|
17
|
+
export type DeclaredDim = number | null;
|
|
18
|
+
/** A declared input/output shape, dynamic axes appearing as `null`. */
|
|
19
|
+
export type DeclaredShape = readonly DeclaredDim[];
|
|
20
|
+
/**
|
|
21
|
+
* Convert ORT value metadata into declared shapes.
|
|
22
|
+
*
|
|
23
|
+
* @param metadata Metadata as reported by `InferenceSession.inputMetadata`, or
|
|
24
|
+
* `undefined` on ORT builds that predate it (added in onnxruntime 1.21).
|
|
25
|
+
* @returns One shape per value, in declaration order. Non-tensor values and
|
|
26
|
+
* builds without metadata yield empty shapes, which read as "nothing
|
|
27
|
+
* declared" everywhere downstream.
|
|
28
|
+
*/
|
|
29
|
+
export declare function declaredShapesFrom(metadata: readonly ort.InferenceSession.ValueMetadata[] | undefined): readonly DeclaredShape[];
|
|
30
|
+
/**
|
|
31
|
+
* Read the spatial input size out of a declared NCHW shape.
|
|
32
|
+
*
|
|
33
|
+
* @param shape The declared shape of the model's image input.
|
|
34
|
+
* @returns `[width, height]` in pixels, or `null` when the shape is not 4D or
|
|
35
|
+
* leaves either spatial axis dynamic — in which case the model accepts more
|
|
36
|
+
* than one resolution and there is nothing to correct.
|
|
37
|
+
*/
|
|
38
|
+
export declare function spatialInputSize(shape: DeclaredShape): readonly [number, number] | null;
|
|
39
|
+
/**
|
|
40
|
+
* Infer how many classes a YOLO detection/segmentation head emits.
|
|
41
|
+
*
|
|
42
|
+
* Such a head declares `(B, 4 + nc, N)` — four box coordinates stacked above one
|
|
43
|
+
* score per class, over `N` candidate anchors. `N` is in the thousands and the
|
|
44
|
+
* batch is 1, so the channel axis is the smallest static axis above 1.
|
|
45
|
+
*
|
|
46
|
+
* @param shape Declared shape of the model's first output.
|
|
47
|
+
* @returns The class count, or `null` when the shape leaves it undeterminable —
|
|
48
|
+
* fully dynamic, or too small to hold boxes plus at least one class.
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectionNumClasses(shape: DeclaredShape): number | null;
|
|
51
|
+
/**
|
|
52
|
+
* Infer how many classes a classification head emits.
|
|
53
|
+
*
|
|
54
|
+
* A classifier declares `(B, nc)`, so the count is the last static axis.
|
|
55
|
+
*
|
|
56
|
+
* @param shape Declared shape of the model's first output.
|
|
57
|
+
* @returns The class count, or `null` when the last axis is dynamic or absent.
|
|
58
|
+
*/
|
|
59
|
+
export declare function classificationNumClasses(shape: DeclaredShape): number | null;
|
|
60
|
+
export interface ResolveInputSizeOptions {
|
|
61
|
+
/** Declared shape of the model's image input, from {@link declaredShapesFrom}. */
|
|
62
|
+
readonly graphShape?: DeclaredShape;
|
|
63
|
+
/** Size the caller asked for, if any. */
|
|
64
|
+
readonly requested?: readonly [number, number];
|
|
65
|
+
/** Size to use when neither the graph nor the caller pins one. */
|
|
66
|
+
readonly fallback: readonly [number, number];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Decide the input size a task will preprocess to.
|
|
70
|
+
*
|
|
71
|
+
* Precedence is graph → caller → fallback. The graph wins over an explicit
|
|
72
|
+
* `inputSize` because a static shape is not a preference, it is what ORT will
|
|
73
|
+
* accept: honoring the caller there would only turn a fixable mismatch into a
|
|
74
|
+
* failed run. A disagreement is a configuration bug in the caller, so it is
|
|
75
|
+
* reported through `console.warn` instead of being swallowed.
|
|
76
|
+
*
|
|
77
|
+
* @param options Graph shape, requested size and per-task fallback.
|
|
78
|
+
* @returns The `[width, height]` to preprocess to.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveInputSize(options: ResolveInputSizeOptions): readonly [number, number];
|
|
81
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/core/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,IAAI,CAAC;AAExC,uEAAuE;AACvE,MAAM,MAAM,aAAa,GAAG,SAAS,WAAW,EAAE,CAAC;AAEnD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,SAAS,GAClE,SAAS,aAAa,EAAE,CAO1B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAMvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAMvE;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAI5E;AAED,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;IACpC,yCAAyC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAW5F"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the ONNX graph itself says about its inputs.
|
|
3
|
+
*
|
|
4
|
+
* The resolution a session must be fed at is a property of the exported model,
|
|
5
|
+
* not of the configuration around it. Feeding a 640x640 tensor to a graph
|
|
6
|
+
* exported at 224x224 makes ORT abort the run with
|
|
7
|
+
* `Got invalid dimensions for input: images ... Got: 640 Expected: 224`, and the
|
|
8
|
+
* caller has no way to see that coming from the outside — the number lives in
|
|
9
|
+
* the file. So the SDK reads it from the graph and treats any configured size as
|
|
10
|
+
* a fallback for when the graph leaves it open.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Convert ORT value metadata into declared shapes.
|
|
14
|
+
*
|
|
15
|
+
* @param metadata Metadata as reported by `InferenceSession.inputMetadata`, or
|
|
16
|
+
* `undefined` on ORT builds that predate it (added in onnxruntime 1.21).
|
|
17
|
+
* @returns One shape per value, in declaration order. Non-tensor values and
|
|
18
|
+
* builds without metadata yield empty shapes, which read as "nothing
|
|
19
|
+
* declared" everywhere downstream.
|
|
20
|
+
*/
|
|
21
|
+
export function declaredShapesFrom(metadata) {
|
|
22
|
+
if (metadata === undefined)
|
|
23
|
+
return [];
|
|
24
|
+
return metadata.map((value) => value.isTensor
|
|
25
|
+
? value.shape.map((dim) => (typeof dim === "number" && Number.isInteger(dim) && dim > 0 ? dim : null))
|
|
26
|
+
: []);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read the spatial input size out of a declared NCHW shape.
|
|
30
|
+
*
|
|
31
|
+
* @param shape The declared shape of the model's image input.
|
|
32
|
+
* @returns `[width, height]` in pixels, or `null` when the shape is not 4D or
|
|
33
|
+
* leaves either spatial axis dynamic — in which case the model accepts more
|
|
34
|
+
* than one resolution and there is nothing to correct.
|
|
35
|
+
*/
|
|
36
|
+
export function spatialInputSize(shape) {
|
|
37
|
+
if (shape.length !== 4)
|
|
38
|
+
return null;
|
|
39
|
+
const height = shape[2];
|
|
40
|
+
const width = shape[3];
|
|
41
|
+
if (height === null || height === undefined || width === null || width === undefined)
|
|
42
|
+
return null;
|
|
43
|
+
return [width, height];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Infer how many classes a YOLO detection/segmentation head emits.
|
|
47
|
+
*
|
|
48
|
+
* Such a head declares `(B, 4 + nc, N)` — four box coordinates stacked above one
|
|
49
|
+
* score per class, over `N` candidate anchors. `N` is in the thousands and the
|
|
50
|
+
* batch is 1, so the channel axis is the smallest static axis above 1.
|
|
51
|
+
*
|
|
52
|
+
* @param shape Declared shape of the model's first output.
|
|
53
|
+
* @returns The class count, or `null` when the shape leaves it undeterminable —
|
|
54
|
+
* fully dynamic, or too small to hold boxes plus at least one class.
|
|
55
|
+
*/
|
|
56
|
+
export function detectionNumClasses(shape) {
|
|
57
|
+
const staticDims = shape.filter((dim) => dim !== null && dim > 1);
|
|
58
|
+
if (staticDims.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
const channels = Math.min(...staticDims);
|
|
61
|
+
if (channels < 5)
|
|
62
|
+
return null;
|
|
63
|
+
return channels - 4;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Infer how many classes a classification head emits.
|
|
67
|
+
*
|
|
68
|
+
* A classifier declares `(B, nc)`, so the count is the last static axis.
|
|
69
|
+
*
|
|
70
|
+
* @param shape Declared shape of the model's first output.
|
|
71
|
+
* @returns The class count, or `null` when the last axis is dynamic or absent.
|
|
72
|
+
*/
|
|
73
|
+
export function classificationNumClasses(shape) {
|
|
74
|
+
const last = shape[shape.length - 1];
|
|
75
|
+
if (last === null || last === undefined || last < 1)
|
|
76
|
+
return null;
|
|
77
|
+
return last;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Decide the input size a task will preprocess to.
|
|
81
|
+
*
|
|
82
|
+
* Precedence is graph → caller → fallback. The graph wins over an explicit
|
|
83
|
+
* `inputSize` because a static shape is not a preference, it is what ORT will
|
|
84
|
+
* accept: honoring the caller there would only turn a fixable mismatch into a
|
|
85
|
+
* failed run. A disagreement is a configuration bug in the caller, so it is
|
|
86
|
+
* reported through `console.warn` instead of being swallowed.
|
|
87
|
+
*
|
|
88
|
+
* @param options Graph shape, requested size and per-task fallback.
|
|
89
|
+
* @returns The `[width, height]` to preprocess to.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveInputSize(options) {
|
|
92
|
+
const graph = options.graphShape === undefined ? null : spatialInputSize(options.graphShape);
|
|
93
|
+
const requested = options.requested;
|
|
94
|
+
if (graph === null)
|
|
95
|
+
return requested ?? options.fallback;
|
|
96
|
+
if (requested !== undefined && (requested[0] !== graph[0] || requested[1] !== graph[1])) {
|
|
97
|
+
console.warn(`[ort-vision-sdk] The model declares a ${graph[0]}x${graph[1]} input; ` +
|
|
98
|
+
`ignoring the requested ${requested[0]}x${requested[1]}, which ONNX Runtime would reject.`);
|
|
99
|
+
}
|
|
100
|
+
return graph;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/core/graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the metadata an exporter baked into a `.onnx` file.
|
|
3
|
+
*
|
|
4
|
+
* `onnxruntime-web` exposes input/output metadata but **not** the model's
|
|
5
|
+
* custom metadata map, which is where Ultralytics writes `names`, `task` and
|
|
6
|
+
* `imgsz`. The Python SDK gets it for free from
|
|
7
|
+
* `InferenceSession.get_modelmeta().custom_metadata_map`; in the browser the
|
|
8
|
+
* only way to the same information is to read it out of the file, so this
|
|
9
|
+
* module walks just enough of the ModelProto wire format to collect
|
|
10
|
+
* `metadata_props`.
|
|
11
|
+
*
|
|
12
|
+
* It never throws and never allocates unbounded: a truncated, hostile or
|
|
13
|
+
* simply unexpected file yields an empty map, and every caller treats that as
|
|
14
|
+
* "the model says nothing", falling back to what it was given.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Collect a model's custom metadata map straight out of its bytes.
|
|
18
|
+
*
|
|
19
|
+
* @param model The `.onnx` file contents.
|
|
20
|
+
* @returns Key/value metadata — `names`, `task`, `imgsz`, ... for an
|
|
21
|
+
* Ultralytics export — or an empty object when the file carries none or
|
|
22
|
+
* cannot be walked.
|
|
23
|
+
*/
|
|
24
|
+
export declare function readModelMetadata(model: Uint8Array | ArrayBufferLike): Readonly<Record<string, string>>;
|
|
25
|
+
/**
|
|
26
|
+
* Read the class names an export baked into the model metadata.
|
|
27
|
+
*
|
|
28
|
+
* Ultralytics writes `names` as the Python `repr` of a `dict[int, str]` — e.g.
|
|
29
|
+
* `"{0: 'deworm', 1: 'not_deworm'}"`. The value is parsed structurally (never
|
|
30
|
+
* evaluated), and anything unparseable, non-`dict`, or not keyed by contiguous
|
|
31
|
+
* integers from zero is rejected whole rather than half-applied: a partial name
|
|
32
|
+
* map would silently mislabel predictions.
|
|
33
|
+
*
|
|
34
|
+
* @param metadata A model's custom metadata map.
|
|
35
|
+
* @returns Class names in class-id order, or `null` when the model carries no
|
|
36
|
+
* usable `names` entry.
|
|
37
|
+
*/
|
|
38
|
+
export declare function modelNames(metadata: Readonly<Record<string, string>> | undefined): readonly string[] | null;
|
|
39
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyIH;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,GAAG,eAAe,GAClC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAqBlC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GACrD,SAAS,MAAM,EAAE,GAAG,IAAI,CA6B1B"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the metadata an exporter baked into a `.onnx` file.
|
|
3
|
+
*
|
|
4
|
+
* `onnxruntime-web` exposes input/output metadata but **not** the model's
|
|
5
|
+
* custom metadata map, which is where Ultralytics writes `names`, `task` and
|
|
6
|
+
* `imgsz`. The Python SDK gets it for free from
|
|
7
|
+
* `InferenceSession.get_modelmeta().custom_metadata_map`; in the browser the
|
|
8
|
+
* only way to the same information is to read it out of the file, so this
|
|
9
|
+
* module walks just enough of the ModelProto wire format to collect
|
|
10
|
+
* `metadata_props`.
|
|
11
|
+
*
|
|
12
|
+
* It never throws and never allocates unbounded: a truncated, hostile or
|
|
13
|
+
* simply unexpected file yields an empty map, and every caller treats that as
|
|
14
|
+
* "the model says nothing", falling back to what it was given.
|
|
15
|
+
*/
|
|
16
|
+
/** Field number of `metadata_props` in `ModelProto` (repeated StringStringEntryProto). */
|
|
17
|
+
const MODEL_METADATA_PROPS_FIELD = 14;
|
|
18
|
+
/** Field numbers of `key` and `value` in `StringStringEntryProto`. */
|
|
19
|
+
const ENTRY_KEY_FIELD = 1;
|
|
20
|
+
const ENTRY_VALUE_FIELD = 2;
|
|
21
|
+
/** Protobuf wire types this reader understands. */
|
|
22
|
+
const WIRE_VARINT = 0;
|
|
23
|
+
const WIRE_FIXED64 = 1;
|
|
24
|
+
const WIRE_LENGTH_DELIMITED = 2;
|
|
25
|
+
const WIRE_FIXED32 = 5;
|
|
26
|
+
/**
|
|
27
|
+
* Hard ceiling on a single length-delimited field, as a guard against a corrupt
|
|
28
|
+
* length turning into a huge slice. Model metadata values are strings — a class
|
|
29
|
+
* name map for thousands of classes still fits well inside this.
|
|
30
|
+
*/
|
|
31
|
+
const MAX_FIELD_BYTES = 1 << 20;
|
|
32
|
+
/**
|
|
33
|
+
* Read a base-128 varint.
|
|
34
|
+
*
|
|
35
|
+
* @param cursor Cursor to advance.
|
|
36
|
+
* @returns The value, or `null` when the varint is truncated or overlong
|
|
37
|
+
* (beyond the 64-bit range protobuf allows).
|
|
38
|
+
*/
|
|
39
|
+
function readVarint(cursor) {
|
|
40
|
+
let result = 0;
|
|
41
|
+
let shift = 0;
|
|
42
|
+
while (cursor.pos < cursor.end) {
|
|
43
|
+
const byte = cursor.bytes[cursor.pos];
|
|
44
|
+
cursor.pos += 1;
|
|
45
|
+
result += (byte & 0x7f) * 2 ** shift;
|
|
46
|
+
if ((byte & 0x80) === 0)
|
|
47
|
+
return result;
|
|
48
|
+
shift += 7;
|
|
49
|
+
if (shift > 63)
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Skip a field whose contents are not needed.
|
|
56
|
+
*
|
|
57
|
+
* @param cursor Cursor to advance past the field's payload.
|
|
58
|
+
* @param wireType Wire type read from the field's tag.
|
|
59
|
+
* @returns `true` when the field was skipped, `false` when the stream is
|
|
60
|
+
* unreadable from here (unknown wire type or truncated payload).
|
|
61
|
+
*/
|
|
62
|
+
function skipField(cursor, wireType) {
|
|
63
|
+
switch (wireType) {
|
|
64
|
+
case WIRE_VARINT:
|
|
65
|
+
return readVarint(cursor) !== null;
|
|
66
|
+
case WIRE_FIXED64:
|
|
67
|
+
cursor.pos += 8;
|
|
68
|
+
return cursor.pos <= cursor.end;
|
|
69
|
+
case WIRE_LENGTH_DELIMITED: {
|
|
70
|
+
const length = readVarint(cursor);
|
|
71
|
+
if (length === null)
|
|
72
|
+
return false;
|
|
73
|
+
cursor.pos += length;
|
|
74
|
+
return cursor.pos <= cursor.end;
|
|
75
|
+
}
|
|
76
|
+
case WIRE_FIXED32:
|
|
77
|
+
cursor.pos += 4;
|
|
78
|
+
return cursor.pos <= cursor.end;
|
|
79
|
+
default:
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read a length-delimited payload as a byte range.
|
|
85
|
+
*
|
|
86
|
+
* @param cursor Cursor to advance past the payload.
|
|
87
|
+
* @returns Start and end offsets of the payload, or `null` when the length is
|
|
88
|
+
* truncated, overruns the buffer, or exceeds {@link MAX_FIELD_BYTES}.
|
|
89
|
+
*/
|
|
90
|
+
function readLengthDelimited(cursor) {
|
|
91
|
+
const length = readVarint(cursor);
|
|
92
|
+
if (length === null || length > MAX_FIELD_BYTES)
|
|
93
|
+
return null;
|
|
94
|
+
const start = cursor.pos;
|
|
95
|
+
const end = start + length;
|
|
96
|
+
if (end > cursor.end)
|
|
97
|
+
return null;
|
|
98
|
+
cursor.pos = end;
|
|
99
|
+
return { start, end };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Decode one `StringStringEntryProto` into a key/value pair.
|
|
103
|
+
*
|
|
104
|
+
* @param bytes The model buffer.
|
|
105
|
+
* @param start Offset the entry's payload starts at.
|
|
106
|
+
* @param end Offset the entry's payload ends at.
|
|
107
|
+
* @returns The pair, or `null` when either half is missing or undecodable.
|
|
108
|
+
*/
|
|
109
|
+
function readEntry(bytes, start, end) {
|
|
110
|
+
const cursor = { bytes, end, pos: start };
|
|
111
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
112
|
+
let key = null;
|
|
113
|
+
let value = null;
|
|
114
|
+
while (cursor.pos < end) {
|
|
115
|
+
const tag = readVarint(cursor);
|
|
116
|
+
if (tag === null)
|
|
117
|
+
return null;
|
|
118
|
+
const field = tag >>> 3;
|
|
119
|
+
const wireType = tag & 0x07;
|
|
120
|
+
if (wireType === WIRE_LENGTH_DELIMITED &&
|
|
121
|
+
(field === ENTRY_KEY_FIELD || field === ENTRY_VALUE_FIELD)) {
|
|
122
|
+
const range = readLengthDelimited(cursor);
|
|
123
|
+
if (range === null)
|
|
124
|
+
return null;
|
|
125
|
+
const text = decoder.decode(bytes.subarray(range.start, range.end));
|
|
126
|
+
if (field === ENTRY_KEY_FIELD)
|
|
127
|
+
key = text;
|
|
128
|
+
else
|
|
129
|
+
value = text;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!skipField(cursor, wireType))
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (key === null || value === null)
|
|
136
|
+
return null;
|
|
137
|
+
return [key, value];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Collect a model's custom metadata map straight out of its bytes.
|
|
141
|
+
*
|
|
142
|
+
* @param model The `.onnx` file contents.
|
|
143
|
+
* @returns Key/value metadata — `names`, `task`, `imgsz`, ... for an
|
|
144
|
+
* Ultralytics export — or an empty object when the file carries none or
|
|
145
|
+
* cannot be walked.
|
|
146
|
+
*/
|
|
147
|
+
export function readModelMetadata(model) {
|
|
148
|
+
const bytes = model instanceof Uint8Array ? model : new Uint8Array(model);
|
|
149
|
+
const cursor = { bytes, end: bytes.length, pos: 0 };
|
|
150
|
+
const metadata = {};
|
|
151
|
+
while (cursor.pos < cursor.end) {
|
|
152
|
+
const tag = readVarint(cursor);
|
|
153
|
+
if (tag === null)
|
|
154
|
+
break;
|
|
155
|
+
const field = tag >>> 3;
|
|
156
|
+
const wireType = tag & 0x07;
|
|
157
|
+
if (field === MODEL_METADATA_PROPS_FIELD && wireType === WIRE_LENGTH_DELIMITED) {
|
|
158
|
+
const range = readLengthDelimited(cursor);
|
|
159
|
+
if (range === null)
|
|
160
|
+
break;
|
|
161
|
+
const entry = readEntry(bytes, range.start, range.end);
|
|
162
|
+
if (entry)
|
|
163
|
+
metadata[entry[0]] = entry[1];
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!skipField(cursor, wireType))
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
return metadata;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Read the class names an export baked into the model metadata.
|
|
173
|
+
*
|
|
174
|
+
* Ultralytics writes `names` as the Python `repr` of a `dict[int, str]` — e.g.
|
|
175
|
+
* `"{0: 'deworm', 1: 'not_deworm'}"`. The value is parsed structurally (never
|
|
176
|
+
* evaluated), and anything unparseable, non-`dict`, or not keyed by contiguous
|
|
177
|
+
* integers from zero is rejected whole rather than half-applied: a partial name
|
|
178
|
+
* map would silently mislabel predictions.
|
|
179
|
+
*
|
|
180
|
+
* @param metadata A model's custom metadata map.
|
|
181
|
+
* @returns Class names in class-id order, or `null` when the model carries no
|
|
182
|
+
* usable `names` entry.
|
|
183
|
+
*/
|
|
184
|
+
export function modelNames(metadata) {
|
|
185
|
+
const raw = metadata?.names?.trim();
|
|
186
|
+
if (!raw || !raw.startsWith("{") || !raw.endsWith("}"))
|
|
187
|
+
return null;
|
|
188
|
+
const body = raw.slice(1, -1).trim();
|
|
189
|
+
if (!body)
|
|
190
|
+
return null;
|
|
191
|
+
const names = new Map();
|
|
192
|
+
const entryPattern = /(-?\d+)\s*:\s*(?:'((?:[^'\\]|\\.)*)'|"((?:[^"\\]|\\.)*)")/g;
|
|
193
|
+
let consumed = 0;
|
|
194
|
+
for (const match of body.matchAll(entryPattern)) {
|
|
195
|
+
const id = Number(match[1]);
|
|
196
|
+
const text = match[2] ?? match[3];
|
|
197
|
+
if (!Number.isInteger(id) || text === undefined)
|
|
198
|
+
return null;
|
|
199
|
+
names.set(id, unescapeQuoted(text));
|
|
200
|
+
consumed += match[0].length;
|
|
201
|
+
}
|
|
202
|
+
if (names.size === 0)
|
|
203
|
+
return null;
|
|
204
|
+
const separators = body.length - consumed;
|
|
205
|
+
if (separators > names.size * 3)
|
|
206
|
+
return null;
|
|
207
|
+
const ordered = [];
|
|
208
|
+
for (let id = 0; id < names.size; id += 1) {
|
|
209
|
+
const name = names.get(id);
|
|
210
|
+
if (name === undefined)
|
|
211
|
+
return null;
|
|
212
|
+
ordered.push(name);
|
|
213
|
+
}
|
|
214
|
+
return ordered;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Resolve the backslash escapes Python's `repr` emits inside a quoted string.
|
|
218
|
+
*
|
|
219
|
+
* @param text The quoted string's contents, escapes intact.
|
|
220
|
+
* @returns The same text with `\\`, `\'`, `\"`, `\n`, `\r` and `\t` resolved.
|
|
221
|
+
*/
|
|
222
|
+
function unescapeQuoted(text) {
|
|
223
|
+
return text.replace(/\\(.)/g, (_, char) => {
|
|
224
|
+
if (char === "n")
|
|
225
|
+
return "\n";
|
|
226
|
+
if (char === "r")
|
|
227
|
+
return "\r";
|
|
228
|
+
if (char === "t")
|
|
229
|
+
return "\t";
|
|
230
|
+
return char;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/core/metadata.ts"],"names":[],"mappings":"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"}
|