@trustchex/react-native-sdk 1.361.0 → 1.362.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 (53) hide show
  1. package/TrustchexSDK.podspec +3 -1
  2. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +0 -13
  3. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraManager.kt +0 -8
  4. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +59 -39
  5. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +94 -13
  6. package/ios/Camera/TrustchexCameraManager.m +0 -2
  7. package/ios/Camera/TrustchexCameraManager.swift +0 -7
  8. package/ios/Camera/TrustchexCameraView.swift +16 -47
  9. package/ios/OpenCV/OpenCVHelper.h +17 -0
  10. package/ios/OpenCV/OpenCVHelper.mm +128 -0
  11. package/ios/OpenCV/OpenCVModule.h +6 -0
  12. package/ios/OpenCV/OpenCVModule.mm +141 -0
  13. package/ios/TrustchexSDK-Bridging-Header.h +8 -0
  14. package/lib/module/Screens/Debug/MRZTestScreen.js +175 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +4 -0
  16. package/lib/module/Shared/Components/EIDScanner.js +0 -78
  17. package/lib/module/Shared/Components/IdentityDocumentCamera.js +188 -150
  18. package/lib/module/Shared/Components/QrCodeScannerCamera.js +0 -3
  19. package/lib/module/Shared/Libs/mrz.utils.js +265 -0
  20. package/lib/module/Trustchex.js +4 -0
  21. package/lib/module/index.js +1 -0
  22. package/lib/module/version.js +1 -1
  23. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts +3 -0
  24. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -0
  25. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  26. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  27. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -1
  28. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  30. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +0 -19
  31. package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
  32. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +18 -1
  33. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  34. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  35. package/lib/typescript/src/index.d.ts +3 -0
  36. package/lib/typescript/src/index.d.ts.map +1 -1
  37. package/lib/typescript/src/version.d.ts +1 -1
  38. package/package.json +2 -1
  39. package/src/Screens/Debug/MRZTestScreen.tsx +209 -0
  40. package/src/Shared/Components/DebugNavigationPanel.tsx +5 -0
  41. package/src/Shared/Components/EIDScanner.tsx +0 -53
  42. package/src/Shared/Components/IdentityDocumentCamera.tsx +228 -146
  43. package/src/Shared/Components/QrCodeScannerCamera.tsx +0 -9
  44. package/src/Shared/Components/TrustchexCamera.tsx +0 -20
  45. package/src/Shared/Libs/mrz.utils.ts +289 -1
  46. package/src/Trustchex.tsx +5 -0
  47. package/src/index.tsx +3 -0
  48. package/src/version.ts +1 -1
  49. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidationModule.kt +0 -785
  50. package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidator.kt +0 -419
  51. package/ios/MRZValidation.m +0 -39
  52. package/ios/MRZValidation.swift +0 -802
  53. package/ios/MRZValidator.swift +0 -466
@@ -14,7 +14,9 @@ Pod::Spec.new do |s|
14
14
  s.source = { :git => "https://github.com/trustchex/trustchex.git", :tag => "#{s.version}" }
15
15
 
16
16
  s.source_files = "ios/**/*.{h,m,mm,cpp,swift}"
17
- s.private_header_files = "ios/**/*.h"
17
+
18
+ # Make OpenCVHelper accessible to Swift code
19
+ s.public_header_files = "ios/OpenCV/OpenCVHelper.h"
18
20
 
19
21
  # ML Kit dependencies
20
22
  s.dependency "GoogleMLKit/TextRecognition"
@@ -8,7 +8,6 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
8
8
  import com.facebook.react.uimanager.ViewManager
9
9
  import com.trustchex.reactnativesdk.camera.TrustchexCameraManager
10
10
  import com.trustchex.reactnativesdk.mlkit.MLKitModule
11
- import com.trustchex.reactnativesdk.mrz.MRZValidationModule
12
11
  import com.trustchex.reactnativesdk.opencv.OpenCVModule
13
12
  import java.util.HashMap
14
13
 
