@trustchex/react-native-sdk 1.355.1 → 1.357.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +2 -9
  2. package/TrustchexSDK.podspec +5 -4
  3. package/android/build.gradle +6 -4
  4. package/android/src/main/AndroidManifest.xml +1 -1
  5. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +45 -25
  6. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraManager.kt +168 -0
  7. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +871 -0
  8. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +245 -0
  9. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidationModule.kt +785 -0
  10. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidator.kt +419 -0
  11. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +818 -0
  12. package/ios/Camera/TrustchexCameraManager.m +37 -0
  13. package/ios/Camera/TrustchexCameraManager.swift +125 -0
  14. package/ios/Camera/TrustchexCameraView.swift +1176 -0
  15. package/ios/MLKit/MLKitModule.m +23 -0
  16. package/ios/MLKit/MLKitModule.swift +250 -0
  17. package/ios/MRZValidation.m +39 -0
  18. package/ios/MRZValidation.swift +802 -0
  19. package/ios/MRZValidator.swift +466 -0
  20. package/ios/OpenCV/OpenCVModule.h +4 -0
  21. package/ios/OpenCV/OpenCVModule.mm +810 -0
  22. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +2 -3
  23. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +1 -2
  24. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +418 -193
  25. package/lib/module/Screens/Static/OTPVerificationScreen.js +11 -11
  26. package/lib/module/Screens/Static/QrCodeScanningScreen.js +5 -1
  27. package/lib/module/Screens/Static/ResultScreen.js +25 -2
  28. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +25 -7
  29. package/lib/module/Shared/Components/DebugNavigationPanel.js +234 -24
  30. package/lib/module/Shared/Components/EIDScanner.js +99 -9
  31. package/lib/module/Shared/Components/FaceCamera.js +170 -179
  32. package/lib/module/Shared/Components/IdentityDocumentCamera.js +2151 -771
  33. package/lib/module/Shared/Components/QrCodeScannerCamera.js +109 -107
  34. package/lib/module/Shared/Components/TrustchexCamera.js +122 -0
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +91 -0
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +2 -124
  37. package/lib/module/Shared/EIDReader/tlv/tlvInputStream.js +4 -4
  38. package/lib/module/Shared/EIDReader/tlv/tlvOutputState.js +4 -4
  39. package/lib/module/Shared/EIDReader/tlv/tlvOutputStream.js +4 -4
  40. package/lib/module/Shared/Libs/analytics.utils.js +2 -2
  41. package/lib/module/Shared/Libs/debug.utils.js +132 -0
  42. package/lib/module/Shared/Libs/deeplink.utils.js +6 -5
  43. package/lib/module/Shared/Libs/demo.utils.js +13 -3
  44. package/lib/module/Shared/Libs/mrz.utils.js +1 -175
  45. package/lib/module/Shared/Libs/native-device-info.utils.js +12 -6
  46. package/lib/module/Shared/Libs/tts.utils.js +40 -6
  47. package/lib/module/Shared/Services/AnalyticsService.js +9 -8
  48. package/lib/module/Shared/Types/mrzFields.js +1 -0
  49. package/lib/module/Translation/Resources/en.js +87 -88
  50. package/lib/module/Translation/Resources/tr.js +84 -85
  51. package/lib/module/Trustchex.js +9 -2
  52. package/lib/module/index.js +1 -0
  53. package/lib/module/version.js +1 -1
  54. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  58. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  59. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts +2 -2
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts +18 -4
  65. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -4
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts +2 -1
  69. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +124 -0
  71. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -0
  72. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.helpers.d.ts +11 -0
  73. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.helpers.d.ts.map +1 -0
  74. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts +2 -39
  75. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  76. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/Libs/debug.utils.d.ts +42 -0
  78. package/lib/typescript/src/Shared/Libs/debug.utils.d.ts.map +1 -0
  79. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +0 -4
  83. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  84. package/lib/typescript/src/Shared/Libs/native-device-info.utils.d.ts.map +1 -1
  85. package/lib/typescript/src/Shared/Libs/tts.utils.d.ts +4 -3
  86. package/lib/typescript/src/Shared/Libs/tts.utils.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts.map +1 -1
  88. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +2 -2
  89. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  90. package/lib/typescript/src/Shared/Types/mrzFields.d.ts +11 -0
  91. package/lib/typescript/src/Shared/Types/mrzFields.d.ts.map +1 -0
  92. package/lib/typescript/src/Translation/Resources/en.d.ts +4 -5
  93. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  94. package/lib/typescript/src/Translation/Resources/tr.d.ts +4 -5
  95. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  96. package/lib/typescript/src/Trustchex.d.ts +2 -0
  97. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  98. package/lib/typescript/src/index.d.ts +1 -0
  99. package/lib/typescript/src/index.d.ts.map +1 -1
  100. package/lib/typescript/src/version.d.ts +1 -1
  101. package/package.json +4 -35
  102. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -1
  103. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +7 -5
  104. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +2 -3
  105. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +498 -216
  106. package/src/Screens/Static/OTPVerificationScreen.tsx +37 -31
  107. package/src/Screens/Static/QrCodeScanningScreen.tsx +8 -1
  108. package/src/Screens/Static/ResultScreen.tsx +136 -88
  109. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +46 -13
  110. package/src/Shared/Components/DebugNavigationPanel.tsx +290 -34
  111. package/src/Shared/Components/EIDScanner.tsx +94 -16
  112. package/src/Shared/Components/FaceCamera.tsx +236 -203
  113. package/src/Shared/Components/IdentityDocumentCamera.tsx +3073 -1030
  114. package/src/Shared/Components/QrCodeScannerCamera.tsx +133 -127
  115. package/src/Shared/Components/TrustchexCamera.tsx +289 -0
  116. package/src/Shared/Config/camera-enhancement.config.ts +2 -2
  117. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +96 -0
  118. package/src/Shared/EIDReader/tlv/tlv.utils.ts +2 -125
  119. package/src/Shared/EIDReader/tlv/tlvInputStream.ts +4 -4
  120. package/src/Shared/EIDReader/tlv/tlvOutputState.ts +4 -4
  121. package/src/Shared/EIDReader/tlv/tlvOutputStream.ts +4 -4
  122. package/src/Shared/Libs/analytics.utils.ts +48 -20
  123. package/src/Shared/Libs/debug.utils.ts +149 -0
  124. package/src/Shared/Libs/deeplink.utils.ts +7 -5
  125. package/src/Shared/Libs/demo.utils.ts +4 -0
  126. package/src/Shared/Libs/http-client.ts +12 -8
  127. package/src/Shared/Libs/mrz.utils.ts +1 -163
  128. package/src/Shared/Libs/native-device-info.utils.ts +12 -6
  129. package/src/Shared/Libs/tts.utils.ts +48 -6
  130. package/src/Shared/Services/AnalyticsService.ts +69 -24
  131. package/src/Shared/Types/identificationInfo.ts +2 -2
  132. package/src/Shared/Types/mrzFields.ts +29 -0
  133. package/src/Translation/Resources/en.ts +90 -100
  134. package/src/Translation/Resources/tr.ts +89 -97
  135. package/src/Translation/index.ts +1 -1
  136. package/src/Trustchex.tsx +21 -4
  137. package/src/index.tsx +14 -0
  138. package/src/version.ts +1 -1
  139. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/barcodescanner/BarcodeScannerFrameProcessorPlugin.kt +0 -301
  140. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/BitmapUtils.kt +0 -205
  141. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/CropperPlugin.kt +0 -72
  142. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/cropper/FrameMetadata.kt +0 -4
  143. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/facedetector/FaceDetectorFrameProcessorPlugin.kt +0 -303
  144. package/android/src/main/java/com/trustchex/reactnativesdk/visioncameraplugins/textrecognition/TextRecognitionFrameProcessorPlugin.kt +0 -115
  145. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin-Bridging-Header.h +0 -9
  146. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin.mm +0 -22
  147. package/ios/VisionCameraPlugins/BarcodeScanner/BarcodeScannerFrameProcessorPlugin.swift +0 -188
  148. package/ios/VisionCameraPlugins/Cropper/Cropper-Bridging-Header.h +0 -13
  149. package/ios/VisionCameraPlugins/Cropper/Cropper.h +0 -20
  150. package/ios/VisionCameraPlugins/Cropper/Cropper.mm +0 -22
  151. package/ios/VisionCameraPlugins/Cropper/Cropper.swift +0 -145
  152. package/ios/VisionCameraPlugins/Cropper/CropperUtils.swift +0 -49
  153. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin-Bridging-Header.h +0 -4
  154. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin.mm +0 -22
  155. package/ios/VisionCameraPlugins/FaceDetector/FaceDetectorFrameProcessorPlugin.swift +0 -320
  156. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin-Bridging-Header.h +0 -4
  157. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin.mm +0 -27
  158. package/ios/VisionCameraPlugins/TextRecognition/TextRecognitionFrameProcessorPlugin.swift +0 -144
  159. package/lib/module/Shared/Libs/camera.utils.js +0 -308
  160. package/lib/module/Shared/Libs/frame-enhancement.utils.js +0 -133
  161. package/lib/module/Shared/Libs/opencv.utils.js +0 -21
  162. package/lib/module/Shared/Libs/worklet.utils.js +0 -68
  163. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.js +0 -46
  164. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.js +0 -35
  165. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/index.js +0 -19
  166. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.js +0 -26
  167. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/types.js +0 -3
  168. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.js +0 -197
  169. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.js +0 -101
  170. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.js +0 -60
  171. package/lib/module/Shared/VisionCameraPlugins/Cropper/index.js +0 -47
  172. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/Camera.js +0 -42
  173. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/detectFaces.js +0 -35
  174. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/index.js +0 -4
  175. package/lib/module/Shared/VisionCameraPlugins/FaceDetector/types.js +0 -3
  176. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/Camera.js +0 -56
  177. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.js +0 -20
  178. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.js +0 -9
  179. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/index.js +0 -6
  180. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/scanText.js +0 -20
  181. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/translateText.js +0 -19
  182. package/lib/module/Shared/VisionCameraPlugins/TextRecognition/types.js +0 -3
  183. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +0 -87
  184. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +0 -1
  185. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +0 -25
  186. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +0 -1
  187. package/lib/typescript/src/Shared/Libs/opencv.utils.d.ts +0 -3
  188. package/lib/typescript/src/Shared/Libs/opencv.utils.d.ts.map +0 -1
  189. package/lib/typescript/src/Shared/Libs/worklet.utils.d.ts +0 -9
  190. package/lib/typescript/src/Shared/Libs/worklet.utils.d.ts.map +0 -1
  191. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.d.ts +0 -13
  192. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.d.ts.map +0 -1
  193. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.d.ts +0 -6
  194. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.d.ts.map +0 -1
  195. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/index.d.ts +0 -12
  196. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/index.d.ts.map +0 -1
  197. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.d.ts +0 -3
  198. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.d.ts.map +0 -1
  199. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/types.d.ts +0 -52
  200. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/types.d.ts.map +0 -1
  201. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.d.ts +0 -62
  202. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.d.ts.map +0 -1
  203. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.d.ts +0 -34
  204. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.d.ts.map +0 -1
  205. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.d.ts +0 -32
  206. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.d.ts.map +0 -1
  207. package/lib/typescript/src/Shared/VisionCameraPlugins/Cropper/index.d.ts +0 -23
  208. package/lib/typescript/src/Shared/VisionCameraPlugins/Cropper/index.d.ts.map +0 -1
  209. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/Camera.d.ts +0 -9
  210. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/Camera.d.ts.map +0 -1
  211. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.d.ts +0 -3
  212. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.d.ts.map +0 -1
  213. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/index.d.ts +0 -3
  214. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/index.d.ts.map +0 -1
  215. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/types.d.ts +0 -79
  216. package/lib/typescript/src/Shared/VisionCameraPlugins/FaceDetector/types.d.ts.map +0 -1
  217. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/Camera.d.ts +0 -6
  218. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/Camera.d.ts.map +0 -1
  219. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.d.ts +0 -3
  220. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.d.ts.map +0 -1
  221. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.d.ts +0 -3
  222. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.d.ts.map +0 -1
  223. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/index.d.ts +0 -5
  224. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/index.d.ts.map +0 -1
  225. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/scanText.d.ts +0 -3
  226. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/scanText.d.ts.map +0 -1
  227. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/translateText.d.ts +0 -3
  228. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/translateText.d.ts.map +0 -1
  229. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/types.d.ts +0 -67
  230. package/lib/typescript/src/Shared/VisionCameraPlugins/TextRecognition/types.d.ts.map +0 -1
  231. package/src/Shared/Libs/camera.utils.ts +0 -345
  232. package/src/Shared/Libs/frame-enhancement.utils.ts +0 -217
  233. package/src/Shared/Libs/opencv.utils.ts +0 -40
  234. package/src/Shared/Libs/worklet.utils.ts +0 -72
  235. package/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useBarcodeScanner.ts +0 -79
  236. package/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.ts +0 -46
  237. package/src/Shared/VisionCameraPlugins/BarcodeScanner/index.ts +0 -60
  238. package/src/Shared/VisionCameraPlugins/BarcodeScanner/scanCodes.ts +0 -32
  239. package/src/Shared/VisionCameraPlugins/BarcodeScanner/types.ts +0 -82
  240. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/convert.ts +0 -195
  241. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/geometry.ts +0 -135
  242. package/src/Shared/VisionCameraPlugins/BarcodeScanner/utils/highlights.ts +0 -84
  243. package/src/Shared/VisionCameraPlugins/Cropper/index.ts +0 -78
  244. package/src/Shared/VisionCameraPlugins/FaceDetector/Camera.tsx +0 -63
  245. package/src/Shared/VisionCameraPlugins/FaceDetector/detectFaces.ts +0 -44
  246. package/src/Shared/VisionCameraPlugins/FaceDetector/index.ts +0 -3
  247. package/src/Shared/VisionCameraPlugins/FaceDetector/types.ts +0 -99
  248. package/src/Shared/VisionCameraPlugins/TextRecognition/Camera.tsx +0 -76
  249. package/src/Shared/VisionCameraPlugins/TextRecognition/PhotoRecognizer.ts +0 -18
  250. package/src/Shared/VisionCameraPlugins/TextRecognition/RemoveLanguageModel.ts +0 -7
  251. package/src/Shared/VisionCameraPlugins/TextRecognition/index.ts +0 -7
  252. package/src/Shared/VisionCameraPlugins/TextRecognition/scanText.ts +0 -27
  253. package/src/Shared/VisionCameraPlugins/TextRecognition/translateText.ts +0 -21
  254. package/src/Shared/VisionCameraPlugins/TextRecognition/types.ts +0 -141
