react-native-rectangle-doc-scanner 10.46.0 → 10.47.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.
- package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/CameraController.kt +29 -4
- package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/DocumentScannerView.kt +10 -13
- package/android/src/common/kotlin/com/reactnativerectangledocscanner/DocumentDetector.kt +25 -3
- package/package.json +1 -1
|
@@ -246,8 +246,20 @@ class CameraController(
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
// Frame rate throttling for better performance
|
|
250
|
+
private var lastAnalysisTimestamp: Long = 0
|
|
251
|
+
private val minAnalysisIntervalMs: Long = 150 // Process at most ~6-7 fps instead of 30 fps
|
|
252
|
+
|
|
249
253
|
@androidx.annotation.OptIn(ExperimentalGetImage::class)
|
|
250
254
|
private fun analyzeImage(imageProxy: ImageProxy) {
|
|
255
|
+
// Throttle frame processing to improve performance
|
|
256
|
+
val currentTime = System.currentTimeMillis()
|
|
257
|
+
if (currentTime - lastAnalysisTimestamp < minAnalysisIntervalMs) {
|
|
258
|
+
imageProxy.close()
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
lastAnalysisTimestamp = currentTime
|
|
262
|
+
|
|
251
263
|
val mediaImage = imageProxy.image
|
|
252
264
|
if (mediaImage == null) {
|
|
253
265
|
imageProxy.close()
|
|
@@ -533,10 +545,23 @@ class CameraController(
|
|
|
533
545
|
val centerX = viewWidth / 2f
|
|
534
546
|
val centerY = viewHeight / 2f
|
|
535
547
|
|
|
536
|
-
//
|
|
537
|
-
//
|
|
538
|
-
|
|
539
|
-
|
|
548
|
+
// For sensor=0 (tablet landscape), we need to manually rotate the buffer
|
|
549
|
+
// CameraX only handles rotation automatically for sensor=90 (phone portrait)
|
|
550
|
+
if (sensorOrientation == 0 && displayRotationDegrees != 0) {
|
|
551
|
+
matrix.postRotate(displayRotationDegrees.toFloat(), centerX, centerY)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Calculate rotated buffer dimensions
|
|
555
|
+
val rotatedBufferWidth = if (sensorOrientation == 0 && (displayRotationDegrees == 90 || displayRotationDegrees == 270)) {
|
|
556
|
+
bufferHeight
|
|
557
|
+
} else {
|
|
558
|
+
bufferWidth
|
|
559
|
+
}
|
|
560
|
+
val rotatedBufferHeight = if (sensorOrientation == 0 && (displayRotationDegrees == 90 || displayRotationDegrees == 270)) {
|
|
561
|
+
bufferWidth
|
|
562
|
+
} else {
|
|
563
|
+
bufferHeight
|
|
564
|
+
}
|
|
540
565
|
|
|
541
566
|
// Scale to fill the view while maintaining aspect ratio (center-crop).
|
|
542
567
|
val scaleX = viewWidth.toFloat() / rotatedBufferWidth.toFloat()
|
package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/DocumentScannerView.kt
CHANGED
|
@@ -694,14 +694,16 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
694
694
|
|
|
695
695
|
/**
|
|
696
696
|
* Overlay view for drawing detected rectangle
|
|
697
|
+
* Performance optimized: draws only corner points instead of filled polygon
|
|
697
698
|
*/
|
|
698
699
|
private class OverlayView(context: Context) : View(context) {
|
|
699
700
|
private var rectangle: Rectangle? = null
|
|
700
701
|
private var overlayColor: Int = Color.parseColor("#80FFFFFF")
|
|
701
|
-
private val
|
|
702
|
+
private val cornerPaint = Paint().apply {
|
|
702
703
|
style = Paint.Style.FILL
|
|
703
|
-
|
|
704
|
+
isAntiAlias = true
|
|
704
705
|
}
|
|
706
|
+
private val cornerRadius = 12f // Circle radius for corner points
|
|
705
707
|
|
|
706
708
|
fun setRectangle(rect: Rectangle?, color: Int) {
|
|
707
709
|
this.rectangle = rect
|
|
@@ -713,18 +715,13 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
713
715
|
super.onDraw(canvas)
|
|
714
716
|
|
|
715
717
|
rectangle?.let { rect ->
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
// Draw the rectangle overlay (simplified - just a filled polygon)
|
|
719
|
-
val path = android.graphics.Path().apply {
|
|
720
|
-
moveTo(rect.topLeft.x.toFloat(), rect.topLeft.y.toFloat())
|
|
721
|
-
lineTo(rect.topRight.x.toFloat(), rect.topRight.y.toFloat())
|
|
722
|
-
lineTo(rect.bottomRight.x.toFloat(), rect.bottomRight.y.toFloat())
|
|
723
|
-
lineTo(rect.bottomLeft.x.toFloat(), rect.bottomLeft.y.toFloat())
|
|
724
|
-
close()
|
|
725
|
-
}
|
|
718
|
+
cornerPaint.color = overlayColor
|
|
726
719
|
|
|
727
|
-
|
|
720
|
+
// Draw only corner points for better performance
|
|
721
|
+
canvas.drawCircle(rect.topLeft.x.toFloat(), rect.topLeft.y.toFloat(), cornerRadius, cornerPaint)
|
|
722
|
+
canvas.drawCircle(rect.topRight.x.toFloat(), rect.topRight.y.toFloat(), cornerRadius, cornerPaint)
|
|
723
|
+
canvas.drawCircle(rect.bottomLeft.x.toFloat(), rect.bottomLeft.y.toFloat(), cornerRadius, cornerPaint)
|
|
724
|
+
canvas.drawCircle(rect.bottomRight.x.toFloat(), rect.bottomRight.y.toFloat(), cornerRadius, cornerPaint)
|
|
728
725
|
}
|
|
729
726
|
}
|
|
730
727
|
}
|
|
@@ -472,6 +472,7 @@ class DocumentDetector {
|
|
|
472
472
|
|
|
473
473
|
/**
|
|
474
474
|
* Evaluate rectangle quality in view coordinates (closer to iOS behavior).
|
|
475
|
+
* Improved thresholds for better detection accuracy.
|
|
475
476
|
*/
|
|
476
477
|
fun evaluateRectangleQualityInView(
|
|
477
478
|
rectangle: Rectangle,
|
|
@@ -483,18 +484,39 @@ class DocumentDetector {
|
|
|
483
484
|
}
|
|
484
485
|
|
|
485
486
|
val minDim = kotlin.math.min(viewWidth.toDouble(), viewHeight.toDouble())
|
|
486
|
-
|
|
487
|
+
|
|
488
|
+
// Calculate actual edge lengths for better angle evaluation
|
|
489
|
+
val topEdgeLength = distance(rectangle.topLeft, rectangle.topRight)
|
|
490
|
+
val bottomEdgeLength = distance(rectangle.bottomLeft, rectangle.bottomRight)
|
|
491
|
+
val leftEdgeLength = distance(rectangle.topLeft, rectangle.bottomLeft)
|
|
492
|
+
val rightEdgeLength = distance(rectangle.topRight, rectangle.bottomRight)
|
|
493
|
+
|
|
494
|
+
// Use more lenient threshold: 12% of minimum dimension (reduced from 18%)
|
|
495
|
+
// This allows for more realistic perspective angles
|
|
496
|
+
val angleThreshold = max(80.0, minDim * 0.12)
|
|
487
497
|
|
|
488
498
|
val topYDiff = abs(rectangle.topRight.y - rectangle.topLeft.y)
|
|
489
499
|
val bottomYDiff = abs(rectangle.bottomLeft.y - rectangle.bottomRight.y)
|
|
490
500
|
val leftXDiff = abs(rectangle.topLeft.x - rectangle.bottomLeft.x)
|
|
491
501
|
val rightXDiff = abs(rectangle.topRight.x - rectangle.bottomRight.x)
|
|
492
502
|
|
|
493
|
-
|
|
503
|
+
// Check if edges are too skewed (perspective too extreme)
|
|
504
|
+
if (topYDiff > angleThreshold || bottomYDiff > angleThreshold ||
|
|
505
|
+
leftXDiff > angleThreshold || rightXDiff > angleThreshold) {
|
|
506
|
+
return RectangleQuality.BAD_ANGLE
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Additional check: opposite edges should be somewhat similar in length
|
|
510
|
+
// Allow up to 60% difference (more lenient for perspective)
|
|
511
|
+
val topBottomRatio = if (bottomEdgeLength > 0) topEdgeLength / bottomEdgeLength else 0.0
|
|
512
|
+
val leftRightRatio = if (rightEdgeLength > 0) leftEdgeLength / rightEdgeLength else 0.0
|
|
513
|
+
if (topBottomRatio < 0.4 || topBottomRatio > 2.5 ||
|
|
514
|
+
leftRightRatio < 0.4 || leftRightRatio > 2.5) {
|
|
494
515
|
return RectangleQuality.BAD_ANGLE
|
|
495
516
|
}
|
|
496
517
|
|
|
497
|
-
|
|
518
|
+
// More lenient margin check: 8% of minimum dimension (increased from 5%)
|
|
519
|
+
val margin = max(80.0, minDim * 0.08)
|
|
498
520
|
if (rectangle.topLeft.y > margin ||
|
|
499
521
|
rectangle.topRight.y > margin ||
|
|
500
522
|
rectangle.bottomLeft.y < (viewHeight - margin) ||
|
package/package.json
CHANGED