@@ -19,7 +18,6 @@ class TrustchexSDKPackage : BaseReactPackage() {
19
18
  "DeviceBrightness" -> DeviceBrightnessModule(reactContext)
20
19
  "MLKitModule" -> MLKitModule(reactContext)
21
20
  "OpenCVModule" -> OpenCVModule(reactContext)
22
- "MRZValidation" -> MRZValidationModule(reactContext)
23
21
  else -> null
24
22
  }
25
23
  }
@@ -76,17 +74,6 @@ class TrustchexSDKPackage : BaseReactPackage() {
76
74
  false // isTurboModule
77
75
  )
78
76
 
79
- // MRZ Validation module
80
- moduleInfos["MRZValidation"] =
81
- ReactModuleInfo(
82
- "MRZValidation",
83
- "MRZValidation",
84
- false, // canOverrideExistingModule
85
- false, // needsEagerInit
86
- false, // isCxxModule
87
- false // isTurboModule
88
- )
89
-
90
77
  moduleInfos
91
78
  }
92
79
  }
@@ -63,11 +63,6 @@ class TrustchexCameraManager : SimpleViewManager<TrustchexCameraView>() {
63
63
  view.setBarcodeScanningEnabled(enabled)
64
64
  }
65
65
 
66
- @ReactProp(name = "enableMrzValidation")
67
- fun setEnableMrzValidation(view: TrustchexCameraView, enabled: Boolean) {
68
- view.setMrzValidationEnabled(enabled)
69
- }
70
-
71
66
  @ReactProp(name = "includeBase64")