@@ -0,0 +1,818 @@
1
+ package com.trustchex.reactnativesdk.opencv
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.BitmapFactory
5
+ import android.util.Base64
6
+ import com.facebook.react.bridge.*
7
+ import org.opencv.android.OpenCVLoader
8
+ import org.opencv.android.Utils
9
+ import org.opencv.core.*
10
+ import org.opencv.imgproc.Imgproc
11
+ import java.io.ByteArrayOutputStream
12
+
13
+ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
14
+
15
+ override fun getName() = "OpenCVModule"
16
+
17
+ companion object {
18
+ private var opencvInitialized = false
19
+ private const val HOLOGRAM_NON_ZERO_THRESHOLD = 600
20
+ }
21
+
22
+ init {
23
+ if (!opencvInitialized) {
24
+ opencvInitialized = OpenCVLoader.initLocal()
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Detect hologram from a sequence of face images captured while torch is toggling.
30
+ * Mirrors the JS detectHologram logic:
31
+ * 1. Compute absdiff between consecutive frames
32
+ * 2. Convert to HSV, inRange filter for holographic colors
33
+ * 3. Blend significant diffs
34
+ * 4. Adaptive threshold on combined mask
35
+ * 5. Return hologram overlay + mask if non-zero count exceeds threshold
36
+ *
37
+ * @param base64Images Array of base64 JPEG face images
38
+ * @param threshold Non-zero pixel count threshold (default 3500)
39
+ * @param promise Returns { hologramImage, hologramMask } or null
40
+ */
41
+ @ReactMethod
42
+ fun detectHologram(base64Images: ReadableArray, threshold: Int, promise: Promise) {
43
+ // Run on background thread to avoid blocking the main/JS thread (matching iOS)
44
+ Thread {
45
+ try {
46
+ if (!opencvInitialized) {
47
+ promise.reject("OPENCV_ERROR", "OpenCV not initialized")
48
+ return@Thread
49
+ }
50
+
51
+ val imageCount = base64Images.size()
52
+ if (imageCount < 2) {
53
+ promise.resolve(null)
54
+ return@Thread
55
+ }
56
+
57
+ // Decode all images to Mat
58
+ val mats = mutableListOf<Mat>()
59
+ for (i in 0 until imageCount) {
60
+ val b64 = base64Images.getString(i) ?: continue
61
+ val mat = base64ToMat(b64) ?: continue
62
+ mats.add(mat)
63
+ }
64
+
65
+ if (mats.size < 2) {
66
+ mats.forEach { it.release() }
67
+ promise.resolve(null)
68
+ return@Thread
69
+ }
70
+
71
+ // Multi-range HSV filtering for holographic colors
72
+ // Range 1: Cyan-green holographic reflections
73
+ val lowerBound1 = Scalar(35.0, 80.0, 80.0)
74
+ val upperBound1 = Scalar(85.0, 255.0, 255.0)
75
+ // Range 2: Blue-violet holographic reflections
76
+ val lowerBound2 = Scalar(100.0, 80.0, 80.0)
77
+ val upperBound2 = Scalar(160.0, 255.0, 255.0)
78
+ val diffs = mutableListOf<Mat>()
79
+ val brightestImages = mutableListOf<Mat>()
80
+
81
+ for (i in 0 until mats.size - 1) {
82
+ val diff = Mat()
83
+ Core.absdiff(mats[i], mats[i + 1], diff)
84
+
85
+ val hsv = Mat()
86
+ Imgproc.cvtColor(diff, hsv, Imgproc.COLOR_RGB2HSV)
87
+
88
+ // Apply multi-range HSV filtering
89
+ val mask1 = Mat()
90
+ val mask2 = Mat()
91
+ val mask = Mat()
92
+ Core.inRange(hsv, lowerBound1, upperBound1, mask1)
93
+ Core.inRange(hsv, lowerBound2, upperBound2, mask2)
94
+ Core.bitwise_or(mask1, mask2, mask)
95
+ mask1.release()
96
+ mask2.release()
97
+
98
+ val maskNonZero = Core.countNonZero(mask)
99
+
100
+ if (maskNonZero > HOLOGRAM_NON_ZERO_THRESHOLD) {
101
+ diffs.add(mask)
102
+ brightestImages.add(mats[i].clone())
103
+ brightestImages.add(mats[i + 1].clone())
104
+
105
+ // Early termination: if first pair has very strong signal, skip rest
106
+ if (i == 0 && maskNonZero > HOLOGRAM_NON_ZERO_THRESHOLD * 4) {
107
+ diff.release()
108
+ hsv.release()
109
+ break
110
+ }
111
+ } else {
112
+ mask.release()
113
+ }
114
+
115
+ diff.release()
116
+ hsv.release()
117
+ }
118
+
119
+ mats.forEach { it.release() }
120
+ mats.clear()
121
+
122
+ if (diffs.isEmpty()) {
123
+ brightestImages.forEach { it.release() }
124
+ promise.resolve(null)
125
+ return@Thread
126
+ }
127
+
128
+ // Combine all difference masks
129
+ val hologramMask = diffs[0].clone()
130
+
131
+ for (i in 1 until diffs.size) {
132
+ Core.bitwise_or(hologramMask, diffs[i], hologramMask)
133
+ }
134
+
135
+ diffs.forEach { it.release() }
136
+
137
+ // Apply morphological operations to clean up the mask
138
+ val kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, org.opencv.core.Size(3.0, 3.0))
139
+ Imgproc.morphologyEx(hologramMask, hologramMask, Imgproc.MORPH_CLOSE, kernel)
140
+ Imgproc.morphologyEx(hologramMask, hologramMask, Imgproc.MORPH_OPEN, kernel)
141
+ kernel.release()
142
+
143
+ // Check if significant hologram detected
144
+ val count = Core.countNonZero(hologramMask)
145
+
146
+ if (count < threshold) {
147
+ hologramMask.release()
148
+ brightestImages.forEach { it.release() }
149
+ promise.resolve(null)
150
+ return@Thread
151
+ }
152
+
153
+ // Find the best image that shows the most hologram features
154
+ // Score each image based on how much hologram variation it shows in the mask regions
155
+ var bestImage = brightestImages[0]
156
+ var maxHologramScore = 0.0
157
+
158
+ for (img in brightestImages) {
159
+ // Calculate hologram visibility score by measuring color variation in mask regions
160
+ val maskedRegion = Mat()
161
+ img.copyTo(maskedRegion, hologramMask)
162
+
163
+ // Convert to HSV to measure saturation (hologram has high saturation)
164
+ val hsv = Mat()
165
+ Imgproc.cvtColor(maskedRegion, hsv, Imgproc.COLOR_RGB2HSV)
166
+
167
+ val channels = mutableListOf<Mat>()
168
+ Core.split(hsv, channels)
169
+
170
+ // Score based on average saturation in hologram regions
171
+ val saturationMean = Core.mean(channels[1], hologramMask)
172
+ val score = saturationMean.`val`[0]
173
+
174
+ channels.forEach { it.release() }
175
+ hsv.release()
176
+ maskedRegion.release()
177
+
178
+ if (score > maxHologramScore) {
179
+ maxHologramScore = score
180
+ bestImage = img
181
+ }
182
+ }
183
+
184
+ // Use the best image that shows the most prominent hologram - show full document
185
+ val hologram = Mat()
186
+ bestImage.copyTo(hologram)
187
+
188
+ brightestImages.forEach { it.release() }
189
+
190
+ // Convert results to base64
191
+ val hologramBase64 = matToBase64(hologram)
192
+ val maskBase64 = matToBase64(hologramMask)
193
+
194
+ hologram.release()
195
+ hologramMask.release()
196
+
197
+ if (hologramBase64 != null && maskBase64 != null) {
198
+ val resultMap = Arguments.createMap()
199
+ resultMap.putString("hologramImage", hologramBase64)
200
+ resultMap.putString("hologramMask", maskBase64)
201
+ promise.resolve(resultMap)
202
+ } else {
203
+ promise.resolve(null)
204
+ }
205
+ } catch (e: Exception) {
206
+ promise.reject("HOLOGRAM_ERROR", e.message)
207
+ }
208
+ }.start()
209
+ }
210
+
211
+ /**
212
+ * Compare two images for similarity using thresholded absdiff.
213
+ * Mirrors the JS areImagesSimilar logic:
214
+ * 1. Grayscale → GaussianBlur → Otsu threshold on both
215
+ * 2. absdiff between thresholded images
216
+ * 3. Count non-zero pixels
217
+ *
218
+ * @param base64Image1 First image (base64 JPEG)
219
+ * @param base64Image2 Second image (base64 JPEG)
220
+ * @param threshold Non-zero pixel count below which images are "similar" (default 15000)
221
+ * @param promise Returns boolean
222
+ */
223
+ @ReactMethod
224
+ fun areImagesSimilar(base64Image1: String, base64Image2: String, threshold: Int, promise: Promise) {
225
+ try {
226
+ if (!opencvInitialized) {
227
+ promise.resolve(false)
228
+ return
229
+ }
230
+
231
+ val mat1 = base64ToMat(base64Image1)
232
+ val mat2 = base64ToMat(base64Image2)
233
+
234
+ if (mat1 == null || mat2 == null) {
235
+ mat1?.release()
236
+ mat2?.release()
237
+ promise.resolve(false)
238
+ return
239
+ }
240
+
241
+ val thresh1 = applyThreshold(mat1)
242
+ val thresh2 = applyThreshold(mat2)
243
+
244
+ // Resize to match if different sizes
245
+ if (thresh1.size() != thresh2.size()) {
246
+ Imgproc.resize(thresh2, thresh2, thresh1.size())
247
+ }
248
+
249
+ val diff = Mat()
250
+ Core.absdiff(thresh1, thresh2, diff)
251
+ val count = Core.countNonZero(diff)
252
+
253
+ mat1.release()
254
+ mat2.release()
255
+ thresh1.release()
256
+ thresh2.release()
257
+ diff.release()
258
+
259
+ promise.resolve(count < threshold)
260
+ } catch (e: Exception) {
261
+ promise.resolve(false)
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Crop face images from a full frame using face bounds from ML Kit.
267
+ * Returns array of base64 face images sorted by x-position (left to right).
268
+ *
269
+ * @param base64Image Full frame as base64 JPEG
270
+ * @param faceBounds ReadableArray of { x, y, width, height } face bounds
271
+ * @param imageWidth Full frame width
272
+ * @param imageHeight Full frame height
273
+ * @param promise Returns array of base64 cropped face images (240x320)
274
+ */
275
+ @ReactMethod
276
+ fun cropFaceImages(base64Image: String, faceBounds: ReadableArray, imageWidth: Int, imageHeight: Int, promise: Promise) {
277
+ try {
278
+ if (!opencvInitialized) {
279
+ promise.resolve(Arguments.createArray())
280
+ return
281
+ }
282
+
283
+ val mat = base64ToMat(base64Image)
284
+ if (mat == null) {
285
+ promise.resolve(Arguments.createArray())
286
+ return
287
+ }
288
+
289
+ // Collect bounds and sort by x position (left to right)
290
+ val boundsWithIndex = mutableListOf<Pair<Int, ReadableMap>>()
291
+ for (i in 0 until faceBounds.size()) {
292
+ val bounds = faceBounds.getMap(i) ?: continue
293
+ boundsWithIndex.add(Pair(i, bounds))
294
+ }
295
+ boundsWithIndex.sortBy { it.second.getDouble("x").toInt() }
296
+
297
+ val result = Arguments.createArray()
298
+
299
+ for ((_, bounds) in boundsWithIndex) {
300
+ val x = bounds.getDouble("x").toInt()
301
+ val y = bounds.getDouble("y").toInt()
302
+ val w = bounds.getDouble("width").toInt()
303
+ val h = bounds.getDouble("height").toInt()
304
+
305
+ if (w <= 0 || h <= 0) continue
306
+
307
+ // Add 25% padding around the face bounding box
308
+ val padX = (w * 0.25).toInt()
309
+ val padY = (h * 0.25).toInt()
310
+
311
+ // Face bounds from ML Kit are in portrait coordinates (matching the rotated JPEG)
312
+ val cropX = (x - padX).coerceAtLeast(0)
313
+ val cropY = (y - padY).coerceAtLeast(0)
314
+ val cropRight = (x + w + padX).coerceAtMost(mat.cols())
315
+ val cropBottom = (y + h + padY).coerceAtMost(mat.rows())
316
+ val cropW = cropRight - cropX
317
+ val cropH = cropBottom - cropY
318
+
319
+ if (cropW <= 0 || cropH <= 0) continue
320
+
321
+ val roi = Rect(cropX, cropY, cropW, cropH)
322
+ val cropped = Mat(mat, roi)
323
+
324
+ // Resize to standard 240x320
325
+ val resized = Mat()
326
+ Imgproc.resize(cropped, resized, Size(240.0, 320.0))
327
+
328
+ // Add face without blur check
329
+ val faceBase64 = matToBase64(resized)
330
+ if (faceBase64 != null) {
331
+ result.pushString(faceBase64)
332
+ }
333
+
334
+ cropped.release()
335
+ resized.release()
336
+ }
337
+
338
+ mat.release()
339
+ promise.resolve(result)
340
+ } catch (e: Exception) {
341
+ promise.resolve(Arguments.createArray())
342
+ }
343
+ }
344
+
345
+ private fun applyThreshold(src: Mat): Mat {
346
+ val gray = Mat()
347
+ if (src.channels() > 1) {
348
+ Imgproc.cvtColor(src, gray, Imgproc.COLOR_RGB2GRAY)
349
+ } else {
350
+ src.copyTo(gray)
351
+ }
352
+ val ksize = Size(5.0, 5.0)
353
+ Imgproc.GaussianBlur(gray, gray, ksize, 0.0)
354
+ Imgproc.threshold(gray, gray, 0.0, 255.0, Imgproc.THRESH_BINARY + Imgproc.THRESH_OTSU)
355
+ return gray
356
+ }
357
+
358
+ /**
359
+ * Calculate average brightness of the full image.
360
+ * @param base64Image Base64 JPEG image
361
+ * @param promise Returns average brightness value (0-255)
362
+ */
363
+ @ReactMethod
364
+ fun getAverageBrightness(base64Image: String, promise: Promise) {
365
+ try {
366
+ val mat = base64ToMat(base64Image)
367
+ if (mat == null) {
368
+ promise.resolve(0.0)
369
+ return
370
+ }
371
+ val gray = Mat()
372
+ Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY)
373
+ val mean = Core.mean(gray)
374
+ mat.release()
375
+ gray.release()
376
+ promise.resolve(mean.`val`[0])
377
+ } catch (e: Exception) {
378
+ promise.resolve(0.0)
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Check average brightness of a circular region within the image.
384
+ * @param base64Image Base64 JPEG image
385
+ * @param minX Region origin X
386
+ * @param minY Region origin Y
387
+ * @param width Region width
388
+ * @param height Region height
389
+ * @param threshold Brightness threshold
390
+ * @param promise Returns true if average brightness in circular region exceeds threshold
391
+ */
392
+ @ReactMethod
393
+ fun isCircularRegionBright(base64Image: String, minX: Int, minY: Int, width: Int, height: Int, threshold: Double, promise: Promise) {
394
+ try {
395
+ val mat = base64ToMat(base64Image)
396
+ if (mat == null) {
397
+ promise.resolve(false)
398
+ return
399
+ }
400
+ val gray = Mat()
401
+ Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY)
402
+
403
+ // Create circular mask
404
+ val mask = Mat.zeros(gray.rows(), gray.cols(), CvType.CV_8U)
405
+ val centerX = minX + width / 2
406
+ val centerY = minY + height / 2
407
+ val radius = Math.min(width, height) / 2
408
+ Imgproc.circle(mask, Point(centerX.toDouble(), centerY.toDouble()), radius, Scalar(255.0), -1)
409
+
410
+ val mean = Core.mean(gray, mask)
411
+ mat.release()
412
+ gray.release()
413
+ mask.release()
414
+ promise.resolve(mean.`val`[0] > threshold)
415
+ } catch (e: Exception) {
416
+ promise.resolve(false)
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Check average brightness of a rectangular region within the image.
422
+ * @param base64Image Base64 JPEG image
423
+ * @param minX Region origin X
424
+ * @param minY Region origin Y
425
+ * @param width Region width
426
+ * @param height Region height
427
+ * @param threshold Brightness threshold
428
+ * @param promise Returns true if average brightness in rectangular region exceeds threshold
429
+ */
430
+ @ReactMethod
431
+ fun isRectangularRegionBright(base64Image: String, minX: Int, minY: Int, width: Int, height: Int, threshold: Double, promise: Promise) {
432
+ try {
433
+ val mat = base64ToMat(base64Image)
434
+ if (mat == null) {
435
+ promise.resolve(false)
436
+ return
437
+ }
438
+ val gray = Mat()
439
+ Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY)
440
+
441
+ // Validate region bounds
442
+ val x = Math.max(0, minX)
443
+ val y = Math.max(0, minY)
444
+ val w = Math.min(width, gray.cols() - x)
445
+ val h = Math.min(height, gray.rows() - y)
446
+
447
+ if (w <= 0 || h <= 0) {
448
+ mat.release()
449
+ gray.release()
450
+ promise.resolve(false)
451
+ return
452
+ }
453
+
454
+ // Extract ROI (Region of Interest)
455
+ val roi = org.opencv.core.Rect(x, y, w, h)
456
+ val regionMat = Mat(gray, roi)
457
+
458
+ // Calculate mean brightness within the region
459
+ val mean = Core.mean(regionMat)
460
+
461
+ mat.release()
462
+ gray.release()
463
+ regionMat.release()
464
+
465
+ promise.resolve(mean.`val`[0] > threshold)
466
+ } catch (e: Exception) {
467
+ promise.resolve(false)
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Check if an image is blurry using Laplacian variance.
473
+ * @param base64Image Base64 JPEG image
474
+ * @param threshold Variance below this = blurry (default 10)
475
+ * @param promise Returns true if image is blurry
476
+ */
477
+ @ReactMethod
478
+ fun checkBlurry(base64Image: String, threshold: Double, promise: Promise) {
479
+ try {
480
+ val mat = base64ToMat(base64Image)
481
+ if (mat == null) {
482
+ promise.resolve(false)
483
+ return
484
+ }
485
+ val gray = Mat()
486
+ Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY)
487
+
488
+ val laplacian = Mat()
489
+ Imgproc.Laplacian(gray, laplacian, CvType.CV_64F)
490
+
491
+ val mean = MatOfDouble()
492
+ val stdDev = MatOfDouble()
493
+ Core.meanStdDev(laplacian, mean, stdDev)
494
+
495
+ val variance = if (stdDev.rows() > 0) {
496
+ val std = stdDev.get(0, 0)[0]
497
+ std * std
498
+ } else 0.0
499
+
500
+ mat.release()
501
+ gray.release()
502
+ laplacian.release()
503
+ mean.release()
504
+ stdDev.release()
505
+
506
+ promise.resolve(variance < threshold)
507
+ } catch (e: Exception) {
508
+ promise.resolve(false)
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Check if a specific region of an image is blurry using Laplacian variance.
514
+ * This is useful for checking blur only in the area of interest (e.g., center region)
515
+ * while ignoring background blur from depth-of-field effects.
516
+ *
517
+ * @param base64Image Base64 JPEG image
518
+ * @param centerXPercent Center X position as percentage (0.0-1.0)
519
+ * @param centerYPercent Center Y position as percentage (0.0-1.0)
520
+ * @param widthPercent Region width as percentage (0.0-1.0)
521
+ * @param heightPercent Region height as percentage (0.0-1.0)
522
+ * @param threshold Variance below this = blurry (default 100)
523
+ * @param promise Returns true if region is blurry
524
+ */
525
+ @ReactMethod
526
+ fun checkBlurryInRegion(
527
+ base64Image: String,
528
+ centerXPercent: Double,
529
+ centerYPercent: Double,
530
+ widthPercent: Double,
531
+ heightPercent: Double,
532
+ threshold: Double,
533
+ promise: Promise
534
+ ) {
535
+ try {
536
+ val mat = base64ToMat(base64Image)
537
+ if (mat == null) {
538
+ promise.resolve(false)
539
+ return
540
+ }
541
+
542
+ val imgWidth = mat.cols()
543
+ val imgHeight = mat.rows()
544
+
545
+ // Calculate region bounds
546
+ val regionWidth = (imgWidth * widthPercent).toInt().coerceAtLeast(1)
547
+ val regionHeight = (imgHeight * heightPercent).toInt().coerceAtLeast(1)
548
+ val regionX = ((imgWidth * centerXPercent) - regionWidth / 2).toInt().coerceIn(0, imgWidth - regionWidth)
549
+ val regionY = ((imgHeight * centerYPercent) - regionHeight / 2).toInt().coerceIn(0, imgHeight - regionHeight)
550
+
551
+ // Extract region of interest
552
+ val roi = Rect(regionX, regionY, regionWidth, regionHeight)
553
+ val regionMat = Mat(mat, roi)
554
+
555
+ val gray = Mat()
556
+ Imgproc.cvtColor(regionMat, gray, Imgproc.COLOR_RGB2GRAY)
557
+
558
+ val laplacian = Mat()
559
+ Imgproc.Laplacian(gray, laplacian, CvType.CV_64F)
560
+
561
+ val mean = MatOfDouble()
562
+ val stdDev = MatOfDouble()
563
+ Core.meanStdDev(laplacian, mean, stdDev)
564
+
565
+ val variance = if (stdDev.rows() > 0) {
566
+ val std = stdDev.get(0, 0)[0]
567
+ std * std
568
+ } else 0.0
569
+
570
+ mat.release()
571
+ regionMat.release()
572
+ gray.release()
573
+ laplacian.release()
574
+ mean.release()
575
+ stdDev.release()
576
+
577
+ promise.resolve(variance < threshold)
578
+ } catch (e: Exception) {
579
+ promise.resolve(false)
580
+ }
581
+ }
582
+
583
+ private fun base64ToMat(base64: String): Mat? {
584
+ return try {
585
+ val bytes = Base64.decode(base64, Base64.NO_WRAP)
586
+ val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) ?: return null
587
+ val mat = Mat()
588
+ Utils.bitmapToMat(bitmap, mat)
589
+ bitmap.recycle()
590
+ mat
591
+ } catch (e: Exception) {
592
+ null
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Detect ID card boundaries based on text blocks and face positions.
598
+ * Creates a bounding rectangle that encompasses all detected elements.
599
+ *
600
+ * @param base64Image Base64 JPEG image
601
+ * @param textBlocks Array of detected text blocks with bounding boxes
602
+ * @param faces Array of detected faces with bounding boxes
603
+ * @param imageWidth Width of the image
604
+ * @param imageHeight Height of the image
605
+ * @param promise Returns card bounds as { x, y, width, height, corners: [{x, y}, ...] } or null
606
+ */
607
+ @ReactMethod
608
+ fun detectCardBounds(
609
+ base64Image: String,
610
+ textBlocks: ReadableArray,
611
+ faces: ReadableArray,
612
+ imageWidth: Int,
613
+ imageHeight: Int,
614
+ promise: Promise
615
+ ) {
616
+ try {
617
+ // Collect all element bounds for clustering
618
+ val allElements = mutableListOf<Rect>()
619
+
620
+ // Process text blocks
621
+ for (i in 0 until textBlocks.size()) {
622
+ val block = textBlocks.getMap(i) ?: continue
623
+ val frame = block.getMap("blockFrame") ?: continue
624
+
625
+ val x = frame.getInt("x")
626
+ val y = frame.getInt("y")
627
+ val width = frame.getInt("width")
628
+ val height = frame.getInt("height")
629
+
630
+ allElements.add(Rect(x, y, width, height))
631
+ }
632
+
633
+ // Process faces
634
+ for (i in 0 until faces.size()) {
635
+ val face = faces.getMap(i) ?: continue
636
+ val bounds = face.getMap("bounds") ?: continue
637
+
638
+ val x = bounds.getInt("x")
639
+ val y = bounds.getInt("y")
640
+ val width = bounds.getInt("width")
641
+ val height = bounds.getInt("height")
642
+
643
+ allElements.add(Rect(x, y, width, height))
644
+ }
645
+
646
+ if (allElements.isEmpty()) {
647
+ android.util.Log.d("OpenCVModule", "No elements detected for card bounds")
648
+ promise.resolve(null)
649
+ return
650
+ }
651
+
652
+ // Calculate the centroid of all elements
653
+ var centerX = 0
654
+ var centerY = 0
655
+ for (rect in allElements) {
656
+ val rectRight = rect.x + rect.width
657
+ val rectBottom = rect.y + rect.height
658
+ centerX += (rect.x + rectRight) / 2
659
+ centerY += (rect.y + rectBottom) / 2
660
+ }
661
+ centerX /= allElements.size
662
+ centerY /= allElements.size
663
+
664
+ // Calculate distances from centroid and filter outliers
665
+ val distances = allElements.map { rect ->
666
+ val rectRight = rect.x + rect.width
667
+ val rectBottom = rect.y + rect.height
668
+ val elemCenterX = (rect.x + rectRight) / 2
669
+ val elemCenterY = (rect.y + rectBottom) / 2
670
+ val dx = elemCenterX - centerX
671
+ val dy = elemCenterY - centerY
672
+ Math.sqrt((dx * dx + dy * dy).toDouble())
673
+ }
674
+
675
+ // Calculate median distance
676
+ val sortedDistances = distances.sorted()
677
+ val medianDistance = sortedDistances[sortedDistances.size / 2]
678
+
679
+ // Filter out elements that are more than 2x the median distance from center
680
+ // This removes outliers that are far from the main cluster
681
+ val threshold = medianDistance * 2.0
682
+ val filteredElements = allElements.filterIndexed { index, _ ->
683
+ distances[index] <= threshold
684
+ }
685
+
686
+ android.util.Log.d("OpenCVModule", "Filtered ${allElements.size - filteredElements.size} outlier elements (${allElements.size} -> ${filteredElements.size})")
687
+
688
+ if (filteredElements.isEmpty()) {
689
+ android.util.Log.d("OpenCVModule", "No elements after filtering outliers")
690
+ promise.resolve(null)
691
+ return
692
+ }
693
+
694
+ // Now calculate bounds from filtered elements
695
+ var minX = imageWidth
696
+ var minY = imageHeight
697
+ var maxX = 0
698
+ var maxY = 0
699
+
700
+ for (rect in filteredElements) {
701
+ val rectRight = rect.x + rect.width
702
+ val rectBottom = rect.y + rect.height
703
+ minX = Math.min(minX, rect.x)
704
+ minY = Math.min(minY, rect.y)
705
+ maxX = Math.max(maxX, rectRight)
706
+ maxY = Math.max(maxY, rectBottom)
707
+ }
708
+
709
+ val elementCount = filteredElements.size
710
+
711
+ android.util.Log.d("OpenCVModule", "Detected elements: $elementCount, bounds: ($minX, $minY) to ($maxX, $maxY)")
712
+
713
+ // Calculate raw bounding box from elements
714
+ val elementsWidth = maxX - minX
715
+ val elementsHeight = maxY - minY
716
+
717
+ android.util.Log.d("OpenCVModule", "Elements size: ${elementsWidth}x${elementsHeight}, frame: ${imageWidth}x${imageHeight}")
718
+
719
+ // Validate minimum size (elements should occupy at least 5% of frame)
720
+ val minArea = (imageWidth * imageHeight * 0.05).toInt()
721
+ if (elementsWidth * elementsHeight < minArea) {
722
+ android.util.Log.d("OpenCVModule", "Elements too small: ${elementsWidth * elementsHeight} < $minArea")
723
+ promise.resolve(null)
724
+ return
725
+ }
726
+
727
+ // Add minimal padding (5% on all sides) to create tight frame
728
+ val paddingX = (elementsWidth * 0.05).toInt()
729
+ val paddingY = (elementsHeight * 0.05).toInt()
730
+
731
+ // Calculate card bounds with padding, clamped to image boundaries
732
+ val cardX = Math.max(0, minX - paddingX)
733
+ val cardY = Math.max(0, minY - paddingY)
734
+ val cardRight = Math.min(imageWidth, maxX + paddingX)
735
+ val cardBottom = Math.min(imageHeight, maxY + paddingY)
736
+ val cardWidth = cardRight - cardX
737
+ val cardHeight = cardBottom - cardY
738
+
739
+ android.util.Log.d("OpenCVModule", "Card bounds: ($cardX, $cardY) ${cardWidth}x${cardHeight}")
740
+
741
+ // Validate aspect ratio is reasonable for a document (very lenient: 1.0 - 2.5)
742
+ val aspectRatio = cardWidth.toDouble() / cardHeight.toDouble().coerceAtLeast(1.0)
743
+ android.util.Log.d("OpenCVModule", "Card aspect ratio: $aspectRatio")
744
+
745
+ if (aspectRatio < 1.0 || aspectRatio > 2.5) {
746
+ android.util.Log.d("OpenCVModule", "Aspect ratio out of range: $aspectRatio")
747
+ promise.resolve(null)
748
+ return
749
+ }
750
+
751
+ // Create corner points (rectangular bounds)
752
+ val corners = WritableNativeArray()
753
+
754
+ // Top-left
755
+ val tl = WritableNativeMap()
756
+ tl.putDouble("x", cardX.toDouble())
757
+ tl.putDouble("y", cardY.toDouble())
758
+ corners.pushMap(tl)
759
+
760
+ // Top-right
761
+ val tr = WritableNativeMap()
762
+ tr.putDouble("x", (cardX + cardWidth).toDouble())
763
+ tr.putDouble("y", cardY.toDouble())
764
+ corners.pushMap(tr)
765
+
766
+ // Bottom-right
767
+ val br = WritableNativeMap()
768
+ br.putDouble("x", (cardX + cardWidth).toDouble())
769
+ br.putDouble("y", (cardY + cardHeight).toDouble())
770
+ corners.pushMap(br)
771
+
772
+ // Bottom-left
773
+ val bl = WritableNativeMap()
774
+ bl.putDouble("x", cardX.toDouble())
775
+ bl.putDouble("y", (cardY + cardHeight).toDouble())
776
+ corners.pushMap(bl)
777
+
778
+ val result = WritableNativeMap()
779
+ result.putInt("x", cardX)
780
+ result.putInt("y", cardY)
781
+ result.putInt("width", cardWidth)
782
+ result.putInt("height", cardHeight)
783
+ result.putArray("corners", corners)
784
+ result.putDouble("angle", 0.0) // Rectangular alignment
785
+
786
+ promise.resolve(result)
787
+ } catch (e: Exception) {
788
+ promise.resolve(null)
789
+ }
790
+ }
791
+
792
+ private fun matToBase64(mat: Mat): String? {
793
+ return try {
794
+ val bitmap = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888)
795
+ // Convert single-channel to 3-channel for bitmap conversion
796
+ val colorMat = if (mat.channels() == 1) {
797
+ val rgb = Mat()
798
+ Imgproc.cvtColor(mat, rgb, Imgproc.COLOR_GRAY2RGBA)
799
+ rgb
800
+ } else if (mat.channels() == 3) {
801
+ val rgba = Mat()
802
+ Imgproc.cvtColor(mat, rgba, Imgproc.COLOR_RGB2RGBA)
803
+ rgba
804
+ } else {
805
+ mat
806
+ }
807
+ Utils.matToBitmap(colorMat, bitmap)
808
+ if (colorMat !== mat) colorMat.release()
809
+
810
+ val stream = ByteArrayOutputStream()
811
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream)
812
+ bitmap.recycle()
813
+ Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP)
814
+ } catch (e: Exception) {
815
+ null
816
+ }
817
+ }
818
+ }