@mleonard9/vin-scanner 0.2.5 → 0.2.7

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.
Files changed (35) hide show
  1. package/README.md +9 -0
  2. package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerModule.kt +96 -54
  3. package/android/src/main/java/com/visioncamerabarcodescanner/VisionCameraBarcodeScannerPackage.kt +2 -2
  4. package/android/src/main/java/com/visioncameratextrecognition/VisionCameraTextRecognitionModule.kt +139 -95
  5. package/ios/VisionCameraBarcodeScanner.m +87 -37
  6. package/ios/VisionCameraTextRecognition.m +130 -56
  7. package/lib/commonjs/scanBarcodes.js +76 -6
  8. package/lib/commonjs/scanBarcodes.js.map +1 -1
  9. package/lib/commonjs/scanText.js +78 -4
  10. package/lib/commonjs/scanText.js.map +1 -1
  11. package/lib/commonjs/useVinScanner.js +20 -3
  12. package/lib/commonjs/useVinScanner.js.map +1 -1
  13. package/lib/commonjs/vinUtils.js +7 -3
  14. package/lib/commonjs/vinUtils.js.map +1 -1
  15. package/lib/module/scanBarcodes.js +76 -6
  16. package/lib/module/scanBarcodes.js.map +1 -1
  17. package/lib/module/scanText.js +78 -4
  18. package/lib/module/scanText.js.map +1 -1
  19. package/lib/module/useVinScanner.js +20 -3
  20. package/lib/module/useVinScanner.js.map +1 -1
  21. package/lib/module/vinUtils.js +7 -3
  22. package/lib/module/vinUtils.js.map +1 -1
  23. package/lib/typescript/src/scanBarcodes.d.ts.map +1 -1
  24. package/lib/typescript/src/scanText.d.ts.map +1 -1
  25. package/lib/typescript/src/types.d.ts +21 -2
  26. package/lib/typescript/src/types.d.ts.map +1 -1
  27. package/lib/typescript/src/useVinScanner.d.ts.map +1 -1
  28. package/lib/typescript/src/vinUtils.d.ts +3 -1
  29. package/lib/typescript/src/vinUtils.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/scanBarcodes.ts +90 -8
  32. package/src/scanText.ts +93 -4
  33. package/src/types.ts +31 -2
  34. package/src/useVinScanner.ts +30 -3
  35. package/src/vinUtils.ts +11 -0
package/README.md CHANGED
@@ -102,8 +102,17 @@ 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` |
106
+ | `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` |
105
107
  | `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
108
 
