web-sdk-pp-detection 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](#english) | [在线 Demo](https://chenmohan123.github.io/web-sdk-PP-Detection/)
4
4
 
5
- 基于 ONNX Runtime Web 的浏览器端 PP-Detection 版面分析 SDK,支持 PC、移动端与各类 H5 页面。
5
+ 基于 ONNX Runtime Web 的浏览器端 PP-Detection 目标检测 SDK,支持 PC、移动端与各类 H5 页面。
6
6
 
7
7
  ## 安装
8
8
 
@@ -25,16 +25,15 @@ const detector = await createPPDetection({
25
25
  const result = await detector.detect(file, {
26
26
  threshold: 0.5,
27
27
  classThresholds: {
28
- formula: 0.4,
29
- table: 0.55,
30
- text: 0.6
28
+ person: 0.6,
29
+ car: 0.5
31
30
  }
32
31
  });
33
32
  console.log(result.detections, result.runtime, result.timings);
34
33
  await detector.dispose();
35
34
  ```
36
35
 
37
- `classThresholds` 按 manifest 标签名称覆盖置信度过滤阈值,未配置的类别回退到 `threshold`。全局 `threshold` 仍用于 mask 二值化和多边形提取。未知类别名称或超出 `0` 到 `1` 的值会被拒绝。
36
+ `classThresholds` 按 manifest 标签名称覆盖目标检测置信度过滤阈值,未配置的类别回退到全局 `threshold`。未知类别名称或超出 `0` 到 `1` 的值会被拒绝。
38
37
 
39
38
  当前发布版本内置 PicoDet 1.0.1 FP32 stable manifest;使用默认模型时可以省略 `model`。自定义模型仍应传入经过验证的 runtime manifest 或清单对象。
40
39
 
@@ -42,7 +41,7 @@ await detector.dispose();
42
41
 
43
42
  ## 运行后端与精度
44
43
 
45
- - `backend: "auto"` 优先使用 WebGPU;只有显式设置 `allowFallback: true` 时,WebGPU 会话失败才会尝试 WASM(CPU)。也可手动指定 `"webgpu"` 或 `"wasm"`。
44
+ - `backend: "auto"` 优先使用 WebGPU;设置 `allowFallback: true` 后,WebGPU 会话或推理失败才会尝试 WASM(CPU)。也可手动指定 `"webgpu"` 或 `"wasm"`。
46
45
  - `precision: "auto"` 选择清单中可用的默认稳定精度;当前默认 PicoDet 仅验证 FP32,因此不会臆测切换到 FP16。已验证其他精度时可手动指定 `"fp16"`、`"int8"` 等。
47
46
  - 默认 PicoDet 1.0.1 FP32 已发布可下载的 stable ONNX 资产,并通过 Linux WASM 与 Windows NVIDIA WebGPU 七张 fixture 验证;FP16、INT8、INT4 和 FP8 仍需独立证据。
48
47
  - 使用默认模型时,显式请求清单中未声明的组合会抛出 `CAPABILITY_UNSUPPORTED`,不会改写无效组合;自定义清单可在单独验证后声明其他组合。上游模型是 float32,不支持 FP64;FP32 约为 FP16 两倍大小并可能更慢、更占显存。
@@ -75,7 +74,7 @@ const detector = await createPPDetection({
75
74
 
76
75
  ## English
77
76
 
78
- A browser-first PP-Detection document layout analysis SDK powered by ONNX Runtime Web for desktop, mobile, and H5 pages.
77
+ A browser-first PP-Detection object detection SDK powered by ONNX Runtime Web for desktop, mobile, and H5 pages.
79
78
 
80
79
  ### Installation
81
80
 
@@ -98,16 +97,15 @@ const detector = await createPPDetection({
98
97
  const result = await detector.detect(file, {
99
98
  threshold: 0.5,
100
99
  classThresholds: {
101
- formula: 0.4,
102
- table: 0.55,
103
- text: 0.6
100
+ person: 0.6,
101
+ car: 0.5
104
102
  }
105
103
  });
106
104
  console.log(result.detections, result.runtime, result.timings);
107
105
  await detector.dispose();
108
106
  ```
109
107
 
110
- `classThresholds` overrides confidence filtering for matching manifest label names and falls back to `threshold` for unspecified classes. The global `threshold` still controls mask binarization and polygon extraction. Unknown class names and values outside `0` through `1` are rejected.
108
+ `classThresholds` overrides object-detection confidence filtering for matching manifest label names and falls back to the global `threshold` for unspecified classes. Unknown class names and values outside `0` through `1` are rejected.
111
109
 
112
110
  The release includes a built-in PicoDet 1.0.1 FP32 stable manifest, so `model` may be omitted for the default model. Pass a verified runtime or custom manifest when using another model.
113
111
 
@@ -115,7 +113,7 @@ Detailed initialization timings are available through `detector.loadTimings`. `t
115
113
 
116
114
  ### Backend and precision
117
115
 
118
- - `backend: "auto"` prefers WebGPU; only `allowFallback: true` permits a failed WebGPU session to try WASM (CPU). Use `"webgpu"` or `"wasm"` for an explicit choice.
116
+ - `backend: "auto"` prefers WebGPU; with `allowFallback: true`, a failed WebGPU session or inference attempts WASM (CPU). Use `"webgpu"` or `"wasm"` for an explicit choice.
119
117
  - `precision: "auto"` selects the available default stable precision from the manifest. The default PicoDet 1.0.1 validates FP32 only, so the SDK does not guess an FP16 switch. When another precision has been validated, select it explicitly with `"fp16"`, `"int8"`, and so on.
120
118
  - The default PicoDet 1.0.1 FP32 variant is a downloadable stable ONNX asset and has passed seven-fixture validation on Linux WASM and Windows NVIDIA WebGPU. FP16, INT8, INT4, and FP8 still require independent release-source and backend evidence.
121
119
  - `detect` accepts image Blob/File, Canvas/ImageData, `HTMLVideoElement`, or a single `VideoFrame`. Hosts own camera/video permissions and frame pacing; await each frame Promise and call `dispose()` when media stops.
@@ -15323,14 +15323,15 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15323
15323
  function mapError(error, phase) {
15324
15324
  if (error instanceof PPDetectionError) return error;
15325
15325
  const message = error instanceof Error ? error.message : String(error);
15326
+ const details = { phase, causeMessage: message };
15326
15327
  if (/abort|cancel/i.test(message))
15327
- return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", { phase }, { cause: error });
15328
+ return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", details, { cause: error });
15328
15329
  if (/memory|out.of.memory|allocation/i.test(message))
15329
- return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", { phase }, { cause: error });
15330
+ return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", details, { cause: error });
15330
15331
  return new PPDetectionError(
15331
15332
  phase === "create" ? "SESSION_CREATE_FAILED" : "INFERENCE_FAILED",
15332
15333
  phase === "create" ? "\u521B\u5EFA ONNX Runtime \u4F1A\u8BDD\u5931\u8D25" : "ONNX Runtime \u63A8\u7406\u5931\u8D25",
15333
- { phase },
15334
+ details,
15334
15335
  { cause: error }
15335
15336
  );
15336
15337
  }
@@ -15488,14 +15489,13 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15488
15489
  function fail(code, message, details) {
15489
15490
  throw new PPDetectionError(code, message, details);
15490
15491
  }
15491
- function candidatesForBackend(requested, capabilities, allowFallback) {
15492
+ function candidatesForBackend(requested, capabilities) {
15492
15493
  if (requested === "wasm") return ["wasm"];
15493
15494
  if (requested === "webgpu")
15494
15495
  return capabilities.webgpu ? ["webgpu"] : fail("CAPABILITY_UNSUPPORTED", "\u8BF7\u6C42\u7684 webgpu \u4E0D\u53EF\u7528", { requestedBackend: requested });
15495
15496
  const available = [];
15496
15497
  if (capabilities.webgpu) available.push("webgpu");
15497
15498
  available.push("wasm");
15498
- if (!allowFallback) return available.slice(0, 1);
15499
15499
  return available;
15500
15500
  }
15501
15501
  function selectExecutionPlan(options, capabilities, manifest) {
@@ -15513,19 +15513,18 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15513
15513
  requestedPrecision
15514
15514
  });
15515
15515
  }
15516
- const candidates = candidatesForBackend(
15517
- requestedBackend,
15518
- capabilities,
15519
- options.allowFallback === true
15520
- ).filter((backend) => variant3.backends.includes(backend));
15521
- if (candidates.length === 0) {
15516
+ const candidates = candidatesForBackend(requestedBackend, capabilities).filter(
15517
+ (backend) => variant3.backends.includes(backend)
15518
+ );
15519
+ const selectedCandidates = options.allowFallback === true ? candidates : candidates.slice(0, 1);
15520
+ if (selectedCandidates.length === 0) {
15522
15521
  fail("CAPABILITY_UNSUPPORTED", "\u6CA1\u6709\u4E0E\u6A21\u578B\u53D8\u4F53\u5339\u914D\u7684\u53EF\u7528\u540E\u7AEF", {
15523
15522
  requestedBackend,
15524
15523
  requestedPrecision,
15525
15524
  availableBackends: variant3.backends
15526
15525
  });
15527
15526
  }
15528
- const actualBackend = candidates[0];
15527
+ const actualBackend = selectedCandidates[0];
15529
15528
  return {
15530
15529
  variantId: variant3.id,
15531
15530
  requestedBackend,
@@ -15533,7 +15532,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15533
15532
  requestedPrecision,
15534
15533
  actualPrecision: variant3.precision,
15535
15534
  executionMode,
15536
- candidates: candidates.map((backend) => ({
15535
+ candidates: selectedCandidates.map((backend) => ({
15537
15536
  variantId: variant3.id,
15538
15537
  backend,
15539
15538
  precision: variant3.precision,
@@ -15688,7 +15687,7 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15688
15687
  };
15689
15688
 
15690
15689
  // src/index.ts
15691
- var CURRENT_SDK_VERSION = "0.1.0";
15690
+ var CURRENT_SDK_VERSION = "0.1.1";
15692
15691
  function probePPDetectionCapabilities(options = {}) {
15693
15692
  return probeCapabilities(options);
15694
15693
  }
@@ -15814,7 +15813,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15814
15813
  cache: options.cache === false ? false : options.cache === "memory" ? "memory" : void 0
15815
15814
  });
15816
15815
  let executor;
15817
- let activeBridge;
15818
15816
  try {
15819
15817
  const loadStartedAt = now4();
15820
15818
  options.onProgress?.({ phase: "model", status: "start" });
@@ -15847,29 +15845,16 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15847
15845
  }
15848
15846
  options.onProgress?.({ phase: "model", status: "complete" });
15849
15847
  const fallbacks = [];
15850
- let selectedPlan = plan;
15851
- let sessionMs = 0;
15852
- for (const candidate of plan.candidates) {
15853
- const candidatePlan = {
15854
- ...plan,
15855
- variantId: candidate.variantId,
15856
- actualBackend: candidate.backend,
15857
- actualPrecision: candidate.precision,
15858
- executionMode: candidate.executionMode,
15859
- candidates: [candidate]
15860
- };
15848
+ const createExecutorForPlan = async (candidatePlan) => {
15861
15849
  options.onProgress?.({ phase: "session", status: "start" });
15850
+ let bridge;
15862
15851
  try {
15863
- if (candidate.executionMode === "worker") {
15852
+ if (candidatePlan.executionMode === "worker") {
15864
15853
  if (typeof Worker !== "function")
15865
15854
  throw new PPDetectionError("CAPABILITY_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Worker");
15866
- const worker = new Worker(workerUrl(), {
15867
- type: "module"
15868
- });
15869
- const bridge = new WorkerBridge(worker);
15870
- activeBridge = bridge;
15871
- const workerModelBytes = modelBytes.slice(0);
15872
- await bridge.load(workerModelBytes, candidatePlan, {
15855
+ const worker = new Worker(workerUrl(), { type: "module" });
15856
+ bridge = new WorkerBridge(worker);
15857
+ await bridge.load(modelBytes.slice(0), candidatePlan, {
15873
15858
  onProgress: (event) => options.onProgress?.({
15874
15859
  phase: "session",
15875
15860
  status: event.status
@@ -15879,35 +15864,65 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15879
15864
  numThreads: options.ort?.wasm?.numThreads
15880
15865
  }
15881
15866
  });
15882
- executor = {
15883
- run(input, signal) {
15884
- return bridge.run(
15885
- { [input.inputName]: { data: input.data, dims: input.dims } },
15886
- { signal }
15887
- );
15888
- },
15889
- dispose: () => bridge.dispose()
15890
- };
15891
- activeBridge = void 0;
15892
- } else {
15893
- const session = await createOrtSession(modelBytes, candidatePlan, {
15894
- ort: options.ort?.module,
15895
- wasmPaths: options.ort?.wasm?.paths,
15896
- numThreads: options.ort?.wasm?.numThreads
15897
- });
15898
- sessionMs = session.sessionMs;
15899
- executor = {
15867
+ const activeBridge = bridge;
15868
+ options.onProgress?.({ phase: "session", status: "complete" });
15869
+ return {
15900
15870
  run(input, signal) {
15901
- return session.run(
15871
+ return activeBridge.run(
15902
15872
  { [input.inputName]: { data: input.data, dims: input.dims } },
15903
15873
  { signal }
15904
15874
  );
15905
15875
  },
15906
- dispose: () => session.dispose()
15876
+ dispose: () => activeBridge.dispose()
15907
15877
  };
15908
15878
  }
15909
- selectedPlan = candidatePlan;
15879
+ const session = await createOrtSession(modelBytes, candidatePlan, {
15880
+ ort: options.ort?.module,
15881
+ wasmPaths: options.ort?.wasm?.paths,
15882
+ numThreads: options.ort?.wasm?.numThreads
15883
+ });
15884
+ sessionMs = session.sessionMs;
15910
15885
  options.onProgress?.({ phase: "session", status: "complete" });
15886
+ return {
15887
+ run(input, signal) {
15888
+ return session.run(
15889
+ { [input.inputName]: { data: input.data, dims: input.dims } },
15890
+ { signal }
15891
+ );
15892
+ },
15893
+ dispose: () => session.dispose()
15894
+ };
15895
+ } catch (error) {
15896
+ try {
15897
+ await bridge?.dispose();
15898
+ } catch {
15899
+ }
15900
+ if (error instanceof PPDetectionError) throw error;
15901
+ const message = error instanceof Error ? error.message : String(error);
15902
+ throw new PPDetectionError(
15903
+ "SESSION_CREATE_FAILED",
15904
+ "\u521B\u5EFA ONNX Runtime \u4F1A\u8BDD\u5931\u8D25",
15905
+ { phase: "create", causeMessage: message },
15906
+ { cause: error }
15907
+ );
15908
+ }
15909
+ };
15910
+ let selectedPlan = plan;
15911
+ let sessionMs = 0;
15912
+ let selectedCandidateIndex = -1;
15913
+ for (const [candidateIndex, candidate] of plan.candidates.entries()) {
15914
+ const candidatePlan = {
15915
+ ...plan,
15916
+ variantId: candidate.variantId,
15917
+ actualBackend: candidate.backend,
15918
+ actualPrecision: candidate.precision,
15919
+ executionMode: candidate.executionMode,
15920
+ candidates: [candidate]
15921
+ };
15922
+ try {
15923
+ executor = await createExecutorForPlan(candidatePlan);
15924
+ selectedPlan = candidatePlan;
15925
+ selectedCandidateIndex = candidateIndex;
15911
15926
  break;
15912
15927
  } catch (error) {
15913
15928
  const mapped = error instanceof PPDetectionError ? error : new PPDetectionError("SESSION_CREATE_FAILED", String(error));
@@ -15917,11 +15932,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15917
15932
  } catch {
15918
15933
  }
15919
15934
  executor = void 0;
15920
- try {
15921
- await activeBridge?.dispose();
15922
- } catch {
15923
- }
15924
- activeBridge = void 0;
15925
15935
  if (!hasNext) {
15926
15936
  throw mapped;
15927
15937
  }
@@ -15939,22 +15949,67 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15939
15949
  }
15940
15950
  }
15941
15951
  if (!executor) throw new PPDetectionError("SESSION_CREATE_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u68C0\u6D4B Session");
15942
- const loadedExecutor = executor;
15952
+ const runtime = {
15953
+ requestedBackend: options.backend ?? "auto",
15954
+ backend: selectedPlan.actualBackend,
15955
+ precision: selectedPlan.actualPrecision,
15956
+ mode: selectedPlan.executionMode,
15957
+ fallbacks,
15958
+ capabilities
15959
+ };
15960
+ let activeExecutor = executor;
15961
+ const fallbackExecutor = {
15962
+ async run(input, signal) {
15963
+ try {
15964
+ return await activeExecutor.run(input, signal);
15965
+ } catch (error) {
15966
+ if (options.allowFallback !== true || selectedCandidateIndex < 0 || selectedCandidateIndex >= plan.candidates.length - 1) {
15967
+ throw error;
15968
+ }
15969
+ const failedCandidate = plan.candidates[selectedCandidateIndex];
15970
+ const mapped = error instanceof PPDetectionError ? error : new PPDetectionError("INFERENCE_FAILED", String(error), {}, { cause: error });
15971
+ if (mapped.code === "ABORTED") throw mapped;
15972
+ const nextCandidate = plan.candidates[selectedCandidateIndex + 1];
15973
+ const nextPlan = {
15974
+ ...plan,
15975
+ variantId: nextCandidate.variantId,
15976
+ actualBackend: nextCandidate.backend,
15977
+ actualPrecision: nextCandidate.precision,
15978
+ executionMode: nextCandidate.executionMode,
15979
+ candidates: [nextCandidate]
15980
+ };
15981
+ await activeExecutor.dispose();
15982
+ const nextExecutor = await createExecutorForPlan(nextPlan);
15983
+ activeExecutor = nextExecutor;
15984
+ selectedCandidateIndex += 1;
15985
+ selectedPlan = nextPlan;
15986
+ runtime.backend = nextPlan.actualBackend;
15987
+ runtime.precision = nextPlan.actualPrecision;
15988
+ runtime.mode = nextPlan.executionMode;
15989
+ const fallback = {
15990
+ cause: mapped.cause ?? mapped,
15991
+ code: mapped.code,
15992
+ message: mapped.message,
15993
+ precision: failedCandidate.precision,
15994
+ provider: failedCandidate.backend,
15995
+ stage: "inference",
15996
+ variantId: failedCandidate.variantId
15997
+ };
15998
+ fallbacks.push(fallback);
15999
+ options.onProgress?.({ phase: "fallback", status: "complete", fallback });
16000
+ return activeExecutor.run(input, signal);
16001
+ }
16002
+ },
16003
+ dispose: () => activeExecutor.dispose()
16004
+ };
15943
16005
  loadTimings = { ...loadTimings, sessionMs, totalMs: now4() - loadStartedAt };
15944
16006
  const detector = new PPDetectionDetectorImplementation({
15945
16007
  capabilities,
15946
16008
  manifest: runtimeManifest,
15947
16009
  model: modelInfo(runtimeManifest, variant3.id, actualSource),
15948
- runtime: {
15949
- requestedBackend: options.backend ?? "auto",
15950
- backend: selectedPlan.actualBackend,
15951
- precision: selectedPlan.actualPrecision,
15952
- mode: selectedPlan.executionMode,
15953
- fallbacks,
15954
- capabilities
15955
- },
16010
+ runtime,
15956
16011
  loadTimings,
15957
- loadExecutor: () => Promise.resolve(loadedExecutor),
16012
+ loadExecutor: () => Promise.resolve(fallbackExecutor),
15958
16013
  onProgress: options.onProgress,
15959
16014
  clearCurrentModelCache: () => modelManager.clearCurrentModelCache(),
15960
16015
  clearAllCache: () => modelManager.clearAllCache(),
@@ -15969,10 +16024,6 @@ ${u}`, c = n.createShaderModule({ code: d, label: e.name });
15969
16024
  await executor?.dispose();
15970
16025
  } catch {
15971
16026
  }
15972
- try {
15973
- await activeBridge?.dispose();
15974
- } catch {
15975
- }
15976
16027
  try {
15977
16028
  await modelManager.dispose();
15978
16029
  } catch {