@mleonard9/vin-scanner 0.2.3 → 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 +7 -4
- 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 +7 -4
- 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 +11 -5
package/README.md
CHANGED
|
@@ -102,8 +102,15 @@ type VinScannerEvent = {
|
|
|
102
102
|
| `options.detection.preferBarcode` | boolean | Prefer barcode hits when selecting the best VIN | `true` |
|
|
103
103
|
| `options.detection.validateChecksum` | boolean | Run VIN checksum validation | `true` |
|
|
104
104
|
| `options.detection.emitDuplicates` | boolean | Emit unchanged payloads every frame | `false` for `'best'`, `true` for `'all'` |
|
|
105
|
+
| `options.detection.maxFrameRate` | number | Max FPS budget for frame processing (drops surplus frames to avoid blocking) | `30` |
|
|
105
106
|
| `options.onResult` | `(result, event) => void` | Convenience callback when using `useVinScanner`; receives either the best candidate, all candidates, or `null` plus the raw event | `undefined` |
|
|
106
107
|
|
|
108
|
+
### Advanced frame-processor controls
|
|
109
|
+
|
|
110
|
+
- **Per-frame plugin overrides:** both barcode and text frame processor plugins accept per-frame arguments, so you can dynamically change ML Kit barcode formats or text recognition language without reinitializing the plugin. Call `barcodeScanner.scanBarcodes(frame, { 'pdf-417': true })` or `textScanner.scanText(frame, { language: 'japanese' })` inside your worklet to override the resolved defaults for a single frame.
|
|
111
|
+
- **Shared bounding boxes:** native plugins now stream bounding box coordinates via zero-copy shared arrays, minimizing JSI serialization. The hook translates these buffers into the familiar `BoundingBox` structures before running VIN heuristics, so no API change is required.
|
|
112
|
+
- **Orientation-safe processing:** the native plugins forward VisionCamera’s frame orientation metadata directly into ML Kit as recommended in the [VisionCamera orientation guide](https://react-native-vision-camera.com/docs/guides/orientation), ensuring portrait VIN scans stay upright.
|
|
113
|
+
|
|
107
114
|
### Hook-only usage
|
|
108
115
|
|
|
109
116
|
If you prefer to configure `react-native-vision-camera` yourself, grab the frame processor from the hook:
|
package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package com.visioncamerabarcodescanner
|
|
2
2
|
|
|
3
3
|
import android.media.Image
|
|
4
|
-
import com.facebook.react.bridge.WritableNativeArray
|
|
5
|
-
import com.facebook.react.bridge.WritableNativeMap
|
|
6
4
|
import com.google.android.gms.tasks.Task
|
|
7
5
|
import com.google.android.gms.tasks.Tasks
|
|
8
6
|
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
|
|
@@ -25,69 +23,101 @@ import com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_13
|
|
|
25
23
|
import com.google.mlkit.vision.common.InputImage
|
|
26
24
|
import com.mrousavy.camera.frameprocessors.Frame
|
|
27
25
|
import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
|
|
26
|
+
import com.mrousavy.camera.frameprocessors.SharedArray
|
|
28
27
|
import com.mrousavy.camera.frameprocessors.VisionCameraProxy
|
|
28
|
+
import java.nio.ByteOrder
|
|
29
|
+
import java.util.HashMap
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
private const val BOX_STRIDE = 6
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
class VisionCameraBarcodeScannerModule(
|
|
34
|
+
private val proxy : VisionCameraProxy,
|
|
35
|
+
options: Map<String, Any>?
|
|
36
|
+
): FrameProcessorPlugin() {
|
|
37
|
+
|
|
38
|
+
private val initializerOptions: Map<String, Any> = options ?: emptyMap()
|
|
39
|
+
|
|
40
|
+
private fun mergedOptions(overrides: Map<String, Any>?): Map<String, Any> {
|
|
41
|
+
if (overrides == null || overrides.isEmpty()) {
|
|
42
|
+
return initializerOptions
|
|
43
|
+
}
|
|
44
|
+
val merged = HashMap(initializerOptions)
|
|
45
|
+
merged.putAll(overrides)
|
|
46
|
+
return merged
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private fun buildScannerOptions(effective: Map<String, Any>): BarcodeScannerOptions {
|
|
50
|
+
val builder = BarcodeScannerOptions.Builder()
|
|
51
|
+
when {
|
|
52
|
+
effective["code-128"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_CODE_128)
|
|
53
|
+
effective["code-39"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_CODE_39)
|
|
54
|
+
effective["code-93"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_CODE_93)
|
|
55
|
+
effective["codabar"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_CODABAR)
|
|
56
|
+
effective["ean-13"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_EAN_13)
|
|
57
|
+
effective["ean-8"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_EAN_8)
|
|
58
|
+
effective["itf"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_ITF)
|
|
59
|
+
effective["upc-e"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_UPC_E)
|
|
60
|
+
effective["upc-a"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_UPC_A)
|
|
61
|
+
effective["qr"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_QR_CODE)
|
|
62
|
+
effective["pdf-417"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_PDF417)
|
|
63
|
+
effective["aztec"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_AZTEC)
|
|
64
|
+
effective["data-matrix"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_DATA_MATRIX)
|
|
65
|
+
effective["all"].toString().toBoolean() -> builder.setBarcodeFormats(FORMAT_ALL_FORMATS)
|
|
66
|
+
else -> builder.setBarcodeFormats(FORMAT_ALL_FORMATS)
|
|
67
|
+
}
|
|
68
|
+
return builder.build()
|
|
69
|
+
}
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
|
|
72
|
+
return try {
|
|
73
|
+
val options = mergedOptions(arguments)
|
|
74
|
+
val scanner = BarcodeScanning.getClient(buildScannerOptions(options))
|
|
52
75
|
val mediaImage: Image = frame.image
|
|
53
76
|
val image = InputImage.fromMediaImage(mediaImage, frame.imageProxy.imageInfo.rotationDegrees)
|
|
54
77
|
val task: Task<List<Barcode>> = scanner.process(image)
|
|
55
78
|
val barcodes: List<Barcode> = Tasks.await(task)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
|
|
80
|
+
val detections = ArrayList<Map<String, Any?>>()
|
|
81
|
+
val sharedArray =
|
|
82
|
+
if (barcodes.isNotEmpty()) SharedArray(proxy, barcodes.size * BOX_STRIDE * java.lang.Float.BYTES)
|
|
83
|
+
else null
|
|
84
|
+
val floatBuffer = sharedArray?.byteBuffer
|
|
85
|
+
?.order(ByteOrder.nativeOrder())
|
|
86
|
+
?.asFloatBuffer()
|
|
87
|
+
|
|
88
|
+
barcodes.forEachIndexed { index, barcode ->
|
|
89
|
+
val detection = HashMap<String, Any?>()
|
|
90
|
+
detection["rawValue"] = barcode.rawValue
|
|
91
|
+
detection["displayValue"] = barcode.displayValue
|
|
92
|
+
detection["format"] = barcode.format
|
|
93
|
+
detection["boxIndex"] = index
|
|
94
|
+
floatBuffer?.let { buffer ->
|
|
95
|
+
val bounds = barcode.boundingBox
|
|
96
|
+
val floatIndex = index * BOX_STRIDE
|
|
97
|
+
if (bounds != null) {
|
|
98
|
+
buffer.put(floatIndex, bounds.top.toFloat())
|
|
99
|
+
buffer.put(floatIndex + 1, bounds.bottom.toFloat())
|
|
100
|
+
buffer.put(floatIndex + 2, bounds.left.toFloat())
|
|
101
|
+
buffer.put(floatIndex + 3, bounds.right.toFloat())
|
|
102
|
+
buffer.put(floatIndex + 4, bounds.width().toFloat())
|
|
103
|
+
buffer.put(floatIndex + 5, bounds.height().toFloat())
|
|
104
|
+
} else {
|
|
105
|
+
for (offset in 0 until BOX_STRIDE) {
|
|
106
|
+
buffer.put(floatIndex + offset, -1f)
|
|
107
|
+
}
|
|
83
108
|
}
|
|
84
109
|
}
|
|
85
|
-
|
|
110
|
+
detections.add(detection)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
val response = HashMap<String, Any?>()
|
|
114
|
+
response["detections"] = detections
|
|
115
|
+
if (sharedArray != null) {
|
|
116
|
+
response["boxes"] = sharedArray
|
|
86
117
|
}
|
|
87
|
-
|
|
118
|
+
response
|
|
88
119
|
} catch (e: Exception) {
|
|
89
|
-
|
|
120
|
+
throw Exception("Error processing barcode scanner: $e")
|
|
90
121
|
}
|
|
91
122
|
}
|
|
92
|
-
|
|
93
123
|
}
|
package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerPackage.kt
CHANGED
|
@@ -10,10 +10,10 @@ import com.visioncameratextrecognition.VisionCameraTextRecognitionModule
|
|
|
10
10
|
class VisionCameraBarcodeScannerPackage : ReactPackage {
|
|
11
11
|
companion object {
|
|
12
12
|
init {
|
|
13
|
-
FrameProcessorPluginRegistry.addFrameProcessorPlugin("
|
|
13
|
+
FrameProcessorPluginRegistry.addFrameProcessorPlugin("vinScannerBarcode") {proxy,options ->
|
|
14
14
|
VisionCameraBarcodeScannerModule(proxy,options)
|
|
15
15
|
}
|
|
16
|
-
FrameProcessorPluginRegistry.addFrameProcessorPlugin("
|
|
16
|
+
FrameProcessorPluginRegistry.addFrameProcessorPlugin("vinScannerText") { proxy, options ->
|
|
17
17
|
VisionCameraTextRecognitionModule(proxy, options)
|
|
18
18
|
}
|
|
19
19
|
}
|
package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package com.visioncameratextrecognition
|
|
2
2
|
|
|
3
3
|
import android.media.Image
|
|
4
|
-
import com.facebook.react.bridge.WritableNativeArray
|
|
5
|
-
import com.facebook.react.bridge.WritableNativeMap
|
|
6
4
|
import com.google.android.gms.tasks.Task
|
|
7
5
|
import com.google.android.gms.tasks.Tasks
|
|
8
6
|
import com.google.mlkit.vision.common.InputImage
|
|
@@ -14,114 +12,149 @@ import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
|
|
|
14
12
|
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
|
|
15
13
|
import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions
|
|
16
14
|
import com.google.mlkit.vision.text.latin.TextRecognizerOptions
|
|
17
|
-
import com.mrousavy.camera.
|
|
18
|
-
import com.mrousavy.camera.
|
|
19
|
-
import com.mrousavy.camera.
|
|
20
|
-
import com.mrousavy.camera.
|
|
15
|
+
import com.mrousavy.camera.frameprocessors.Frame
|
|
16
|
+
import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
|
|
17
|
+
import com.mrousavy.camera.frameprocessors.SharedArray
|
|
18
|
+
import com.mrousavy.camera.frameprocessors.VisionCameraProxy
|
|
19
|
+
import java.nio.ByteOrder
|
|
20
|
+
import java.util.HashMap
|
|
21
21
|
import java.util.ArrayList
|
|
22
22
|
|
|
23
|
+
private const val TEXT_BOX_STRIDE = 12
|
|
23
24
|
|
|
24
|
-
class VisionCameraTextRecognitionModule(
|
|
25
|
-
private
|
|
26
|
-
|
|
25
|
+
class VisionCameraTextRecognitionModule(
|
|
26
|
+
private val proxy : VisionCameraProxy,
|
|
27
|
+
options: Map<String, Any>?
|
|
28
|
+
): FrameProcessorPlugin() {
|
|
29
|
+
private var language = options?.get("language").toString().ifEmpty { "latin" }
|
|
30
|
+
private val recognizers = mutableMapOf<String, TextRecognizer>()
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
private fun recognizerFor(languageCode: String): TextRecognizer {
|
|
33
|
+
return recognizers.getOrPut(languageCode) {
|
|
34
|
+
when (languageCode) {
|
|
35
|
+
"latin" -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
|
|
36
|
+
"chinese" -> TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
|
|
37
|
+
"devanagari" -> TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())
|
|
38
|
+
"japanese" -> TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())
|
|
39
|
+
"korean" -> TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
|
|
40
|
+
else -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
|
-
|
|
44
|
+
|
|
45
|
+
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
|
|
39
46
|
try {
|
|
40
47
|
val mediaImage: Image = frame.image
|
|
41
|
-
val
|
|
42
|
-
val
|
|
43
|
-
val
|
|
48
|
+
val rotationDegrees = frame.imageProxy.imageInfo.rotationDegrees
|
|
49
|
+
val requestedLanguage = arguments?.get("language")?.toString()?.ifEmpty { null }
|
|
50
|
+
val effectiveLanguage = requestedLanguage ?: language
|
|
51
|
+
val recognizer = recognizerFor(effectiveLanguage)
|
|
52
|
+
val image = InputImage.fromMediaImage(mediaImage, rotationDegrees)
|
|
44
53
|
val task: Task<Text> = recognizer.process(image)
|
|
45
54
|
val result: Text? = Tasks.await(task)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
val resultText = result?.text
|
|
56
|
+
val detections = ArrayList<Map<String, Any?>>()
|
|
57
|
+
val boxValues = ArrayList<FloatArray>()
|
|
58
|
+
|
|
59
|
+
result?.textBlocks?.forEach { block ->
|
|
60
|
+
val blockBounds = block.boundingBox
|
|
61
|
+
if (block.lines.isEmpty()) {
|
|
62
|
+
val detection = HashMap<String, Any?>()
|
|
63
|
+
detection["resultText"] = resultText
|
|
64
|
+
detection["blockText"] = block.text
|
|
65
|
+
detection["boxIndex"] = boxValues.size
|
|
66
|
+
detections.add(detection)
|
|
67
|
+
boxValues.add(
|
|
68
|
+
floatArrayOf(
|
|
69
|
+
blockBounds?.top?.toFloat() ?: -1f,
|
|
70
|
+
blockBounds?.bottom?.toFloat() ?: -1f,
|
|
71
|
+
blockBounds?.left?.toFloat() ?: -1f,
|
|
72
|
+
blockBounds?.right?.toFloat() ?: -1f,
|
|
73
|
+
-1f,
|
|
74
|
+
-1f,
|
|
75
|
+
-1f,
|
|
76
|
+
-1f,
|
|
77
|
+
-1f,
|
|
78
|
+
-1f,
|
|
79
|
+
-1f,
|
|
80
|
+
-1f,
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
block.lines.forEach { line ->
|
|
85
|
+
if (line.elements.isEmpty()) {
|
|
86
|
+
val detection = HashMap<String, Any?>()
|
|
87
|
+
detection["resultText"] = resultText
|
|
88
|
+
detection["blockText"] = block.text
|
|
89
|
+
detection["lineText"] = line.text
|
|
90
|
+
detection["boxIndex"] = boxValues.size
|
|
91
|
+
detections.add(detection)
|
|
92
|
+
boxValues.add(
|
|
93
|
+
floatArrayOf(
|
|
94
|
+
blockBounds?.top?.toFloat() ?: -1f,
|
|
95
|
+
blockBounds?.bottom?.toFloat() ?: -1f,
|
|
96
|
+
blockBounds?.left?.toFloat() ?: -1f,
|
|
97
|
+
blockBounds?.right?.toFloat() ?: -1f,
|
|
98
|
+
line.boundingBox?.top?.toFloat() ?: -1f,
|
|
99
|
+
line.boundingBox?.bottom?.toFloat() ?: -1f,
|
|
100
|
+
line.boundingBox?.left?.toFloat() ?: -1f,
|
|
101
|
+
line.boundingBox?.right?.toFloat() ?: -1f,
|
|
102
|
+
-1f,
|
|
103
|
+
-1f,
|
|
104
|
+
-1f,
|
|
105
|
+
-1f,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
68
108
|
}
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
line.elements.forEach { element ->
|
|
110
|
+
val detection = HashMap<String, Any?>()
|
|
111
|
+
detection["resultText"] = resultText
|
|
112
|
+
detection["blockText"] = block.text
|
|
113
|
+
detection["lineText"] = line.text
|
|
114
|
+
detection["elementText"] = element.text
|
|
115
|
+
detection["boxIndex"] = boxValues.size
|
|
116
|
+
detections.add(detection)
|
|
117
|
+
boxValues.add(
|
|
118
|
+
floatArrayOf(
|
|
119
|
+
blockBounds?.top?.toFloat() ?: -1f,
|
|
120
|
+
blockBounds?.bottom?.toFloat() ?: -1f,
|
|
121
|
+
blockBounds?.left?.toFloat() ?: -1f,
|
|
122
|
+
blockBounds?.right?.toFloat() ?: -1f,
|
|
123
|
+
line.boundingBox?.top?.toFloat() ?: -1f,
|
|
124
|
+
line.boundingBox?.bottom?.toFloat() ?: -1f,
|
|
125
|
+
line.boundingBox?.left?.toFloat() ?: -1f,
|
|
126
|
+
line.boundingBox?.right?.toFloat() ?: -1f,
|
|
127
|
+
element.boundingBox?.top?.toFloat() ?: -1f,
|
|
128
|
+
element.boundingBox?.bottom?.toFloat() ?: -1f,
|
|
129
|
+
element.boundingBox?.left?.toFloat() ?: -1f,
|
|
130
|
+
element.boundingBox?.right?.toFloat() ?: -1f,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
71
133
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
map.putString("lineText",lineText)
|
|
75
|
-
val lineCornerPoints = line.cornerPoints?.size
|
|
76
|
-
if (lineCornerPoints != null) {
|
|
77
|
-
map.putInt("size",lineCornerPoints)
|
|
78
|
-
}
|
|
79
|
-
val lineFrameBottom = line.boundingBox?.bottom
|
|
80
|
-
val lineFrameTop = line.boundingBox?.top
|
|
81
|
-
val lineFrameLeft = line.boundingBox?.left
|
|
82
|
-
val lineFrameRight = line.boundingBox?.right
|
|
83
|
-
if (lineFrameBottom != null) {
|
|
84
|
-
map.putInt("lineFrameBottom",lineFrameBottom)
|
|
85
|
-
}
|
|
86
|
-
if (lineFrameLeft != null) {
|
|
87
|
-
map.putInt("lineFrameLeft",lineFrameLeft)
|
|
88
|
-
}
|
|
89
|
-
if (lineFrameTop != null) {
|
|
90
|
-
map.putInt("lineFrameTop",lineFrameTop)
|
|
91
|
-
}
|
|
92
|
-
if (lineFrameRight != null) {
|
|
93
|
-
map.putInt("lineFrameRight",lineFrameRight)
|
|
94
|
-
}
|
|
95
|
-
for (element in line.elements) {
|
|
96
|
-
val elementText = element.text
|
|
97
|
-
map.putString("elementText",elementText)
|
|
98
|
-
|
|
99
|
-
val elementCornerPoints = line.cornerPoints?.size
|
|
100
|
-
if (elementCornerPoints != null) {
|
|
101
|
-
map.putInt("size",elementCornerPoints)
|
|
102
|
-
}
|
|
103
|
-
val elementFrameBottom = line.boundingBox?.bottom
|
|
104
|
-
val elementFrameTop = line.boundingBox?.top
|
|
105
|
-
val elementFrameLeft = line.boundingBox?.left
|
|
106
|
-
val elementFrameRight = line.boundingBox?.right
|
|
107
|
-
if (elementFrameBottom != null) {
|
|
108
|
-
map.putInt("elementFrameBottom",elementFrameBottom)
|
|
109
|
-
}
|
|
110
|
-
if (elementFrameLeft != null) {
|
|
111
|
-
map.putInt("elementFrameLeft",elementFrameLeft)
|
|
112
|
-
}
|
|
113
|
-
if (elementFrameTop != null) {
|
|
114
|
-
map.putInt("elementFrameTop",elementFrameTop)
|
|
115
|
-
}
|
|
116
|
-
if (elementFrameRight != null) {
|
|
117
|
-
map.putInt("elementFrameRight",elementFrameRight)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
array.pushMap(map)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
121
136
|
|
|
137
|
+
val sharedArray =
|
|
138
|
+
if (boxValues.isNotEmpty()) SharedArray(proxy, boxValues.size * TEXT_BOX_STRIDE * java.lang.Float.BYTES)
|
|
139
|
+
else null
|
|
140
|
+
val floatBuffer = sharedArray?.byteBuffer
|
|
141
|
+
?.order(ByteOrder.nativeOrder())
|
|
142
|
+
?.asFloatBuffer()
|
|
143
|
+
floatBuffer?.let { buffer ->
|
|
144
|
+
boxValues.forEachIndexed { index, values ->
|
|
145
|
+
val floatIndex = index * TEXT_BOX_STRIDE
|
|
146
|
+
for (offset in 0 until TEXT_BOX_STRIDE) {
|
|
147
|
+
buffer.put(floatIndex + offset, values[offset])
|
|
122
148
|
}
|
|
123
149
|
}
|
|
124
|
-
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
val response = HashMap<String, Any?>()
|
|
153
|
+
response["detections"] = detections
|
|
154
|
+
if (sharedArray != null) {
|
|
155
|
+
response["boxes"] = sharedArray
|
|
156
|
+
}
|
|
157
|
+
return response
|
|
125
158
|
} catch (e: Exception) {
|
|
126
159
|
throw Exception("Error processing text recognition: $e ")
|
|
127
160
|
}
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
#import <VisionCamera/FrameProcessorPlugin.h>
|
|
4
4
|
#import <VisionCamera/FrameProcessorPluginRegistry.h>
|
|
5
5
|
#import <VisionCamera/Frame.h>
|
|
6
|
+
#import <VisionCamera/SharedArray.h>
|
|
6
7
|
#import <React/RCTBridgeModule.h>
|
|
7
8
|
@import MLKitVision;
|
|
8
9
|
|
|
9
10
|
@interface VisionCameraBarcodeScannerPlugin : FrameProcessorPlugin
|
|
10
11
|
|
|
11
12
|
@property(nonatomic, strong) NSDictionary *configuration;
|
|
13
|
+
@property(nonatomic, strong) VisionCameraProxyHolder *proxyHolder;
|
|
12
14
|
|
|
13
15
|
@end
|
|
14
16
|
|
|
@@ -28,13 +30,18 @@
|
|
|
28
30
|
self = [super initWithProxy:proxy withOptions:options];
|
|
29
31
|
if (self) {
|
|
30
32
|
_configuration = options ?: @{};
|
|
33
|
+
_proxyHolder = proxy;
|
|
31
34
|
}
|
|
32
35
|
return self;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
- (id _Nullable)callback:(Frame* _Nonnull)frame
|
|
36
39
|
withArguments:(NSDictionary* _Nullable)arguments {
|
|
37
|
-
|
|
40
|
+
NSMutableDictionary *config = [self.configuration mutableCopy];
|
|
41
|
+
if (!config) {
|
|
42
|
+
config = [NSMutableDictionary dictionary];
|
|
43
|
+
}
|
|
44
|
+
[config addEntriesFromDictionary:arguments ?: @{}];
|
|
38
45
|
MLKBarcodeFormat formatMask = [self formatMaskFromConfig:config];
|
|
39
46
|
options = [[MLKBarcodeScannerOptions alloc] initWithFormats:formatMask];
|
|
40
47
|
|
|
@@ -42,9 +49,12 @@
|
|
|
42
49
|
|
|
43
50
|
CMSampleBufferRef buffer = frame.buffer;
|
|
44
51
|
UIImageOrientation orientation = frame.orientation;
|
|
52
|
+
// VisionCamera already normalizes orientation per https://react-native-vision-camera.com/docs/guides/orientation,
|
|
53
|
+
// so ML Kit just needs the frame’s orientation metadata instead of rotating pixels manually.
|
|
45
54
|
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:buffer];
|
|
46
55
|
image.orientation = orientation;
|
|
47
|
-
NSMutableArray *
|
|
56
|
+
NSMutableArray *detections = [NSMutableArray array];
|
|
57
|
+
__block NSDictionary *resultPayload = @{};
|
|
48
58
|
dispatch_group_t dispatchGroup = dispatch_group_create();
|
|
49
59
|
dispatch_group_enter(dispatchGroup);
|
|
50
60
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
@@ -52,54 +62,66 @@
|
|
|
52
62
|
completion:^(NSArray<MLKBarcode *> *_Nullable barcodes,
|
|
53
63
|
NSError *_Nullable error) {
|
|
54
64
|
if (error != nil) {
|
|
55
|
-
|
|
65
|
+
dispatch_group_leave(dispatchGroup);
|
|
56
66
|
return;
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
obj[@"title"] = barcode.URL.title;
|
|
84
|
-
break;
|
|
85
|
-
default:
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
[data addObject:obj];
|
|
68
|
+
SharedArray *sharedBoxes = nil;
|
|
69
|
+
float *boxData = nil;
|
|
70
|
+
const NSUInteger count = barcodes.count;
|
|
71
|
+
if (count > 0 && self.proxyHolder != nil) {
|
|
72
|
+
sharedBoxes = [[SharedArray alloc] initWithProxy:self.proxyHolder
|
|
73
|
+
allocateWithSize:count * sizeof(float) * 6];
|
|
74
|
+
boxData = (float *)sharedBoxes.data;
|
|
75
|
+
}
|
|
76
|
+
[barcodes enumerateObjectsUsingBlock:^(MLKBarcode * _Nonnull barcode, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
77
|
+
NSMutableDictionary *obj = [[NSMutableDictionary alloc] init];
|
|
78
|
+
obj[@"displayValue"] = barcode.displayValue ?: (id)kCFNull;
|
|
79
|
+
obj[@"rawValue"] = barcode.rawValue ?: (id)kCFNull;
|
|
80
|
+
obj[@"format"] = [self stringFromFormat:barcode.format];
|
|
81
|
+
obj[@"boxIndex"] = @(idx);
|
|
82
|
+
|
|
83
|
+
if (boxData != nil) {
|
|
84
|
+
CGRect frameRect = barcode.frame;
|
|
85
|
+
const NSUInteger baseIndex = idx * 6;
|
|
86
|
+
boxData[baseIndex] = CGRectGetMinY(frameRect);
|
|
87
|
+
boxData[baseIndex + 1] = CGRectGetMaxY(frameRect);
|
|
88
|
+
boxData[baseIndex + 2] = CGRectGetMinX(frameRect);
|
|
89
|
+
boxData[baseIndex + 3] = CGRectGetMaxX(frameRect);
|
|
90
|
+
boxData[baseIndex + 4] = CGRectGetWidth(frameRect);
|
|
91
|
+
boxData[baseIndex + 5] = CGRectGetHeight(frameRect);
|
|
92
|
+
}
|
|
89
93
|
|
|
94
|
+
MLKBarcodeValueType valueType = barcode.valueType;
|
|
95
|
+
switch (valueType) {
|
|
96
|
+
case MLKBarcodeValueTypeWiFi:
|
|
97
|
+
obj[@"ssid"] = barcode.wifi.ssid ?: (id)kCFNull;
|
|
98
|
+
obj[@"password"] = barcode.wifi.password ?: (id)kCFNull;
|
|
99
|
+
break;
|
|
100
|
+
case MLKBarcodeValueTypeURL:
|
|
101
|
+
obj[@"url"] = barcode.URL.url ?: (id)kCFNull;
|
|
102
|
+
obj[@"title"] = barcode.URL.title ?: (id)kCFNull;
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
break;
|
|
90
106
|
}
|
|
107
|
+
[detections addObject:obj];
|
|
108
|
+
}];
|
|
91
109
|
|
|
110
|
+
NSMutableDictionary *payload = [NSMutableDictionary dictionary];
|
|
111
|
+
payload[@"detections"] = detections;
|
|
112
|
+
if (sharedBoxes != nil) {
|
|
113
|
+
payload[@"boxes"] = sharedBoxes;
|
|
92
114
|
}
|
|
93
|
-
|
|
115
|
+
resultPayload = payload;
|
|
94
116
|
dispatch_group_leave(dispatchGroup);
|
|
95
117
|
}];
|
|
96
118
|
});
|
|
97
119
|
|
|
98
120
|
dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);
|
|
99
|
-
return
|
|
121
|
+
return resultPayload;
|
|
100
122
|
}
|
|
101
123
|
|
|
102
|
-
VISION_EXPORT_FRAME_PROCESSOR(VisionCameraBarcodeScannerPlugin,
|
|
124
|
+
VISION_EXPORT_FRAME_PROCESSOR(VisionCameraBarcodeScannerPlugin, vinScannerBarcode)
|
|
103
125
|
|
|
104
126
|
@end
|
|
105
127
|
|