72
67
  fun setIncludeBase64(view: TrustchexCameraView, enabled: Boolean) {
73
68
  view.setIncludeBase64(enabled)
@@ -95,7 +90,6 @@ class TrustchexCameraManager : SimpleViewManager<TrustchexCameraView>() {
95
90
  }
96
91
 
97
92
  override fun receiveCommand(view: TrustchexCameraView, commandId: Int, args: com.facebook.react.bridge.ReadableArray?) {
98
- android.util.Log.d("TrustchexCamera", "receiveCommand(Int) called with commandId: $commandId")
99
93
  when (commandId) {
100
94
  COMMAND_SET_FOCUS_POINT -> {
101
95
  if (args != null && args.size() >= 2) {
@@ -108,7 +102,6 @@ class TrustchexCameraManager : SimpleViewManager<TrustchexCameraView>() {
108
102
  }
109
103
  }
110
104
  COMMAND_START_RECORDING -> {
111
- android.util.Log.d("TrustchexCamera", "COMMAND_START_RECORDING received")
112
105
  view.startRecording()
113
106
  }
114
107
  COMMAND_STOP_RECORDING -> {
@@ -129,7 +122,6 @@ class TrustchexCameraManager : SimpleViewManager<TrustchexCameraView>() {
129
122
  }
130
123
 
131
124
  override fun receiveCommand(view: TrustchexCameraView, commandName: String, args: com.facebook.react.bridge.ReadableArray?) {
132
- android.util.Log.d("TrustchexCamera", "receiveCommand(String) called with commandName: $commandName")
133
125
  when (commandName) {
134
126
  "setFocusPoint" -> {
135
127
  if (args != null && args.size() >= 2) {
@@ -1,11 +1,13 @@
1
1
  package com.trustchex.reactnativesdk.camera
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.graphics.Bitmap
4
5
  import android.graphics.BitmapFactory
5
6
  import android.graphics.ImageFormat
6
7
  import android.graphics.Matrix
7
8
  import android.graphics.Rect
8
9
  import android.graphics.YuvImage
10
+ import android.util.Base64
9
11
  import android.util.Size
10
12
  import android.widget.FrameLayout
11
13
  import androidx.annotation.OptIn
@@ -25,7 +27,6 @@ import com.google.mlkit.vision.barcode.BarcodeScanning
25
27
  import com.google.mlkit.vision.barcode.BarcodeScannerOptions
26
28
  import com.google.mlkit.vision.barcode.common.Barcode
27
29
  import com.google.mlkit.vision.common.InputImage
28
- import com.trustchex.reactnativesdk.mrz.MRZValidator
29
30
  import com.google.mlkit.vision.face.FaceDetection
30
31
  import com.google.mlkit.vision.face.FaceDetectorOptions
31
32
  import com.google.mlkit.vision.text.TextRecognition
@@ -67,7 +68,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
67
68
  private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
68
69
  private var torchEnabled = false
69
70
  private var frameProcessingEnabled = false
70
- private var targetFps = 6
71
+ private var targetFps = 10
71
72
  private var isCameraInitialized = false
72
73
  private var isStoppingRecording = false // Track if stopRecording was called to prevent cancelRecording from deleting the file
73
74
 
@@ -75,7 +76,6 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
75
76
  private var faceDetectionEnabled = false
76
77
  private var textRecognitionEnabled = false
77
78
  private var barcodeScanningEnabled = false
78
- private var mrzValidationEnabled = false
79
79
  private var includeBase64 = false
80
80
 
81
81
  // ML Kit detector instances (lazy, created once)
@@ -98,10 +98,15 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
98
98
  .setBarcodeFormats(
99
99
  Barcode.FORMAT_PDF417,
100
100
  Barcode.FORMAT_QR_CODE,
101
+ Barcode.FORMAT_CODABAR,
101
102
  Barcode.FORMAT_CODE_128,
102
103
  Barcode.FORMAT_CODE_39,
104
+ Barcode.FORMAT_CODE_93,
103
105
  Barcode.FORMAT_EAN_13,
104
106
  Barcode.FORMAT_EAN_8,
107
+ Barcode.FORMAT_ITF,
108
+ Barcode.FORMAT_UPC_A,
109
+ Barcode.FORMAT_UPC_E,
105
110
  Barcode.FORMAT_AZTEC,
106
111
  Barcode.FORMAT_DATA_MATRIX
107
112
  )
@@ -178,7 +183,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
178
183
  // Heuristic: If we have 3+ back cameras, index 2 is often Ultra Wide
179
184
  }
180
185
  } catch (e: Exception) {
181
- android.util.Log.e("TrustchexCamera", "Failed to enumerate cameras: ${e.message}")
186
+ // Failed to enumerate cameras
182
187
  }
183
188
 
184
189
  return CameraSelector.DEFAULT_BACK_CAMERA
@@ -209,10 +214,6 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
209
214
  barcodeScanningEnabled = enabled
210
215
  }
211
216
 
212
- fun setMrzValidationEnabled(enabled: Boolean) {
213
- mrzValidationEnabled = enabled
214
- }
215
-
216
217
  fun setIncludeBase64(enabled: Boolean) {
217
218
  includeBase64 = enabled
218
219
  }
@@ -357,11 +358,15 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
357
358
  return
358
359
  }
359
360
 
360
- // Create InputImage directly from camera frame — zero-copy, no base64 for ML Kit
361
- val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
362
-
361
+ // Get rotation degrees early for preprocessing
363
362
  val rotationDegrees = imageProxy.imageInfo.rotationDegrees
364
363
 
364
+ // Create InputImage directly from camera frame — zero-copy, no base64 for ML Kit
365
+ val inputImage = InputImage.fromMediaImage(mediaImage, rotationDegrees)
366
+
367
+ // Use original image for text recognition
368
+ val textInputImage = inputImage
369
+
365
370
  // Calculate portrait-oriented dimensions early (JPEG is already rotated)
366
371
  val isRotated = rotationDegrees == 90 || rotationDegrees == 270
367
372
  val reportedWidth = if (isRotated) imageProxy.height else imageProxy.width
@@ -385,7 +390,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
385
390
  } else null
386
391
 
387
392
  val textTask = if (textRecognitionEnabled) {
388
- textRecognizer.process(inputImage).also { tasks.add(it) }
393
+ textRecognizer.process(textInputImage).also { tasks.add(it) }
389
394
  } else null
390
395
 
391
396
  val barcodeTask = if (barcodeScanningEnabled) {
@@ -471,28 +476,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
471
476
  }
472
477
  }
473
478
 
474
- // MRZ validation (if enabled and text was recognized)
475
- if (mrzValidationEnabled && textTask != null && textTask.isSuccessful) {
476
- val rawText = textTask.result.text
477
- val mrzResult = MRZValidator.validateWithCorrections(rawText)
478
- val mrzMap = Arguments.createMap()
479
- mrzMap.putBoolean("valid", mrzResult.valid)
480
- mrzMap.putString("format", mrzResult.format)
481
- mrzResult.error?.let { mrzMap.putString("error", it) }
482
- mrzResult.documentCode?.let { mrzMap.putString("documentCode", it) }
483
- mrzResult.issuingState?.let { mrzMap.putString("issuingState", it) }
484
- mrzResult.documentNumber?.let { mrzMap.putString("documentNumber", it) }
485
- mrzResult.lastName?.let { mrzMap.putString("lastName", it) }
486
- mrzResult.firstName?.let { mrzMap.putString("firstName", it) }
487
- mrzResult.birthDate?.let { mrzMap.putString("birthDate", it) }
488
- mrzResult.sex?.let { mrzMap.putString("sex", it) }
489
- mrzResult.expirationDate?.let { mrzMap.putString("expirationDate", it) }
490
- mrzResult.nationality?.let { mrzMap.putString("nationality", it) }
491
- mrzResult.optional1?.let { mrzMap.putString("optional1", it) }
492
- mrzResult.optional2?.let { mrzMap.putString("optional2", it) }
493
- mrzResult.rawLines?.let { mrzMap.putString("rawLines", it) }
494
- frameData.putMap("mrzResult", mrzMap)
495
- }
479
+ // MRZ validation moved to JavaScript - native implementation removed
496
480
 
497
481
  // Barcode scanning results
498
482
  if (barcodeTask != null) {
@@ -605,7 +589,6 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
605
589
  // Check plane count
606
590
  val planeCount = imageProxy.planes.size
607
591
  if (planeCount < 3) {
608
- android.util.Log.e("TrustchexCamera", "[yuvImageProxyToJpegBase64] ERROR: Expected 3 planes (YUV), got $planeCount")
609
592
  return null
610
593
  }
611
594
 
@@ -644,7 +627,6 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
644
627
  nv21[nv21Offset++] = vBuffer.get(uvIndex)
645
628
  nv21[nv21Offset++] = uBuffer.get(uvIndex)
646
629
  } else {
647
- android.util.Log.w("TrustchexCamera", "UV index out of bounds: $uvIndex at row=$row, col=$col")
648
630
  nv21[nv21Offset++] = 128.toByte()
649
631
  nv21[nv21Offset++] = 128.toByte()
650
632
  }
@@ -673,7 +655,6 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
673
655
 
674
656
  return android.util.Base64.encodeToString(jpegStream.toByteArray(), android.util.Base64.NO_WRAP)
675
657
  } catch (e: Exception) {
676
- android.util.Log.e("TrustchexCamera", "[yuvImageProxyToJpegBase64] ERROR: ${e.message}", e)
677
658
  return null
678
659
  }
679
660
  }
@@ -800,7 +781,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
800
781
 
801
782
  camera.cameraControl.startFocusAndMetering(afAction)
802
783
  } catch (e: Exception) {
803
- android.util.Log.w("TrustchexCamera", "Failed to apply camera settings: ${e.message}")
784
+ // Failed to apply camera settings
804
785
  }
