@mleonard9/vin-scanner 1.2.2 → 1.2.4
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 +9 -7
- package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +52 -2
- package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +45 -1
- package/ios/VisionCameraBarcodeScanner.m +35 -2
- package/ios/VisionCameraTextRecognition.m +46 -2
- package/lib/commonjs/index.js +0 -8
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/scanBarcodes.js +16 -4
- package/lib/commonjs/scanBarcodes.js.map +1 -1
- package/lib/commonjs/scanText.js +16 -4
- package/lib/commonjs/scanText.js.map +1 -1
- package/lib/commonjs/useVinScanner.js +37 -16
- package/lib/commonjs/useVinScanner.js.map +1 -1
- package/lib/commonjs/vinUtils.js +30 -21
- package/lib/commonjs/vinUtils.js.map +1 -1
- package/lib/module/index.js +1 -9
- package/lib/module/index.js.map +1 -1
- package/lib/module/scanBarcodes.js +16 -4
- package/lib/module/scanBarcodes.js.map +1 -1
- package/lib/module/scanText.js +16 -4
- package/lib/module/scanText.js.map +1 -1
- package/lib/module/useVinScanner.js +37 -16
- package/lib/module/useVinScanner.js.map +1 -1
- package/lib/module/vinUtils.js +30 -21
- package/lib/module/vinUtils.js.map +1 -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 +29 -6
- 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 +5 -2
- package/lib/typescript/src/vinUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +1 -17
- package/src/scanBarcodes.ts +29 -6
- package/src/scanText.ts +33 -9
- package/src/types.ts +34 -7
- package/src/useVinScanner.ts +47 -25
- package/src/vinUtils.ts +34 -31
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Every frame, the camera runs ML Kit barcode + text recognition, extracts 17-char
|
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
type VinScannerEvent = {
|
|
80
|
-
mode: '
|
|
80
|
+
mode: 'first' | 'all';
|
|
81
81
|
timestamp: number;
|
|
82
82
|
best?: VinCandidate | null;
|
|
83
83
|
candidates: VinCandidate[];
|
|
@@ -89,7 +89,7 @@ type VinScannerEvent = {
|
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
`VinCandidate` contains `{ value, source: 'barcode' | 'text', boundingBox }`.
|
|
92
|
-
`resultMode === '
|
|
92
|
+
`resultMode === 'first'` returns at most one candidate per frame, while `'all'` returns every candidate so you can render overlays/selectors.
|
|
93
93
|
|
|
94
94
|
### Options
|
|
95
95
|
|
|
@@ -99,12 +99,14 @@ type VinScannerEvent = {
|
|
|
99
99
|
| `options.barcode.formats` | `BarcodeFormat[]` | Restrict ML Kit formats (`'code-39'`, `'code-128'`, `'pdf-417'`, etc.) | `['all']` |
|
|
100
100
|
| `options.text.enabled` | boolean | Enable text recognition | `true` |
|
|
101
101
|
| `options.text.language` | `'latin' \| 'chinese' \| 'devanagari' \| 'japanese' \| 'korean'` | ML Kit language pack | `'latin'` |
|
|
102
|
-
| `options.detection.resultMode` | `'
|
|
103
|
-
| `options.detection.
|
|
104
|
-
| `options.detection.emitDuplicates` | boolean | Emit unchanged payloads every frame | `false` for `'best'`, `true` for `'all'` |
|
|
102
|
+
| `options.detection.resultMode` | `'first' \| 'all'` | Emit the first candidate (barcodes preferred) or every candidate | `'first'` |
|
|
103
|
+
| `options.detection.textScanInterval` | number | Run text recognition every Nth frame (1 = every frame) | `1` |
|
|
105
104
|
| `options.detection.maxFrameRate` | number | Max FPS budget for frame processing (drops surplus frames to avoid blocking) | `30` |
|
|
106
105
|
| `options.detection.forceOrientation` | `'portrait' \| 'portrait-upside-down' \| 'landscape-left' \| 'landscape-right'` | Forces ML Kit to interpret every frame using the given orientation (useful when the UI is locked to portrait but the sensor reports landscape) | `null` |
|
|
107
|
-
| `options.onResult` | `(result, event) => void` | Convenience callback when using `useVinScanner`; receives either the
|
|
106
|
+
| `options.onResult` | `(result, event) => void` | Convenience callback when using `useVinScanner`; receives either the first candidate, all candidates, or `null` plus the raw event | `undefined` |
|
|
107
|
+
|
|
108
|
+
Using `resultMode: 'first'` automatically prefers barcode candidates before text, so there is no `preferBarcode` toggle.
|
|
109
|
+
Duplicates are always emitted so consumers can track every detection even when the VIN value remains unchanged.
|
|
108
110
|
|
|
109
111
|
### Advanced frame-processor controls
|
|
110
112
|
|
|
@@ -119,7 +121,7 @@ If you prefer to configure `react-native-vision-camera` yourself, grab the frame
|
|
|
119
121
|
|
|
120
122
|
```tsx
|
|
121
123
|
const { frameProcessor } = useVinScanner({
|
|
122
|
-
detection: { resultMode: '
|
|
124
|
+
detection: { resultMode: 'first' },
|
|
123
125
|
onResult: (vin, event) => {
|
|
124
126
|
console.log('Current VIN', vin, event);
|
|
125
127
|
},
|
package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.visioncamerabarcodescanner
|
|
2
2
|
|
|
3
|
+
import android.graphics.Rect
|
|
3
4
|
import android.media.Image
|
|
4
5
|
import com.google.android.gms.tasks.Task
|
|
5
6
|
import com.google.android.gms.tasks.Tasks
|
|
@@ -78,6 +79,14 @@ class VisionCameraBarcodeScannerModule(
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
private fun isInRegion(rect: Rect, roi: Rect?): Boolean {
|
|
83
|
+
if (roi == null) return true
|
|
84
|
+
// Check if the detection center is within the ROI
|
|
85
|
+
val centerX = rect.centerX()
|
|
86
|
+
val centerY = rect.centerY()
|
|
87
|
+
return roi.contains(centerX, centerY)
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
|
|
82
91
|
return try {
|
|
83
92
|
val options = mergedOptions(arguments)
|
|
@@ -89,15 +98,56 @@ class VisionCameraBarcodeScannerModule(
|
|
|
89
98
|
val task: Task<List<Barcode>> = scanner.process(image)
|
|
90
99
|
val barcodes: List<Barcode> = Tasks.await(task)
|
|
91
100
|
|
|
101
|
+
// Region of Interest processing
|
|
102
|
+
// ROI is passed as fractional coordinates (0.0-1.0) relative to the image size
|
|
103
|
+
var roiRect: Rect? = null
|
|
104
|
+
val roiMap = options["regionOfInterest"] as? Map<*, *>
|
|
105
|
+
if (roiMap != null) {
|
|
106
|
+
val top = (roiMap["top"] as? Double ?: 0.0)
|
|
107
|
+
val left = (roiMap["left"] as? Double ?: 0.0)
|
|
108
|
+
val width = (roiMap["width"] as? Double ?: 1.0)
|
|
109
|
+
val height = (roiMap["height"] as? Double ?: 1.0)
|
|
110
|
+
|
|
111
|
+
// The input image dimensions (rotated)
|
|
112
|
+
// MLKit coordinates match the InputImage dimensions.
|
|
113
|
+
// If we have rotation, we need to be careful.
|
|
114
|
+
// However, for simplicity in V1, let's assume ROI is relative to the *displayed* frame
|
|
115
|
+
// which matches MLKit's coordinate system after rotation usually?
|
|
116
|
+
// Actually, MLKit reports coordinates in the image's coordinate system.
|
|
117
|
+
// If rotation is 90, width/height swap.
|
|
118
|
+
// Let's use the dimensions from MLKit's InputImage if accessible, or Frame.
|
|
119
|
+
// VisionCamera Frame width/height are oriented.
|
|
120
|
+
|
|
121
|
+
val imgWidth = image.width
|
|
122
|
+
val imgHeight = image.height
|
|
123
|
+
|
|
124
|
+
val rLeft = (left * imgWidth).toInt()
|
|
125
|
+
val rTop = (top * imgHeight).toInt()
|
|
126
|
+
val rRight = rLeft + (width * imgWidth).toInt()
|
|
127
|
+
val rBottom = rTop + (height * imgHeight).toInt()
|
|
128
|
+
|
|
129
|
+
roiRect = Rect(rLeft, rTop, rRight, rBottom)
|
|
130
|
+
}
|
|
131
|
+
|
|
92
132
|
val detections = ArrayList<Map<String, Any?>>()
|
|
133
|
+
// We can't predict exact size if we filter by ROI, but allocating max size is safe for SharedArray
|
|
134
|
+
// Ideally we would compact it, but SharedArray is fixed size.
|
|
135
|
+
// We'll filter in the loop and just leave unused slots at the end (or compact indices).
|
|
136
|
+
// Actually, simpler to just build the list and then write to SharedArray.
|
|
137
|
+
|
|
138
|
+
val filteredBarcodes = barcodes.filter { barcode ->
|
|
139
|
+
val box = barcode.boundingBox
|
|
140
|
+
box == null || isInRegion(box, roiRect)
|
|
141
|
+
}
|
|
142
|
+
|
|
93
143
|
val sharedArray =
|
|
94
|
-
if (
|
|
144
|
+
if (filteredBarcodes.isNotEmpty()) SharedArray(proxy, filteredBarcodes.size * BOX_STRIDE * java.lang.Float.BYTES)
|
|
95
145
|
else null
|
|
96
146
|
val floatBuffer = sharedArray?.byteBuffer
|
|
97
147
|
?.order(ByteOrder.nativeOrder())
|
|
98
148
|
?.asFloatBuffer()
|
|
99
149
|
|
|
100
|
-
|
|
150
|
+
filteredBarcodes.forEachIndexed { index, barcode ->
|
|
101
151
|
val detection = HashMap<String, Any?>()
|
|
102
152
|
detection["rawValue"] = barcode.rawValue
|
|
103
153
|
detection["displayValue"] = barcode.displayValue
|
package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.visioncameratextrecognition
|
|
2
2
|
|
|
3
|
+
import android.graphics.Rect
|
|
3
4
|
import android.media.Image
|
|
4
5
|
import com.google.android.gms.tasks.Task
|
|
5
6
|
import com.google.android.gms.tasks.Tasks
|
|
@@ -19,6 +20,7 @@ import com.mrousavy.camera.frameprocessors.VisionCameraProxy
|
|
|
19
20
|
import java.nio.ByteOrder
|
|
20
21
|
import java.util.HashMap
|
|
21
22
|
import java.util.ArrayList
|
|
23
|
+
import java.util.regex.Pattern
|
|
22
24
|
|
|
23
25
|
private const val TEXT_BOX_STRIDE = 12
|
|
24
26
|
|
|
@@ -52,6 +54,12 @@ class VisionCameraTextRecognitionModule(
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
private fun isInRegion(rect: Rect?, roi: Rect?): Boolean {
|
|
58
|
+
if (roi == null || rect == null) return true
|
|
59
|
+
// Check if overlap or contained. For VIN scanning, contained center is usually best
|
|
60
|
+
return roi.contains(rect.centerX(), rect.centerY())
|
|
61
|
+
}
|
|
62
|
+
|
|
55
63
|
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
|
|
56
64
|
try {
|
|
57
65
|
val mediaImage: Image = frame.image
|
|
@@ -59,15 +67,50 @@ class VisionCameraTextRecognitionModule(
|
|
|
59
67
|
val rotationDegrees = rotationOverride ?: frame.imageProxy.imageInfo.rotationDegrees
|
|
60
68
|
val requestedLanguage = arguments?.get("language")?.toString()?.ifEmpty { null }
|
|
61
69
|
val effectiveLanguage = requestedLanguage ?: language
|
|
70
|
+
val validationPattern = arguments?.get("validationPattern")?.toString()?.ifEmpty { null }
|
|
71
|
+
|
|
62
72
|
val recognizer = recognizerFor(effectiveLanguage)
|
|
63
73
|
val image = InputImage.fromMediaImage(mediaImage, rotationDegrees)
|
|
64
74
|
val task: Task<Text> = recognizer.process(image)
|
|
65
75
|
val result: Text? = Tasks.await(task)
|
|
76
|
+
|
|
66
77
|
val resultText = result?.text
|
|
67
78
|
val detections = ArrayList<Map<String, Any?>>()
|
|
68
79
|
val boxValues = ArrayList<FloatArray>()
|
|
69
80
|
|
|
81
|
+
// Calculate ROI if present
|
|
82
|
+
var roiRect: Rect? = null
|
|
83
|
+
val roiMap = arguments?.get("regionOfInterest") as? Map<*, *>
|
|
84
|
+
if (roiMap != null) {
|
|
85
|
+
val top = (roiMap["top"] as? Double ?: 0.0)
|
|
86
|
+
val left = (roiMap["left"] as? Double ?: 0.0)
|
|
87
|
+
val width = (roiMap["width"] as? Double ?: 1.0)
|
|
88
|
+
val height = (roiMap["height"] as? Double ?: 1.0)
|
|
89
|
+
|
|
90
|
+
val imgWidth = image.width
|
|
91
|
+
val imgHeight = image.height
|
|
92
|
+
|
|
93
|
+
val rLeft = (left * imgWidth).toInt()
|
|
94
|
+
val rTop = (top * imgHeight).toInt()
|
|
95
|
+
val rRight = rLeft + (width * imgWidth).toInt()
|
|
96
|
+
val rBottom = rTop + (height * imgHeight).toInt()
|
|
97
|
+
|
|
98
|
+
roiRect = Rect(rLeft, rTop, rRight, rBottom)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Compile regex if pattern provided
|
|
102
|
+
val pattern = if (validationPattern != null) Pattern.compile(validationPattern) else null
|
|
103
|
+
|
|
70
104
|
result?.textBlocks?.forEach { block ->
|
|
105
|
+
if (!isInRegion(block.boundingBox, roiRect)) return@forEach
|
|
106
|
+
|
|
107
|
+
// Native filtering optimization:
|
|
108
|
+
// If a validation pattern is provided, we check if the block text matches it.
|
|
109
|
+
// If not, we skip it. This significantly reduces bridge traffic.
|
|
110
|
+
if (pattern != null && !pattern.matcher(block.text).find()) {
|
|
111
|
+
return@forEach
|
|
112
|
+
}
|
|
113
|
+
|
|
71
114
|
val blockBounds = block.boundingBox
|
|
72
115
|
if (block.lines.isEmpty()) {
|
|
73
116
|
val detection = HashMap<String, Any?>()
|
|
@@ -93,6 +136,8 @@ class VisionCameraTextRecognitionModule(
|
|
|
93
136
|
)
|
|
94
137
|
}
|
|
95
138
|
block.lines.forEach { line ->
|
|
139
|
+
// Optional: we could also check line bounding box against ROI, but block check is usually sufficient and faster
|
|
140
|
+
|
|
96
141
|
if (line.elements.isEmpty()) {
|
|
97
142
|
val detection = HashMap<String, Any?>()
|
|
98
143
|
detection["resultText"] = resultText
|
|
@@ -171,4 +216,3 @@ class VisionCameraTextRecognitionModule(
|
|
|
171
216
|
}
|
|
172
217
|
}
|
|
173
218
|
}
|
|
174
|
-
|
|
@@ -37,6 +37,12 @@
|
|
|
37
37
|
return self;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
- (BOOL)isRect:(CGRect)rect withinROI:(CGRect)roi {
|
|
41
|
+
// Check if the center of the rect is within the ROI
|
|
42
|
+
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
|
|
43
|
+
return CGRectContainsPoint(roi, center);
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
- (id _Nullable)callback:(Frame* _Nonnull)frame
|
|
41
47
|
withArguments:(NSDictionary* _Nullable)arguments {
|
|
42
48
|
NSMutableDictionary *config = [self.configuration mutableCopy] ?: [NSMutableDictionary dictionary];
|
|
@@ -84,6 +90,22 @@
|
|
|
84
90
|
image.orientation = correctedOrientation;
|
|
85
91
|
NSMutableArray *detections = [NSMutableArray array];
|
|
86
92
|
__block NSDictionary *resultPayload = @{};
|
|
93
|
+
|
|
94
|
+
// Region of Interest
|
|
95
|
+
NSDictionary *roiConfig = config[@"regionOfInterest"];
|
|
96
|
+
CGRect roiRect = CGRectNull;
|
|
97
|
+
if (roiConfig) {
|
|
98
|
+
CGFloat top = [roiConfig[@"top"] doubleValue];
|
|
99
|
+
CGFloat left = [roiConfig[@"left"] doubleValue];
|
|
100
|
+
CGFloat width = [roiConfig[@"width"] doubleValue];
|
|
101
|
+
CGFloat height = [roiConfig[@"height"] doubleValue];
|
|
102
|
+
|
|
103
|
+
CGFloat imgWidth = CVPixelBufferGetWidth(CMSampleBufferGetImageBuffer(buffer));
|
|
104
|
+
CGFloat imgHeight = CVPixelBufferGetHeight(CMSampleBufferGetImageBuffer(buffer));
|
|
105
|
+
|
|
106
|
+
roiRect = CGRectMake(left * imgWidth, top * imgHeight, width * imgWidth, height * imgHeight);
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
dispatch_group_t dispatchGroup = dispatch_group_create();
|
|
88
110
|
dispatch_group_enter(dispatchGroup);
|
|
89
111
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
@@ -94,15 +116,26 @@
|
|
|
94
116
|
dispatch_group_leave(dispatchGroup);
|
|
95
117
|
return;
|
|
96
118
|
}
|
|
119
|
+
|
|
120
|
+
// Filter barcodes by ROI
|
|
121
|
+
NSMutableArray<MLKBarcode *> *filteredBarcodes = [NSMutableArray array];
|
|
122
|
+
if (barcodes.count > 0) {
|
|
123
|
+
for (MLKBarcode *barcode in barcodes) {
|
|
124
|
+
if (CGRectIsNull(roiRect) || [self isRect:barcode.frame withinROI:roiRect]) {
|
|
125
|
+
[filteredBarcodes addObject:barcode];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
97
130
|
SharedArray *sharedBoxes = nil;
|
|
98
131
|
float *boxData = nil;
|
|
99
|
-
const NSUInteger count =
|
|
132
|
+
const NSUInteger count = filteredBarcodes.count;
|
|
100
133
|
if (count > 0 && self.proxyHolder != nil) {
|
|
101
134
|
sharedBoxes = [[SharedArray alloc] initWithProxy:self.proxyHolder
|
|
102
135
|
allocateWithSize:count * sizeof(float) * 6];
|
|
103
136
|
boxData = (float *)sharedBoxes.data;
|
|
104
137
|
}
|
|
105
|
-
[
|
|
138
|
+
[filteredBarcodes enumerateObjectsUsingBlock:^(MLKBarcode * _Nonnull barcode, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
106
139
|
NSMutableDictionary *obj = [[NSMutableDictionary alloc] init];
|
|
107
140
|
obj[@"displayValue"] = barcode.displayValue ?: (id)kCFNull;
|
|
108
141
|
obj[@"rawValue"] = barcode.rawValue ?: (id)kCFNull;
|
|
@@ -78,6 +78,12 @@
|
|
|
78
78
|
return fallback;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
- (BOOL)isRect:(CGRect)rect withinROI:(CGRect)roi {
|
|
82
|
+
// Check if the center of the rect is within the ROI
|
|
83
|
+
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
|
|
84
|
+
return CGRectContainsPoint(roi, center);
|
|
85
|
+
}
|
|
86
|
+
|
|
81
87
|
- (id _Nullable)callback:(Frame* _Nonnull)frame
|
|
82
88
|
withArguments:(NSDictionary* _Nullable)arguments {
|
|
83
89
|
CMSampleBufferRef buffer = frame.buffer;
|
|
@@ -112,6 +118,33 @@
|
|
|
112
118
|
NSMutableArray *detections = [NSMutableArray array];
|
|
113
119
|
NSMutableArray<NSArray<NSNumber *> *> *boxValues = [NSMutableArray array];
|
|
114
120
|
NSString *language = arguments[@"language"] ?: self.preferredLanguage ?: @"latin";
|
|
121
|
+
NSString *validationPattern = arguments[@"validationPattern"];
|
|
122
|
+
|
|
123
|
+
// Region of Interest
|
|
124
|
+
NSDictionary *roiConfig = arguments[@"regionOfInterest"];
|
|
125
|
+
CGRect roiRect = CGRectNull;
|
|
126
|
+
if (roiConfig) {
|
|
127
|
+
CGFloat top = [roiConfig[@"top"] doubleValue];
|
|
128
|
+
CGFloat left = [roiConfig[@"left"] doubleValue];
|
|
129
|
+
CGFloat width = [roiConfig[@"width"] doubleValue];
|
|
130
|
+
CGFloat height = [roiConfig[@"height"] doubleValue];
|
|
131
|
+
|
|
132
|
+
CGFloat imgWidth = CVPixelBufferGetWidth(CMSampleBufferGetImageBuffer(buffer));
|
|
133
|
+
CGFloat imgHeight = CVPixelBufferGetHeight(CMSampleBufferGetImageBuffer(buffer));
|
|
134
|
+
|
|
135
|
+
roiRect = CGRectMake(left * imgWidth, top * imgHeight, width * imgWidth, height * imgHeight);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
NSRegularExpression *regex = nil;
|
|
139
|
+
if (validationPattern.length > 0) {
|
|
140
|
+
NSError *regexError = nil;
|
|
141
|
+
regex = [NSRegularExpression regularExpressionWithPattern:validationPattern options:0 error:®exError];
|
|
142
|
+
if (regexError) {
|
|
143
|
+
// Fallback to no regex if invalid
|
|
144
|
+
regex = nil;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
115
148
|
MLKTextRecognizer *recognizer = [self recognizerForLanguage:language];
|
|
116
149
|
dispatch_group_t dispatchGroup = dispatch_group_create();
|
|
117
150
|
dispatch_group_enter(dispatchGroup);
|
|
@@ -125,8 +158,20 @@
|
|
|
125
158
|
}
|
|
126
159
|
NSString *resultText = result.text;
|
|
127
160
|
for (MLKTextBlock *block in result.blocks) {
|
|
161
|
+
// Filter by ROI
|
|
162
|
+
if (!CGRectIsNull(roiRect) && ![self isRect:block.frame withinROI:roiRect]) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Filter by Regex
|
|
167
|
+
if (regex != nil) {
|
|
168
|
+
NSRange range = NSMakeRange(0, block.text.length);
|
|
169
|
+
if ([regex numberOfMatchesInString:block.text options:0 range:range] == 0) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
128
174
|
CGRect blockFrame = block.frame;
|
|
129
|
-
// Coordinates are now correct after orientation fix
|
|
130
175
|
|
|
131
176
|
if (block.lines.count == 0) {
|
|
132
177
|
NSMutableDictionary *entry = [[NSMutableDictionary alloc] init];
|
|
@@ -210,4 +255,3 @@
|
|
|
210
255
|
VISION_EXPORT_FRAME_PROCESSOR(VisionCameraTextRecognitionPlugin, vinScannerText)
|
|
211
256
|
|
|
212
257
|
@end
|
|
213
|
-
|
package/lib/commonjs/index.js
CHANGED
|
@@ -35,19 +35,11 @@ const Camera = exports.Camera = /*#__PURE__*/(0, _react.forwardRef)(function Cam
|
|
|
35
35
|
const resolvedOptions = (0, _react.useMemo)(() => (0, _vinUtils.resolveOptions)(options), [options]);
|
|
36
36
|
const barcodeScanner = useBarcodeScanner(resolvedOptions.barcode);
|
|
37
37
|
const textScanner = useTextScanner(resolvedOptions.text);
|
|
38
|
-
const lastEmissionRef = (0, _react.useRef)(undefined);
|
|
39
38
|
const handleDetections = (0, _reactNativeWorkletsCore.useRunOnJS)(payload => {
|
|
40
39
|
const candidates = (0, _vinUtils.buildVinCandidates)(payload, resolvedOptions);
|
|
41
40
|
const best = (0, _vinUtils.pickFirstCandidate)(candidates, resolvedOptions);
|
|
42
41
|
const mode = resolvedOptions.detection.resultMode;
|
|
43
42
|
const filteredCandidates = mode === 'all' ? candidates : best ? [best] : [];
|
|
44
|
-
const fingerprint = mode === 'all' ? filteredCandidates.map(candidate => candidate.value).join('|') || null : (best === null || best === void 0 ? void 0 : best.value) ?? null;
|
|
45
|
-
if (!resolvedOptions.detection.emitDuplicates) {
|
|
46
|
-
if (lastEmissionRef.current !== undefined && fingerprint === lastEmissionRef.current) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
lastEmissionRef.current = fingerprint;
|
|
50
|
-
}
|
|
51
43
|
callback({
|
|
52
44
|
mode,
|
|
53
45
|
timestamp: payload.timestamp,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNativeVisionCamera","_reactNativeWorkletsCore","_scanBarcodes","_scanText","_vinUtils","_useVinScanner","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","Camera","exports","forwardRef","props","ref","device","callback","options","rest","resolvedOptions","useMemo","resolveOptions","barcodeScanner","useBarcodeScanner","barcode","textScanner","useTextScanner","text","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNativeVisionCamera","_reactNativeWorkletsCore","_scanBarcodes","_scanText","_vinUtils","_useVinScanner","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","Camera","exports","forwardRef","props","ref","device","callback","options","rest","resolvedOptions","useMemo","resolveOptions","barcodeScanner","useBarcodeScanner","barcode","textScanner","useTextScanner","text","handleDetections","useRunOnJS","payload","candidates","buildVinCandidates","best","pickFirstCandidate","mode","detection","resultMode","filteredCandidates","timestamp","raw","barcodes","textBlocks","scanBarcodes","scanText","frameProcessor","useFrameProcessor","frame","Date","now","createElement","Fragment","pixelFormat","enabled","createBarcodeScannerPlugin","formats","createTextRecognitionPlugin","language"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -70,7 +70,7 @@ const normalizeBarcodeDetections = payload => {
|
|
|
70
70
|
return output;
|
|
71
71
|
});
|
|
72
72
|
};
|
|
73
|
-
const mergeArgs = (baseArgs, overrides, orientation) => {
|
|
73
|
+
const mergeArgs = (baseArgs, overrides, orientation, regionOfInterest) => {
|
|
74
74
|
'worklet';
|
|
75
75
|
|
|
76
76
|
const merged = {
|
|
@@ -79,6 +79,7 @@ const mergeArgs = (baseArgs, overrides, orientation) => {
|
|
|
79
79
|
if (overrides) {
|
|
80
80
|
const {
|
|
81
81
|
orientation: overrideOrientation,
|
|
82
|
+
regionOfInterest: overrideRoi,
|
|
82
83
|
...formatMap
|
|
83
84
|
} = overrides;
|
|
84
85
|
Object.entries(formatMap).forEach(([key, value]) => {
|
|
@@ -89,8 +90,18 @@ const mergeArgs = (baseArgs, overrides, orientation) => {
|
|
|
89
90
|
} else if (orientation) {
|
|
90
91
|
merged.orientation = orientation;
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
if (overrideRoi) {
|
|
94
|
+
merged.regionOfInterest = overrideRoi;
|
|
95
|
+
} else if (regionOfInterest) {
|
|
96
|
+
merged.regionOfInterest = regionOfInterest;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (orientation) {
|
|
100
|
+
merged.orientation = orientation;
|
|
101
|
+
}
|
|
102
|
+
if (regionOfInterest) {
|
|
103
|
+
merged.regionOfInterest = regionOfInterest;
|
|
104
|
+
}
|
|
94
105
|
}
|
|
95
106
|
return merged;
|
|
96
107
|
};
|
|
@@ -107,7 +118,8 @@ function createBarcodeScannerPlugin(formats) {
|
|
|
107
118
|
'worklet';
|
|
108
119
|
|
|
109
120
|
const orientation = overrides === null || overrides === void 0 ? void 0 : overrides.orientation;
|
|
110
|
-
const
|
|
121
|
+
const regionOfInterest = overrides === null || overrides === void 0 ? void 0 : overrides.regionOfInterest;
|
|
122
|
+
const args = overrides || orientation || regionOfInterest ? mergeArgs(baseOptions, overrides, orientation ?? null, regionOfInterest ?? null) : baseOptions;
|
|
111
123
|
const result = plugin.call(frame, args);
|
|
112
124
|
return normalizeBarcodeDetections(result);
|
|
113
125
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_reactNativeVisionCamera","LINKING_ERROR","Platform","select","ios","default","DEFAULT_FORMATS","KNOWN_FORMATS","toNativeBarcodeOptions","formats","sanitized","length","includes","all","reduce","acc","format","BOX_STRIDE","toFloat32Array","input","Float32Array","undefined","normalizeBarcodeDetections","payload","Array","isArray","record","detections","buffer","boxes","map","detection","index","output","boxIndex","offset","top","bottom","left","right","width","height","mergeArgs","baseArgs","overrides","orientation","merged","overrideOrientation","formatMap","Object","entries","forEach","key","value","createBarcodeScannerPlugin","baseOptions","plugin","VisionCameraProxy","initFrameProcessorPlugin","Error","scanBarcodes","frame","args","result","call"],"sourceRoot":"../../src","sources":["scanBarcodes.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,wBAAA,GAAAD,OAAA;
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_reactNativeVisionCamera","LINKING_ERROR","Platform","select","ios","default","DEFAULT_FORMATS","KNOWN_FORMATS","toNativeBarcodeOptions","formats","sanitized","length","includes","all","reduce","acc","format","BOX_STRIDE","toFloat32Array","input","Float32Array","undefined","normalizeBarcodeDetections","payload","Array","isArray","record","detections","buffer","boxes","map","detection","index","output","boxIndex","offset","top","bottom","left","right","width","height","mergeArgs","baseArgs","overrides","orientation","regionOfInterest","merged","overrideOrientation","overrideRoi","formatMap","Object","entries","forEach","key","value","createBarcodeScannerPlugin","baseOptions","plugin","VisionCameraProxy","initFrameProcessorPlugin","Error","scanBarcodes","frame","args","result","call"],"sourceRoot":"../../src","sources":["scanBarcodes.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/scanText.js
CHANGED
|
@@ -60,7 +60,7 @@ const normalizeTextDetections = payload => {
|
|
|
60
60
|
return output;
|
|
61
61
|
});
|
|
62
62
|
};
|
|
63
|
-
const mergeTextArgs = (base, overrides, orientation) => {
|
|
63
|
+
const mergeTextArgs = (base, overrides, orientation, regionOfInterest) => {
|
|
64
64
|
'worklet';
|
|
65
65
|
|
|
66
66
|
const merged = {
|
|
@@ -69,6 +69,7 @@ const mergeTextArgs = (base, overrides, orientation) => {
|
|
|
69
69
|
if (overrides) {
|
|
70
70
|
const {
|
|
71
71
|
orientation: overrideOrientation,
|
|
72
|
+
regionOfInterest: overrideRoi,
|
|
72
73
|
...rest
|
|
73
74
|
} = overrides;
|
|
74
75
|
Object.assign(merged, rest);
|
|
@@ -77,8 +78,18 @@ const mergeTextArgs = (base, overrides, orientation) => {
|
|
|
77
78
|
} else if (orientation) {
|
|
78
79
|
merged.orientation = orientation;
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
if (overrideRoi) {
|
|
82
|
+
merged.regionOfInterest = overrideRoi;
|
|
83
|
+
} else if (regionOfInterest) {
|
|
84
|
+
merged.regionOfInterest = regionOfInterest;
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
if (orientation) {
|
|
88
|
+
merged.orientation = orientation;
|
|
89
|
+
}
|
|
90
|
+
if (regionOfInterest) {
|
|
91
|
+
merged.regionOfInterest = regionOfInterest;
|
|
92
|
+
}
|
|
82
93
|
}
|
|
83
94
|
return merged;
|
|
84
95
|
};
|
|
@@ -94,7 +105,8 @@ function createTextRecognitionPlugin(options) {
|
|
|
94
105
|
'worklet';
|
|
95
106
|
|
|
96
107
|
const orientation = overrides === null || overrides === void 0 ? void 0 : overrides.orientation;
|
|
97
|
-
const
|
|
108
|
+
const regionOfInterest = overrides === null || overrides === void 0 ? void 0 : overrides.regionOfInterest;
|
|
109
|
+
const args = overrides || orientation || regionOfInterest ? mergeTextArgs(options, overrides, orientation ?? null, regionOfInterest ?? null) : options;
|
|
98
110
|
const result = plugin.call(frame, args);
|
|
99
111
|
return normalizeTextDetections(result);
|
|
100
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_reactNativeVisionCamera","LINKING_ERROR","Platform","select","ios","default","TEXT_BOX_STRIDE","toFloat32Array","input","Float32Array","undefined","normalizeTextDetections","payload","Array","isArray","record","detections","buffer","boxes","map","detection","index","output","boxIndex","offset","length","blockFrameTop","blockFrameBottom","blockFrameLeft","blockFrameRight","lineFrameTop","lineFrameBottom","lineFrameLeft","lineFrameRight","elementFrameTop","elementFrameBottom","elementFrameLeft","elementFrameRight","mergeTextArgs","base","overrides","orientation","merged","overrideOrientation","rest","Object","assign","createTextRecognitionPlugin","options","plugin","VisionCameraProxy","initFrameProcessorPlugin","Error","scanText","frame","args","result","call"],"sourceRoot":"../../src","sources":["scanText.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,wBAAA,GAAAD,OAAA;
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_reactNativeVisionCamera","LINKING_ERROR","Platform","select","ios","default","TEXT_BOX_STRIDE","toFloat32Array","input","Float32Array","undefined","normalizeTextDetections","payload","Array","isArray","record","detections","buffer","boxes","map","detection","index","output","boxIndex","offset","length","blockFrameTop","blockFrameBottom","blockFrameLeft","blockFrameRight","lineFrameTop","lineFrameBottom","lineFrameLeft","lineFrameRight","elementFrameTop","elementFrameBottom","elementFrameLeft","elementFrameRight","mergeTextArgs","base","overrides","orientation","regionOfInterest","merged","overrideOrientation","overrideRoi","rest","Object","assign","createTextRecognitionPlugin","options","plugin","VisionCameraProxy","initFrameProcessorPlugin","Error","scanText","frame","args","result","call"],"sourceRoot":"../../src","sources":["scanText.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -12,8 +12,8 @@ var _scanText = require("./scanText");
|
|
|
12
12
|
var _vinUtils = require("./vinUtils");
|
|
13
13
|
function useVinScanner(options) {
|
|
14
14
|
const resolvedOptions = (0, _react.useMemo)(() => (0, _vinUtils.resolveOptions)(options), [options]);
|
|
15
|
-
const lastFingerprintRef = (0, _react.useRef)(undefined);
|
|
16
15
|
const lastFrameTimestampRef = (0, _react.useRef)(0);
|
|
16
|
+
const frameCounterRef = (0, _react.useRef)(0);
|
|
17
17
|
const barcodeScanner = (0, _react.useMemo)(() => {
|
|
18
18
|
if (!resolvedOptions.barcode.enabled) {
|
|
19
19
|
return null;
|
|
@@ -25,7 +25,11 @@ function useVinScanner(options) {
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
return (0, _scanText.createTextRecognitionPlugin)({
|
|
28
|
-
language: resolvedOptions.text.language
|
|
28
|
+
language: resolvedOptions.text.language,
|
|
29
|
+
// We can use a broad validation pattern here to help the native side
|
|
30
|
+
// filter obvious non-VIN text, improving bridge performance.
|
|
31
|
+
// VINs are 17 chars alphanumeric.
|
|
32
|
+
validationPattern: '[A-HJ-NPR-Z0-9]{10,}'
|
|
29
33
|
});
|
|
30
34
|
}, [resolvedOptions.text]);
|
|
31
35
|
const emitResult = (0, _reactNativeWorkletsCore.useRunOnJS)((result, event) => {
|
|
@@ -46,14 +50,38 @@ function useVinScanner(options) {
|
|
|
46
50
|
lastFrameTimestampRef.current = now;
|
|
47
51
|
}
|
|
48
52
|
const orientationOverride = resolvedOptions.detection.forceOrientation ?? null;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
const regionOfInterest = resolvedOptions.detection.regionOfInterest;
|
|
54
|
+
const barcodeArgs = {
|
|
55
|
+
orientation: orientationOverride ?? undefined,
|
|
56
|
+
regionOfInterest
|
|
57
|
+
};
|
|
52
58
|
const barcodes = barcodeScanner ? barcodeScanner.scanBarcodes(frame, barcodeArgs) : [];
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
|
|
60
|
+
// Performance Optimization:
|
|
61
|
+
// If we found a valid VIN in the barcode, and we only need the "first" result,
|
|
62
|
+
// skip the expensive text recognition step entirely.
|
|
63
|
+
let hasBarcodeVin = false;
|
|
64
|
+
if (barcodes && barcodes.length > 0) {
|
|
65
|
+
const tempCandidates = (0, _vinUtils.buildVinCandidates)({
|
|
66
|
+
barcodes,
|
|
67
|
+
textBlocks: [],
|
|
68
|
+
timestamp: now
|
|
69
|
+
}, resolvedOptions);
|
|
70
|
+
if (tempCandidates.length > 0) {
|
|
71
|
+
hasBarcodeVin = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const textArgs = {
|
|
75
|
+
orientation: orientationOverride ?? undefined,
|
|
76
|
+
regionOfInterest
|
|
77
|
+
};
|
|
78
|
+
frameCounterRef.current += 1;
|
|
79
|
+
const frameIndex = frameCounterRef.current;
|
|
80
|
+
const textScanInterval = resolvedOptions.detection.textScanInterval;
|
|
81
|
+
const shouldRunText = !hasBarcodeVin &&
|
|
82
|
+
// Skip if we already have a VIN from barcode
|
|
83
|
+
textScanner && (textScanInterval <= 1 || frameIndex % textScanInterval === 0);
|
|
84
|
+
const textBlocks = shouldRunText && textScanner ? textScanner.scanText(frame, textArgs) : [];
|
|
57
85
|
const payload = {
|
|
58
86
|
barcodes: barcodes ?? [],
|
|
59
87
|
textBlocks: textBlocks ?? [],
|
|
@@ -63,13 +91,6 @@ function useVinScanner(options) {
|
|
|
63
91
|
const best = (0, _vinUtils.pickFirstCandidate)(candidates, resolvedOptions);
|
|
64
92
|
const mode = resolvedOptions.detection.resultMode;
|
|
65
93
|
const normalizedResult = mode === 'all' ? candidates.length > 0 ? candidates : null : best ?? null;
|
|
66
|
-
const fingerprint = mode === 'all' ? candidates.map(candidate => candidate.value).join('|') || null : (best === null || best === void 0 ? void 0 : best.value) ?? null;
|
|
67
|
-
if (!resolvedOptions.detection.emitDuplicates) {
|
|
68
|
-
if (lastFingerprintRef.current !== undefined && fingerprint === lastFingerprintRef.current) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
lastFingerprintRef.current = fingerprint;
|
|
72
|
-
}
|
|
73
94
|
const event = {
|
|
74
95
|
mode,
|
|
75
96
|
timestamp: payload.timestamp,
|