react-native-rectangle-doc-scanner 3.239.0 → 3.240.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.
@@ -68,6 +68,9 @@ dependencies {
68
68
  // OpenCV for document detection
69
69
  implementation 'org.opencv:opencv:4.9.0'
70
70
 
71
+ // ML Kit object detection for live rectangle hints
72
+ implementation 'com.google.mlkit:object-detection:17.0.1'
73
+
71
74
  // Coroutines for async operations
72
75
  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
73
76
  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
@@ -23,6 +23,10 @@ import android.util.Size
23
23
  import android.view.Surface
24
24
  import android.view.TextureView
25
25
  import androidx.core.content.ContextCompat
26
+ import com.google.mlkit.vision.common.InputImage
27
+ import com.google.mlkit.vision.objects.ObjectDetection
28
+ import com.google.mlkit.vision.objects.defaults.ObjectDetectorOptions
29
+ import org.opencv.core.Point
26
30
  import java.io.File
27
31
  import java.io.FileOutputStream
28
32
  import java.util.concurrent.atomic.AtomicReference
@@ -58,6 +62,12 @@ class CameraController(
58
62
 
59
63
  private val pendingCapture = AtomicReference<PendingCapture?>()
60
64
  private val analysisInFlight = AtomicBoolean(false)
65
+ private val objectDetector = ObjectDetection.getClient(
66
+ ObjectDetectorOptions.Builder()
67
+ .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)
68
+ .enableMultipleObjects()
69
+ .build()
70
+ )
61
71
 
62
72
  var onFrameAnalyzed: ((Rectangle?, Int, Int) -> Unit)? = null
63
73
 
@@ -179,6 +189,7 @@ class CameraController(
179
189
 
180
190
  fun shutdown() {
181
191
  stopCamera()
192
+ objectDetector.close()
182
193
  cameraThread.quitSafely()
183
194
  analysisThread.quitSafely()
184
195
  }
@@ -352,21 +363,42 @@ class CameraController(
352
363
  }
353
364
 
354
365
  private fun analyzeImage(image: Image) {
355
- try {
356
- val nv21 = image.toNv21()
357
- val rotationDegrees = computeRotationDegrees()
358
- val rectangle = DocumentDetector.detectRectangleInYUV(nv21, image.width, image.height, rotationDegrees)
359
-
360
- val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) image.height else image.width
361
- val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) image.width else image.height
362
-
363
- onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
366
+ val rotationDegrees = computeRotationDegrees()
367
+ val inputImage = try {
368
+ InputImage.fromMediaImage(image, rotationDegrees)
364
369
  } catch (e: Exception) {
365
- Log.e(TAG, "[CAMERA2] Error analyzing frame", e)
366
- } finally {
370
+ Log.e(TAG, "[CAMERA2] Failed to create InputImage", e)
367
371
  image.close()
368
372
  analysisInFlight.set(false)
373
+ return
369
374
  }
375
+
376
+ objectDetector.process(inputImage)
377
+ .addOnSuccessListener { objects ->
378
+ val best = objects.maxByOrNull { obj ->
379
+ val box = obj.boundingBox
380
+ box.width() * box.height()
381
+ }
382
+ val rectangle = best?.boundingBox?.let { box ->
383
+ Rectangle(
384
+ Point(box.left.toDouble(), box.top.toDouble()),
385
+ Point(box.right.toDouble(), box.top.toDouble()),
386
+ Point(box.left.toDouble(), box.bottom.toDouble()),
387
+ Point(box.right.toDouble(), box.bottom.toDouble())
388
+ )
389
+ }
390
+
391
+ val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) image.height else image.width
392
+ val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) image.width else image.height
393
+ onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
394
+ }
395
+ .addOnFailureListener { e ->
396
+ Log.e(TAG, "[CAMERA2] ML Kit detection failed", e)
397
+ }
398
+ .addOnCompleteListener {
399
+ image.close()
400
+ analysisInFlight.set(false)
401
+ }
370
402
  }
371
403
 
372
404
  private fun processCapture(image: Image, pending: PendingCapture) {
@@ -497,48 +529,6 @@ class CameraController(
497
529
  return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
498
530
  }
499
531
 
500
- private fun Image.toNv21(): ByteArray {
501
- val width = width
502
- val height = height
503
- val ySize = width * height
504
- val uvSize = width * height / 2
505
- val nv21 = ByteArray(ySize + uvSize)
506
-
507
- val yBuffer = planes[0].buffer
508
- val uBuffer = planes[1].buffer
509
- val vBuffer = planes[2].buffer
510
-
511
- val yRowStride = planes[0].rowStride
512
- val yPixelStride = planes[0].pixelStride
513
- var outputOffset = 0
514
- for (row in 0 until height) {
515
- var inputOffset = row * yRowStride
516
- for (col in 0 until width) {
517
- nv21[outputOffset++] = yBuffer.get(inputOffset)
518
- inputOffset += yPixelStride
519
- }
520
- }
521
-
522
- val uvRowStride = planes[1].rowStride
523
- val uvPixelStride = planes[1].pixelStride
524
- val vRowStride = planes[2].rowStride
525
- val vPixelStride = planes[2].pixelStride
526
- val uvHeight = height / 2
527
- val uvWidth = width / 2
528
- for (row in 0 until uvHeight) {
529
- var uInputOffset = row * uvRowStride
530
- var vInputOffset = row * vRowStride
531
- for (col in 0 until uvWidth) {
532
- nv21[outputOffset++] = vBuffer.get(vInputOffset)
533
- nv21[outputOffset++] = uBuffer.get(uInputOffset)
534
- uInputOffset += uvPixelStride
535
- vInputOffset += vPixelStride
536
- }
537
- }
538
-
539
- return nv21
540
- }
541
-
542
532
  private fun hasCameraPermission(): Boolean {
543
533
  return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
544
534
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "3.239.0",
3
+ "version": "3.240.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",