805
786
  }
806
787
  private fun sendFrameEvent(frameData: WritableMap) {
@@ -840,7 +821,7 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
840
821
  .getJSModule(RCTEventEmitter::class.java)
841
822
  .receiveEvent(id, TrustchexCameraManager.EVENT_RECORDING_FINISHED, event)
842
823
  } catch (e: Exception) {
843
- android.util.Log.e("TrustchexCamera", "Error sending recording finished event: ${e.message}", e)
824
+ // Error sending recording finished event
844
825
  }
845
826
  }
846
827
 
@@ -864,6 +845,45 @@ class TrustchexCameraView(context: ThemedReactContext) : FrameLayout(context) {
864
845
  }
865
846
  }
866
847
 
848
+ /**
849
+ * Convert YUV ImageProxy to Bitmap with rotation applied
850
+ */
851
+ private fun yuvImageToBitmap(imageProxy: ImageProxy, rotationDegrees: Int): Bitmap? {
852
+ return try {
853
+ val yBuffer = imageProxy.planes[0].buffer
854
+ val uBuffer = imageProxy.planes[1].buffer
855
+ val vBuffer = imageProxy.planes[2].buffer
856
+
857
+ val ySize = yBuffer.remaining()
858
+ val uSize = uBuffer.remaining()
859
+ val vSize = vBuffer.remaining()
860
+
861
+ val nv21 = ByteArray(ySize + uSize + vSize)
862
+ yBuffer.get(nv21, 0, ySize)
863
+ vBuffer.get(nv21, ySize, vSize)
864
+ uBuffer.get(nv21, ySize + vSize, uSize)
865
+
866
+ val yuvImage = YuvImage(nv21, ImageFormat.NV21, imageProxy.width, imageProxy.height, null)
867
+ val out = ByteArrayOutputStream()
868
+ yuvImage.compressToJpeg(Rect(0, 0, imageProxy.width, imageProxy.height), 100, out)
869
+ val imageBytes = out.toByteArray()
870
+ val bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
871
+
872
+ // Apply rotation if needed
873
+ if (rotationDegrees != 0 && bitmap != null) {
874
+ val matrix = Matrix()
875
+ matrix.postRotate(rotationDegrees.toFloat())
876
+ val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
877
+ bitmap.recycle()
878
+ rotatedBitmap
879
+ } else {
880
+ bitmap
881
+ }
882
+ } catch (e: Exception) {
883
+ null
884
+ }
885
+ }
886
+
867
887
  fun cleanup() {
868
888
  // Cancel any active recording and delete file
869
889
  if (activeRecording != null) {
@@ -644,7 +644,6 @@ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
644
644
  }
645
645
 
646
646
  if (allElements.isEmpty()) {
647
- android.util.Log.d("OpenCVModule", "No elements detected for card bounds")
648
647
  promise.resolve(null)
649
648
  return
650
649
  }
@@ -683,10 +682,7 @@ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
683
682
  distances[index] <= threshold
684
683
  }
