quadqr-js 1.5.2 → 1.5.3

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
@@ -1104,7 +1104,7 @@ Scans one frame from an HTML video element. By default, if the video is displaye
1104
1104
 
1105
1105
  ### `startCameraScanner(video, options?)`
1106
1106
 
1107
- Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. Modern browsers use a **dual-worker camera engine**: a lightweight fresh-frame worker continuously runs normal finder/geometry/decode attempts, while an independent recovery worker retains the complete high-resolution, Auto Color, precise-alignment, perspective, multi-frame, ECC, and damaged-code recovery stack. A slow recovery attempt therefore cannot prevent the fast worker from inspecting a newer camera frame. Finder detection remains JavaScript; optional WASM accelerates grayscale/binary preprocessing and CRC beneath the same detector. The scheduler uses `requestVideoFrameCallback()` when available and does not queue stale fast-path frames. Normal camera acquisition requests an environment camera around 1280×720 and crops/resizes the visible preview to a 640 px working bitmap **before** transferring it to the worker. Once a candidate validates structure, Spectrum ECC, and CRC, scanning returns immediately. If the fast worker misses, full recovery runs concurrently on a fresh frame at up to 960 px. Strong finder evidence dispatches recovery quickly; finder-less frames still receive periodic full recovery so severe color casts or damaged locators retain the same rescue paths. QuadQR Auto Color crop profiles, center-weighted histograms, threshold bracketing, precise alignment, projective recovery, QR-region enhancement, multi-frame confidence fusion, and soft-decision Spectrum ECC are unchanged. `cameraHighResolutionMaxDimension` defaults to 960. The optional `onDiagnostic(event)` callback exposes whether an event came from the fast or recovery worker, finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact frame that decoded, including enhanced recovery pixels when applicable, so UIs can keep the frozen frame and finder overlay aligned.
1107
+ Starts a reusable live-camera scanning loop. On supported browsers it requests continuous focus/exposure/white-balance camera modes and scans the CSS-visible preview crop. Modern browsers use a **dual-worker camera engine**: a lightweight fresh-frame worker continuously runs normal finder/geometry/decode attempts, while an independent recovery worker retains the complete high-resolution, Auto Color, precise-alignment, perspective, multi-frame, ECC, and damaged-code recovery stack. A slow recovery attempt therefore cannot prevent the fast worker from inspecting a newer camera frame. Finder detection remains JavaScript; optional WASM accelerates grayscale/binary preprocessing and CRC beneath the same detector. The scheduler uses `requestVideoFrameCallback()` when available and does not queue stale fast-path frames. Normal camera acquisition requests an environment camera around 1280×720 and crops/resizes the visible preview to a 640 px working bitmap **before** transferring it to the worker. Once a candidate validates structure, Spectrum ECC, and CRC, scanning returns immediately. If the fast worker misses, full recovery runs concurrently on a fresh frame at up to 960 px. Strong finder evidence dispatches recovery quickly. Finder-less frames remain on the lightweight fresh-frame detector only, so pointing the camera at an empty scene never starts Auto Color or deeper recovery work. QuadQR Auto Color crop profiles, center-weighted histograms, threshold bracketing, precise alignment, projective recovery, QR-region enhancement, multi-frame confidence fusion, and soft-decision Spectrum ECC are unchanged. `cameraHighResolutionMaxDimension` defaults to 960. The optional `onDiagnostic(event)` callback exposes whether an event came from the fast or recovery worker, finder candidates, active locator method, crop/geometry/version hypothesis, recovery method, timing, and scan dimensions. `onResult(result, frame)` receives the exact frame that decoded, including enhanced recovery pixels when applicable, so UIs can keep the frozen frame and finder overlay aligned.
1108
1108
 
1109
1109
  ### `getVersionInfo(version, options?)`
1110
1110
 
