react-native-rectangle-doc-scanner 11.1.0 → 11.3.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.
|
@@ -280,10 +280,11 @@ class CameraController(
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
// Use the same rotation logic as updateTextureViewTransform
|
|
283
|
+
val tabletUpsideDownFix = if (sensorOrientation == 0 && displayRotationDegrees == 90) 180 else 0
|
|
283
284
|
val effectiveRotation = if (sensorOrientation == 0) {
|
|
284
|
-
displayRotationDegrees
|
|
285
|
+
(displayRotationDegrees + tabletUpsideDownFix) % 360
|
|
285
286
|
} else {
|
|
286
|
-
sensorOrientation
|
|
287
|
+
sensorOrientation
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
Log.d(TAG, "[ANALYZE] Sensor: $sensorOrientation°, Display: $displayRotationDegrees°, Effective: $effectiveRotation°")
|
|
@@ -387,18 +388,30 @@ class CameraController(
|
|
|
387
388
|
mlBox: android.graphics.Rect?
|
|
388
389
|
): Rectangle? {
|
|
389
390
|
return try {
|
|
390
|
-
if (
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
val
|
|
395
|
-
val
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
391
|
+
val frameWidth = if (rotation == 90 || rotation == 270) height else width
|
|
392
|
+
val frameHeight = if (rotation == 90 || rotation == 270) width else height
|
|
393
|
+
val frameArea = frameWidth.toLong() * frameHeight.toLong()
|
|
394
|
+
val roiRect = mlBox?.let { box ->
|
|
395
|
+
val boxArea = box.width().toLong() * box.height().toLong()
|
|
396
|
+
val aspect = if (box.height() > 0) box.width().toDouble() / box.height().toDouble() else 0.0
|
|
397
|
+
val isValidSize = boxArea >= (frameArea * 0.08)
|
|
398
|
+
val isValidAspect = aspect in 0.4..2.5
|
|
399
|
+
if (!isValidSize || !isValidAspect) {
|
|
400
|
+
null
|
|
401
|
+
} else {
|
|
402
|
+
val padX = (box.width() * 0.25f).toInt().coerceAtLeast(32)
|
|
403
|
+
val padY = (box.height() * 0.25f).toInt().coerceAtLeast(32)
|
|
404
|
+
android.graphics.Rect(
|
|
405
|
+
(box.left - padX).coerceAtLeast(0),
|
|
406
|
+
(box.top - padY).coerceAtLeast(0),
|
|
407
|
+
(box.right + padX).coerceAtMost(frameWidth),
|
|
408
|
+
(box.bottom + padY).coerceAtMost(frameHeight)
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (roiRect != null) {
|
|
414
|
+
DocumentDetector.detectRectangleInYUVWithRoi(nv21, width, height, rotation, roiRect)
|
|
402
415
|
?: DocumentDetector.detectRectangleInYUV(nv21, width, height, rotation)
|
|
403
416
|
} else {
|
|
404
417
|
DocumentDetector.detectRectangleInYUV(nv21, width, height, rotation)
|
|
@@ -259,7 +259,7 @@ class DocumentDetector {
|
|
|
259
259
|
|
|
260
260
|
var largestRectangle: Rectangle? = null
|
|
261
261
|
var bestScore = 0.0
|
|
262
|
-
val minArea = max(
|
|
262
|
+
val minArea = max(350.0, (srcMat.rows() * srcMat.cols()) * 0.0005)
|
|
263
263
|
|
|
264
264
|
debugStats.contours = contours.size
|
|
265
265
|
|
|
@@ -289,7 +289,7 @@ class DocumentDetector {
|
|
|
289
289
|
val rect = Imgproc.minAreaRect(MatOfPoint2f(*points))
|
|
290
290
|
val rectArea = rect.size.area()
|
|
291
291
|
val rectangularity = if (rectArea > 1.0) contourArea / rectArea else 0.0
|
|
292
|
-
if (rectangularity >= 0.
|
|
292
|
+
if (rectangularity >= 0.5 && isCandidateValid(ordered, srcMat)) {
|
|
293
293
|
debugStats.candidates += 1
|
|
294
294
|
val score = contourArea * rectangularity
|
|
295
295
|
if (score > bestScore) {
|
|
@@ -313,7 +313,7 @@ class DocumentDetector {
|
|
|
313
313
|
val rectArea = rotated.size.area()
|
|
314
314
|
if (rectArea > 1.0) {
|
|
315
315
|
val rectangularity = contourArea / rectArea
|
|
316
|
-
if (rectangularity >= 0.
|
|
316
|
+
if (rectangularity >= 0.5) {
|
|
317
317
|
debugStats.candidates += 1
|
|
318
318
|
val boxPoints = Array(4) { Point() }
|
|
319
319
|
rotated.points(boxPoints)
|
|
@@ -497,16 +497,16 @@ class DocumentDetector {
|
|
|
497
497
|
val rectHeight = max(leftEdgeLength, rightEdgeLength)
|
|
498
498
|
val rectArea = rectWidth * rectHeight
|
|
499
499
|
|
|
500
|
-
// Check if rectangle is too small (less than
|
|
501
|
-
// or too large (more than
|
|
500
|
+
// Check if rectangle is too small (less than 6% of view area)
|
|
501
|
+
// or too large (more than 95% - likely detecting screen instead of document)
|
|
502
502
|
val areaRatio = rectArea / viewArea
|
|
503
|
-
if (areaRatio < 0.
|
|
503
|
+
if (areaRatio < 0.06) {
|
|
504
504
|
if (BuildConfig.DEBUG) {
|
|
505
505
|
Log.d(TAG, "[QUALITY] TOO_FAR (small): area=${String.format("%.1f", rectArea)}, ratio=${String.format("%.2f", areaRatio)}")
|
|
506
506
|
}
|
|
507
507
|
return RectangleQuality.TOO_FAR
|
|
508
508
|
}
|
|
509
|
-
if (areaRatio > 0.
|
|
509
|
+
if (areaRatio > 0.95) {
|
|
510
510
|
if (BuildConfig.DEBUG) {
|
|
511
511
|
Log.d(TAG, "[QUALITY] TOO_FAR (large): area=${String.format("%.1f", rectArea)}, ratio=${String.format("%.2f", areaRatio)} - likely detecting screen")
|
|
512
512
|
}
|
package/dist/DocScanner.js
CHANGED
|
@@ -428,7 +428,7 @@ const VisionCameraScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor =
|
|
|
428
428
|
const overlayIsActive = autoCapture ? isAutoCapturing : (detectedRectangle?.stableCounter ?? 0) > 0;
|
|
429
429
|
return (react_1.default.createElement(react_native_1.View, { style: styles.container, onLayout: handleLayout },
|
|
430
430
|
CameraComponent && device && hasPermission ? (react_1.default.createElement(CameraComponent, { ref: cameraRef, style: styles.scanner, device: device, isActive: true, photo: true, torch: enableTorch ? 'on' : 'off', frameProcessor: frameProcessor, frameProcessorFps: 10 })) : (react_1.default.createElement(react_native_1.View, { style: styles.scanner })),
|
|
431
|
-
showGrid && overlayPolygon && (react_1.default.createElement(overlay_1.ScannerOverlay, { active: overlayIsActive, color: gridColor ?? overlayColor, lineWidth: gridLineWidth, polygon: overlayPolygon, clipRect: detectedRectangle?.previewViewport ?? null })),
|
|
431
|
+
showGrid && overlayPolygon && (react_1.default.createElement(overlay_1.ScannerOverlay, { active: overlayIsActive, color: gridColor ?? overlayColor, lineWidth: gridLineWidth, polygon: overlayPolygon, clipRect: react_native_1.Platform.OS === 'android' ? null : (detectedRectangle?.previewViewport ?? null) })),
|
|
432
432
|
showManualCaptureButton && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: () => captureVision('manual') })),
|
|
433
433
|
children));
|
|
434
434
|
});
|
|
@@ -640,6 +640,7 @@ const NativeScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAU
|
|
|
640
640
|
let rectangleOnScreen = normalizeRectangle(event.rectangleOnScreen ?? null);
|
|
641
641
|
const density = react_native_1.PixelRatio.get();
|
|
642
642
|
if (react_native_1.Platform.OS === 'android' &&
|
|
643
|
+
!rectangleOnScreen &&
|
|
643
644
|
rectangleCoordinates &&
|
|
644
645
|
event.imageSize &&
|
|
645
646
|
event.previewSize &&
|
|
@@ -715,7 +716,7 @@ const NativeScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAU
|
|
|
715
716
|
const detectionThreshold = autoCapture ? minStableFrames : 99999;
|
|
716
717
|
return (react_1.default.createElement(react_native_1.View, { style: styles.container },
|
|
717
718
|
react_1.default.createElement(react_native_document_scanner_1.default, { ref: scannerRef, style: styles.scanner, detectionCountBeforeCapture: detectionThreshold, overlayColor: overlayColor, enableTorch: enableTorch, quality: normalizedQuality, useBase64: useBase64, manualOnly: false, detectionConfig: detectionConfig, onPictureTaken: handlePictureTaken, onError: handleError, onRectangleDetect: handleRectangleDetect }),
|
|
718
|
-
showGrid && overlayPolygon && (react_1.default.createElement(overlay_1.ScannerOverlay, { active: overlayIsActive, color: gridColor ?? overlayColor, lineWidth: gridLineWidth, polygon: overlayPolygon, clipRect: detectedRectangle?.previewViewport ?? null })),
|
|
719
|
+
showGrid && overlayPolygon && (react_1.default.createElement(overlay_1.ScannerOverlay, { active: overlayIsActive, color: gridColor ?? overlayColor, lineWidth: gridLineWidth, polygon: overlayPolygon, clipRect: react_native_1.Platform.OS === 'android' ? null : (detectedRectangle?.previewViewport ?? null) })),
|
|
719
720
|
showManualCaptureButton && (react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.button, onPress: handleManualCapture })),
|
|
720
721
|
children));
|
|
721
722
|
});
|
package/package.json
CHANGED
package/src/DocScanner.tsx
CHANGED
|
@@ -616,7 +616,7 @@ const VisionCameraScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
616
616
|
color={gridColor ?? overlayColor}
|
|
617
617
|
lineWidth={gridLineWidth}
|
|
618
618
|
polygon={overlayPolygon}
|
|
619
|
-
clipRect={detectedRectangle?.previewViewport ?? null}
|
|
619
|
+
clipRect={Platform.OS === 'android' ? null : (detectedRectangle?.previewViewport ?? null)}
|
|
620
620
|
/>
|
|
621
621
|
)}
|
|
622
622
|
{showManualCaptureButton && (
|
|
@@ -896,6 +896,7 @@ const NativeScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
896
896
|
|
|
897
897
|
if (
|
|
898
898
|
Platform.OS === 'android' &&
|
|
899
|
+
!rectangleOnScreen &&
|
|
899
900
|
rectangleCoordinates &&
|
|
900
901
|
event.imageSize &&
|
|
901
902
|
event.previewSize &&
|
|
@@ -1017,7 +1018,7 @@ const NativeScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
1017
1018
|
color={gridColor ?? overlayColor}
|
|
1018
1019
|
lineWidth={gridLineWidth}
|
|
1019
1020
|
polygon={overlayPolygon}
|
|
1020
|
-
clipRect={detectedRectangle?.previewViewport ?? null}
|
|
1021
|
+
clipRect={Platform.OS === 'android' ? null : (detectedRectangle?.previewViewport ?? null)}
|
|
1021
1022
|
/>
|
|
1022
1023
|
)}
|
|
1023
1024
|
{showManualCaptureButton && (
|