685
684
 
686
- android.util.Log.d("OpenCVModule", "Filtered ${allElements.size - filteredElements.size} outlier elements (${allElements.size} -> ${filteredElements.size})")
687
-
688
685
  if (filteredElements.isEmpty()) {
689
- android.util.Log.d("OpenCVModule", "No elements after filtering outliers")
690
686
  promise.resolve(null)
691
687
  return
692
688
  }
@@ -708,18 +704,13 @@ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
708
704
 
709
705
  val elementCount = filteredElements.size
710
706
 
711
- android.util.Log.d("OpenCVModule", "Detected elements: $elementCount, bounds: ($minX, $minY) to ($maxX, $maxY)")
712
-
713
707
  // Calculate raw bounding box from elements
714
708
  val elementsWidth = maxX - minX
715
709
  val elementsHeight = maxY - minY
716
710
 
717
- android.util.Log.d("OpenCVModule", "Elements size: ${elementsWidth}x${elementsHeight}, frame: ${imageWidth}x${imageHeight}")
718
-
719
711
  // Validate minimum size (elements should occupy at least 5% of frame)
720
712
  val minArea = (imageWidth * imageHeight * 0.05).toInt()
721
713
  if (elementsWidth * elementsHeight < minArea) {
722
- android.util.Log.d("OpenCVModule", "Elements too small: ${elementsWidth * elementsHeight} < $minArea")
723
714
  promise.resolve(null)
724
715
  return
725
716
  }
