{"version":3,"file":"detectClassify.cjs","names":[],"sources":["../../../src/vision/tasks/detectClassify.ts"],"sourcesContent":["/** @generated Vendored from @mauriciobenjamin700/ort-vision-sdk-web. Do not hand-edit — regenerate with `npm run vendor:vision`. */\n/**\n * Run a fused detect→classify pipeline in the browser.\n *\n * The file this loads was built by the Python SDK's `ort_vision_sdk.compose`,\n * and it already contains both models plus the crop-and-resize bridge between\n * them. That matters far more in a tab than on a server: two models mean two\n * `.onnx` downloads, two WASM/WebGPU session initializations, and a per-crop\n * round trip through JavaScript to slice, resize and restack the regions before\n * the second model can see them. A fused pipeline has one download, one session\n * and no round trip — the crops are produced and consumed inside the graph.\n */\n\nimport type * as ort from \"onnxruntime-web\";\n\nimport { FusionError } from \"../core/exceptions\";\nimport { type ModelSource, type OrtSessionOptions, OrtSession } from \"../core/session\";\nimport { SpeedTimer } from \"../core/timing\";\nimport {\n    INPUT_IMAGE,\n    INPUT_PAD,\n    INPUT_SCALE,\n    INPUT_SOURCE,\n    OUTPUT_BOXES,\n    OUTPUT_CLASSES,\n    OUTPUT_NUM_DETECTIONS,\n    OUTPUT_PROBS,\n    OUTPUT_SCORES,\n    type FusionSpec,\n    readFusionSpec,\n} from \"../fusion\";\nimport { type ImageInput, loadImage } from \"../io/image\";\nimport { type LabelSpec, resolveLabels } from \"../labels\";\nimport { softmax, topK } from \"../postprocess/classification\";\nimport { toCHW, toFloat32, toFloat32Tensor } from \"../preprocess/image\";\nimport { LetterboxPipeline, zeroTensorData } from \"../preprocess/pipeline\";\nimport { Boxes, DetectClassifyResults } from \"../results\";\nimport {\n    BoundingBox,\n    RGBImage,\n    type ClassProbability,\n    type ClassificationResult,\n    type DetectionResult,\n} from \"../types\";\nimport { VisionTask, requireDetections } from \"./base\";\n\nexport interface DetectClassifyOptions extends OrtSessionOptions {\n    /**\n     * Class label spec for the **detection** stage — see {@link resolveLabels}.\n     * Defaults to the names recorded at fusion time, falling back to the COCO\n     * 80-class preset when the fusion recorded none.\n     */\n    readonly labels?: LabelSpec;\n    /**\n     * Class label spec for the **classification** stage. Defaults to the recorded\n     * names, falling back to generated `class_<id>` names.\n     */\n    readonly classifierLabels?: LabelSpec;\n    /**\n     * If `true`, a run that finds nothing throws {@link NoDetectionsError}\n     * instead of returning an empty envelope. Default `false`, because looking\n     * and finding nothing is a successful inference. Turn it on when an empty\n     * result means the surrounding pipeline should stop rather than carry on with\n     * zero rows. Can be overridden per `predict` call.\n     */\n    readonly raiseOnEmpty?: boolean;\n}\n\nexport interface DetectClassifyPredictOptions {\n    /**\n     * Drop detections scoring below this. The graph's own NMS threshold was fixed\n     * at fusion time and cannot be lowered here — this only filters further.\n     */\n    readonly confThreshold?: number;\n    /** If set, keep only detections whose detector `classId` is in this list. */\n    readonly classes?: readonly number[];\n    /** Truncate each detection's `classification.probabilities` to its top-k entries. */\n    readonly topK?: number;\n    /** Override the constructor's `raiseOnEmpty` setting for this call. */\n    readonly raiseOnEmpty?: boolean;\n}\n\n/**\n * Detector and classifier running as a single ONNX model.\n *\n * Everything the pipeline needs to know about itself — the resolution to\n * letterbox to, whether it wants the full-resolution image as well, whether its\n * classifier output still needs a softmax, the class names of both stages — was\n * written into the file at fusion time and is read back here. Nothing is\n * restated on the JavaScript side, so nothing can drift out of step with the\n * Python side that built it.\n *\n * @example\n * ```typescript\n * const pipeline = await DetectClassify.create(\"/models/pipeline.onnx\");\n * const result = (await pipeline.predict(\"/images/flock.jpg\"))[0];\n * for (const detection of result) {\n *   console.log(detection.name, detection.conf, detection.classification?.name);\n * }\n * ```\n */\nexport class DetectClassify extends VisionTask {\n    private constructor(\n        session: OrtSession,\n        private readonly _spec: FusionSpec,\n        private readonly _labels: readonly string[],\n        private readonly _names: Readonly<Record<number, string>>,\n        private readonly _classifierLabels: readonly string[],\n        private readonly _classifierNames: Readonly<Record<number, string>>,\n        private readonly _raiseOnEmpty: boolean,\n    ) {\n        super(session);\n    }\n\n    private _pipelineCache: LetterboxPipeline | null = null;\n\n    /**\n     * Run the model once on zero-filled inputs, paying one-time costs up front.\n     *\n     * Worth more here than on a single-stage task: a fused pipeline is two models\n     * plus the bridge in one graph, so the first inference compiles shaders for\n     * all of it. Calling this while a loading spinner is still up moves that cost\n     * somewhere the user is already waiting.\n     *\n     * @param runs How many warm-up inferences to run. One is enough for WASM;\n     *   WebGPU sometimes settles on the second.\n     */\n    async warmup(runs: number = 1): Promise<void> {\n        const [width, height] = this._spec.inputSize;\n        for (let i = 0; i < runs; i++) {\n            const feeds: Record<string, ort.Tensor> = {\n                [INPUT_IMAGE]: toFloat32Tensor(zeroTensorData(width, height), [\n                    1,\n                    3,\n                    height,\n                    width,\n                ]),\n            };\n            if (this._spec.needsSourceImage) {\n                feeds[INPUT_SOURCE] = toFloat32Tensor(zeroTensorData(width, height), [\n                    1,\n                    3,\n                    height,\n                    width,\n                ]);\n                feeds[INPUT_SCALE] = toFloat32Tensor(new Float32Array([1]), [1]);\n                feeds[INPUT_PAD] = toFloat32Tensor(new Float32Array([0, 0]), [2]);\n            }\n            await this._session.run(feeds);\n        }\n    }\n\n    /**\n     * The fused preprocessing pipeline, built on first use.\n     *\n     * Lazily, because constructing it allocates canvases: a pipeline built in an\n     * environment without a canvas implementation stays constructible, and only\n     * fails if it is actually asked to preprocess something.\n     */\n    private get _pipeline(): LetterboxPipeline {\n        if (this._pipelineCache === null) {\n            const [width, height] = this._spec.inputSize;\n            this._pipelineCache = new LetterboxPipeline(width, height);\n        }\n        return this._pipelineCache;\n    }\n\n    /**\n     * Load a fused pipeline and resolve both label spaces.\n     *\n     * @param model The fused `.onnx` — a URL, an `ArrayBuffer`, or bytes.\n     * @param options Label overrides plus the usual session options.\n     * @throws {@link FusionError} when the model carries no pipeline metadata,\n     *   i.e. it is a plain detector or classifier rather than something\n     *   `ort_vision_sdk.compose` produced.\n     */\n    static async create(\n        model: ModelSource,\n        options: DetectClassifyOptions = {},\n    ): Promise<DetectClassify> {\n        const session = await OrtSession.create(model, options);\n        const spec = readFusionSpec(session.metadata);\n        if (spec === null) {\n            throw new FusionError(\n                \"This model carries no fused-pipeline metadata, so DetectClassify cannot tell how to \" +\n                    \"drive it. Build one with ort_vision_sdk.compose.fuse_detect_classify, or load a \" +\n                    \"plain model with Detector/Classifier instead.\",\n            );\n        }\n\n        const labels = resolveLabels(options.labels ?? spec.detectorNames ?? \"coco\");\n        const classifierLabels = resolveLabels(options.classifierLabels ?? spec.classifierNames, {\n            numClasses: classifierClasses(session) ?? undefined,\n        });\n        return new DetectClassify(\n            session,\n            spec,\n            labels,\n            indexNames(labels),\n            classifierLabels,\n            indexNames(classifierLabels),\n            options.raiseOnEmpty ?? false,\n        );\n    }\n\n    /** The pipeline configuration recorded in the model at fusion time. */\n    get spec(): FusionSpec {\n        return this._spec;\n    }\n\n    /** The `[width, height]` the detection stage runs at. */\n    get inputSize(): readonly [number, number] {\n        return this._spec.inputSize;\n    }\n\n    /** Detector class labels indexed by class id. */\n    get labels(): readonly string[] {\n        return this._labels;\n    }\n\n    /** Detector class id → class name (matches Ultralytics' `model.names`). */\n    get names(): Readonly<Record<number, string>> {\n        return this._names;\n    }\n\n    /** Classifier class labels indexed by class id. */\n    get classifierLabels(): readonly string[] {\n        return this._classifierLabels;\n    }\n\n    /** Classifier class id → class name. */\n    get classifierNames(): Readonly<Record<number, string>> {\n        return this._classifierNames;\n    }\n\n    /**\n     * Alias for {@link predict} — call the pipeline like a torch `nn.Module`.\n     *\n     * Use as `pipeline.call(img)` since JavaScript class instances are not\n     * callable; for direct invocation, prefer `pipeline.predict(img)`.\n     */\n    async call(\n        image: ImageInput,\n        options: DetectClassifyPredictOptions = {},\n    ): Promise<DetectClassifyResults[]> {\n        return this.predict(image, options);\n    }\n\n    /**\n     * Run the pipeline on a single image.\n     *\n     * The returned envelope carries a {@link Speed} breakdown in `speed`. Its\n     * `inference` figure covers detection *and* classification, since the\n     * pipeline runs them as one graph and no boundary between them is observable\n     * from outside.\n     */\n    async predict(\n        image: ImageInput,\n        options: DetectClassifyPredictOptions = {},\n    ): Promise<DetectClassifyResults[]> {\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 { feeds, scale, padLeft, padTop } = this._preprocess(original);\n        timer.stage(\"preprocess\");\n        const outputs = await this._session.run(feeds);\n        this._pipeline.release();\n        timer.stage(\"inference\");\n\n        const probsTensor = output(outputs, OUTPUT_PROBS);\n        const boxes = floats(outputs, OUTPUT_BOXES);\n        const scores = floats(outputs, OUTPUT_SCORES);\n        const classes = integers(outputs, OUTPUT_CLASSES);\n        const probs = probsTensor.data as Float32Array;\n        const reported = integers(outputs, OUTPUT_NUM_DETECTIONS)[0] ?? 0;\n        const rows = Math.min(reported, Math.floor(boxes.length / 4));\n        const classCount = probsTensor.dims[probsTensor.dims.length - 1] ?? 0;\n\n        const allowed = options.classes === undefined ? null : new Set(options.classes);\n        const floor = options.confThreshold ?? 0;\n        const detections: DetectionResult[] = [];\n        for (let row = 0; row < rows; row++) {\n            const classId = classes[row] ?? 0;\n            const confidence = scores[row] ?? 0;\n            if (confidence < floor || (allowed !== null && !allowed.has(classId))) continue;\n\n            const bbox = this._toOriginal(boxes, row, { scale, padLeft, padTop, original });\n            const cropped = crop(original, bbox);\n            detections.push(\n                detection(\n                    classId,\n                    this._names[classId] ?? `class_${classId}`,\n                    confidence,\n                    bbox,\n                    cropped,\n                    this._classify(\n                        probs.subarray(row * classCount, (row + 1) * classCount),\n                        cropped,\n                        options.topK,\n                    ),\n                ),\n            );\n        }\n\n        requireDetections(detections.length, {\n            raiseOnEmpty: options.raiseOnEmpty ?? this._raiseOnEmpty,\n            confThreshold: Math.max(floor, this._spec.confThreshold),\n            classes: options.classes,\n            path,\n        });\n\n        const origShape: readonly [number, number] = [original.height, original.width];\n        timer.stage(\"postprocess\");\n        return [\n            new DetectClassifyResults(\n                bulkBoxes(detections, origShape),\n                detections,\n                this._names,\n                this._classifierNames,\n                original,\n                origShape,\n                path,\n                timer.speed(),\n            ),\n        ];\n    }\n\n    /**\n     * Letterbox the image and build the graph's feeds.\n     *\n     * The detector input runs through {@link LetterboxPipeline}, which fuses the\n     * resize, the padding and the HWC-to-CHW float conversion into one\n     * `drawImage` plus one readback, and reuses its output buffer between frames.\n     * That buffer goes straight to ONNX Runtime, so `_pipeline.release()` must not\n     * be called until the run resolves.\n     *\n     * A pipeline fused with `cropSource: \"original\"` also takes the untouched\n     * image as a second input, plus the scale and padding of the letterbox — that\n     * is what lets the graph undo the letterbox transform internally and crop at\n     * native resolution instead of from the downscaled copy. That one is **not**\n     * letterboxed by definition, so it does not go through the fused path.\n     */\n    private _preprocess(image: RGBImage): {\n        feeds: Record<string, ort.Tensor>;\n        scale: number;\n        padLeft: number;\n        padTop: number;\n    } {\n        const [width, height] = this._spec.inputSize;\n        const boxed = this._pipeline.run(image);\n        const feeds: Record<string, ort.Tensor> = {\n            [INPUT_IMAGE]: toFloat32Tensor(boxed.data, [1, 3, height, width]),\n        };\n\n        if (this._spec.needsSourceImage) {\n            feeds[INPUT_SOURCE] = tensorOf(image);\n            feeds[INPUT_SCALE] = toFloat32Tensor(new Float32Array([boxed.scale]), [1]);\n            feeds[INPUT_PAD] = toFloat32Tensor(\n                new Float32Array([boxed.padLeft, boxed.padTop]),\n                [2],\n            );\n        }\n        return { feeds, scale: boxed.scale, padLeft: boxed.padLeft, padTop: boxed.padTop };\n    }\n\n    /**\n     * Map one letterboxed xyxy row back onto the original image.\n     *\n     * The graph always reports boxes in the detector's letterboxed pixel space,\n     * whichever crop source it was fused with, so both sources agree here.\n     */\n    private _toOriginal(\n        boxes: Float32Array,\n        row: number,\n        context: {\n            scale: number;\n            padLeft: number;\n            padTop: number;\n            original: RGBImage;\n        },\n    ): BoundingBox {\n        const { scale, padLeft, padTop, original } = context;\n        const at = (offset: number): number => boxes[row * 4 + offset] ?? 0;\n        const clampX = (value: number): number => Math.min(Math.max(value, 0), original.width);\n        const clampY = (value: number): number => Math.min(Math.max(value, 0), original.height);\n        return new BoundingBox(\n            clampX((at(0) - padLeft) / scale),\n            clampY((at(1) - padTop) / scale),\n            clampX((at(2) - padLeft) / scale),\n            clampY((at(3) - padTop) / scale),\n        );\n    }\n\n    /**\n     * Turn one row of the classifier output into a result object.\n     *\n     * @param row The output row for this detection.\n     * @param image The crop the row describes, carried so callers can display\n     *   what was classified.\n     * @param k Optional truncation of the probability list.\n     */\n    private _classify(\n        row: Float32Array,\n        image: RGBImage,\n        k: number | undefined,\n    ): ClassificationResult {\n        const scores = this._spec.applySoftmax ? softmax(row) : row;\n        const { indices, values } = topK(scores, k ?? null);\n        const probabilities: ClassProbability[] = [];\n        for (let i = 0; i < indices.length; i++) {\n            const classId = indices[i] ?? 0;\n            const probability = values[i] ?? 0;\n            const className = this._classifierLabels[classId] ?? `class_${classId}`;\n            probabilities.push({\n                classId,\n                className,\n                probability,\n                cls: classId,\n                name: className,\n                conf: probability,\n            });\n        }\n        const top = probabilities[0] ?? {\n            classId: 0,\n            className: \"class_0\",\n            probability: 0,\n            cls: 0,\n            name: \"class_0\",\n            conf: 0,\n        };\n        return {\n            classId: top.classId,\n            className: top.className,\n            confidence: top.probability,\n            cls: top.classId,\n            name: top.className,\n            conf: top.probability,\n            image,\n            probabilities,\n        };\n    }\n}\n\n/**\n * Read the classifier stage's class count off the `probs` output shape.\n *\n * @param session The loaded pipeline session.\n * @returns The class count, or `null` when the graph leaves that axis dynamic\n *   or declares no `probs` output — in which case label resolution falls back\n *   to whatever the fusion recorded.\n */\nfunction classifierClasses(session: OrtSession): number | null {\n    const index = session.outputNames.indexOf(OUTPUT_PROBS);\n    if (index < 0) return null;\n    const shape = session.outputShapes[index];\n    if (shape === undefined || shape.length === 0) return null;\n    return shape[shape.length - 1] ?? null;\n}\n\n/**\n * Build a class id → name record from an ordered label list.\n *\n * @param labels Labels indexed by class id.\n * @returns The equivalent record.\n */\nfunction indexNames(labels: readonly string[]): Readonly<Record<number, string>> {\n    const names: Record<number, string> = {};\n    for (let i = 0; i < labels.length; i++) names[i] = labels[i] as string;\n    return names;\n}\n\n/**\n * Fetch an output tensor by name.\n *\n * @param outputs The run's results.\n * @param name The output's name in the pipeline contract.\n * @returns The tensor.\n * @throws {@link FusionError} when the graph does not carry that output, which\n *   means the file is not a pipeline this version can drive.\n */\nfunction output(outputs: Record<string, ort.Tensor>, name: string): ort.Tensor {\n    const tensor = outputs[name];\n    if (tensor === undefined) {\n        throw new FusionError(`The fused pipeline is missing its '${name}' output.`);\n    }\n    return tensor;\n}\n\n/**\n * Fetch a float output by name.\n *\n * @param outputs The run's results.\n * @param name The output's name in the pipeline contract.\n * @returns The output's data.\n * @throws {@link FusionError} when the graph does not carry that output.\n */\nfunction floats(outputs: Record<string, ort.Tensor>, name: string): Float32Array {\n    return output(outputs, name).data as Float32Array;\n}\n\n/**\n * Fetch an integer output by name, normalizing ORT's 64-bit representation.\n *\n * ONNX Runtime Web returns `int64` tensors as `BigInt64Array`, whose values do\n * not compare or index like numbers. Class ids and detection counts are always\n * small, so widening them to `number` here is lossless and keeps every caller\n * free of `BigInt` handling.\n *\n * @param outputs The run's results.\n * @param name The output's name in the pipeline contract.\n * @returns The output's values as plain numbers.\n * @throws {@link FusionError} when the graph does not carry that output.\n */\nfunction integers(outputs: Record<string, ort.Tensor>, name: string): number[] {\n    const data = output(outputs, name).data as BigInt64Array | Int32Array | Float32Array;\n    const values: number[] = [];\n    for (let i = 0; i < data.length; i++) values.push(Number(data[i]));\n    return values;\n}\n\n/**\n * Convert an image to the `[1, 3, H, W]` float32 tensor the graph expects.\n *\n * @param image The image to convert.\n * @returns The batched CHW tensor, scaled to `[0, 1]`.\n */\nfunction tensorOf(image: RGBImage): ort.Tensor {\n    const chw = toCHW(toFloat32(image), image.width, image.height, 3);\n    return toFloat32Tensor(chw, [1, 3, image.height, image.width]);\n}\n\n/**\n * Cut the box region out of the original image.\n *\n * @param image The source image.\n * @param bbox The box, in original-image pixel coordinates.\n * @returns The cropped region, or a zero-sized image for a box with no area.\n */\nfunction crop(image: RGBImage, bbox: BoundingBox): RGBImage {\n    const [rawX1, rawY1, rawX2, rawY2] = bbox.asIntXyxy();\n    const x1 = Math.max(0, rawX1);\n    const y1 = Math.max(0, rawY1);\n    const x2 = Math.min(image.width, rawX2);\n    const y2 = Math.min(image.height, rawY2);\n    if (x2 <= x1 || y2 <= y1) return new RGBImage(new Uint8Array(0), 0, 0);\n\n    const width = x2 - x1;\n    const height = y2 - y1;\n    const out = new Uint8Array(width * height * 3);\n    for (let row = 0; row < height; row++) {\n        const offset = ((y1 + row) * image.width + x1) * 3;\n        out.set(image.data.subarray(offset, offset + width * 3), row * width * 3);\n    }\n    return new RGBImage(out, width, height);\n}\n\n/**\n * Assemble one detection, filling the Ultralytics-style aliases.\n *\n * @param classId Detector class index.\n * @param className Detector class name.\n * @param confidence Detection score.\n * @param bbox Box in original-image coordinates.\n * @param croppedImage The region the box covers.\n * @param classification What the classification stage said about that region.\n * @returns The detection object.\n */\nfunction detection(\n    classId: number,\n    className: string,\n    confidence: number,\n    bbox: BoundingBox,\n    croppedImage: RGBImage,\n    classification: ClassificationResult,\n): DetectionResult {\n    return {\n        classId,\n        className,\n        confidence,\n        bbox,\n        cls: classId,\n        name: className,\n        conf: confidence,\n        box: bbox,\n        croppedImage,\n        classification,\n    };\n}\n\n/**\n * Assemble the bulk-array `Boxes` view from per-instance detections.\n *\n * @param detections The surviving detections.\n * @param origShape `[height, width]` of the original image.\n * @returns The bulk view, empty when nothing survived.\n */\nfunction bulkBoxes(\n    detections: readonly DetectionResult[],\n    origShape: readonly [number, number],\n): Boxes {\n    const xyxy = new Float32Array(detections.length * 4);\n    const cls = new Int32Array(detections.length);\n    const conf = new Float32Array(detections.length);\n    detections.forEach((entry, index) => {\n        const [x1, y1, x2, y2] = entry.bbox.xyxy;\n        xyxy[index * 4] = x1;\n        xyxy[index * 4 + 1] = y1;\n        xyxy[index * 4 + 2] = x2;\n        xyxy[index * 4 + 3] = y2;\n        cls[index] = entry.classId;\n        conf[index] = entry.confidence;\n    });\n    return new Boxes(xyxy, cls, conf, origShape);\n}\n"],"mappings":"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"}