@@ -326,9 +326,10 @@ function processFrame(bitmap, source, frameNumber) {
326
326
  let allowFinderRecovery = false;
327
327
  let allowAutoEnhance = false;
328
328
  try {
329
- allowFinderRecovery = scanOptions.finderRecovery !== false &&
329
+ const fastPipeline = scanOptions.cameraPipelineMode === "fast";
330
+ allowFinderRecovery = !fastPipeline && scanOptions.finderRecovery !== false &&
330
331
  missStreak > 0 && ((missStreak - 1) % cameraFinderRecoveryEvery === 0);
331
- allowAutoEnhance = scanOptions.autoEnhanceRecovery !== false &&
332
+ allowAutoEnhance = !fastPipeline && scanOptions.autoEnhanceRecovery !== false &&
332
333
  missStreak > 0 && ((missStreak - 1) % cameraAutoEnhanceEvery === 0);
333
334
  const method = allowAutoEnhance
334
335
  ? "progressive-color-recovery"
@@ -326,9 +326,10 @@ function processFrame(bitmap, source, frameNumber) {
326
326
  let allowFinderRecovery = false;
327
327
  let allowAutoEnhance = false;
328
328
  try {
329
- allowFinderRecovery = scanOptions.finderRecovery !== false &&
329
+ const fastPipeline = scanOptions.cameraPipelineMode === "fast";
330
+ allowFinderRecovery = !fastPipeline && scanOptions.finderRecovery !== false &&
330
331
  missStreak > 0 && ((missStreak - 1) % cameraFinderRecoveryEvery === 0);
331
- allowAutoEnhance = scanOptions.autoEnhanceRecovery !== false &&
332
+ allowAutoEnhance = !fastPipeline && scanOptions.autoEnhanceRecovery !== false &&
332
333
  missStreak > 0 && ((missStreak - 1) % cameraAutoEnhanceEvery === 0);
333
334
  const method = allowAutoEnhance
334
335
  ? "progressive-color-recovery"
@@ -6422,6 +6422,12 @@ async function startCameraScannerWorker(video, options = {}) {
6422
6422
  const fastWorkerOptions = {
6423
6423
  ...options,
6424
6424
  cameraPipelineMode: "fast",
6425
+ // The fresh-frame worker is intentionally detection/decode only. Recovery
6426
+ // must never become more expensive merely because the camera has been
6427
+ // looking at an empty scene for a while. The parallel recovery worker is
6428
+ // armed only after finder/geometry evidence says a QuadQR candidate is in
6429
+ // view.
6430
+ finderRecovery: false,
6425
6431
  cameraHighResolutionRecovery: false,
6426
6432
  cameraAutoColorRecovery: false,
6427
6433
  autoEnhanceRecovery: false,
@@ -6464,7 +6470,7 @@ async function startCameraScannerWorker(video, options = {}) {
6464
6470
  );
6465
6471
  const recoveryStrongFinderInterval = Math.max(80, Number(options.cameraRecoveryStrongFinderInterval ?? 120));
6466
6472
  const recoveryWeakFinderInterval = Math.max(recoveryStrongFinderInterval, Number(options.cameraRecoveryWeakFinderInterval ?? 260));
6467
- const recoveryNoFinderInterval = Math.max(recoveryWeakFinderInterval, Number(options.cameraRecoveryNoFinderInterval ?? 850));
6473
+ const recoveryWeakFinderFrames = Math.max(2, Math.round(options.cameraRecoveryWeakFinderFrames ?? 2));
6468
6474
  const useVideoFrameCallback = options.useVideoFrameCallback !== false &&
6469
6475
  typeof video.requestVideoFrameCallback === "function";
6470
6476
 
@@ -6481,6 +6487,7 @@ async function startCameraScannerWorker(video, options = {}) {
6481
6487
  let frameNumber = 0;
6482
6488
  let requestToken = 0;
6483
6489
  let recoveryToken = 0;
6490
+ let weakFinderStreak = 0;
6484
6491
 
6485
6492
  const diagnosticsEnabled = typeof options.onDiagnostic === "function";
6486
6493
  const emitDiagnostic = (event) => {
@@ -6532,7 +6539,7 @@ async function startCameraScannerWorker(video, options = {}) {
6532
6539
  emitDiagnostic({
6533
6540
  type: "camera-ready",
6534
6541
  method: "camera-dual-worker",
6535
- message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + parallel recovery`,
6542
+ message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + candidate-gated parallel recovery`,
6536
6543
  camera: {
6537
6544
  width: settings.width ?? video.videoWidth,
6538
6545
  height: settings.height ?? video.videoHeight,
@@ -6616,9 +6623,7 @@ async function startCameraScannerWorker(video, options = {}) {
6616
6623
  method: "parallel-full-recovery",
6617
6624
  frame: triggerFrame,
6618
6625
  finderCount,
6619
- message: finderCount > 0
6620
- ? `Fast frame saw ${finderCount} finder${finderCount === 1 ? "" : "s"} · full recovery running in parallel`
6621
- : "Periodic full recovery running in parallel while fast fresh-frame scanning continues"
6626
+ message: `QuadQR candidate detected (${finderCount} finder${finderCount === 1 ? "" : "s"}) · full recovery running in parallel`
6622
6627
  });
6623
6628
 
6624
6629
  const recoveryPayload = { bitmap, source: captured.source, frame: triggerFrame };
@@ -6661,12 +6666,26 @@ async function startCameraScannerWorker(video, options = {}) {
6661
6666
  const maybeDispatchRecovery = (workerResult, triggerFrame) => {
6662
6667
  if (stopped || recoveryBusy) return;
6663
6668
  const finderCount = maximumFinderCount(workerResult);
6669
+
6670
+ // Do not run Auto Color, high-resolution, multi-frame, or damaged-code
6671
+ // recovery just because time has passed. An empty scene stays on the cheap
6672
+ // fresh-frame detector forever. Two or more finders are strong evidence and
6673
+ // arm recovery immediately. One finder is treated as weak evidence and must
6674
+ // persist across consecutive fresh frames before recovery is allowed.
6675
+ let minimumInterval = null;
6676
+ if (finderCount >= 2) {
6677
+ weakFinderStreak = 0;
6678
+ minimumInterval = recoveryStrongFinderInterval;
6679
+ } else if (finderCount === 1) {
6680
+ weakFinderStreak++;
6681
+ if (weakFinderStreak < recoveryWeakFinderFrames) return;
6682
+ minimumInterval = recoveryWeakFinderInterval;
6683
+ } else {
6684
+ weakFinderStreak = 0;
6685
+ return;
6686
+ }
6687
+
6664
6688
  const elapsed = nowMs() - lastRecoveryStartedAt;
6665
- const minimumInterval = finderCount >= 2
6666
- ? recoveryStrongFinderInterval
6667
- : finderCount === 1
6668
- ? recoveryWeakFinderInterval
6669
- : recoveryNoFinderInterval;
6670
6689
  if (elapsed >= minimumInterval) void runRecovery(triggerFrame, finderCount);
6671
6690
  };
6672
6691
 
package/dist/quadqr.js CHANGED
@@ -12607,6 +12607,12 @@ async function startCameraScannerWorker(video, options = {}) {
12607
12607
  const fastWorkerOptions = {
12608
12608
  ...options,
12609
12609
  cameraPipelineMode: "fast",
12610
+ // The fresh-frame worker is intentionally detection/decode only. Recovery
12611
+ // must never become more expensive merely because the camera has been
12612
+ // looking at an empty scene for a while. The parallel recovery worker is
12613
+ // armed only after finder/geometry evidence says a QuadQR candidate is in
12614
+ // view.
12615
+ finderRecovery: false,
12610
12616
  cameraHighResolutionRecovery: false,
12611
12617
  cameraAutoColorRecovery: false,
12612
12618
  autoEnhanceRecovery: false,
@@ -12649,7 +12655,7 @@ async function startCameraScannerWorker(video, options = {}) {
12649
12655
  );
12650
12656
  const recoveryStrongFinderInterval = Math.max(80, Number(options.cameraRecoveryStrongFinderInterval ?? 120));
12651
12657
  const recoveryWeakFinderInterval = Math.max(recoveryStrongFinderInterval, Number(options.cameraRecoveryWeakFinderInterval ?? 260));
12652
- const recoveryNoFinderInterval = Math.max(recoveryWeakFinderInterval, Number(options.cameraRecoveryNoFinderInterval ?? 850));
12658
+ const recoveryWeakFinderFrames = Math.max(2, Math.round(options.cameraRecoveryWeakFinderFrames ?? 2));
12653
12659
  const useVideoFrameCallback = options.useVideoFrameCallback !== false &&
12654
12660
  typeof video.requestVideoFrameCallback === "function";
12655
12661
 
@@ -12666,6 +12672,7 @@ async function startCameraScannerWorker(video, options = {}) {
12666
12672
  let frameNumber = 0;
12667
12673
  let requestToken = 0;
12668
12674
  let recoveryToken = 0;
12675
+ let weakFinderStreak = 0;
12669
12676
 
12670
12677
  const diagnosticsEnabled = typeof options.onDiagnostic === "function";
12671
12678
  const emitDiagnostic = (event) => {
@@ -12717,7 +12724,7 @@ async function startCameraScannerWorker(video, options = {}) {
12717
12724
  emitDiagnostic({
12718
12725
  type: "camera-ready",
12719
12726
  method: "camera-dual-worker",
12720
- message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + parallel recovery`,
12727
+ message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + candidate-gated parallel recovery`,
12721
12728
  camera: {
12722
12729
  width: settings.width ?? video.videoWidth,
12723
12730
  height: settings.height ?? video.videoHeight,
@@ -12801,9 +12808,7 @@ async function startCameraScannerWorker(video, options = {}) {
12801
12808
  method: "parallel-full-recovery",
12802
12809
  frame: triggerFrame,
12803
12810
  finderCount,
12804
- message: finderCount > 0
12805
- ? `Fast frame saw ${finderCount} finder${finderCount === 1 ? "" : "s"} · full recovery running in parallel`
12806
- : "Periodic full recovery running in parallel while fast fresh-frame scanning continues"
12811
+ message: `QuadQR candidate detected (${finderCount} finder${finderCount === 1 ? "" : "s"}) · full recovery running in parallel`
12807
12812
  });
12808
12813
 
12809
12814
  const recoveryPayload = { bitmap, source: captured.source, frame: triggerFrame };
@@ -12846,12 +12851,26 @@ async function startCameraScannerWorker(video, options = {}) {
12846
12851
  const maybeDispatchRecovery = (workerResult, triggerFrame) => {
12847
12852
  if (stopped || recoveryBusy) return;
12848
12853
  const finderCount = maximumFinderCount(workerResult);
12854
+
12855
+ // Do not run Auto Color, high-resolution, multi-frame, or damaged-code
12856
+ // recovery just because time has passed. An empty scene stays on the cheap
12857
+ // fresh-frame detector forever. Two or more finders are strong evidence and
12858
+ // arm recovery immediately. One finder is treated as weak evidence and must
12859
+ // persist across consecutive fresh frames before recovery is allowed.
12860
+ let minimumInterval = null;
12861
+ if (finderCount >= 2) {
12862
+ weakFinderStreak = 0;
12863
+ minimumInterval = recoveryStrongFinderInterval;
12864
+ } else if (finderCount === 1) {
12865
+ weakFinderStreak++;
12866
+ if (weakFinderStreak < recoveryWeakFinderFrames) return;
12867
+ minimumInterval = recoveryWeakFinderInterval;
12868
+ } else {
12869
+ weakFinderStreak = 0;
12870
+ return;
12871
+ }
12872
+
12849
12873
  const elapsed = nowMs() - lastRecoveryStartedAt;
12850
- const minimumInterval = finderCount >= 2
12851
- ? recoveryStrongFinderInterval
12852
- : finderCount === 1
12853
- ? recoveryWeakFinderInterval
12854
- : recoveryNoFinderInterval;
12855
12874
  if (elapsed >= minimumInterval) void runRecovery(triggerFrame, finderCount);
12856
12875
  };
12857
12876
 
@@ -12428,6 +12428,12 @@ async function startCameraScannerWorker(video, options = {}) {
12428
12428
  const fastWorkerOptions = {
12429
12429
  ...options,
12430
12430
  cameraPipelineMode: "fast",
12431
+ // The fresh-frame worker is intentionally detection/decode only. Recovery
12432
+ // must never become more expensive merely because the camera has been
12433
+ // looking at an empty scene for a while. The parallel recovery worker is
12434
+ // armed only after finder/geometry evidence says a QuadQR candidate is in
12435
+ // view.
12436
+ finderRecovery: false,
12431
12437
  cameraHighResolutionRecovery: false,
12432
12438
  cameraAutoColorRecovery: false,
12433
12439
  autoEnhanceRecovery: false,
@@ -12470,7 +12476,7 @@ async function startCameraScannerWorker(video, options = {}) {
12470
12476
  );
12471
12477
  const recoveryStrongFinderInterval = Math.max(80, Number(options.cameraRecoveryStrongFinderInterval ?? 120));
12472
12478
  const recoveryWeakFinderInterval = Math.max(recoveryStrongFinderInterval, Number(options.cameraRecoveryWeakFinderInterval ?? 260));
12473
- const recoveryNoFinderInterval = Math.max(recoveryWeakFinderInterval, Number(options.cameraRecoveryNoFinderInterval ?? 850));
12479
+ const recoveryWeakFinderFrames = Math.max(2, Math.round(options.cameraRecoveryWeakFinderFrames ?? 2));
12474
12480
  const useVideoFrameCallback = options.useVideoFrameCallback !== false &&
12475
12481
  typeof video.requestVideoFrameCallback === "function";
12476
12482
 
@@ -12487,6 +12493,7 @@ async function startCameraScannerWorker(video, options = {}) {
12487
12493
  let frameNumber = 0;
12488
12494
  let requestToken = 0;
12489
12495
  let recoveryToken = 0;
12496
+ let weakFinderStreak = 0;
12490
12497
 
12491
12498
  const diagnosticsEnabled = typeof options.onDiagnostic === "function";
12492
12499
  const emitDiagnostic = (event) => {
@@ -12538,7 +12545,7 @@ async function startCameraScannerWorker(video, options = {}) {
12538
12545
  emitDiagnostic({
12539
12546
  type: "camera-ready",
12540
12547
  method: "camera-dual-worker",
12541
- message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + parallel recovery`,
12548
+ message: `Camera ready · ${settings.width ?? video.videoWidth}×${settings.height ?? video.videoHeight} · fast fresh-frame scanner + candidate-gated parallel recovery`,
12542
12549
  camera: {
12543
12550
  width: settings.width ?? video.videoWidth,
12544
12551
  height: settings.height ?? video.videoHeight,
@@ -12622,9 +12629,7 @@ async function startCameraScannerWorker(video, options = {}) {
12622
12629
  method: "parallel-full-recovery",
12623
12630
  frame: triggerFrame,
12624
12631
  finderCount,
12625
- message: finderCount > 0
12626
- ? `Fast frame saw ${finderCount} finder${finderCount === 1 ? "" : "s"} · full recovery running in parallel`
12627
- : "Periodic full recovery running in parallel while fast fresh-frame scanning continues"
12632
+ message: `QuadQR candidate detected (${finderCount} finder${finderCount === 1 ? "" : "s"}) · full recovery running in parallel`
12628
12633
  });
12629
12634
 
12630
12635
  const recoveryPayload = { bitmap, source: captured.source, frame: triggerFrame };
@@ -12667,12 +12672,26 @@ async function startCameraScannerWorker(video, options = {}) {
12667
12672
  const maybeDispatchRecovery = (workerResult, triggerFrame) => {
12668
12673
  if (stopped || recoveryBusy) return;
12669
12674
  const finderCount = maximumFinderCount(workerResult);
12675
+
12676
+ // Do not run Auto Color, high-resolution, multi-frame, or damaged-code
12677
+ // recovery just because time has passed. An empty scene stays on the cheap
12678
+ // fresh-frame detector forever. Two or more finders are strong evidence and
12679
+ // arm recovery immediately. One finder is treated as weak evidence and must
12680
+ // persist across consecutive fresh frames before recovery is allowed.
12681
+ let minimumInterval = null;
12682
+ if (finderCount >= 2) {
12683
+ weakFinderStreak = 0;
12684
+ minimumInterval = recoveryStrongFinderInterval;
12685
+ } else if (finderCount === 1) {
12686
+ weakFinderStreak++;
12687
+ if (weakFinderStreak < recoveryWeakFinderFrames) return;
12688
+ minimumInterval = recoveryWeakFinderInterval;
12689
+ } else {
12690
+ weakFinderStreak = 0;
12691
+ return;
12692
+ }
12693
+
12670
12694
  const elapsed = nowMs() - lastRecoveryStartedAt;
12671
- const minimumInterval = finderCount >= 2
12672
- ? recoveryStrongFinderInterval
12673
- : finderCount === 1
12674
- ? recoveryWeakFinderInterval
12675
- : recoveryNoFinderInterval;
12676
12695
  if (elapsed >= minimumInterval) void runRecovery(triggerFrame, finderCount);
12677
12696
  };
12678
12697
 
package/docs/API.md CHANGED
@@ -209,7 +209,7 @@ const result = scanVideoFrame(video);
209
209
 
210
210
  ### `startCameraScanner(video, options?)`
211
211
 
212
- Starts live camera scanning. On browsers that expose camera controls, QuadQR requests continuous autofocus, exposure, and white balance. Modern browsers use two module Web Workers by default: a **fast fresh-frame worker** for normal finder/geometry/decode attempts and a separate **full-recovery worker** for the same perspective, color, high-resolution, ECC, damaged-code, and multi-frame recovery stack used by the main scanner. The recovery worker is lazy and independent, so a difficult old frame cannot block the fast worker from inspecting a newer camera frame. Finder detection itself is JavaScript; WASM only accelerates deterministic grayscale/binary preprocessing and CRC. The loop uses `requestVideoFrameCallback()` when available, drops stale fast-path frames, and measures `scanInterval` from scan start. Worker mode defaults to a 33 ms minimum cadence; automatic main-thread fallback uses 80 ms. The default camera request is approximately 1280×720, while the CSS-visible preview crop is resized to a 640 px working bitmap before crossing the worker boundary. Finder acquisition uses the streaming 1:1:3:1:1 RGB-value pass, direct cross-checks, local-threshold fallback, and directional module-size/version estimation. Once a geometry candidate validates structure, ECC, and CRC, it returns immediately. A miss with strong finder evidence dispatches parallel full recovery quickly; frames with weak or zero finder evidence still receive periodic full recovery so color-cast/damaged locator cases remain recoverable. That recovery can use up to 960 px and retains the full QuadQR Auto Color crop sequence, center-weighted color recovery, threshold bracketing, precise alignment, perspective recovery, affine cross-channel calibration, QR-region enhancement, multi-frame confidence fusion, erasure decoding, and soft-decision Spectrum ECC.
212
+ Starts live camera scanning. On browsers that expose camera controls, QuadQR requests continuous autofocus, exposure, and white balance. Modern browsers use two module Web Workers by default: a **fast fresh-frame worker** for normal finder/geometry/decode attempts and a separate **full-recovery worker** for the same perspective, color, high-resolution, ECC, damaged-code, and multi-frame recovery stack used by the main scanner. The recovery worker is lazy and independent, so a difficult old frame cannot block the fast worker from inspecting a newer camera frame. Finder detection itself is JavaScript; WASM only accelerates deterministic grayscale/binary preprocessing and CRC. The loop uses `requestVideoFrameCallback()` when available, drops stale fast-path frames, and measures `scanInterval` from scan start. Worker mode defaults to a 33 ms minimum cadence; automatic main-thread fallback uses 80 ms. The default camera request is approximately 1280×720, while the CSS-visible preview crop is resized to a 640 px working bitmap before crossing the worker boundary. Finder acquisition uses the streaming 1:1:3:1:1 RGB-value pass, direct cross-checks, local-threshold fallback, and directional module-size/version estimation. Once a geometry candidate validates structure, ECC, and CRC, it returns immediately. A miss with strong finder evidence dispatches parallel full recovery quickly. One finder must persist across consecutive fresh frames before recovery is armed, while zero-finder frames stay on the lightweight detector only. That recovery can use up to 960 px and retains the full QuadQR Auto Color crop sequence, center-weighted color recovery, threshold bracketing, precise alignment, perspective recovery, affine cross-channel calibration, QR-region enhancement, multi-frame confidence fusion, erasure decoding, and soft-decision Spectrum ECC.
213
213
 
214
214
  ```js
215
215
  const scanner = await startCameraScanner(video, {
@@ -235,7 +235,7 @@ const scanner = await startCameraScanner(video, {
235
235
  cameraHighResolutionEvery: 2,
236
236
  cameraRecoveryStrongFinderInterval: 120,
237
237
  cameraRecoveryWeakFinderInterval: 260,
238
- cameraRecoveryNoFinderInterval: 850,
238
+ cameraRecoveryWeakFinderFrames: 2,
239
239
  onResult(result) {
240
240
  console.log(result);
241
241
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quadqr-js",
3
- "version": "1.5.2",
3
+ "version": "1.5.3",
4
4
  "description": "QuadQR: experimental RGBW matrix code with optional experimental High Density Mode, Spectrum ECC 2.0, multi-frame camera scanning, advanced calibration, Reliability Lab, and 3D perspective recovery.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",