@@ -736,14 +727,10 @@ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
736
727
  val cardWidth = cardRight - cardX
737
728
  val cardHeight = cardBottom - cardY
738
729
 
739
- android.util.Log.d("OpenCVModule", "Card bounds: ($cardX, $cardY) ${cardWidth}x${cardHeight}")
740
-
741
730
  // Validate aspect ratio is reasonable for a document (very lenient: 1.0 - 2.5)
742
731
  val aspectRatio = cardWidth.toDouble() / cardHeight.toDouble().coerceAtLeast(1.0)
743
- android.util.Log.d("OpenCVModule", "Card aspect ratio: $aspectRatio")
744
732
 
745
733
  if (aspectRatio < 1.0 || aspectRatio > 2.5) {
746
- android.util.Log.d("OpenCVModule", "Aspect ratio out of range: $aspectRatio")
747
734
  promise.resolve(null)
748
735
  return
749
736
  }
@@ -815,4 +802,98 @@ class OpenCVModule(reactContext: ReactApplicationContext) : ReactContextBaseJava
815
802
  null
816
803
  }
817
804
  }
805
+
806
+ /**
807
+ * Preprocess image for better OCR text recognition using OpenCV.
808
+ * Steps:
809
+ * 1. Convert to grayscale
810
+ * 2. Bilateral filter for noise reduction while preserving edges
811
+ * 3. CLAHE for adaptive contrast enhancement
812
+ * 4. Sharpen the image to enhance text edges
813
+ * 5. Optional: Adaptive threshold for high-contrast text extraction
814
+ *
815
+ * @param base64Image Base64 encoded input image
816
+ * @param applyThresholding Whether to apply adaptive thresholding (default: false)
817
+ * @param promise Returns preprocessed image as base64
818
+ */
819
+ @ReactMethod
820
+ fun preprocessImageForOCR(base64Image: String, applyThresholding: Boolean, promise: Promise) {
821
+ try {
822
+ if (!opencvInitialized) {
823
+ promise.reject("OPENCV_ERROR", "OpenCV not initialized")
824
+ return
825
+ }
826
+
827
+ val mat = base64ToMat(base64Image)
828
+ if (mat == null) {
829
+ promise.reject("DECODE_ERROR", "Failed to decode image")
830
+ return
831
+ }
832
+
833
+ // 1. Convert to grayscale
834
+ val gray = Mat()
835
+ Imgproc.cvtColor(mat, gray, Imgproc.COLOR_RGB2GRAY)
836
+ mat.release()
837
+
838
+ // 2. Apply bilateral filter for noise reduction while preserving edges
839
+ // This is better than Gaussian blur for text as it keeps edges sharp
840
+ val filtered = Mat()
841
+ Imgproc.bilateralFilter(gray, filtered, 9, 75.0, 75.0)
842
+ gray.release()
843
+
844
+ // 3. Apply CLAHE (Contrast Limited Adaptive Histogram Equalization)
845
+ // This enhances local contrast, making text stand out better
846
+ val clahe = Imgproc.createCLAHE(2.0, Size(8.0, 8.0))
847
+ val enhanced = Mat()
848
+ clahe.apply(filtered, enhanced)
849
+ filtered.release()
850
+
851
+ // 4. Sharpen the image to enhance text edges
852
+ // Use unsharp masking: original + (original - blurred) * amount
853
+ val blurred = Mat()
854
+ Imgproc.GaussianBlur(enhanced, blurred, Size(0.0, 0.0), 3.0)
855
+ val sharpened = Mat()
856
+ Core.addWeighted(enhanced, 1.5, blurred, -0.5, 0.0, sharpened)
857
+ blurred.release()
858
+ enhanced.release()
859
+
860
+ // 5. Optional: Apply adaptive thresholding for binary text extraction
861
+ val result = if (applyThresholding) {
862
+ val thresholded = Mat()
863
+ // Use Gaussian adaptive threshold - better for varying illumination
864
+ Imgproc.adaptiveThreshold(
865
+ sharpened,
866
+ thresholded,
867
+ 255.0,
868
+ Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
869
+ Imgproc.THRESH_BINARY,
870
+ 11,
871
+ 2.0
872
+ )
873
+ sharpened.release()
874
+
875
+ // Apply morphological operations to clean up noise
876
+ val kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, Size(2.0, 2.0))
877
+ val cleaned = Mat()
878
+ Imgproc.morphologyEx(thresholded, cleaned, Imgproc.MORPH_CLOSE, kernel)
879
+ thresholded.release()
880
+ kernel.release()
881
+
882
+ cleaned
883
+ } else {
884
+ sharpened
885
+ }
886
+
887
+ val resultBase64 = matToBase64(result)
888
+ result.release()
889
+
890
+ if (resultBase64 != null) {
891
+ promise.resolve(resultBase64)
892
+ } else {
893
+ promise.reject("ENCODE_ERROR", "Failed to encode result")
894
+ }
895
+ } catch (e: Exception) {
896
+ promise.reject("OCR_PREPROCESS_ERROR", e.message)
897
+ }
898
+ }
818
899
  }
