tempest-react-sdk 0.36.0 → 0.38.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/README.md +3 -3
- package/dist/vision/core/graph.cjs +2 -0
- package/dist/vision/core/graph.cjs.map +1 -0
- package/dist/vision/core/graph.js +27 -0
- package/dist/vision/core/graph.js.map +1 -0
- package/dist/vision/core/metadata.cjs +3 -0
- package/dist/vision/core/metadata.cjs.map +1 -0
- package/dist/vision/core/metadata.js +99 -0
- package/dist/vision/core/metadata.js.map +1 -0
- package/dist/vision/core/session.cjs +1 -1
- package/dist/vision/core/session.cjs.map +1 -1
- package/dist/vision/core/session.js +44 -14
- package/dist/vision/core/session.js.map +1 -1
- package/dist/vision/index.cjs +1 -1
- package/dist/vision/index.cjs.map +1 -1
- package/dist/vision/index.js +15 -13
- package/dist/vision/index.js.map +1 -1
- package/dist/vision/tasks/classifier.cjs +1 -1
- package/dist/vision/tasks/classifier.cjs.map +1 -1
- package/dist/vision/tasks/classifier.js +38 -29
- package/dist/vision/tasks/classifier.js.map +1 -1
- package/dist/vision/tasks/detector.cjs +1 -1
- package/dist/vision/tasks/detector.cjs.map +1 -1
- package/dist/vision/tasks/detector.js +35 -26
- package/dist/vision/tasks/detector.js.map +1 -1
- package/dist/vision/tasks/segmenter.cjs +1 -1
- package/dist/vision/tasks/segmenter.cjs.map +1 -1
- package/dist/vision/tasks/segmenter.js +28 -19
- package/dist/vision/tasks/segmenter.js.map +1 -1
- package/dist/vision.cjs +1 -1
- package/dist/vision.d.ts +238 -12
- package/dist/vision.js +17 -15
- package/package.json +1 -1
|
@@ -2,13 +2,15 @@ import { Mask as e, RGBImage as t } from "../types.js";
|
|
|
2
2
|
import { SpeedTimer as n } from "../core/timing.js";
|
|
3
3
|
import { Boxes as r, Masks as i, SegmentationResults as a } from "../results.js";
|
|
4
4
|
import { resolveLabels as o } from "../labels.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { detectionNumClasses as s, resolveInputSize as c } from "../core/graph.js";
|
|
6
|
+
import { modelNames as l } from "../core/metadata.js";
|
|
7
|
+
import { OrtSession as u } from "../core/session.js";
|
|
8
|
+
import { loadImage as d } from "../io/image.js";
|
|
9
|
+
import { letterbox as f, toCHW as p, toFloat32 as m, toFloat32Tensor as h } from "../preprocess/image.js";
|
|
10
|
+
import { decodeYoloSeg as g } from "../postprocess/segmentation.js";
|
|
11
|
+
import { VisionTask as _ } from "./base.js";
|
|
10
12
|
//#region src/vision/tasks/segmenter.ts
|
|
11
|
-
var
|
|
13
|
+
var v = class v extends _ {
|
|
12
14
|
_head;
|
|
13
15
|
_labels;
|
|
14
16
|
_names;
|
|
@@ -23,9 +25,13 @@ var h = class h extends m {
|
|
|
23
25
|
static async create(e, t = {}) {
|
|
24
26
|
let n = t.head ?? "yolo-seg";
|
|
25
27
|
if (n !== "yolo-seg") throw Error(`Unsupported segmenter head '${n}'. Supported: 'yolo-seg'.`);
|
|
26
|
-
let r = await
|
|
28
|
+
let r = await u.create(e, t), i = o(t.labels ?? l(r.metadata) ?? "coco", { numClasses: t.numClasses ?? s(r.outputShape) ?? void 0 }), a = {};
|
|
27
29
|
for (let e = 0; e < i.length; e++) a[e] = i[e];
|
|
28
|
-
return new
|
|
30
|
+
return new v(r, n, i, a, c({
|
|
31
|
+
graphShape: r.inputShape,
|
|
32
|
+
requested: t.inputSize,
|
|
33
|
+
fallback: [640, 640]
|
|
34
|
+
}), t.confThreshold ?? .25, t.iouThreshold ?? .45, t.maxDetections ?? 300, t.maskThreshold ?? .5);
|
|
29
35
|
}
|
|
30
36
|
get head() {
|
|
31
37
|
return this._head;
|
|
@@ -36,6 +42,9 @@ var h = class h extends m {
|
|
|
36
42
|
get names() {
|
|
37
43
|
return this._names;
|
|
38
44
|
}
|
|
45
|
+
get inputSize() {
|
|
46
|
+
return this._inputSize;
|
|
47
|
+
}
|
|
39
48
|
get numClasses() {
|
|
40
49
|
return this._labels.length;
|
|
41
50
|
}
|
|
@@ -43,35 +52,35 @@ var h = class h extends m {
|
|
|
43
52
|
return this.predict(e, t);
|
|
44
53
|
}
|
|
45
54
|
async predict(e, t = {}) {
|
|
46
|
-
let r = new n(), i = typeof e == "string" ? e : null, o = await
|
|
55
|
+
let r = new n(), i = typeof e == "string" ? e : null, o = await d(e);
|
|
47
56
|
r.stage("load");
|
|
48
|
-
let { tensor: s, scale:
|
|
57
|
+
let { tensor: s, scale: c, padLeft: l, padTop: u } = this._preprocess(o);
|
|
49
58
|
r.stage("preprocess");
|
|
50
59
|
let f = await this._session.run({ [this._session.inputName]: s });
|
|
51
60
|
r.stage("inference");
|
|
52
|
-
let { perAnchor:
|
|
61
|
+
let { perAnchor: p, prototypes: m } = this._splitOutputs(f), h = g(p.data, p.dims, m.data, m.dims, {
|
|
53
62
|
numClasses: this._labels.length,
|
|
54
63
|
inputWidth: this._inputSize[0],
|
|
55
64
|
inputHeight: this._inputSize[1],
|
|
56
65
|
originalWidth: o.width,
|
|
57
66
|
originalHeight: o.height,
|
|
58
|
-
padLeft:
|
|
59
|
-
padTop:
|
|
60
|
-
scale:
|
|
67
|
+
padLeft: l,
|
|
68
|
+
padTop: u,
|
|
69
|
+
scale: c,
|
|
61
70
|
confThreshold: t.confThreshold ?? this._confThreshold,
|
|
62
71
|
iouThreshold: t.iouThreshold ?? this._iouThreshold,
|
|
63
72
|
maxDetections: this._maxDetections,
|
|
64
73
|
maskThreshold: this._maskThreshold
|
|
65
|
-
}), _ = (t.classes === void 0 ?
|
|
74
|
+
}), _ = (t.classes === void 0 ? h : (() => {
|
|
66
75
|
let e = new Set(t.classes);
|
|
67
|
-
return
|
|
76
|
+
return h.filter((t) => e.has(t.classId));
|
|
68
77
|
})()).map((e) => this._buildResult(o, e.bbox, e.classId, e.confidence, e.mask)), v = [o.height, o.width], y = this._buildBoxes(_, v), b = this._buildMasks(_, v);
|
|
69
78
|
return r.stage("postprocess"), [new a(y, b, _, this._names, o, v, i, r.speed())];
|
|
70
79
|
}
|
|
71
80
|
_preprocess(e) {
|
|
72
|
-
let [t, n] = this._inputSize, r =
|
|
81
|
+
let [t, n] = this._inputSize, r = f(e, t, n);
|
|
73
82
|
return {
|
|
74
|
-
tensor:
|
|
83
|
+
tensor: h(p(m(r.image), r.image.width, r.image.height, 3), [
|
|
75
84
|
1,
|
|
76
85
|
3,
|
|
77
86
|
r.image.height,
|
|
@@ -146,6 +155,6 @@ var h = class h extends m {
|
|
|
146
155
|
}
|
|
147
156
|
};
|
|
148
157
|
//#endregion
|
|
149
|
-
export {
|
|
158
|
+
export { v as Segmenter };
|
|
150
159
|
|
|
151
160
|
//# sourceMappingURL=segmenter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segmenter.js","names":[],"sources":["../../../src/vision/tasks/segmenter.ts"],"sourcesContent":["/**\n * Instance-segmentation task using YOLO seg ONNX models (v8-seg / v11-seg / ...).\n */\n\nimport type * as ort from \"onnxruntime-web\";\n\nimport { type ModelSource, type OrtSessionOptions, OrtSession } from \"../core/session\";\nimport { SpeedTimer } from \"../core/timing\";\nimport { type ImageInput, loadImage } from \"../io/image\";\nimport { type LabelSpec, resolveLabels } from \"../labels\";\nimport { decodeYoloSeg } from \"../postprocess/segmentation\";\nimport { letterbox, toCHW, toFloat32, toFloat32Tensor } from \"../preprocess/image\";\nimport { Boxes, Masks, SegmentationResults } from \"../results\";\nimport { VisionTask } from \"./base\";\nimport { type BoundingBox, type SegmentationResult, Mask, RGBImage } from \"../types\";\n\n/**\n * Decoder family for the segmentation head.\n *\n * - `\"yolo-seg\"`: YOLO instance-segmentation head with two outputs —\n * `[1, 4 + nc + nm, N]` per-anchor predictions plus `[1, nm, mh, mw]`\n * prototype masks. Covers YOLOv8-seg, v11-seg, v26-seg.\n *\n * The SDK does **not** auto-detect this — the caller is responsible for\n * picking a head that matches their export.\n */\nexport type SegmenterHead = \"yolo-seg\";\n\nexport interface SegmenterOptions extends OrtSessionOptions {\n /**\n * Decoder family for the segmentation head. Default `\"yolo-seg\"` covers\n * YOLOv8-seg/v11-seg/v26-seg.\n */\n readonly head?: SegmenterHead;\n /** Class label spec — see {@link resolveLabels}. Defaults to the COCO 80-class preset. */\n readonly labels?: LabelSpec;\n /** Number of classes — used to validate the supplied labels. */\n readonly numClasses?: number;\n /** Model input `[width, height]` for letterboxing. Defaults to `[640, 640]`. */\n readonly inputSize?: readonly [number, number];\n /** Default minimum class score to keep a candidate. */\n readonly confThreshold?: number;\n /** Default IoU threshold for non-maximum suppression. */\n readonly iouThreshold?: number;\n /** Maximum number of instances per image. */\n readonly maxDetections?: number;\n /** Probability cutoff applied to soft masks. Defaults to `0.5`. */\n readonly maskThreshold?: number;\n}\n\nexport interface SegmenterPredictOptions {\n readonly confThreshold?: number;\n readonly iouThreshold?: number;\n /**\n * If set, keep only instances whose `classId` is in this list.\n * Mirrors Ultralytics' `model.predict(img, classes=[0, 16])`.\n */\n readonly classes?: readonly number[];\n}\n\n/**\n * Instance segmenter for YOLO seg ONNX models (v8-seg / v11-seg / ...).\n *\n * The model is expected to expose two outputs:\n *\n * 1. `output0`: `(1, 4 + numClasses + numMaskCoefs, numAnchors)` — per-anchor\n * predictions (boxes, class scores, mask coefficients).\n * 2. `output1`: `(1, numMaskCoefs, maskH, maskW)` — prototype masks.\n *\n * `predict()` returns `Promise<SegmentationResults[]>` (length 1 for a\n * single image), mirroring Ultralytics' API. The envelope exposes:\n *\n * - `boxes`: bulk numpy view (`xyxy`, `xywh`, `xyxyn`, `xywhn`, `cls`, `conf`).\n * - `masks`: per-instance binary masks cropped to each box.\n * - per-instance {@link SegmentationResult} via iteration.\n *\n * @example\n * ```typescript\n * const seg = await Segmenter.create(\"/models/yolov8n-seg.onnx\");\n * const r = (await seg.predict(\"/images/street.jpg\"))[0];\n * for (const inst of r) {\n * console.log(inst.cls, inst.conf, inst.box.xyxy);\n * }\n * ```\n */\nexport class Segmenter extends VisionTask {\n private constructor(\n session: OrtSession,\n private readonly _head: SegmenterHead,\n private readonly _labels: readonly string[],\n private readonly _names: Readonly<Record<number, string>>,\n private readonly _inputSize: readonly [number, number],\n private readonly _confThreshold: number,\n private readonly _iouThreshold: number,\n private readonly _maxDetections: number,\n private readonly _maskThreshold: number,\n ) {\n super(session);\n }\n\n /** Load the model and resolve labels. */\n static async create(model: ModelSource, options: SegmenterOptions = {}): Promise<Segmenter> {\n const head: SegmenterHead = options.head ?? \"yolo-seg\";\n if (head !== \"yolo-seg\") {\n throw new Error(`Unsupported segmenter head '${head}'. Supported: 'yolo-seg'.`);\n }\n const session = await OrtSession.create(model, options);\n const labels = resolveLabels(options.labels ?? \"coco\", {\n numClasses: options.numClasses,\n });\n const names: Record<number, string> = {};\n for (let i = 0; i < labels.length; i++) {\n names[i] = labels[i] as string;\n }\n return new Segmenter(\n session,\n head,\n labels,\n names,\n options.inputSize ?? [640, 640],\n options.confThreshold ?? 0.25,\n options.iouThreshold ?? 0.45,\n options.maxDetections ?? 300,\n options.maskThreshold ?? 0.5,\n );\n }\n\n /** The decoder family used to interpret the model's output. */\n get head(): SegmenterHead {\n return this._head;\n }\n\n /** Class labels indexed by class id. */\n get labels(): readonly string[] {\n return this._labels;\n }\n\n /** Class id → class name dict (matches Ultralytics' `model.names`). */\n get names(): Readonly<Record<number, string>> {\n return this._names;\n }\n\n /** Number of classes the model predicts. */\n get numClasses(): number {\n return this._labels.length;\n }\n\n /** Alias for {@link predict} (parity with PyTorch `nn.Module.__call__`). */\n async call(\n image: ImageInput,\n options: SegmenterPredictOptions = {},\n ): Promise<SegmentationResults[]> {\n return this.predict(image, options);\n }\n\n /** Run instance segmentation on a single image. */\n async predict(\n image: ImageInput,\n options: SegmenterPredictOptions = {},\n ): Promise<SegmentationResults[]> {\n const timer = new SpeedTimer();\n const path = typeof image === \"string\" ? image : null;\n const original = await loadImage(image);\n timer.stage(\"load\");\n const { tensor, scale, padLeft, padTop } = this._preprocess(original);\n timer.stage(\"preprocess\");\n const outputs = await this._session.run({ [this._session.inputName]: tensor });\n timer.stage(\"inference\");\n\n const { perAnchor, prototypes } = this._splitOutputs(outputs);\n\n const decodedAll = decodeYoloSeg(\n perAnchor.data as Float32Array,\n perAnchor.dims,\n prototypes.data as Float32Array,\n prototypes.dims,\n {\n numClasses: this._labels.length,\n inputWidth: this._inputSize[0],\n inputHeight: this._inputSize[1],\n originalWidth: original.width,\n originalHeight: original.height,\n padLeft,\n padTop,\n scale,\n confThreshold: options.confThreshold ?? this._confThreshold,\n iouThreshold: options.iouThreshold ?? this._iouThreshold,\n maxDetections: this._maxDetections,\n maskThreshold: this._maskThreshold,\n },\n );\n\n const decoded =\n options.classes !== undefined\n ? (() => {\n const allowed = new Set(options.classes);\n return decodedAll.filter((d) => allowed.has(d.classId));\n })()\n : decodedAll;\n\n const detections = decoded.map((d) =>\n this._buildResult(original, d.bbox, d.classId, d.confidence, d.mask),\n );\n\n const orig: readonly [number, number] = [original.height, original.width];\n const boxes = this._buildBoxes(detections, orig);\n const masks = this._buildMasks(detections, orig);\n timer.stage(\"postprocess\");\n return [\n new SegmentationResults(\n boxes,\n masks,\n detections,\n this._names,\n original,\n orig,\n path,\n timer.speed(),\n ),\n ];\n }\n\n private _preprocess(image: RGBImage): {\n tensor: ort.Tensor;\n scale: number;\n padLeft: number;\n padTop: number;\n } {\n const [tw, th] = this._inputSize;\n const lb = letterbox(image, tw, th);\n const f32 = toFloat32(lb.image);\n const chw = toCHW(f32, lb.image.width, lb.image.height, 3);\n return {\n tensor: toFloat32Tensor(chw, [1, 3, lb.image.height, lb.image.width]),\n scale: lb.scale,\n padLeft: lb.padLeft,\n padTop: lb.padTop,\n };\n }\n\n private _splitOutputs(outputs: Record<string, ort.Tensor>): {\n perAnchor: ort.Tensor;\n prototypes: ort.Tensor;\n } {\n let perAnchor: ort.Tensor | undefined;\n let prototypes: ort.Tensor | undefined;\n for (const name of this._session.outputNames) {\n const t = outputs[name];\n if (t === undefined) continue;\n if (t.dims.length === 3 && perAnchor === undefined) {\n perAnchor = t;\n } else if (t.dims.length === 4 && prototypes === undefined) {\n prototypes = t;\n }\n }\n if (perAnchor === undefined || prototypes === undefined) {\n const shapes = this._session.outputNames.map(\n (n) => `${n}: ${JSON.stringify(outputs[n]?.dims ?? [])}`,\n );\n throw new Error(\n `Segmenter expected one 3-D and one 4-D output, got [${shapes.join(\", \")}].`,\n );\n }\n return { perAnchor, prototypes };\n }\n\n private _buildResult(\n original: RGBImage,\n bbox: BoundingBox,\n classId: number,\n confidence: number,\n mask: Mask,\n ): SegmentationResult {\n const [x1, y1, x2, y2] = bbox.asIntXyxy();\n const cx1 = Math.max(0, x1);\n const cy1 = Math.max(0, y1);\n const cx2 = Math.min(original.width, x2);\n const cy2 = Math.min(original.height, y2);\n\n let segmentedImage: RGBImage;\n let finalMask = mask;\n if (cx2 > cx1 && cy2 > cy1 && mask.data.length > 0) {\n const cropW = cx2 - cx1;\n const cropH = cy2 - cy1;\n const mw = Math.min(mask.width, cropW);\n const mh = Math.min(mask.height, cropH);\n const segData = new Uint8Array(mw * mh * 3);\n for (let row = 0; row < mh; row++) {\n const srcRowOffset = ((cy1 + row) * original.width + cx1) * 3;\n const dstRowOffset = row * mw * 3;\n const maskRowOffset = row * mask.width;\n for (let col = 0; col < mw; col++) {\n const m = mask.data[maskRowOffset + col];\n if (m !== 0) {\n const s = srcRowOffset + col * 3;\n const d = dstRowOffset + col * 3;\n segData[d] = original.data[s];\n segData[d + 1] = original.data[s + 1];\n segData[d + 2] = original.data[s + 2];\n }\n }\n }\n segmentedImage = new RGBImage(segData, mw, mh);\n if (mw !== mask.width || mh !== mask.height) {\n const trimmed = new Uint8Array(mw * mh);\n for (let row = 0; row < mh; row++) {\n trimmed.set(\n mask.data.subarray(row * mask.width, row * mask.width + mw),\n row * mw,\n );\n }\n finalMask = new Mask(trimmed, mw, mh);\n }\n } else {\n finalMask = new Mask(new Uint8Array(0), 0, 0);\n segmentedImage = new RGBImage(new Uint8Array(0), 0, 0);\n }\n\n const className = this._names[classId] ?? `class_${classId}`;\n\n return {\n classId,\n className,\n confidence,\n bbox,\n cls: classId,\n name: className,\n conf: confidence,\n box: bbox,\n mask: finalMask,\n segmentedImage,\n };\n }\n\n private _buildBoxes(\n detections: readonly SegmentationResult[],\n origShape: readonly [number, number],\n ): Boxes {\n const n = detections.length;\n const xyxy = new Float32Array(n * 4);\n const cls = new Int32Array(n);\n const conf = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n const d = detections[i] as SegmentationResult;\n xyxy[i * 4] = d.bbox.x1;\n xyxy[i * 4 + 1] = d.bbox.y1;\n xyxy[i * 4 + 2] = d.bbox.x2;\n xyxy[i * 4 + 3] = d.bbox.y2;\n cls[i] = d.classId;\n conf[i] = d.confidence;\n }\n return new Boxes(xyxy, cls, conf, origShape);\n }\n\n private _buildMasks(\n detections: readonly SegmentationResult[],\n origShape: readonly [number, number],\n ): Masks {\n const xyxy = new Float32Array(detections.length * 4);\n for (let i = 0; i < detections.length; i++) {\n const d = detections[i] as SegmentationResult;\n xyxy[i * 4] = d.bbox.x1;\n xyxy[i * 4 + 1] = d.bbox.y1;\n xyxy[i * 4 + 2] = d.bbox.x2;\n xyxy[i * 4 + 3] = d.bbox.y2;\n }\n return new Masks(\n detections.map((d) => d.mask),\n xyxy,\n origShape,\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"segmenter.js","names":[],"sources":["../../../src/vision/tasks/segmenter.ts"],"sourcesContent":["/**\n * Instance-segmentation task using YOLO seg ONNX models (v8-seg / v11-seg / ...).\n */\n\nimport type * as ort from \"onnxruntime-web\";\n\nimport { type ModelSource, type OrtSessionOptions, OrtSession } from \"../core/session\";\nimport { SpeedTimer } from \"../core/timing\";\n\nimport { type ImageInput, loadImage } from \"../io/image\";\nimport { detectionNumClasses, resolveInputSize } from \"../core/graph\";\nimport { modelNames } from \"../core/metadata\";\nimport { type LabelSpec, resolveLabels } from \"../labels\";\nimport { decodeYoloSeg } from \"../postprocess/segmentation\";\nimport { letterbox, toCHW, toFloat32, toFloat32Tensor } from \"../preprocess/image\";\nimport { Boxes, Masks, SegmentationResults } from \"../results\";\nimport { VisionTask } from \"./base\";\nimport { type BoundingBox, type SegmentationResult, Mask, RGBImage } from \"../types\";\n\n/**\n * Decoder family for the segmentation head.\n *\n * - `\"yolo-seg\"`: YOLO instance-segmentation head with two outputs —\n * `[1, 4 + nc + nm, N]` per-anchor predictions plus `[1, nm, mh, mw]`\n * prototype masks. Covers YOLOv8-seg, v11-seg, v26-seg.\n *\n * The SDK does **not** auto-detect this — the caller is responsible for\n * picking a head that matches their export.\n */\nexport type SegmenterHead = \"yolo-seg\";\n\nexport interface SegmenterOptions extends OrtSessionOptions {\n /**\n * Decoder family for the segmentation head. Default `\"yolo-seg\"` covers\n * YOLOv8-seg/v11-seg/v26-seg.\n */\n readonly head?: SegmenterHead;\n /** Class label spec — see {@link resolveLabels}. Defaults to the COCO 80-class preset. */\n readonly labels?: LabelSpec;\n /** Number of classes — used to validate the supplied labels. */\n readonly numClasses?: number;\n /**\n * Model input `[width, height]` in pixels for letterboxing.\n *\n * Only used when the model's graph leaves its spatial axes dynamic: a graph\n * that declares a static size always wins, since that is the only shape ONNX\n * Runtime will accept. Defaults to `[640, 640]`.\n */\n readonly inputSize?: readonly [number, number];\n /** Default minimum class score to keep a candidate. */\n readonly confThreshold?: number;\n /** Default IoU threshold for non-maximum suppression. */\n readonly iouThreshold?: number;\n /** Maximum number of instances per image. */\n readonly maxDetections?: number;\n /** Probability cutoff applied to soft masks. Defaults to `0.5`. */\n readonly maskThreshold?: number;\n}\n\nexport interface SegmenterPredictOptions {\n readonly confThreshold?: number;\n readonly iouThreshold?: number;\n /**\n * If set, keep only instances whose `classId` is in this list.\n * Mirrors Ultralytics' `model.predict(img, classes=[0, 16])`.\n */\n readonly classes?: readonly number[];\n}\n\n/**\n * Instance segmenter for YOLO seg ONNX models (v8-seg / v11-seg / ...).\n *\n * The model is expected to expose two outputs:\n *\n * 1. `output0`: `(1, 4 + numClasses + numMaskCoefs, numAnchors)` — per-anchor\n * predictions (boxes, class scores, mask coefficients).\n * 2. `output1`: `(1, numMaskCoefs, maskH, maskW)` — prototype masks.\n *\n * `predict()` returns `Promise<SegmentationResults[]>` (length 1 for a\n * single image), mirroring Ultralytics' API. The envelope exposes:\n *\n * - `boxes`: bulk numpy view (`xyxy`, `xywh`, `xyxyn`, `xywhn`, `cls`, `conf`).\n * - `masks`: per-instance binary masks cropped to each box.\n * - per-instance {@link SegmentationResult} via iteration.\n *\n * @example\n * ```typescript\n * const seg = await Segmenter.create(\"/models/yolov8n-seg.onnx\");\n * const r = (await seg.predict(\"/images/street.jpg\"))[0];\n * for (const inst of r) {\n * console.log(inst.cls, inst.conf, inst.box.xyxy);\n * }\n * ```\n */\nexport class Segmenter extends VisionTask {\n private constructor(\n session: OrtSession,\n private readonly _head: SegmenterHead,\n private readonly _labels: readonly string[],\n private readonly _names: Readonly<Record<number, string>>,\n private readonly _inputSize: readonly [number, number],\n private readonly _confThreshold: number,\n private readonly _iouThreshold: number,\n private readonly _maxDetections: number,\n private readonly _maskThreshold: number,\n ) {\n super(session);\n }\n\n /** Load the model and resolve labels. */\n static async create(model: ModelSource, options: SegmenterOptions = {}): Promise<Segmenter> {\n const head: SegmenterHead = options.head ?? \"yolo-seg\";\n if (head !== \"yolo-seg\") {\n throw new Error(`Unsupported segmenter head '${head}'. Supported: 'yolo-seg'.`);\n }\n const session = await OrtSession.create(model, options);\n const labels = resolveLabels(options.labels ?? modelNames(session.metadata) ?? \"coco\", {\n numClasses: options.numClasses ?? detectionNumClasses(session.outputShape) ?? undefined,\n });\n const names: Record<number, string> = {};\n for (let i = 0; i < labels.length; i++) {\n names[i] = labels[i] as string;\n }\n return new Segmenter(\n session,\n head,\n labels,\n names,\n resolveInputSize({\n graphShape: session.inputShape,\n requested: options.inputSize,\n fallback: [640, 640],\n }),\n options.confThreshold ?? 0.25,\n options.iouThreshold ?? 0.45,\n options.maxDetections ?? 300,\n options.maskThreshold ?? 0.5,\n );\n }\n\n /** The decoder family used to interpret the model's output. */\n get head(): SegmenterHead {\n return this._head;\n }\n\n /** Class labels indexed by class id. */\n get labels(): readonly string[] {\n return this._labels;\n }\n\n /** Class id → class name dict (matches Ultralytics' `model.names`). */\n get names(): Readonly<Record<number, string>> {\n return this._names;\n }\n\n /**\n * The `[width, height]` this task preprocesses to.\n *\n * Resolved at creation time from the model's graph when it declares a static\n * input, so reading it back tells you the resolution inference really runs at\n * — not merely what was requested.\n */\n get inputSize(): readonly [number, number] {\n return this._inputSize;\n }\n\n /** Number of classes the model predicts. */\n get numClasses(): number {\n return this._labels.length;\n }\n\n /** Alias for {@link predict} (parity with PyTorch `nn.Module.__call__`). */\n async call(\n image: ImageInput,\n options: SegmenterPredictOptions = {},\n ): Promise<SegmentationResults[]> {\n return this.predict(image, options);\n }\n\n /** Run instance segmentation on a single image. */\n async predict(\n image: ImageInput,\n options: SegmenterPredictOptions = {},\n ): Promise<SegmentationResults[]> {\n const timer = new SpeedTimer();\n const path = typeof image === \"string\" ? image : null;\n const original = await loadImage(image);\n timer.stage(\"load\");\n const { tensor, scale, padLeft, padTop } = this._preprocess(original);\n timer.stage(\"preprocess\");\n const outputs = await this._session.run({ [this._session.inputName]: tensor });\n timer.stage(\"inference\");\n\n const { perAnchor, prototypes } = this._splitOutputs(outputs);\n\n const decodedAll = decodeYoloSeg(\n perAnchor.data as Float32Array,\n perAnchor.dims,\n prototypes.data as Float32Array,\n prototypes.dims,\n {\n numClasses: this._labels.length,\n inputWidth: this._inputSize[0],\n inputHeight: this._inputSize[1],\n originalWidth: original.width,\n originalHeight: original.height,\n padLeft,\n padTop,\n scale,\n confThreshold: options.confThreshold ?? this._confThreshold,\n iouThreshold: options.iouThreshold ?? this._iouThreshold,\n maxDetections: this._maxDetections,\n maskThreshold: this._maskThreshold,\n },\n );\n\n const decoded =\n options.classes !== undefined\n ? (() => {\n const allowed = new Set(options.classes);\n return decodedAll.filter((d) => allowed.has(d.classId));\n })()\n : decodedAll;\n\n const detections = decoded.map((d) =>\n this._buildResult(original, d.bbox, d.classId, d.confidence, d.mask),\n );\n\n const orig: readonly [number, number] = [original.height, original.width];\n const boxes = this._buildBoxes(detections, orig);\n const masks = this._buildMasks(detections, orig);\n timer.stage(\"postprocess\");\n return [\n new SegmentationResults(\n boxes,\n masks,\n detections,\n this._names,\n original,\n orig,\n path,\n timer.speed(),\n ),\n ];\n }\n\n private _preprocess(image: RGBImage): {\n tensor: ort.Tensor;\n scale: number;\n padLeft: number;\n padTop: number;\n } {\n const [tw, th] = this._inputSize;\n const lb = letterbox(image, tw, th);\n const f32 = toFloat32(lb.image);\n const chw = toCHW(f32, lb.image.width, lb.image.height, 3);\n return {\n tensor: toFloat32Tensor(chw, [1, 3, lb.image.height, lb.image.width]),\n scale: lb.scale,\n padLeft: lb.padLeft,\n padTop: lb.padTop,\n };\n }\n\n private _splitOutputs(outputs: Record<string, ort.Tensor>): {\n perAnchor: ort.Tensor;\n prototypes: ort.Tensor;\n } {\n let perAnchor: ort.Tensor | undefined;\n let prototypes: ort.Tensor | undefined;\n for (const name of this._session.outputNames) {\n const t = outputs[name];\n if (t === undefined) continue;\n if (t.dims.length === 3 && perAnchor === undefined) {\n perAnchor = t;\n } else if (t.dims.length === 4 && prototypes === undefined) {\n prototypes = t;\n }\n }\n if (perAnchor === undefined || prototypes === undefined) {\n const shapes = this._session.outputNames.map(\n (n) => `${n}: ${JSON.stringify(outputs[n]?.dims ?? [])}`,\n );\n throw new Error(\n `Segmenter expected one 3-D and one 4-D output, got [${shapes.join(\", \")}].`,\n );\n }\n return { perAnchor, prototypes };\n }\n\n private _buildResult(\n original: RGBImage,\n bbox: BoundingBox,\n classId: number,\n confidence: number,\n mask: Mask,\n ): SegmentationResult {\n const [x1, y1, x2, y2] = bbox.asIntXyxy();\n const cx1 = Math.max(0, x1);\n const cy1 = Math.max(0, y1);\n const cx2 = Math.min(original.width, x2);\n const cy2 = Math.min(original.height, y2);\n\n let segmentedImage: RGBImage;\n let finalMask = mask;\n if (cx2 > cx1 && cy2 > cy1 && mask.data.length > 0) {\n const cropW = cx2 - cx1;\n const cropH = cy2 - cy1;\n const mw = Math.min(mask.width, cropW);\n const mh = Math.min(mask.height, cropH);\n const segData = new Uint8Array(mw * mh * 3);\n for (let row = 0; row < mh; row++) {\n const srcRowOffset = ((cy1 + row) * original.width + cx1) * 3;\n const dstRowOffset = row * mw * 3;\n const maskRowOffset = row * mask.width;\n for (let col = 0; col < mw; col++) {\n const m = mask.data[maskRowOffset + col];\n if (m !== 0) {\n const s = srcRowOffset + col * 3;\n const d = dstRowOffset + col * 3;\n segData[d] = original.data[s];\n segData[d + 1] = original.data[s + 1];\n segData[d + 2] = original.data[s + 2];\n }\n }\n }\n segmentedImage = new RGBImage(segData, mw, mh);\n if (mw !== mask.width || mh !== mask.height) {\n const trimmed = new Uint8Array(mw * mh);\n for (let row = 0; row < mh; row++) {\n trimmed.set(\n mask.data.subarray(row * mask.width, row * mask.width + mw),\n row * mw,\n );\n }\n finalMask = new Mask(trimmed, mw, mh);\n }\n } else {\n finalMask = new Mask(new Uint8Array(0), 0, 0);\n segmentedImage = new RGBImage(new Uint8Array(0), 0, 0);\n }\n\n const className = this._names[classId] ?? `class_${classId}`;\n\n return {\n classId,\n className,\n confidence,\n bbox,\n cls: classId,\n name: className,\n conf: confidence,\n box: bbox,\n mask: finalMask,\n segmentedImage,\n };\n }\n\n private _buildBoxes(\n detections: readonly SegmentationResult[],\n origShape: readonly [number, number],\n ): Boxes {\n const n = detections.length;\n const xyxy = new Float32Array(n * 4);\n const cls = new Int32Array(n);\n const conf = new Float32Array(n);\n for (let i = 0; i < n; i++) {\n const d = detections[i] as SegmentationResult;\n xyxy[i * 4] = d.bbox.x1;\n xyxy[i * 4 + 1] = d.bbox.y1;\n xyxy[i * 4 + 2] = d.bbox.x2;\n xyxy[i * 4 + 3] = d.bbox.y2;\n cls[i] = d.classId;\n conf[i] = d.confidence;\n }\n return new Boxes(xyxy, cls, conf, origShape);\n }\n\n private _buildMasks(\n detections: readonly SegmentationResult[],\n origShape: readonly [number, number],\n ): Masks {\n const xyxy = new Float32Array(detections.length * 4);\n for (let i = 0; i < detections.length; i++) {\n const d = detections[i] as SegmentationResult;\n xyxy[i * 4] = d.bbox.x1;\n xyxy[i * 4 + 1] = d.bbox.y1;\n xyxy[i * 4 + 2] = d.bbox.x2;\n xyxy[i * 4 + 3] = d.bbox.y2;\n }\n return new Masks(\n detections.map((d) => d.mask),\n xyxy,\n origShape,\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/vision.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./vision/use-camera-stream.cjs"),t=require("./vision/core/exceptions.cjs"),n=require("./vision/types.cjs"),r=require("./vision/core/timing.cjs"),i=require("./vision/results.cjs"),a=require("./vision/labels.cjs"),o=require("./vision/core/
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./vision/use-camera-stream.cjs"),t=require("./vision/core/exceptions.cjs"),n=require("./vision/types.cjs"),r=require("./vision/core/timing.cjs"),i=require("./vision/results.cjs"),a=require("./vision/labels.cjs"),o=require("./vision/core/graph.cjs"),s=require("./vision/core/metadata.cjs"),c=require("./vision/core/providers.cjs"),l=require("./vision/core/session.cjs"),u=require("./vision/io/image.cjs"),d=require("./vision/preprocess/image.cjs"),f=require("./vision/postprocess/classification.cjs"),p=require("./vision/postprocess/detection.cjs"),m=require("./vision/postprocess/segmentation.cjs"),h=require("./vision/tasks/base.cjs"),g=require("./vision/tasks/classifier.cjs"),_=require("./vision/tasks/detector.cjs"),v=require("./vision/tasks/segmenter.cjs"),y=require("./vision/index.cjs"),b=require("./vision/luminance.cjs"),x=require("./vision/use-live-luminance.cjs");exports.BoundingBox=n.BoundingBox,exports.Boxes=i.Boxes,exports.COCO_CLASSES=a.COCO_CLASSES,exports.ClassificationResults=i.ClassificationResults,exports.Classifier=g.Classifier,exports.DEFAULT_PROVIDERS=c.DEFAULT_PROVIDERS,exports.DetectionResults=i.DetectionResults,exports.Detector=_.Detector,exports.ImageLoadError=t.ImageLoadError,exports.InferenceError=t.InferenceError,exports.LUMINANCE_SAMPLE_MAX_EDGE=b.LUMINANCE_SAMPLE_MAX_EDGE,exports.LabelMapError=t.LabelMapError,exports.LowLuminanceError=b.LowLuminanceError,exports.Mask=n.Mask,exports.Masks=i.Masks,exports.ModelLoadError=t.ModelLoadError,exports.OrtSession=l.OrtSession,exports.OrtVisionError=t.OrtVisionError,exports.Probs=i.Probs,exports.ProviderNotAvailableError=t.ProviderNotAvailableError,exports.RGBImage=n.RGBImage,exports.SegmentationResults=i.SegmentationResults,exports.Segmenter=v.Segmenter,exports.SpeedTimer=r.SpeedTimer,exports.VERSION=y.VERSION,exports.VisionTask=h.VisionTask,exports.batchedNms=p.batchedNms,exports.classificationNumClasses=o.classificationNumClasses,exports.computeImageLuminance=b.computeImageLuminance,exports.declaredShapesFrom=o.declaredShapesFrom,exports.decodeYolo=p.decodeYolo,exports.decodeYoloAnchors=p.decodeYoloAnchors,exports.decodeYoloSeg=m.decodeYoloSeg,exports.decodeYoloV8=p.decodeYoloV8,exports.decodeYoloV8Anchors=p.decodeYoloV8Anchors,exports.decodeYoloV8Seg=m.decodeYoloV8Seg,exports.detectionNumClasses=o.detectionNumClasses,exports.fromCv2=d.fromCv2,exports.isLuminanceAcceptable=b.isLuminanceAcceptable,exports.letterbox=d.letterbox,exports.loadImage=u.loadImage,exports.modelNames=s.modelNames,exports.nms=p.nms,exports.normalize=d.normalize,exports.readModelMetadata=s.readModelMetadata,exports.resize=d.resize,exports.resolveInputSize=o.resolveInputSize,exports.resolveLabels=a.resolveLabels,exports.resolveProviders=c.resolveProviders,exports.softmax=f.softmax,exports.spatialInputSize=o.spatialInputSize,exports.toCHW=d.toCHW,exports.toCv2=d.toCv2,exports.toFloat32=d.toFloat32,exports.toFloat32Tensor=d.toFloat32Tensor,exports.toTensor=d.toTensor,exports.topK=f.topK,exports.useCameraStream=e.useCameraStream,exports.useLiveLuminance=x.useLiveLuminance;
|
package/dist/vision.d.ts
CHANGED
|
@@ -116,6 +116,16 @@ export declare type CameraStreamErrorKind = "unsupported" | "permission-denied"
|
|
|
116
116
|
/** Lifecycle status of the camera stream. */
|
|
117
117
|
export declare type CameraStreamStatus = "idle" | "loading" | "ready" | "error";
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Infer how many classes a classification head emits.
|
|
121
|
+
*
|
|
122
|
+
* A classifier declares `(B, nc)`, so the count is the last static axis.
|
|
123
|
+
*
|
|
124
|
+
* @param shape Declared shape of the model's first output.
|
|
125
|
+
* @returns The class count, or `null` when the last axis is dynamic or absent.
|
|
126
|
+
*/
|
|
127
|
+
export declare function classificationNumClasses(shape: DeclaredShape): number | null;
|
|
128
|
+
|
|
119
129
|
/**
|
|
120
130
|
* Output of an image classification inference.
|
|
121
131
|
*/
|
|
@@ -191,11 +201,19 @@ export declare class Classifier extends VisionTask {
|
|
|
191
201
|
private readonly _applySoftmax;
|
|
192
202
|
private constructor();
|
|
193
203
|
/** Load the model and resolve labels. */
|
|
194
|
-
static create(model: ModelSource, options
|
|
204
|
+
static create(model: ModelSource, options?: ClassifierOptions): Promise<Classifier>;
|
|
195
205
|
/** Class labels indexed by class id. */
|
|
196
206
|
get labels(): readonly string[];
|
|
197
207
|
/** Class id → class name dict (matches Ultralytics' `model.names`). */
|
|
198
208
|
get names(): Readonly<Record<number, string>>;
|
|
209
|
+
/**
|
|
210
|
+
* The `[width, height]` this task preprocesses to.
|
|
211
|
+
*
|
|
212
|
+
* Resolved at creation time from the model's graph when it declares a static
|
|
213
|
+
* input, so reading it back tells you the resolution inference really runs at
|
|
214
|
+
* — not merely what was requested.
|
|
215
|
+
*/
|
|
216
|
+
get inputSize(): readonly [number, number];
|
|
199
217
|
/** Number of classes the model can predict. */
|
|
200
218
|
get numClasses(): number;
|
|
201
219
|
/** Alias for {@link predict} (parity with PyTorch `nn.Module.__call__`). */
|
|
@@ -207,14 +225,29 @@ export declare class Classifier extends VisionTask {
|
|
|
207
225
|
}
|
|
208
226
|
|
|
209
227
|
export declare interface ClassifierOptions extends OrtSessionOptions {
|
|
210
|
-
/** Class label spec — see {@link resolveLabels}. */
|
|
211
|
-
readonly labels: LabelSpec;
|
|
212
228
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
229
|
+
* Class label spec — see {@link resolveLabels}.
|
|
230
|
+
*
|
|
231
|
+
* Optional: when omitted, the names the export baked into the model are used
|
|
232
|
+
* (Ultralytics writes them as `names` in the metadata map). Only when the
|
|
233
|
+
* model carries none does this fall back to generated `class_<id>` labels.
|
|
234
|
+
* Passing a spec always wins, for a model whose names are wrong or absent.
|
|
235
|
+
*/
|
|
236
|
+
readonly labels?: LabelSpec;
|
|
237
|
+
/**
|
|
238
|
+
* Number of classes the model can predict.
|
|
239
|
+
*
|
|
240
|
+
* Optional: inferred from the classification head's declared output shape
|
|
241
|
+
* `(B, nc)`. Pass it to validate that the supplied labels match the model.
|
|
215
242
|
*/
|
|
216
243
|
readonly numClasses?: number;
|
|
217
|
-
/**
|
|
244
|
+
/**
|
|
245
|
+
* Model input `[width, height]` in pixels.
|
|
246
|
+
*
|
|
247
|
+
* Only used when the model's graph leaves its spatial axes dynamic: a graph
|
|
248
|
+
* that declares a static size always wins, since that is the only shape ONNX
|
|
249
|
+
* Runtime will accept. Defaults to `[224, 224]`.
|
|
250
|
+
*/
|
|
218
251
|
readonly inputSize?: readonly [number, number];
|
|
219
252
|
/** Per-channel RGB mean used for normalization. Defaults to ImageNet. */
|
|
220
253
|
readonly mean?: readonly [number, number, number];
|
|
@@ -277,6 +310,26 @@ export declare const COCO_CLASSES: readonly string[];
|
|
|
277
310
|
*/
|
|
278
311
|
export declare function computeImageLuminance(source: LuminanceSource, reusableCanvas?: HTMLCanvasElement): number;
|
|
279
312
|
|
|
313
|
+
/**
|
|
314
|
+
* One declared dimension: a number when the graph pins it, `null` when the
|
|
315
|
+
* dimension is symbolic (dynamic).
|
|
316
|
+
*/
|
|
317
|
+
export declare type DeclaredDim = number | null;
|
|
318
|
+
|
|
319
|
+
/** A declared input/output shape, dynamic axes appearing as `null`. */
|
|
320
|
+
export declare type DeclaredShape = readonly DeclaredDim[];
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Convert ORT value metadata into declared shapes.
|
|
324
|
+
*
|
|
325
|
+
* @param metadata Metadata as reported by `InferenceSession.inputMetadata`, or
|
|
326
|
+
* `undefined` on ORT builds that predate it (added in onnxruntime 1.21).
|
|
327
|
+
* @returns One shape per value, in declaration order. Non-tensor values and
|
|
328
|
+
* builds without metadata yield empty shapes, which read as "nothing
|
|
329
|
+
* declared" everywhere downstream.
|
|
330
|
+
*/
|
|
331
|
+
export declare function declaredShapesFrom(metadata: readonly ort.InferenceSession.ValueMetadata[] | undefined): readonly DeclaredShape[];
|
|
332
|
+
|
|
280
333
|
export declare interface DecodedAnchors {
|
|
281
334
|
/** Indices into the original `numAnchors` axis, in descending confidence order. */
|
|
282
335
|
readonly anchorIndices: Int32Array;
|
|
@@ -414,6 +467,19 @@ export declare type DecodeYoloV8SegOptions = DecodeYoloSegOptions;
|
|
|
414
467
|
*/
|
|
415
468
|
export declare const DEFAULT_PROVIDERS: readonly string[];
|
|
416
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Infer how many classes a YOLO detection/segmentation head emits.
|
|
472
|
+
*
|
|
473
|
+
* Such a head declares `(B, 4 + nc, N)` — four box coordinates stacked above one
|
|
474
|
+
* score per class, over `N` candidate anchors. `N` is in the thousands and the
|
|
475
|
+
* batch is 1, so the channel axis is the smallest static axis above 1.
|
|
476
|
+
*
|
|
477
|
+
* @param shape Declared shape of the model's first output.
|
|
478
|
+
* @returns The class count, or `null` when the shape leaves it undeterminable —
|
|
479
|
+
* fully dynamic, or too small to hold boxes plus at least one class.
|
|
480
|
+
*/
|
|
481
|
+
export declare function detectionNumClasses(shape: DeclaredShape): number | null;
|
|
482
|
+
|
|
417
483
|
/**
|
|
418
484
|
* Single detected object produced by an object-detection model.
|
|
419
485
|
*/
|
|
@@ -504,6 +570,14 @@ export declare class Detector extends VisionTask {
|
|
|
504
570
|
get labels(): readonly string[];
|
|
505
571
|
/** Class id → class name dict (matches Ultralytics' `model.names`). */
|
|
506
572
|
get names(): Readonly<Record<number, string>>;
|
|
573
|
+
/**
|
|
574
|
+
* The `[width, height]` this task preprocesses to.
|
|
575
|
+
*
|
|
576
|
+
* Resolved at creation time from the model's graph when it declares a static
|
|
577
|
+
* input, so reading it back tells you the resolution inference really runs at
|
|
578
|
+
* — not merely what was requested.
|
|
579
|
+
*/
|
|
580
|
+
get inputSize(): readonly [number, number];
|
|
507
581
|
/** Number of classes the model predicts. */
|
|
508
582
|
get numClasses(): number;
|
|
509
583
|
/**
|
|
@@ -548,7 +622,13 @@ export declare interface DetectorOptions extends OrtSessionOptions {
|
|
|
548
622
|
readonly labels?: LabelSpec;
|
|
549
623
|
/** Number of classes — used to validate the supplied labels. */
|
|
550
624
|
readonly numClasses?: number;
|
|
551
|
-
/**
|
|
625
|
+
/**
|
|
626
|
+
* Model input `[width, height]` in pixels for letterboxing.
|
|
627
|
+
*
|
|
628
|
+
* Only used when the model's graph leaves its spatial axes dynamic: a graph
|
|
629
|
+
* that declares a static size always wins, since that is the only shape ONNX
|
|
630
|
+
* Runtime will accept. Defaults to `[640, 640]`.
|
|
631
|
+
*/
|
|
552
632
|
readonly inputSize?: readonly [number, number];
|
|
553
633
|
/** Default minimum class score to keep a candidate. */
|
|
554
634
|
readonly confThreshold?: number;
|
|
@@ -737,6 +817,21 @@ export declare interface LetterboxResult {
|
|
|
737
817
|
export declare class ModelLoadError extends OrtVisionError {
|
|
738
818
|
}
|
|
739
819
|
|
|
820
|
+
/**
|
|
821
|
+
* Read the class names an export baked into the model metadata.
|
|
822
|
+
*
|
|
823
|
+
* Ultralytics writes `names` as the Python `repr` of a `dict[int, str]` — e.g.
|
|
824
|
+
* `"{0: 'deworm', 1: 'not_deworm'}"`. The value is parsed structurally (never
|
|
825
|
+
* evaluated), and anything unparseable, non-`dict`, or not keyed by contiguous
|
|
826
|
+
* integers from zero is rejected whole rather than half-applied: a partial name
|
|
827
|
+
* map would silently mislabel predictions.
|
|
828
|
+
*
|
|
829
|
+
* @param metadata A model's custom metadata map.
|
|
830
|
+
* @returns Class names in class-id order, or `null` when the model carries no
|
|
831
|
+
* usable `names` entry.
|
|
832
|
+
*/
|
|
833
|
+
export declare function modelNames(metadata: Readonly<Record<string, string>> | undefined): readonly string[] | null;
|
|
834
|
+
|
|
740
835
|
/** Anything `InferenceSession.create` accepts. */
|
|
741
836
|
export declare type ModelSource = string | ArrayBufferLike | Uint8Array;
|
|
742
837
|
|
|
@@ -763,18 +858,21 @@ export declare interface LetterboxResult {
|
|
|
763
858
|
/**
|
|
764
859
|
* Wrap an ONNX Runtime Web `InferenceSession` with convenient metadata access.
|
|
765
860
|
*
|
|
766
|
-
* The wrapper exposes input/output names
|
|
767
|
-
* selection,
|
|
861
|
+
* The wrapper exposes input/output names and the shapes the graph declares,
|
|
862
|
+
* manages execution-provider selection, provides a typed {@link OrtSession.run}
|
|
863
|
+
* method, and releases the native session through {@link OrtSession.release}.
|
|
768
864
|
*/
|
|
769
865
|
export declare class OrtSession {
|
|
770
866
|
private readonly _session;
|
|
771
867
|
readonly providers: readonly string[];
|
|
868
|
+
private readonly _metadata;
|
|
772
869
|
private constructor();
|
|
773
870
|
/**
|
|
774
871
|
* Load an ONNX model into an ORT inference session.
|
|
775
872
|
*
|
|
776
|
-
* @param model Either a URL string
|
|
777
|
-
* @param options Provider list
|
|
873
|
+
* @param model Either a URL string, or a `Uint8Array`/`ArrayBuffer` containing the model bytes.
|
|
874
|
+
* @param options Provider list, pass-through `SessionOptions`, and whether to
|
|
875
|
+
* read the model's metadata map (see {@link OrtSessionOptions.readMetadata}).
|
|
778
876
|
* @throws {@link ModelLoadError} if the model cannot be loaded.
|
|
779
877
|
*/
|
|
780
878
|
static create(model: ModelSource, options?: OrtSessionOptions): Promise<OrtSession>;
|
|
@@ -784,6 +882,51 @@ export declare interface LetterboxResult {
|
|
|
784
882
|
get inputName(): string;
|
|
785
883
|
/** Names of the model's outputs, in declaration order. */
|
|
786
884
|
get outputNames(): readonly string[];
|
|
885
|
+
/**
|
|
886
|
+
* Shapes the graph declares for its inputs, in declaration order.
|
|
887
|
+
*
|
|
888
|
+
* Dynamic (symbolic) axes appear as `null`. Empty shapes mean the runtime
|
|
889
|
+
* reported no metadata — either a non-tensor input, or an `onnxruntime-web`
|
|
890
|
+
* older than 1.21, which predates input metadata.
|
|
891
|
+
*/
|
|
892
|
+
get inputShapes(): readonly DeclaredShape[];
|
|
893
|
+
/**
|
|
894
|
+
* Shape the graph declares for its first input, dynamic axes as `null`.
|
|
895
|
+
*
|
|
896
|
+
* Empty when the runtime reports no metadata for it.
|
|
897
|
+
*/
|
|
898
|
+
get inputShape(): DeclaredShape;
|
|
899
|
+
/**
|
|
900
|
+
* Shapes the graph declares for its outputs, in declaration order.
|
|
901
|
+
*
|
|
902
|
+
* Dynamic (symbolic) axes appear as `null`. Reading them is how a task can
|
|
903
|
+
* tell how many classes a head emits without being told.
|
|
904
|
+
*/
|
|
905
|
+
get outputShapes(): readonly DeclaredShape[];
|
|
906
|
+
/**
|
|
907
|
+
* Shape the graph declares for its first output, dynamic axes as `null`.
|
|
908
|
+
*
|
|
909
|
+
* Empty when the runtime reports no metadata for it.
|
|
910
|
+
*/
|
|
911
|
+
get outputShape(): DeclaredShape;
|
|
912
|
+
/**
|
|
913
|
+
* The model's custom metadata map — `names`, `task`, `imgsz`, ... for an
|
|
914
|
+
* Ultralytics export.
|
|
915
|
+
*
|
|
916
|
+
* Read from the model's bytes at load time, since the runtime does not expose
|
|
917
|
+
* it. Empty when the session was created with `readMetadata: false`, from a
|
|
918
|
+
* URL that could not be fetched here, or from a model carrying no metadata.
|
|
919
|
+
*/
|
|
920
|
+
get metadata(): Readonly<Record<string, string>>;
|
|
921
|
+
/**
|
|
922
|
+
* Release the native session and free its memory.
|
|
923
|
+
*
|
|
924
|
+
* Call it when a session is discarded while the page lives on — rebuilding a
|
|
925
|
+
* task at a different input size, swapping in a newer model. A failure from
|
|
926
|
+
* the runtime is ignored: a session being torn down has nothing left to fail
|
|
927
|
+
* at, and the caller is already moving on.
|
|
928
|
+
*/
|
|
929
|
+
release(): Promise<void>;
|
|
787
930
|
/** The underlying `onnxruntime-web` session, for advanced use cases. */
|
|
788
931
|
get raw(): ort.InferenceSession;
|
|
789
932
|
/**
|
|
@@ -800,6 +943,17 @@ export declare interface LetterboxResult {
|
|
|
800
943
|
readonly providers?: readonly string[];
|
|
801
944
|
/** Optional ORT session options forwarded to `InferenceSession.create`. */
|
|
802
945
|
readonly sessionOptions?: ort.InferenceSession.SessionOptions;
|
|
946
|
+
/**
|
|
947
|
+
* Whether to read the model's custom metadata map (`names`, `task`, `imgsz`).
|
|
948
|
+
* Defaults to `true`.
|
|
949
|
+
*
|
|
950
|
+
* The runtime does not expose that map, so it is read from the file itself —
|
|
951
|
+
* which means a URL model is fetched here and handed to ORT as bytes instead
|
|
952
|
+
* of letting ORT fetch it. That is the same single download either way, and
|
|
953
|
+
* it is what lets a task resolve its labels off the model. Set to `false` to
|
|
954
|
+
* keep the URL path untouched and leave {@link OrtSession.metadata} empty.
|
|
955
|
+
*/
|
|
956
|
+
readonly readMetadata?: boolean;
|
|
803
957
|
}
|
|
804
958
|
|
|
805
959
|
/**
|
|
@@ -840,9 +994,57 @@ export declare interface LetterboxResult {
|
|
|
840
994
|
export declare class ProviderNotAvailableError extends OrtVisionError {
|
|
841
995
|
}
|
|
842
996
|
|
|
997
|
+
/**
|
|
998
|
+
* Read the metadata an exporter baked into a `.onnx` file.
|
|
999
|
+
*
|
|
1000
|
+
* `onnxruntime-web` exposes input/output metadata but **not** the model's
|
|
1001
|
+
* custom metadata map, which is where Ultralytics writes `names`, `task` and
|
|
1002
|
+
* `imgsz`. The Python SDK gets it for free from
|
|
1003
|
+
* `InferenceSession.get_modelmeta().custom_metadata_map`; in the browser the
|
|
1004
|
+
* only way to the same information is to read it out of the file, so this
|
|
1005
|
+
* module walks just enough of the ModelProto wire format to collect
|
|
1006
|
+
* `metadata_props`.
|
|
1007
|
+
*
|
|
1008
|
+
* It never throws and never allocates unbounded: a truncated, hostile or
|
|
1009
|
+
* simply unexpected file yields an empty map, and every caller treats that as
|
|
1010
|
+
* "the model says nothing", falling back to what it was given.
|
|
1011
|
+
*/
|
|
1012
|
+
/**
|
|
1013
|
+
* Collect a model's custom metadata map straight out of its bytes.
|
|
1014
|
+
*
|
|
1015
|
+
* @param model The `.onnx` file contents.
|
|
1016
|
+
* @returns Key/value metadata — `names`, `task`, `imgsz`, ... for an
|
|
1017
|
+
* Ultralytics export — or an empty object when the file carries none or
|
|
1018
|
+
* cannot be walked.
|
|
1019
|
+
*/
|
|
1020
|
+
export declare function readModelMetadata(model: Uint8Array | ArrayBufferLike): Readonly<Record<string, string>>;
|
|
1021
|
+
|
|
843
1022
|
/** Resize an image to `(targetWidth, targetHeight)` using high-quality canvas resampling. */
|
|
844
1023
|
export declare function resize(image: RGBImage, targetWidth: number, targetHeight: number): RGBImage;
|
|
845
1024
|
|
|
1025
|
+
/**
|
|
1026
|
+
* Decide the input size a task will preprocess to.
|
|
1027
|
+
*
|
|
1028
|
+
* Precedence is graph → caller → fallback. The graph wins over an explicit
|
|
1029
|
+
* `inputSize` because a static shape is not a preference, it is what ORT will
|
|
1030
|
+
* accept: honoring the caller there would only turn a fixable mismatch into a
|
|
1031
|
+
* failed run. A disagreement is a configuration bug in the caller, so it is
|
|
1032
|
+
* reported through `console.warn` instead of being swallowed.
|
|
1033
|
+
*
|
|
1034
|
+
* @param options Graph shape, requested size and per-task fallback.
|
|
1035
|
+
* @returns The `[width, height]` to preprocess to.
|
|
1036
|
+
*/
|
|
1037
|
+
export declare function resolveInputSize(options: ResolveInputSizeOptions): readonly [number, number];
|
|
1038
|
+
|
|
1039
|
+
export declare interface ResolveInputSizeOptions {
|
|
1040
|
+
/** Declared shape of the model's image input, from {@link declaredShapesFrom}. */
|
|
1041
|
+
readonly graphShape?: DeclaredShape;
|
|
1042
|
+
/** Size the caller asked for, if any. */
|
|
1043
|
+
readonly requested?: readonly [number, number];
|
|
1044
|
+
/** Size to use when neither the graph nor the caller pins one. */
|
|
1045
|
+
readonly fallback: readonly [number, number];
|
|
1046
|
+
}
|
|
1047
|
+
|
|
846
1048
|
/**
|
|
847
1049
|
* Resolve a labels specification into an ordered array of class names.
|
|
848
1050
|
*
|
|
@@ -991,6 +1193,14 @@ export declare interface LetterboxResult {
|
|
|
991
1193
|
get labels(): readonly string[];
|
|
992
1194
|
/** Class id → class name dict (matches Ultralytics' `model.names`). */
|
|
993
1195
|
get names(): Readonly<Record<number, string>>;
|
|
1196
|
+
/**
|
|
1197
|
+
* The `[width, height]` this task preprocesses to.
|
|
1198
|
+
*
|
|
1199
|
+
* Resolved at creation time from the model's graph when it declares a static
|
|
1200
|
+
* input, so reading it back tells you the resolution inference really runs at
|
|
1201
|
+
* — not merely what was requested.
|
|
1202
|
+
*/
|
|
1203
|
+
get inputSize(): readonly [number, number];
|
|
994
1204
|
/** Number of classes the model predicts. */
|
|
995
1205
|
get numClasses(): number;
|
|
996
1206
|
/** Alias for {@link predict} (parity with PyTorch `nn.Module.__call__`). */
|
|
@@ -1026,7 +1236,13 @@ export declare interface LetterboxResult {
|
|
|
1026
1236
|
readonly labels?: LabelSpec;
|
|
1027
1237
|
/** Number of classes — used to validate the supplied labels. */
|
|
1028
1238
|
readonly numClasses?: number;
|
|
1029
|
-
/**
|
|
1239
|
+
/**
|
|
1240
|
+
* Model input `[width, height]` in pixels for letterboxing.
|
|
1241
|
+
*
|
|
1242
|
+
* Only used when the model's graph leaves its spatial axes dynamic: a graph
|
|
1243
|
+
* that declares a static size always wins, since that is the only shape ONNX
|
|
1244
|
+
* Runtime will accept. Defaults to `[640, 640]`.
|
|
1245
|
+
*/
|
|
1030
1246
|
readonly inputSize?: readonly [number, number];
|
|
1031
1247
|
/** Default minimum class score to keep a candidate. */
|
|
1032
1248
|
readonly confThreshold?: number;
|
|
@@ -1054,6 +1270,16 @@ export declare interface LetterboxResult {
|
|
|
1054
1270
|
/** Apply numerically-stable softmax to a 1-D vector of logits. */
|
|
1055
1271
|
export declare function softmax(logits: Float32Array | readonly number[]): Float32Array;
|
|
1056
1272
|
|
|
1273
|
+
/**
|
|
1274
|
+
* Read the spatial input size out of a declared NCHW shape.
|
|
1275
|
+
*
|
|
1276
|
+
* @param shape The declared shape of the model's image input.
|
|
1277
|
+
* @returns `[width, height]` in pixels, or `null` when the shape is not 4D or
|
|
1278
|
+
* leaves either spatial axis dynamic — in which case the model accepts more
|
|
1279
|
+
* than one resolution and there is nothing to correct.
|
|
1280
|
+
*/
|
|
1281
|
+
export declare function spatialInputSize(shape: DeclaredShape): readonly [number, number] | null;
|
|
1282
|
+
|
|
1057
1283
|
/**
|
|
1058
1284
|
* Per-stage timing for a single `predict()` call.
|
|
1059
1285
|
*
|
package/dist/vision.js
CHANGED
|
@@ -4,18 +4,20 @@ import { BoundingBox as s, Mask as c, RGBImage as l } from "./vision/types.js";
|
|
|
4
4
|
import { SpeedTimer as u } from "./vision/core/timing.js";
|
|
5
5
|
import { Boxes as d, ClassificationResults as f, DetectionResults as p, Masks as m, Probs as h, SegmentationResults as g } from "./vision/results.js";
|
|
6
6
|
import { COCO_CLASSES as _, resolveLabels as v } from "./vision/labels.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
|
|
7
|
+
import { classificationNumClasses as y, declaredShapesFrom as b, detectionNumClasses as x, resolveInputSize as S, spatialInputSize as C } from "./vision/core/graph.js";
|
|
8
|
+
import { modelNames as w, readModelMetadata as T } from "./vision/core/metadata.js";
|
|
9
|
+
import { DEFAULT_PROVIDERS as E, resolveProviders as D } from "./vision/core/providers.js";
|
|
10
|
+
import { OrtSession as O } from "./vision/core/session.js";
|
|
11
|
+
import { loadImage as k } from "./vision/io/image.js";
|
|
12
|
+
import { fromCv2 as A, letterbox as j, normalize as M, resize as N, toCHW as P, toCv2 as F, toFloat32 as I, toFloat32Tensor as L, toTensor as R } from "./vision/preprocess/image.js";
|
|
13
|
+
import { softmax as z, topK as B } from "./vision/postprocess/classification.js";
|
|
14
|
+
import { batchedNms as V, decodeYolo as H, decodeYoloAnchors as U, decodeYoloV8 as W, decodeYoloV8Anchors as G, nms as K } from "./vision/postprocess/detection.js";
|
|
15
|
+
import { decodeYoloSeg as q, decodeYoloV8Seg as J } from "./vision/postprocess/segmentation.js";
|
|
16
|
+
import { VisionTask as Y } from "./vision/tasks/base.js";
|
|
17
|
+
import { Classifier as X } from "./vision/tasks/classifier.js";
|
|
18
|
+
import { Detector as Z } from "./vision/tasks/detector.js";
|
|
19
|
+
import { Segmenter as Q } from "./vision/tasks/segmenter.js";
|
|
20
|
+
import { VERSION as $ } from "./vision/index.js";
|
|
21
|
+
import { LUMINANCE_SAMPLE_MAX_EDGE as ee, LowLuminanceError as te, computeImageLuminance as ne, isLuminanceAcceptable as re } from "./vision/luminance.js";
|
|
22
|
+
import { useLiveLuminance as ie } from "./vision/use-live-luminance.js";
|
|
23
|
+
export { s as BoundingBox, d as Boxes, _ as COCO_CLASSES, f as ClassificationResults, X as Classifier, E as DEFAULT_PROVIDERS, p as DetectionResults, Z as Detector, t as ImageLoadError, n as InferenceError, ee as LUMINANCE_SAMPLE_MAX_EDGE, r as LabelMapError, te as LowLuminanceError, c as Mask, m as Masks, i as ModelLoadError, O as OrtSession, a as OrtVisionError, h as Probs, o as ProviderNotAvailableError, l as RGBImage, g as SegmentationResults, Q as Segmenter, u as SpeedTimer, $ as VERSION, Y as VisionTask, V as batchedNms, y as classificationNumClasses, ne as computeImageLuminance, b as declaredShapesFrom, H as decodeYolo, U as decodeYoloAnchors, q as decodeYoloSeg, W as decodeYoloV8, G as decodeYoloV8Anchors, J as decodeYoloV8Seg, x as detectionNumClasses, A as fromCv2, re as isLuminanceAcceptable, j as letterbox, k as loadImage, w as modelNames, K as nms, M as normalize, T as readModelMetadata, N as resize, S as resolveInputSize, v as resolveLabels, D as resolveProviders, z as softmax, C as spatialInputSize, P as toCHW, F as toCv2, I as toFloat32, L as toFloat32Tensor, R as toTensor, B as topK, e as useCameraStream, ie as useLiveLuminance };
|