@mleonard9/vin-scanner 0.2.5 → 0.2.6
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 +7 -0
- package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +83 -53
- package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerPackage.kt +2 -2
- package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +128 -95
- package/ios/VisionCameraBarcodeScanner.m +59 -37
- package/ios/VisionCameraTextRecognition.m +106 -56
- package/lib/commonjs/scanBarcodes.js +54 -6
- package/lib/commonjs/scanBarcodes.js.map +1 -1
- package/lib/commonjs/scanText.js +58 -4
- package/lib/commonjs/scanText.js.map +1 -1
- package/lib/commonjs/useVinScanner.js +11 -1
- package/lib/commonjs/useVinScanner.js.map +1 -1
- package/lib/commonjs/vinUtils.js +5 -3
- package/lib/commonjs/vinUtils.js.map +1 -1
- package/lib/module/scanBarcodes.js +54 -6
- package/lib/module/scanBarcodes.js.map +1 -1
- package/lib/module/scanText.js +58 -4
- package/lib/module/scanText.js.map +1 -1
- package/lib/module/useVinScanner.js +11 -1
- package/lib/module/useVinScanner.js.map +1 -1
- package/lib/module/vinUtils.js +5 -3
- package/lib/module/vinUtils.js.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 +9 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useVinScanner.d.ts.map +1 -1
- package/lib/typescript/src/vinUtils.d.ts +1 -0
- package/lib/typescript/src/vinUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/scanBarcodes.ts +71 -8
- package/src/scanText.ts +72 -4
- package/src/types.ts +16 -2
- package/src/useVinScanner.ts +16 -1
- package/src/vinUtils.ts +5 -0
package/src/scanText.ts
CHANGED
|
@@ -16,21 +16,89 @@ const LINKING_ERROR: string =
|
|
|
16
16
|
'- You rebuilt the app after installing the package\n' +
|
|
17
17
|
'- You are not using Expo Go\n';
|
|
18
18
|
|
|
19
|
+
const TEXT_BOX_STRIDE = 12;
|
|
20
|
+
|
|
21
|
+
const toFloat32Array = (input: unknown): Float32Array | undefined => {
|
|
22
|
+
'worklet';
|
|
23
|
+
if (
|
|
24
|
+
input &&
|
|
25
|
+
typeof input === 'object' &&
|
|
26
|
+
'byteLength' in (input as ArrayBuffer)
|
|
27
|
+
) {
|
|
28
|
+
try {
|
|
29
|
+
return new Float32Array(input as ArrayBuffer);
|
|
30
|
+
} catch {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const normalizeTextDetections = (payload: unknown): TextDetection[] => {
|
|
38
|
+
'worklet';
|
|
39
|
+
if (!payload) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(payload)) {
|
|
43
|
+
return payload as TextDetection[];
|
|
44
|
+
}
|
|
45
|
+
const record = payload as {
|
|
46
|
+
detections?: Array<TextDetection & { boxIndex?: number }>;
|
|
47
|
+
boxes?: ArrayBuffer;
|
|
48
|
+
};
|
|
49
|
+
const detections = Array.isArray(record?.detections)
|
|
50
|
+
? record.detections
|
|
51
|
+
: [];
|
|
52
|
+
const buffer = toFloat32Array(record?.boxes);
|
|
53
|
+
return detections.map((detection, index) => {
|
|
54
|
+
const output: TextDetection = { ...detection };
|
|
55
|
+
const boxIndex =
|
|
56
|
+
typeof detection?.boxIndex === 'number'
|
|
57
|
+
? detection.boxIndex
|
|
58
|
+
: index;
|
|
59
|
+
if (buffer && boxIndex >= 0) {
|
|
60
|
+
const offset = boxIndex * TEXT_BOX_STRIDE;
|
|
61
|
+
if (offset + TEXT_BOX_STRIDE <= buffer.length) {
|
|
62
|
+
output.blockFrameTop = buffer[offset];
|
|
63
|
+
output.blockFrameBottom = buffer[offset + 1];
|
|
64
|
+
output.blockFrameLeft = buffer[offset + 2];
|
|
65
|
+
output.blockFrameRight = buffer[offset + 3];
|
|
66
|
+
output.lineFrameTop = buffer[offset + 4];
|
|
67
|
+
output.lineFrameBottom = buffer[offset + 5];
|
|
68
|
+
output.lineFrameLeft = buffer[offset + 6];
|
|
69
|
+
output.lineFrameRight = buffer[offset + 7];
|
|
70
|
+
output.elementFrameTop = buffer[offset + 8];
|
|
71
|
+
output.elementFrameBottom = buffer[offset + 9];
|
|
72
|
+
output.elementFrameLeft = buffer[offset + 10];
|
|
73
|
+
output.elementFrameRight = buffer[offset + 11];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return output;
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
19
80
|
export function createTextRecognitionPlugin(
|
|
20
81
|
options: TextRecognitionOptions
|
|
21
82
|
): TextRecognitionPlugin {
|
|
22
83
|
const plugin: FrameProcessorPlugin | undefined =
|
|
23
|
-
VisionCameraProxy.initFrameProcessorPlugin('
|
|
84
|
+
VisionCameraProxy.initFrameProcessorPlugin('vinScannerText', {
|
|
24
85
|
...options,
|
|
25
86
|
});
|
|
26
87
|
if (!plugin) {
|
|
27
88
|
throw new Error(LINKING_ERROR);
|
|
28
89
|
}
|
|
29
90
|
return {
|
|
30
|
-
scanText: (
|
|
91
|
+
scanText: (
|
|
92
|
+
frame: Frame,
|
|
93
|
+
overrides?: TextRecognitionOptions
|
|
94
|
+
): TextDetection[] => {
|
|
31
95
|
'worklet';
|
|
32
|
-
const
|
|
33
|
-
|
|
96
|
+
const args =
|
|
97
|
+
overrides && Object.keys(overrides).length > 0
|
|
98
|
+
? { ...options, ...overrides }
|
|
99
|
+
: options;
|
|
100
|
+
const result = plugin.call(frame, args);
|
|
101
|
+
return normalizeTextDetections(result);
|
|
34
102
|
},
|
|
35
103
|
};
|
|
36
104
|
}
|
package/src/types.ts
CHANGED
|
@@ -25,6 +25,8 @@ export type BarcodeFormat =
|
|
|
25
25
|
|
|
26
26
|
export type ScanBarcodeOptions = BarcodeFormat[];
|
|
27
27
|
|
|
28
|
+
export type NativeBarcodeFormatMap = Partial<Record<BarcodeFormat, boolean>>;
|
|
29
|
+
|
|
28
30
|
export type BarcodeDetection = {
|
|
29
31
|
width?: number;
|
|
30
32
|
height?: number;
|
|
@@ -121,6 +123,12 @@ export type DetectionOptions = {
|
|
|
121
123
|
emitDuplicates?: boolean;
|
|
122
124
|
includePartialVins?: boolean; // Allow detection of incomplete VINs (13-16 chars)
|
|
123
125
|
minPartialLength?: number; // Minimum length for partial VINs (default: 13)
|
|
126
|
+
/**
|
|
127
|
+
* Maximum frame rate (in FPS) at which the frame processor should execute.
|
|
128
|
+
* Frames that arrive faster than this budget are dropped to keep the
|
|
129
|
+
* worklet responsive when ML Kit takes longer than the camera cadence.
|
|
130
|
+
*/
|
|
131
|
+
maxFrameRate?: number;
|
|
124
132
|
};
|
|
125
133
|
|
|
126
134
|
export type VinScannerOptions = {
|
|
@@ -139,11 +147,17 @@ export type CameraTypes = {
|
|
|
139
147
|
} & CameraProps;
|
|
140
148
|
|
|
141
149
|
export type BarcodeScannerPlugin = {
|
|
142
|
-
scanBarcodes: (
|
|
150
|
+
scanBarcodes: (
|
|
151
|
+
frame: Frame,
|
|
152
|
+
overrides?: NativeBarcodeFormatMap
|
|
153
|
+
) => BarcodeDetection[] | undefined;
|
|
143
154
|
};
|
|
144
155
|
|
|
145
156
|
export type TextRecognitionPlugin = {
|
|
146
|
-
scanText: (
|
|
157
|
+
scanText: (
|
|
158
|
+
frame: Frame,
|
|
159
|
+
overrides?: TextRecognitionOptions
|
|
160
|
+
) => TextDetection[] | undefined;
|
|
147
161
|
};
|
|
148
162
|
|
|
149
163
|
export type WorkletPayload = {
|
package/src/useVinScanner.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
export function useVinScanner(options?: VinScannerOptions) {
|
|
15
15
|
const resolvedOptions = useMemo(() => resolveOptions(options), [options]);
|
|
16
16
|
const lastFingerprintRef = useRef<string | null | undefined>(undefined);
|
|
17
|
+
const lastFrameTimestampRef = useRef(0);
|
|
17
18
|
|
|
18
19
|
const barcodeScanner = useMemo(() => {
|
|
19
20
|
if (!resolvedOptions.barcode.enabled) {
|
|
@@ -43,12 +44,26 @@ export function useVinScanner(options?: VinScannerOptions) {
|
|
|
43
44
|
const frameProcessor = useFrameProcessor(
|
|
44
45
|
(frame: Frame) => {
|
|
45
46
|
'worklet';
|
|
47
|
+
const now =
|
|
48
|
+
typeof frame?.timestamp === 'number' ? frame.timestamp : Date.now();
|
|
49
|
+
const maxFps = resolvedOptions.detection.maxFrameRate;
|
|
50
|
+
if (maxFps > 0) {
|
|
51
|
+
const minInterval = 1000 / maxFps;
|
|
52
|
+
if (
|
|
53
|
+
lastFrameTimestampRef.current > 0 &&
|
|
54
|
+
now - lastFrameTimestampRef.current < minInterval
|
|
55
|
+
) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
lastFrameTimestampRef.current = now;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame) : [];
|
|
47
62
|
const textBlocks = textScanner ? textScanner.scanText(frame) : [];
|
|
48
63
|
const payload = {
|
|
49
64
|
barcodes: barcodes ?? [],
|
|
50
65
|
textBlocks: textBlocks ?? [],
|
|
51
|
-
timestamp:
|
|
66
|
+
timestamp: now,
|
|
52
67
|
};
|
|
53
68
|
const candidates = buildVinCandidates(payload, resolvedOptions);
|
|
54
69
|
const best = pickBestCandidate(candidates, resolvedOptions);
|
package/src/vinUtils.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type ResolvedVinScannerOptions = {
|
|
|
38
38
|
resultMode: VinResultMode;
|
|
39
39
|
includePartialVins: boolean;
|
|
40
40
|
minPartialLength: number;
|
|
41
|
+
maxFrameRate: number;
|
|
41
42
|
};
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -57,6 +58,7 @@ const DEFAULT_RESOLVED_OPTIONS: ResolvedVinScannerOptions = {
|
|
|
57
58
|
emitDuplicates: false,
|
|
58
59
|
includePartialVins: false,
|
|
59
60
|
minPartialLength: 13,
|
|
61
|
+
maxFrameRate: 30,
|
|
60
62
|
},
|
|
61
63
|
};
|
|
62
64
|
|
|
@@ -101,6 +103,9 @@ export const resolveOptions = (
|
|
|
101
103
|
minPartialLength:
|
|
102
104
|
options?.detection?.minPartialLength ??
|
|
103
105
|
DEFAULT_RESOLVED_OPTIONS.detection.minPartialLength,
|
|
106
|
+
maxFrameRate:
|
|
107
|
+
options?.detection?.maxFrameRate ??
|
|
108
|
+
DEFAULT_RESOLVED_OPTIONS.detection.maxFrameRate,
|
|
104
109
|
},
|
|
105
110
|
};
|
|
106
111
|
};
|