@@ -13,7 +13,6 @@ RCT_EXPORT_VIEW_PROPERTY(enableFrameProcessing, BOOL)
13
13
  RCT_EXPORT_VIEW_PROPERTY(enableFaceDetection, BOOL)
14
14
  RCT_EXPORT_VIEW_PROPERTY(enableTextRecognition, BOOL)
15
15
  RCT_EXPORT_VIEW_PROPERTY(enableBarcodeScanning, BOOL)
16
- RCT_EXPORT_VIEW_PROPERTY(enableMrzValidation, BOOL)
17
16
  RCT_EXPORT_VIEW_PROPERTY(includeBase64, BOOL)
18
17
  RCT_EXPORT_VIEW_PROPERTY(torchEnabled, BOOL)
19
18
  RCT_EXPORT_VIEW_PROPERTY(targetFps, NSNumber)
@@ -24,7 +23,6 @@ RCT_EXTERN_METHOD(setEnableFrameProcessing:(nonnull NSNumber *)reactTag enabled:
24
23
  RCT_EXTERN_METHOD(setEnableFaceDetection:(nonnull NSNumber *)reactTag enabled:(BOOL)enabled)
25
24
  RCT_EXTERN_METHOD(setEnableTextRecognition:(nonnull NSNumber *)reactTag enabled:(BOOL)enabled)
26
25
  RCT_EXTERN_METHOD(setEnableBarcodeScanning:(nonnull NSNumber *)reactTag enabled:(BOOL)enabled)
27
- RCT_EXTERN_METHOD(setEnableMrzValidation:(nonnull NSNumber *)reactTag enabled:(BOOL)enabled)
28
26
  RCT_EXTERN_METHOD(setIncludeBase64:(nonnull NSNumber *)reactTag enabled:(BOOL)enabled)
29
27
  RCT_EXTERN_METHOD(setTargetFps:(nonnull NSNumber *)reactTag fps:(nonnull NSNumber *)fps)
30
28
  RCT_EXTERN_METHOD(setFocusPoint:(nonnull NSNumber *)reactTag x:(nonnull NSNumber *)x y:(nonnull NSNumber *)y)
@@ -56,13 +56,6 @@ class TrustchexCameraManager: RCTViewManager {
56
56
  }
57
57
  }
58
58
 
59
- @objc func setEnableMrzValidation(_ reactTag: NSNumber, enabled: Bool) {
60
- self.bridge.uiManager.addUIBlock { (_, viewRegistry) in
61
- guard let view = viewRegistry?[reactTag] as? TrustchexCameraView else { return }
62
- view.enableMrzValidation = enabled
63
- }
64
- }
65
-
66
59
  @objc func setIncludeBase64(_ reactTag: NSNumber, enabled: Bool) {
67
60
  self.bridge.uiManager.addUIBlock { (_, viewRegistry) in
68
61
  guard let view = viewRegistry?[reactTag] as? TrustchexCameraView else { return }