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 +1 -1
- package/dist/camera-scanner-worker.js +3 -2
- package/dist/esm/camera-scanner-worker.js +3 -2
- package/dist/esm/quadqr.js +29 -10
- package/dist/quadqr.js +29 -10
- package/dist/quadqr.min.js +29 -10
- package/docs/API.md +2 -2
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
-
|
|
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"
|
package/dist/esm/quadqr.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
package/dist/quadqr.min.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|