@mleonard9/vin-scanner 1.2.6 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -16
- package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +76 -23
- package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +69 -26
- package/ios/VisionCameraBarcodeScanner.m +60 -6
- package/ios/VisionCameraTextRecognition.m +67 -13
- package/lib/commonjs/ManualVinInput.js +147 -0
- package/lib/commonjs/ManualVinInput.js.map +1 -0
- package/lib/commonjs/PendingVinBanner.js +120 -0
- package/lib/commonjs/PendingVinBanner.js.map +1 -0
- package/lib/commonjs/TextVinPrompt.js +132 -0
- package/lib/commonjs/TextVinPrompt.js.map +1 -0
- package/lib/commonjs/haptics.js +36 -0
- package/lib/commonjs/haptics.js.map +1 -0
- package/lib/commonjs/index.js +196 -15
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/scanBarcodes.js +14 -3
- package/lib/commonjs/scanBarcodes.js.map +1 -1
- package/lib/commonjs/scanText.js +14 -3
- package/lib/commonjs/scanText.js.map +1 -1
- package/lib/commonjs/useVinScanner.js +205 -33
- package/lib/commonjs/useVinScanner.js.map +1 -1
- package/lib/commonjs/vinUtils.js +165 -32
- package/lib/commonjs/vinUtils.js.map +1 -1
- package/lib/module/ManualVinInput.js +139 -0
- package/lib/module/ManualVinInput.js.map +1 -0
- package/lib/module/PendingVinBanner.js +112 -0
- package/lib/module/PendingVinBanner.js.map +1 -0
- package/lib/module/TextVinPrompt.js +124 -0
- package/lib/module/TextVinPrompt.js.map +1 -0
- package/lib/module/haptics.js +27 -0
- package/lib/module/haptics.js.map +1 -0
- package/lib/module/index.js +179 -16
- package/lib/module/index.js.map +1 -1
- package/lib/module/scanBarcodes.js +14 -3
- package/lib/module/scanBarcodes.js.map +1 -1
- package/lib/module/scanText.js +14 -3
- package/lib/module/scanText.js.map +1 -1
- package/lib/module/useVinScanner.js +206 -34
- package/lib/module/useVinScanner.js.map +1 -1
- package/lib/module/vinUtils.js +165 -32
- package/lib/module/vinUtils.js.map +1 -1
- package/lib/typescript/src/ManualVinInput.d.ts +11 -0
- package/lib/typescript/src/ManualVinInput.d.ts.map +1 -0
- package/lib/typescript/src/PendingVinBanner.d.ts +17 -0
- package/lib/typescript/src/PendingVinBanner.d.ts.map +1 -0
- package/lib/typescript/src/TextVinPrompt.d.ts +20 -0
- package/lib/typescript/src/TextVinPrompt.d.ts.map +1 -0
- package/lib/typescript/src/haptics.d.ts +4 -0
- package/lib/typescript/src/haptics.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/scanBarcodes.d.ts.map +1 -1
- package/lib/typescript/src/scanText.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +136 -7
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useVinScanner.d.ts +3 -1
- package/lib/typescript/src/useVinScanner.d.ts.map +1 -1
- package/lib/typescript/src/vinUtils.d.ts +12 -3
- package/lib/typescript/src/vinUtils.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/ManualVinInput.tsx +145 -0
- package/src/PendingVinBanner.tsx +128 -0
- package/src/TextVinPrompt.tsx +139 -0
- package/src/haptics.ts +32 -0
- package/src/index.tsx +203 -24
- package/src/scanBarcodes.ts +16 -4
- package/src/scanText.ts +16 -4
- package/src/types.ts +140 -11
- package/src/useVinScanner.ts +232 -39
- package/src/vinUtils.ts +210 -79
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { useMemo, useRef } from 'react';
|
|
1
|
+
import { useMemo, useRef, useState, useCallback } from 'react';
|
|
2
2
|
import { useFrameProcessor } from 'react-native-vision-camera';
|
|
3
3
|
import { useRunOnJS } from 'react-native-worklets-core';
|
|
4
4
|
import { createBarcodeScannerPlugin } from './scanBarcodes';
|
|
5
5
|
import { createTextRecognitionPlugin } from './scanText';
|
|
6
6
|
import { buildVinCandidates, pickFirstCandidate, resolveOptions } from './vinUtils';
|
|
7
|
+
import { triggerSuccessHaptic, triggerSoftHaptic } from './haptics';
|
|
7
8
|
export function useVinScanner(options) {
|
|
8
9
|
const resolvedOptions = useMemo(() => resolveOptions(options), [options]);
|
|
9
10
|
const lastFrameTimestampRef = useRef(0);
|
|
10
11
|
const frameCounterRef = useRef(0);
|
|
12
|
+
const lastEmittedVin = useRef(null);
|
|
13
|
+
const lastEmitTimestamp = useRef(0);
|
|
14
|
+
const [pendingTextCandidates, setPendingTextCandidates] = useState([]);
|
|
15
|
+
const pendingTextTimestampRef = useRef(null);
|
|
16
|
+
const pendingTextRef = useRef([]);
|
|
17
|
+
const pendingTimerRef = useRef(null);
|
|
18
|
+
const hapticsEnabled = (options === null || options === void 0 ? void 0 : options.haptics) ?? true;
|
|
19
|
+
const sessionSeen = useRef(new Set());
|
|
20
|
+
const noBarcodeFrameCount = useRef(0);
|
|
21
|
+
const useBarcodeFallback = useRef(false);
|
|
11
22
|
const barcodeScanner = useMemo(() => {
|
|
12
23
|
if (!resolvedOptions.barcode.enabled) {
|
|
13
24
|
return null;
|
|
@@ -20,9 +31,6 @@ export function useVinScanner(options) {
|
|
|
20
31
|
}
|
|
21
32
|
return createTextRecognitionPlugin({
|
|
22
33
|
language: resolvedOptions.text.language,
|
|
23
|
-
// We can use a broad validation pattern here to help the native side
|
|
24
|
-
// filter obvious non-VIN text, improving bridge performance.
|
|
25
|
-
// VINs are 17 chars alphanumeric.
|
|
26
34
|
validationPattern: '[A-HJ-NPR-Z0-9]{10,}'
|
|
27
35
|
});
|
|
28
36
|
}, [resolvedOptions.text]);
|
|
@@ -31,6 +39,28 @@ export function useVinScanner(options) {
|
|
|
31
39
|
options.onResult(result, event);
|
|
32
40
|
}
|
|
33
41
|
}, [options === null || options === void 0 ? void 0 : options.onResult]);
|
|
42
|
+
const emitTextPending = useRunOnJS((pending, ts) => {
|
|
43
|
+
pendingTextTimestampRef.current = ts;
|
|
44
|
+
pendingTextRef.current = pending;
|
|
45
|
+
setPendingTextCandidates(pending);
|
|
46
|
+
if (pendingTimerRef.current) {
|
|
47
|
+
clearTimeout(pendingTimerRef.current);
|
|
48
|
+
}
|
|
49
|
+
const ttl = resolvedOptions.text.pendingTtlMs;
|
|
50
|
+
if (ttl > 0) {
|
|
51
|
+
pendingTimerRef.current = setTimeout(() => {
|
|
52
|
+
pendingTextTimestampRef.current = null;
|
|
53
|
+
pendingTextRef.current = [];
|
|
54
|
+
setPendingTextCandidates([]);
|
|
55
|
+
}, ttl);
|
|
56
|
+
}
|
|
57
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.onTextPending) === 'function') {
|
|
58
|
+
options.onTextPending(pending);
|
|
59
|
+
}
|
|
60
|
+
if (hapticsEnabled) {
|
|
61
|
+
triggerSoftHaptic();
|
|
62
|
+
}
|
|
63
|
+
}, [options === null || options === void 0 ? void 0 : options.onTextPending, resolvedOptions.text.pendingTtlMs, hapticsEnabled]);
|
|
34
64
|
const frameProcessor = useFrameProcessor(frame => {
|
|
35
65
|
'worklet';
|
|
36
66
|
|
|
@@ -44,58 +74,200 @@ export function useVinScanner(options) {
|
|
|
44
74
|
lastFrameTimestampRef.current = now;
|
|
45
75
|
}
|
|
46
76
|
const orientationOverride = resolvedOptions.detection.forceOrientation ?? null;
|
|
47
|
-
const
|
|
48
|
-
orientation: orientationOverride
|
|
49
|
-
} : undefined;
|
|
50
|
-
const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame, barcodeArgs) : [];
|
|
77
|
+
const regionOverride = resolvedOptions.detection.scanRegion ?? null;
|
|
51
78
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
// Frame quality gate (luma + sharpness)
|
|
80
|
+
const minLuma = resolvedOptions.detection.minLuma;
|
|
81
|
+
const minSharpness = resolvedOptions.detection.minSharpness;
|
|
82
|
+
if (minLuma > 0 || minSharpness > 0) {
|
|
83
|
+
const planes = frame === null || frame === void 0 ? void 0 : frame.planes;
|
|
84
|
+
if ((planes === null || planes === void 0 ? void 0 : planes.length) > 0) {
|
|
85
|
+
const yPlane = planes[0];
|
|
86
|
+
const bytes = yPlane === null || yPlane === void 0 ? void 0 : yPlane.bytes;
|
|
87
|
+
const width = yPlane === null || yPlane === void 0 ? void 0 : yPlane.width;
|
|
88
|
+
const height = yPlane === null || yPlane === void 0 ? void 0 : yPlane.height;
|
|
89
|
+
const stride = yPlane === null || yPlane === void 0 ? void 0 : yPlane.bytesPerRow;
|
|
90
|
+
if (bytes && width && height && stride) {
|
|
91
|
+
let lumaSum = 0;
|
|
92
|
+
const sampleStep = 8;
|
|
93
|
+
const sampleCount = Math.floor(bytes.length / sampleStep);
|
|
94
|
+
for (let i = 0; i < bytes.length; i += sampleStep) {
|
|
95
|
+
lumaSum += bytes[i];
|
|
96
|
+
}
|
|
97
|
+
const meanLuma = lumaSum / Math.max(1, sampleCount);
|
|
98
|
+
if (minLuma > 0 && meanLuma < minLuma) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (minSharpness > 0) {
|
|
102
|
+
const step = Math.max(2, Math.floor(width / 96));
|
|
103
|
+
let sharpAccum = 0;
|
|
104
|
+
let sharpCount = 0;
|
|
105
|
+
for (let y = step; y < height - step; y += step) {
|
|
106
|
+
const row = y * stride;
|
|
107
|
+
for (let x = step; x < width - step; x += step) {
|
|
108
|
+
const idx = row + x;
|
|
109
|
+
const gx = bytes[idx + 1] - bytes[idx - 1];
|
|
110
|
+
const gy = bytes[idx + stride] - bytes[idx - stride];
|
|
111
|
+
sharpAccum += Math.abs(gx) + Math.abs(gy);
|
|
112
|
+
sharpCount += 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const sharpness = sharpAccum / Math.max(1, sharpCount);
|
|
116
|
+
if (sharpness < minSharpness) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
64
121
|
}
|
|
65
122
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
123
|
+
const barcodeArgs = orientationOverride || regionOverride ? {
|
|
124
|
+
...(orientationOverride && {
|
|
125
|
+
orientation: orientationOverride
|
|
126
|
+
}),
|
|
127
|
+
...(regionOverride && {
|
|
128
|
+
scanRegion: regionOverride
|
|
129
|
+
})
|
|
130
|
+
} : undefined;
|
|
131
|
+
|
|
132
|
+
// Start performance tracking
|
|
133
|
+
const t0 = Date.now();
|
|
134
|
+
const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame, useBarcodeFallback.current ? {
|
|
135
|
+
all: true
|
|
136
|
+
} : barcodeArgs) ?? [] : [];
|
|
137
|
+
const t1 = Date.now();
|
|
138
|
+
const textArgs = orientationOverride || regionOverride ? {
|
|
139
|
+
...(orientationOverride && {
|
|
140
|
+
orientation: orientationOverride
|
|
141
|
+
}),
|
|
142
|
+
...(regionOverride && {
|
|
143
|
+
scanRegion: regionOverride
|
|
144
|
+
})
|
|
68
145
|
} : undefined;
|
|
69
146
|
frameCounterRef.current += 1;
|
|
70
147
|
const frameIndex = frameCounterRef.current;
|
|
71
148
|
const textScanInterval = resolvedOptions.detection.textScanInterval;
|
|
72
|
-
const shouldRunText =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
149
|
+
const shouldRunText = textScanner && (textScanInterval <= 1 || frameIndex % textScanInterval === 0);
|
|
150
|
+
let textBlocks = shouldRunText && textScanner ? textScanner.scanText(frame, textArgs) ?? [] : [];
|
|
151
|
+
|
|
152
|
+
// Two-pass OCR: if we skipped this frame or got nothing and barcodes are empty, run one immediate OCR pass.
|
|
153
|
+
if (textScanner && textBlocks.length === 0 && barcodes.length === 0 && !shouldRunText) {
|
|
154
|
+
textBlocks = textScanner.scanText(frame, textArgs) ?? [];
|
|
155
|
+
}
|
|
156
|
+
const t2 = Date.now();
|
|
76
157
|
const payload = {
|
|
77
158
|
barcodes: barcodes ?? [],
|
|
78
159
|
textBlocks: textBlocks ?? [],
|
|
79
160
|
timestamp: now
|
|
80
161
|
};
|
|
81
162
|
const candidates = buildVinCandidates(payload, resolvedOptions);
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const
|
|
163
|
+
const filtered = candidates.filter(c => c.confidence >= resolvedOptions.detection.minConfidence);
|
|
164
|
+
const barcodeCandidates = filtered.filter(c => c.source === 'barcode');
|
|
165
|
+
const textCandidates = filtered.filter(c => c.source === 'text');
|
|
166
|
+
|
|
167
|
+
// Adaptive barcode fallback: if no barcode hits for N frames, scan all formats.
|
|
168
|
+
if (barcodeCandidates.length === 0) {
|
|
169
|
+
noBarcodeFrameCount.current += 1;
|
|
170
|
+
if (noBarcodeFrameCount.current >= resolvedOptions.detection.barcodeFallbackAfter) {
|
|
171
|
+
useBarcodeFallback.current = true;
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
noBarcodeFrameCount.current = 0;
|
|
175
|
+
useBarcodeFallback.current = false;
|
|
176
|
+
}
|
|
177
|
+
const firstCandidate = pickFirstCandidate(candidates, resolvedOptions);
|
|
178
|
+
const t3 = Date.now();
|
|
179
|
+
const duration = Date.now() - now;
|
|
85
180
|
const event = {
|
|
86
|
-
mode,
|
|
87
181
|
timestamp: payload.timestamp,
|
|
182
|
+
duration,
|
|
88
183
|
candidates,
|
|
89
|
-
|
|
184
|
+
firstCandidate,
|
|
90
185
|
raw: {
|
|
91
186
|
barcodes: payload.barcodes,
|
|
92
187
|
textBlocks: payload.textBlocks
|
|
188
|
+
},
|
|
189
|
+
performance: {
|
|
190
|
+
barcodeMs: t1 - t0,
|
|
191
|
+
textMs: t2 - t1,
|
|
192
|
+
validationMs: t3 - t2,
|
|
193
|
+
totalMs: t3 - t0
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// If a barcode is present, emit immediately (highest confidence path)
|
|
198
|
+
if (barcodeCandidates.length > 0) {
|
|
199
|
+
const barcodeFirst = pickFirstCandidate(barcodeCandidates, resolvedOptions);
|
|
200
|
+
if (barcodeFirst) {
|
|
201
|
+
const debounceMs = resolvedOptions.duplicateDebounceMs ?? 1500;
|
|
202
|
+
const isDuplicate = barcodeFirst.value === lastEmittedVin.current && now - lastEmitTimestamp.current < debounceMs;
|
|
203
|
+
if (!isDuplicate) {
|
|
204
|
+
lastEmittedVin.current = barcodeFirst.value;
|
|
205
|
+
lastEmitTimestamp.current = now;
|
|
206
|
+
sessionSeen.current.add(barcodeFirst.value);
|
|
207
|
+
emitResult(barcodeCandidates, {
|
|
208
|
+
...event,
|
|
209
|
+
candidates: barcodeCandidates,
|
|
210
|
+
firstCandidate: barcodeFirst
|
|
211
|
+
});
|
|
212
|
+
if (hapticsEnabled) {
|
|
213
|
+
triggerSuccessHaptic();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// For text-only detections, optionally require user confirmation
|
|
221
|
+
if (resolvedOptions.text.requireConfirmation && textCandidates.length > 0) {
|
|
222
|
+
emitTextPending(textCandidates, payload.timestamp);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Default behavior: emit first candidate (text)
|
|
227
|
+
if (firstCandidate) {
|
|
228
|
+
const debounceMs = resolvedOptions.duplicateDebounceMs ?? 1500;
|
|
229
|
+
const isDuplicate = firstCandidate.value === lastEmittedVin.current && now - lastEmitTimestamp.current < debounceMs;
|
|
230
|
+
const seen = sessionSeen.current.has(firstCandidate.value);
|
|
231
|
+
if (!isDuplicate && !seen) {
|
|
232
|
+
lastEmittedVin.current = firstCandidate.value;
|
|
233
|
+
lastEmitTimestamp.current = now;
|
|
234
|
+
sessionSeen.current.add(firstCandidate.value);
|
|
235
|
+
emitResult(candidates, event);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}, [barcodeScanner, textScanner, emitResult, emitTextPending, resolvedOptions]);
|
|
239
|
+
const confirmTextCandidate = useCallback(vinValue => {
|
|
240
|
+
const pending = pendingTextRef.current;
|
|
241
|
+
if (!pending.length) return;
|
|
242
|
+
const selected = vinValue && pending.find(c => c.value === vinValue) || pending[0];
|
|
243
|
+
const ts = pendingTextTimestampRef.current ?? Date.now();
|
|
244
|
+
|
|
245
|
+
// Clear pending state
|
|
246
|
+
if (pendingTimerRef.current) {
|
|
247
|
+
clearTimeout(pendingTimerRef.current);
|
|
248
|
+
pendingTimerRef.current = null;
|
|
249
|
+
}
|
|
250
|
+
pendingTextTimestampRef.current = null;
|
|
251
|
+
pendingTextRef.current = [];
|
|
252
|
+
setPendingTextCandidates([]);
|
|
253
|
+
const event = {
|
|
254
|
+
timestamp: ts,
|
|
255
|
+
duration: 0,
|
|
256
|
+
candidates: pending,
|
|
257
|
+
firstCandidate: selected,
|
|
258
|
+
raw: {
|
|
259
|
+
barcodes: [],
|
|
260
|
+
textBlocks: []
|
|
93
261
|
}
|
|
94
262
|
};
|
|
95
|
-
emitResult(
|
|
96
|
-
|
|
263
|
+
emitResult(pending, event);
|
|
264
|
+
lastEmittedVin.current = (selected === null || selected === void 0 ? void 0 : selected.value) ?? null;
|
|
265
|
+
lastEmitTimestamp.current = Date.now();
|
|
266
|
+
}, [emitResult]);
|
|
97
267
|
return {
|
|
98
|
-
frameProcessor
|
|
268
|
+
frameProcessor,
|
|
269
|
+
pendingTextCandidates,
|
|
270
|
+
confirmTextCandidate
|
|
99
271
|
};
|
|
100
272
|
}
|
|
101
273
|
//# sourceMappingURL=useVinScanner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useMemo","useRef","useFrameProcessor","useRunOnJS","createBarcodeScannerPlugin","createTextRecognitionPlugin","buildVinCandidates","pickFirstCandidate","resolveOptions","useVinScanner","options","resolvedOptions","lastFrameTimestampRef","frameCounterRef","barcodeScanner","barcode","enabled","formats","textScanner","text","language","validationPattern","emitResult","result","event","onResult","frameProcessor","frame","now","timestamp","Date","maxFps","detection","maxFrameRate","minInterval","current","orientationOverride","forceOrientation","barcodeArgs","orientation","undefined","barcodes","scanBarcodes","hasBarcodeVin","length","tempCandidates","textBlocks","textArgs","frameIndex","textScanInterval","shouldRunText","scanText","payload","candidates","best","mode","resultMode","normalizedResult","raw"],"sourceRoot":"../../src","sources":["useVinScanner.ts"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["useMemo","useRef","useState","useCallback","useFrameProcessor","useRunOnJS","createBarcodeScannerPlugin","createTextRecognitionPlugin","buildVinCandidates","pickFirstCandidate","resolveOptions","triggerSuccessHaptic","triggerSoftHaptic","useVinScanner","options","resolvedOptions","lastFrameTimestampRef","frameCounterRef","lastEmittedVin","lastEmitTimestamp","pendingTextCandidates","setPendingTextCandidates","pendingTextTimestampRef","pendingTextRef","pendingTimerRef","hapticsEnabled","haptics","sessionSeen","Set","noBarcodeFrameCount","useBarcodeFallback","barcodeScanner","barcode","enabled","formats","textScanner","text","language","validationPattern","emitResult","result","event","onResult","emitTextPending","pending","ts","current","clearTimeout","ttl","pendingTtlMs","setTimeout","onTextPending","frameProcessor","frame","now","timestamp","Date","maxFps","detection","maxFrameRate","minInterval","orientationOverride","forceOrientation","regionOverride","scanRegion","minLuma","minSharpness","planes","length","yPlane","bytes","width","height","stride","bytesPerRow","lumaSum","sampleStep","sampleCount","Math","floor","i","meanLuma","max","step","sharpAccum","sharpCount","y","row","x","idx","gx","gy","abs","sharpness","barcodeArgs","orientation","undefined","t0","barcodes","scanBarcodes","all","t1","textArgs","frameIndex","textScanInterval","shouldRunText","textBlocks","scanText","t2","payload","candidates","filtered","filter","c","confidence","minConfidence","barcodeCandidates","source","textCandidates","barcodeFallbackAfter","firstCandidate","t3","duration","raw","performance","barcodeMs","textMs","validationMs","totalMs","barcodeFirst","debounceMs","duplicateDebounceMs","isDuplicate","value","add","requireConfirmation","seen","has","confirmTextCandidate","vinValue","selected","find"],"sourceRoot":"../../src","sources":["useVinScanner.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/module/vinUtils.js
CHANGED
|
@@ -18,25 +18,42 @@
|
|
|
18
18
|
const DEFAULT_RESOLVED_OPTIONS = {
|
|
19
19
|
barcode: {
|
|
20
20
|
enabled: true,
|
|
21
|
-
formats: ['
|
|
21
|
+
formats: ['code-39', 'code-128', 'pdf-417']
|
|
22
22
|
},
|
|
23
23
|
text: {
|
|
24
24
|
enabled: true,
|
|
25
|
-
language: 'latin'
|
|
25
|
+
language: 'latin',
|
|
26
|
+
requireConfirmation: false,
|
|
27
|
+
pendingTtlMs: 5000
|
|
26
28
|
},
|
|
27
29
|
detection: {
|
|
28
|
-
|
|
29
|
-
textScanInterval: 1,
|
|
30
|
+
textScanInterval: 3,
|
|
30
31
|
maxFrameRate: 30,
|
|
31
|
-
forceOrientation: null
|
|
32
|
-
|
|
32
|
+
forceOrientation: null,
|
|
33
|
+
scanRegion: {
|
|
34
|
+
x: 0.15,
|
|
35
|
+
y: 0.15,
|
|
36
|
+
width: 0.7,
|
|
37
|
+
height: 0.7
|
|
38
|
+
},
|
|
39
|
+
minLuma: 30,
|
|
40
|
+
minSharpness: 12,
|
|
41
|
+
minConfidence: 0.6,
|
|
42
|
+
barcodeFallbackAfter: 45
|
|
43
|
+
},
|
|
44
|
+
showOverlay: false,
|
|
45
|
+
overlayColors: {
|
|
46
|
+
high: "#00FF00",
|
|
47
|
+
medium: "#FFFF00",
|
|
48
|
+
low: "#FF0000"
|
|
49
|
+
},
|
|
50
|
+
duplicateDebounceMs: 1500
|
|
33
51
|
};
|
|
34
52
|
export const resolveOptions = options => {
|
|
35
53
|
'worklet';
|
|
36
54
|
|
|
37
|
-
var _options$detection, _options$
|
|
38
|
-
const
|
|
39
|
-
const textScanInterval = Math.max(1, (options === null || options === void 0 || (_options$detection2 = options.detection) === null || _options$detection2 === void 0 ? void 0 : _options$detection2.textScanInterval) ?? DEFAULT_RESOLVED_OPTIONS.detection.textScanInterval);
|
|
55
|
+
var _options$detection, _options$barcode, _options$barcode2, _options$text, _options$text2, _options$text3, _options$text4, _options$detection2, _options$detection3, _options$detection4, _options$detection5, _options$detection6, _options$detection7, _options$detection8;
|
|
56
|
+
const textScanInterval = Math.max(1, (options === null || options === void 0 || (_options$detection = options.detection) === null || _options$detection === void 0 ? void 0 : _options$detection.textScanInterval) ?? DEFAULT_RESOLVED_OPTIONS.detection.textScanInterval);
|
|
40
57
|
return {
|
|
41
58
|
barcode: {
|
|
42
59
|
enabled: (options === null || options === void 0 || (_options$barcode = options.barcode) === null || _options$barcode === void 0 ? void 0 : _options$barcode.enabled) ?? DEFAULT_RESOLVED_OPTIONS.barcode.enabled,
|
|
@@ -44,14 +61,26 @@ export const resolveOptions = options => {
|
|
|
44
61
|
},
|
|
45
62
|
text: {
|
|
46
63
|
enabled: (options === null || options === void 0 || (_options$text = options.text) === null || _options$text === void 0 ? void 0 : _options$text.enabled) ?? DEFAULT_RESOLVED_OPTIONS.text.enabled,
|
|
47
|
-
language: (options === null || options === void 0 || (_options$text2 = options.text) === null || _options$text2 === void 0 ? void 0 : _options$text2.language) ?? DEFAULT_RESOLVED_OPTIONS.text.language
|
|
64
|
+
language: (options === null || options === void 0 || (_options$text2 = options.text) === null || _options$text2 === void 0 ? void 0 : _options$text2.language) ?? DEFAULT_RESOLVED_OPTIONS.text.language,
|
|
65
|
+
requireConfirmation: (options === null || options === void 0 || (_options$text3 = options.text) === null || _options$text3 === void 0 ? void 0 : _options$text3.requireConfirmation) ?? DEFAULT_RESOLVED_OPTIONS.text.requireConfirmation,
|
|
66
|
+
pendingTtlMs: (options === null || options === void 0 || (_options$text4 = options.text) === null || _options$text4 === void 0 ? void 0 : _options$text4.pendingTtlMs) ?? DEFAULT_RESOLVED_OPTIONS.text.pendingTtlMs
|
|
48
67
|
},
|
|
49
68
|
detection: {
|
|
50
|
-
resultMode,
|
|
51
69
|
textScanInterval,
|
|
52
|
-
maxFrameRate: (options === null || options === void 0 || (_options$
|
|
53
|
-
forceOrientation: (options === null || options === void 0 || (_options$
|
|
54
|
-
|
|
70
|
+
maxFrameRate: (options === null || options === void 0 || (_options$detection2 = options.detection) === null || _options$detection2 === void 0 ? void 0 : _options$detection2.maxFrameRate) ?? DEFAULT_RESOLVED_OPTIONS.detection.maxFrameRate,
|
|
71
|
+
forceOrientation: (options === null || options === void 0 || (_options$detection3 = options.detection) === null || _options$detection3 === void 0 ? void 0 : _options$detection3.forceOrientation) ?? DEFAULT_RESOLVED_OPTIONS.detection.forceOrientation,
|
|
72
|
+
scanRegion: (options === null || options === void 0 || (_options$detection4 = options.detection) === null || _options$detection4 === void 0 ? void 0 : _options$detection4.scanRegion) ?? DEFAULT_RESOLVED_OPTIONS.detection.scanRegion,
|
|
73
|
+
minLuma: (options === null || options === void 0 || (_options$detection5 = options.detection) === null || _options$detection5 === void 0 ? void 0 : _options$detection5.minLuma) ?? DEFAULT_RESOLVED_OPTIONS.detection.minLuma,
|
|
74
|
+
minSharpness: (options === null || options === void 0 || (_options$detection6 = options.detection) === null || _options$detection6 === void 0 ? void 0 : _options$detection6.minSharpness) ?? DEFAULT_RESOLVED_OPTIONS.detection.minSharpness,
|
|
75
|
+
minConfidence: (options === null || options === void 0 || (_options$detection7 = options.detection) === null || _options$detection7 === void 0 ? void 0 : _options$detection7.minConfidence) ?? DEFAULT_RESOLVED_OPTIONS.detection.minConfidence,
|
|
76
|
+
barcodeFallbackAfter: (options === null || options === void 0 || (_options$detection8 = options.detection) === null || _options$detection8 === void 0 ? void 0 : _options$detection8.barcodeFallbackAfter) ?? DEFAULT_RESOLVED_OPTIONS.detection.barcodeFallbackAfter
|
|
77
|
+
},
|
|
78
|
+
showOverlay: (options === null || options === void 0 ? void 0 : options.showOverlay) ?? false,
|
|
79
|
+
overlayColors: {
|
|
80
|
+
...DEFAULT_RESOLVED_OPTIONS.overlayColors,
|
|
81
|
+
...(options === null || options === void 0 ? void 0 : options.overlayColors)
|
|
82
|
+
},
|
|
83
|
+
duplicateDebounceMs: (options === null || options === void 0 ? void 0 : options.duplicateDebounceMs) ?? 1500
|
|
55
84
|
};
|
|
56
85
|
};
|
|
57
86
|
const transliteration = {
|
|
@@ -187,6 +216,107 @@ const extractWithContext = text => {
|
|
|
187
216
|
return Array.from(matches);
|
|
188
217
|
};
|
|
189
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Calculate confidence score for a VIN candidate based on multiple factors.
|
|
221
|
+
* Returns a score from 0.0 (lowest confidence) to 1.0 (highest confidence).
|
|
222
|
+
*/
|
|
223
|
+
const calculateConfidence = (value, source, origin, rawPayload) => {
|
|
224
|
+
'worklet';
|
|
225
|
+
|
|
226
|
+
let score = 0.5; // Base confidence
|
|
227
|
+
|
|
228
|
+
// Analyze VIN value characteristics
|
|
229
|
+
// Known manufacturer WMI prefixes (first 3 characters) increase confidence
|
|
230
|
+
const wmi = value.slice(0, 3);
|
|
231
|
+
const knownManufacturers = ['1G1', '1G2', '1G3', '1G4', '1G6',
|
|
232
|
+
// GM
|
|
233
|
+
'1FA', '1FB', '1FC', '1FD', '1FM', '1FT',
|
|
234
|
+
// Ford
|
|
235
|
+
'1HG', '1HC',
|
|
236
|
+
// Honda
|
|
237
|
+
'1N4', '1N6',
|
|
238
|
+
// Nissan
|
|
239
|
+
'2C3', '2C4',
|
|
240
|
+
// Chrysler
|
|
241
|
+
'2HG', '2HM',
|
|
242
|
+
// Honda
|
|
243
|
+
'2T1', '2T2', '2T3',
|
|
244
|
+
// Toyota
|
|
245
|
+
'3FA', '3FE',
|
|
246
|
+
// Ford Mexico
|
|
247
|
+
'4T1', '4T3',
|
|
248
|
+
// Toyota
|
|
249
|
+
'5FN', '5FY',
|
|
250
|
+
// Honda
|
|
251
|
+
'5TD', '5TF', '5TJ',
|
|
252
|
+
// Toyota
|
|
253
|
+
'JHM', 'JHL',
|
|
254
|
+
// Honda Japan
|
|
255
|
+
'JT2', 'JT3', 'JTD',
|
|
256
|
+
// Toyota Japan
|
|
257
|
+
'KM8', 'KNA', 'KND',
|
|
258
|
+
// Hyundai/Kia
|
|
259
|
+
'WBA', 'WBS', 'WBY',
|
|
260
|
+
// BMW
|
|
261
|
+
'WDB', 'WDC', 'WDD',
|
|
262
|
+
// Mercedes
|
|
263
|
+
'WAU', 'WA1',
|
|
264
|
+
// Audi
|
|
265
|
+
'WVW', 'WV1', 'WV2' // VW
|
|
266
|
+
];
|
|
267
|
+
if (knownManufacturers.includes(wmi)) {
|
|
268
|
+
score += 0.15;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Check character distribution - VINs with too many repeated characters are suspicious
|
|
272
|
+
const charCounts = new Map();
|
|
273
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
274
|
+
const char = value[i];
|
|
275
|
+
if (char) {
|
|
276
|
+
charCounts.set(char, (charCounts.get(char) ?? 0) + 1);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const maxRepeat = Math.max(...Array.from(charCounts.values()));
|
|
280
|
+
if (maxRepeat > 5) {
|
|
281
|
+
// Too many repeated characters (e.g., "11111111111111111")
|
|
282
|
+
score -= 0.2;
|
|
283
|
+
} else if (maxRepeat <= 2) {
|
|
284
|
+
// Good character diversity
|
|
285
|
+
score += 0.1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Barcodes are generally more reliable than OCR
|
|
289
|
+
if (source === 'barcode') {
|
|
290
|
+
score += 0.3;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Text from element-level is more precise than block
|
|
294
|
+
if (origin === 'element') {
|
|
295
|
+
score += 0.2;
|
|
296
|
+
} else if (origin === 'line') {
|
|
297
|
+
score += 0.1;
|
|
298
|
+
} else if (origin === 'rawValue' || origin === 'displayValue') {
|
|
299
|
+
// Barcode origins get slight boost
|
|
300
|
+
score += 0.15;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Context-aware detection (VIN prefix found) increases confidence
|
|
304
|
+
const resultText = rawPayload === null || rawPayload === void 0 ? void 0 : rawPayload.resultText;
|
|
305
|
+
if (resultText && typeof resultText === 'string') {
|
|
306
|
+
const lowerText = resultText.toLowerCase();
|
|
307
|
+
if (lowerText.includes('vin') || lowerText.includes('serial') || lowerText.includes('chassis')) {
|
|
308
|
+
score += 0.2;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Valid checksum confirmed (already validated by isValidVin)
|
|
313
|
+
// All candidates here have passed validation, so add bonus
|
|
314
|
+
score += 0.2;
|
|
315
|
+
|
|
316
|
+
// Clamp to valid range
|
|
317
|
+
return Math.min(1.0, Math.max(0.0, score));
|
|
318
|
+
};
|
|
319
|
+
|
|
190
320
|
/**
|
|
191
321
|
* Tokenize and extract VIN candidates from raw text
|
|
192
322
|
* Includes OCR error correction and context-aware extraction
|
|
@@ -253,9 +383,13 @@ const isValidWmi = value => {
|
|
|
253
383
|
return false;
|
|
254
384
|
}
|
|
255
385
|
const firstChar = value[0];
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
386
|
+
if (!firstChar) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Allow any WMI that starts with a VIN-valid character (A–Z minus I/O/Q, or 1–9).
|
|
391
|
+
// This keeps validation global while still rejecting impossible leading chars.
|
|
392
|
+
return /^[A-HJ-NPR-Z1-9]$/.test(firstChar);
|
|
259
393
|
};
|
|
260
394
|
|
|
261
395
|
/**
|
|
@@ -353,13 +487,17 @@ const candidateFromBarcode = detection => {
|
|
|
353
487
|
tokenizeCandidate(detection.rawValue ?? '').forEach(value => values.add(value));
|
|
354
488
|
tokenizeCandidate(detection.displayValue ?? '').forEach(value => values.add(value));
|
|
355
489
|
const boundingBox = toBoundingBox(detection, 'barcode');
|
|
356
|
-
return Array.from(values).filter(value => isValidVin(value)).map(value =>
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
490
|
+
return Array.from(values).filter(value => isValidVin(value)).map(value => {
|
|
491
|
+
const origin = detection.displayValue ? 'displayValue' : 'rawValue';
|
|
492
|
+
return {
|
|
493
|
+
value,
|
|
494
|
+
source: 'barcode',
|
|
495
|
+
confidence: calculateConfidence(value, 'barcode', origin, detection),
|
|
496
|
+
boundingBox,
|
|
497
|
+
origin,
|
|
498
|
+
rawPayload: detection
|
|
499
|
+
};
|
|
500
|
+
});
|
|
363
501
|
};
|
|
364
502
|
const candidateFromText = detection => {
|
|
365
503
|
'worklet';
|
|
@@ -402,6 +540,7 @@ const candidateFromText = detection => {
|
|
|
402
540
|
return {
|
|
403
541
|
value,
|
|
404
542
|
source: 'text',
|
|
543
|
+
confidence: calculateConfidence(value, 'text', origin, detection),
|
|
405
544
|
boundingBox,
|
|
406
545
|
origin,
|
|
407
546
|
rawPayload: detection
|
|
@@ -425,7 +564,6 @@ export const buildVinCandidates = (payload, options) => {
|
|
|
425
564
|
'worklet';
|
|
426
565
|
|
|
427
566
|
const list = [];
|
|
428
|
-
const prefersBarcodeFirst = options.detection.resultMode === 'first';
|
|
429
567
|
const addBarcodeCandidates = () => {
|
|
430
568
|
if (!options.barcode.enabled) {
|
|
431
569
|
return;
|
|
@@ -442,13 +580,8 @@ export const buildVinCandidates = (payload, options) => {
|
|
|
442
580
|
list.push(...candidateFromText(textBlock));
|
|
443
581
|
});
|
|
444
582
|
};
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
addTextCandidates();
|
|
448
|
-
} else {
|
|
449
|
-
addTextCandidates();
|
|
450
|
-
addBarcodeCandidates();
|
|
451
|
-
}
|
|
583
|
+
addBarcodeCandidates();
|
|
584
|
+
addTextCandidates();
|
|
452
585
|
return dedupeCandidates(list);
|
|
453
586
|
};
|
|
454
587
|
export const pickFirstCandidate = (candidates, options) => {
|