109
+ ### Advanced frame-processor controls
110
+
111
+ - **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.
112
+ - **Orientation overrides:** If your UI is locked to portrait (e.g., iPad kiosks) but VisionCamera streams landscape buffers, set `detection.forceOrientation: 'portrait'`. The JS hook forwards that override to the native plugins so ML Kit always interprets frames with the requested rotation, eliminating the “upside-down unless I flip the paper” problem described in the [VisionCamera orientation guide](https://react-native-vision-camera.com/docs/guides/orientation).
113
+ - **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.
114
+ - **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.
115
+
107
116
  ### Hook-only usage
108
117
 
109
118
  If you prefer to configure `react-native-vision-camera` yourself, grab the frame processor from the hook:
@@ -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,113 @@ 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
- class VisionCameraBarcodeScannerModule(proxy : VisionCameraProxy, options: Map<String, Any>?): FrameProcessorPlugin() {
31
+ private const val BOX_STRIDE = 6
31
32
 
32
- override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
33
- try {
34
- val optionsBuilder = BarcodeScannerOptions.Builder()
35
- if (arguments?.get("code-128").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_128)
36
- else if (arguments?.get("code-39").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_39)
37
- else if (arguments?.get("code-93").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODE_93)
38
- else if (arguments?.get("codabar").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_CODABAR)
39
- else if (arguments?.get("ean-13").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_EAN_13)
40
- else if (arguments?.get("ean-8").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_EAN_8)
41
- else if (arguments?.get("itf").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_ITF)
42
- else if (arguments?.get("upc-e").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_UPC_E)
43
- else if (arguments?.get("upc-a").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_UPC_A)
44
- else if (arguments?.get("qr").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_QR_CODE)
45
- else if (arguments?.get("pdf-417").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_PDF417)
46
- else if (arguments?.get("aztec").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_AZTEC)
47
- else if (arguments?.get("data-matrix").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_DATA_MATRIX)
48
- else if (arguments?.get("all").toString().toBoolean()) optionsBuilder.setBarcodeFormats(FORMAT_ALL_FORMATS)
49
- else optionsBuilder.setBarcodeFormats(FORMAT_ALL_FORMATS)
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
+ }
50
48
 
51
- val scanner = BarcodeScanning.getClient(optionsBuilder.build())
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
+ }
70
+
71
+ private fun orientationToDegrees(orientation: String?): Int? {
72
+ return when (orientation) {
73
+ "portrait" -> 0
74
+ "portrait-upside-down" -> 180
75
+ "landscape-left" -> 90
76
+ "landscape-right" -> 270
77
+ else -> null
78
+ }
79
+ }
80
+
81
+ override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
82
+ return try {
83
+ val options = mergedOptions(arguments)
84
+ val scanner = BarcodeScanning.getClient(buildScannerOptions(options))
52
85
  val mediaImage: Image = frame.image
53
- val image = InputImage.fromMediaImage(mediaImage, frame.imageProxy.imageInfo.rotationDegrees)
86
+ val rotationOverride = orientationToDegrees(options["orientation"] as? String)
87
+ val rotationDegrees = rotationOverride ?: frame.imageProxy.imageInfo.rotationDegrees
88
+ val image = InputImage.fromMediaImage(mediaImage, rotationDegrees)
54
89
  val task: Task<List<Barcode>> = scanner.process(image)
55
90
  val barcodes: List<Barcode> = Tasks.await(task)
56
- val array = WritableNativeArray()
57
- for (barcode in barcodes) {
58
- val map = WritableNativeMap()
59
- val bounds = barcode.boundingBox
60
- if (bounds != null) {
61
- map.putInt("width",bounds.width())
62
- map.putInt("height",bounds.height())
63
- map.putInt("top",bounds.top)
64
- map.putInt("bottom",bounds.bottom)
65
- map.putInt("left",bounds.left)
66
- map.putInt("right",bounds.right)
67
- }
68
- val rawValue = barcode.rawValue
69
- map.putString("rawValue",rawValue)
70
- val valueType = barcode.valueType
71
- when (valueType) {
72
- Barcode.TYPE_WIFI -> {
73
- val ssid = barcode.wifi!!.ssid
74
- map.putString("ssid",ssid)
75
- val password = barcode.wifi!!.password
76
- map.putString("password",password)
77
- }
78
- Barcode.TYPE_URL -> {
79
- val title = barcode.url!!.title
80
- map.putString("title",title)
81
- val url = barcode.url!!.url
82
- map.putString("url",url)
91
+
92
+ val detections = ArrayList<Map<String, Any?>>()
93
+ val sharedArray =
94
+ if (barcodes.isNotEmpty()) SharedArray(proxy, barcodes.size * BOX_STRIDE * java.lang.Float.BYTES)
95
+ else null
96
+ val floatBuffer = sharedArray?.byteBuffer
97
+ ?.order(ByteOrder.nativeOrder())
98
+ ?.asFloatBuffer()
99
+
100
+ barcodes.forEachIndexed { index, barcode ->
101
+ val detection = HashMap<String, Any?>()
102
+ detection["rawValue"] = barcode.rawValue
103
+ detection["displayValue"] = barcode.displayValue
104
+ detection["format"] = barcode.format
105
+ detection["boxIndex"] = index
106
+ floatBuffer?.let { buffer ->
107
+ val bounds = barcode.boundingBox
108
+ val floatIndex = index * BOX_STRIDE
109
+ if (bounds != null) {
110
+ buffer.put(floatIndex, bounds.top.toFloat())
111
+ buffer.put(floatIndex + 1, bounds.bottom.toFloat())
112
+ buffer.put(floatIndex + 2, bounds.left.toFloat())
113
+ buffer.put(floatIndex + 3, bounds.right.toFloat())
114
+ buffer.put(floatIndex + 4, bounds.width().toFloat())
115
+ buffer.put(floatIndex + 5, bounds.height().toFloat())
116
+ } else {
117
+ for (offset in 0 until BOX_STRIDE) {
118
+ buffer.put(floatIndex + offset, -1f)
119
+ }
83
120
  }
84
121
  }
85
- array.pushMap(map)
122
+ detections.add(detection)
123
+ }
124
+
125
+ val response = HashMap<String, Any?>()
126
+ response["detections"] = detections
127
+ if (sharedArray != null) {
128
+ response["boxes"] = sharedArray
86
129
  }
87
- return array.toArrayList()
130
+ response
88
131
  } catch (e: Exception) {
89
- throw Exception("Error processing barcode scanner: $e ")
132
+ throw Exception("Error processing barcode scanner: $e")
90
133
  }
91
134
  }
92
-
93
135
  }
@@ -10,10 +10,10 @@ import com.visioncameratextrecognition.VisionCameraTextRecognitionModule
10
10
  class VisionCameraBarcodeScannerPackage : ReactPackage {
11
11
  companion object {
12
12
  init {
13
- FrameProcessorPluginRegistry.addFrameProcessorPlugin("scanBarcodes") {proxy,options ->
13
+ FrameProcessorPluginRegistry.addFrameProcessorPlugin("vinScannerBarcode") {proxy,options ->
14
14
  VisionCameraBarcodeScannerModule(proxy,options)
15
15
  }
16
- FrameProcessorPluginRegistry.addFrameProcessorPlugin("scanText") { proxy, options ->
16
+ FrameProcessorPluginRegistry.addFrameProcessorPlugin("vinScannerText") { proxy, options ->
17
17
  VisionCameraTextRecognitionModule(proxy, options)
18
18
  }
19
19
  }
@@ -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,160 @@ 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.frameprocessor.Frame
18
- import com.mrousavy.camera.frameprocessor.FrameProcessorPlugin
19
- import com.mrousavy.camera.frameprocessor.VisionCameraProxy
20
- import com.mrousavy.camera.types.Orientation
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(proxy : VisionCameraProxy, options: Map<String, Any>?): FrameProcessorPlugin() {
25
- private var recognizer: TextRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
26
- private var language = options?.get("language").toString()
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
- init {
29
- recognizer = when (language) {
30
- "latin" -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
31
- "chinese" -> TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
32
- "devanagari" -> TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())
33
- "japanese" -> TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())
34
- "korean" -> TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
35
- else -> TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
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
+ }
42
+ }
43
+ }
44
+
45
+ private fun orientationToDegrees(orientation: String?): Int? {
46
+ return when (orientation) {
47
+ "portrait" -> 0
48
+ "portrait-upside-down" -> 180
49
+ "landscape-left" -> 90
50
+ "landscape-right" -> 270
51
+ else -> null
36
52
  }
37
53
  }
38
- override fun callback(frame: Frame, arguments: Map<String, Any>?): ArrayList<Any> {
54
+
55
+ override fun callback(frame: Frame, arguments: Map<String, Any>?): Any {
39
56
  try {
40
57
  val mediaImage: Image = frame.image
41
- val orientation : Orientation = frame.orientation
42
- val array = WritableNativeArray()
43
- val image = InputImage.fromMediaImage(mediaImage, orientation.toDegrees())
58
+ val rotationOverride = orientationToDegrees(arguments?.get("orientation") as? String)
59
+ val rotationDegrees = rotationOverride ?: frame.imageProxy.imageInfo.rotationDegrees
60
+ val requestedLanguage = arguments?.get("language")?.toString()?.ifEmpty { null }
61
+ val effectiveLanguage = requestedLanguage ?: language
62
+ val recognizer = recognizerFor(effectiveLanguage)
63
+ val image = InputImage.fromMediaImage(mediaImage, rotationDegrees)
44
64
  val task: Task<Text> = recognizer.process(image)
45
65
  val result: Text? = Tasks.await(task)
46
- val resultText = result?.text
47
- for (block in result?.textBlocks!!) {
48
- val map = WritableNativeMap()
49
- map.putString("resultText",resultText)
50
- val blockText = block.text
51
- map.putString("blockText",blockText)
52
- val blockCornerPoints = block.cornerPoints?.size
53
- if (blockCornerPoints != null) {
54
- map.putInt("size",blockCornerPoints)
55
- }
56
- val blockFrameBottom = block.boundingBox?.bottom
57
- val blockFrameTop = block.boundingBox?.top
58
- val blockFrameLeft = block.boundingBox?.left
59
- val blockFrameRight = block.boundingBox?.right
60
- if (blockFrameBottom != null) {
61
- map.putInt("blockFrameBottom",blockFrameBottom)
62
- }
63
- if (blockFrameLeft != null) {
64
- map.putInt("blockFrameLeft",blockFrameLeft)
65
- }
66
- if (blockFrameTop != null) {
67
- map.putInt("blockFrameTop",blockFrameTop)
66
+ val resultText = result?.text
67
+ val detections = ArrayList<Map<String, Any?>>()
68
+ val boxValues = ArrayList<FloatArray>()
69
+
70
+ result?.textBlocks?.forEach { block ->
71
+ val blockBounds = block.boundingBox
72
+ if (block.lines.isEmpty()) {
73
+ val detection = HashMap<String, Any?>()
74
+ detection["resultText"] = resultText
75
+ detection["blockText"] = block.text
76
+ detection["boxIndex"] = boxValues.size
77
+ detections.add(detection)
78
+ boxValues.add(
79
+ floatArrayOf(
80
+ blockBounds?.top?.toFloat() ?: -1f,
81
+ blockBounds?.bottom?.toFloat() ?: -1f,
82
+ blockBounds?.left?.toFloat() ?: -1f,
83
+ blockBounds?.right?.toFloat() ?: -1f,
84
+ -1f,
85
+ -1f,
86
+ -1f,
87
+ -1f,
88
+ -1f,
89
+ -1f,
90
+ -1f,
91
+ -1f,
92
+ )
93
+ )
94
+ }
95
+ block.lines.forEach { line ->
96
+ if (line.elements.isEmpty()) {
97
+ val detection = HashMap<String, Any?>()
98
+ detection["resultText"] = resultText
99
+ detection["blockText"] = block.text
100
+ detection["lineText"] = line.text
101
+ detection["boxIndex"] = boxValues.size
102
+ detections.add(detection)
103
+ boxValues.add(
104
+ floatArrayOf(
105
+ blockBounds?.top?.toFloat() ?: -1f,
106
+ blockBounds?.bottom?.toFloat() ?: -1f,
107
+ blockBounds?.left?.toFloat() ?: -1f,
108
+ blockBounds?.right?.toFloat() ?: -1f,
109
+ line.boundingBox?.top?.toFloat() ?: -1f,
110
+ line.boundingBox?.bottom?.toFloat() ?: -1f,
111
+ line.boundingBox?.left?.toFloat() ?: -1f,
112
+ line.boundingBox?.right?.toFloat() ?: -1f,
113
+ -1f,
114
+ -1f,
115
+ -1f,
116
+ -1f,
117
+ )
118
+ )
68
119
  }
69
- if (blockFrameRight != null) {
70
- map.putInt("blockFrameRight",blockFrameRight)
120
+ line.elements.forEach { element ->
121
+ val detection = HashMap<String, Any?>()
122
+ detection["resultText"] = resultText
123
+ detection["blockText"] = block.text
124
+ detection["lineText"] = line.text
125
+ detection["elementText"] = element.text
126
+ detection["boxIndex"] = boxValues.size
127
+ detections.add(detection)
128
+ boxValues.add(
129
+ floatArrayOf(
130
+ blockBounds?.top?.toFloat() ?: -1f,
131
+ blockBounds?.bottom?.toFloat() ?: -1f,
132
+ blockBounds?.left?.toFloat() ?: -1f,
133
+ blockBounds?.right?.toFloat() ?: -1f,
134
+ line.boundingBox?.top?.toFloat() ?: -1f,
135
+ line.boundingBox?.bottom?.toFloat() ?: -1f,
136
+ line.boundingBox?.left?.toFloat() ?: -1f,
137
+ line.boundingBox?.right?.toFloat() ?: -1f,
138
+ element.boundingBox?.top?.toFloat() ?: -1f,
139
+ element.boundingBox?.bottom?.toFloat() ?: -1f,
140
+ element.boundingBox?.left?.toFloat() ?: -1f,
141
+ element.boundingBox?.right?.toFloat() ?: -1f,
142
+ )
143
+ )
71
144
  }
72
- for (line in block.lines) {
73
- val lineText = line.text
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)
145
+ }
146
+ }
121
147
 
148
+ val sharedArray =
149
+ if (boxValues.isNotEmpty()) SharedArray(proxy, boxValues.size * TEXT_BOX_STRIDE * java.lang.Float.BYTES)
150
+ else null
151
+ val floatBuffer = sharedArray?.byteBuffer
152
+ ?.order(ByteOrder.nativeOrder())
153
+ ?.asFloatBuffer()
154
+ floatBuffer?.let { buffer ->
155
+ boxValues.forEachIndexed { index, values ->
156
+ val floatIndex = index * TEXT_BOX_STRIDE
157
+ for (offset in 0 until TEXT_BOX_STRIDE) {
158
+ buffer.put(floatIndex + offset, values[offset])
122
159
  }
123
160
  }
124
- return array.toArrayList()
161
+ }
162
+
163
+ val response = HashMap<String, Any?>()
164
+ response["detections"] = detections
165
+ if (sharedArray != null) {
166
+ response["boxes"] = sharedArray
167
+ }
168
+ return response
125
169
  } catch (e: Exception) {
126
170
  throw Exception("Error processing text recognition: $e ")
127
171
  }