react-native-rectangle-doc-scanner 13.0.0 → 13.1.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.
|
@@ -123,6 +123,7 @@ class CameraController(
|
|
|
123
123
|
Log.d(TAG, "[CAMERAX] Setting target rotation to ROTATION_0 (portrait-only app)")
|
|
124
124
|
|
|
125
125
|
preview = Preview.Builder()
|
|
126
|
+
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
|
|
126
127
|
.setTargetRotation(targetRotation) // Force portrait
|
|
127
128
|
.build()
|
|
128
129
|
.also { previewUseCase ->
|
|
@@ -185,7 +186,8 @@ class CameraController(
|
|
|
185
186
|
// ImageAnalysis UseCase for document detection
|
|
186
187
|
imageAnalyzer = ImageAnalysis.Builder()
|
|
187
188
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
188
|
-
|
|
189
|
+
// Match preview aspect ratio to avoid square analysis frames on some devices.
|
|
190
|
+
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
|
|
189
191
|
.setTargetRotation(targetRotation) // Match preview rotation
|
|
190
192
|
.build()
|
|
191
193
|
.also {
|
|
@@ -201,6 +203,7 @@ class CameraController(
|
|
|
201
203
|
// ImageCapture UseCase
|
|
202
204
|
imageCapture = ImageCapture.Builder()
|
|
203
205
|
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
|
|
206
|
+
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
|
|
204
207
|
.setTargetRotation(targetRotation) // Match preview rotation
|
|
205
208
|
.build()
|
|
206
209
|
|
|
@@ -265,10 +268,11 @@ class CameraController(
|
|
|
265
268
|
return
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
|
|
269
271
|
val imageWidth = imageProxy.width
|
|
270
272
|
val imageHeight = imageProxy.height
|
|
271
273
|
|
|
274
|
+
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
|
|
275
|
+
|
|
272
276
|
// Calculate rotation using the same logic as TextureView transform
|
|
273
277
|
val sensorOrientation = getCameraSensorOrientation()
|
|
274
278
|
val displayRotationDegrees = when (textureView.display?.rotation ?: Surface.ROTATION_0) {
|
|
@@ -281,16 +285,16 @@ class CameraController(
|
|
|
281
285
|
|
|
282
286
|
// Use the same rotation logic as updateTextureViewTransform
|
|
283
287
|
val tabletUpsideDownFix = if (sensorOrientation == 0 && displayRotationDegrees == 90) 180 else 0
|
|
284
|
-
val effectiveRotation =
|
|
285
|
-
(displayRotationDegrees + tabletUpsideDownFix) % 360
|
|
286
|
-
} else {
|
|
287
|
-
sensorOrientation
|
|
288
|
-
}
|
|
288
|
+
val effectiveRotation = (displayRotationDegrees + tabletUpsideDownFix) % 360
|
|
289
289
|
|
|
290
|
-
Log.d(
|
|
290
|
+
Log.d(
|
|
291
|
+
TAG,
|
|
292
|
+
"[ANALYZE] Sensor: $sensorOrientation°, Display: $displayRotationDegrees°, " +
|
|
293
|
+
"ImageProxy: $rotationDegrees°, Effective: $effectiveRotation°"
|
|
294
|
+
)
|
|
291
295
|
|
|
292
|
-
// Try ML Kit first
|
|
293
|
-
val inputImage = InputImage.fromMediaImage(mediaImage,
|
|
296
|
+
// Try ML Kit first (use the same rotation as preview/OpenCV)
|
|
297
|
+
val inputImage = InputImage.fromMediaImage(mediaImage, effectiveRotation)
|
|
294
298
|
|
|
295
299
|
objectDetector.process(inputImage)
|
|
296
300
|
.addOnSuccessListener { objects ->
|
package/android/src/camera2/kotlin/com/reactnativerectangledocscanner/DocumentScannerView.kt
CHANGED
|
@@ -67,6 +67,7 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
67
67
|
|
|
68
68
|
companion object {
|
|
69
69
|
private const val TAG = "DocumentScannerView"
|
|
70
|
+
private const val PREVIEW_ASPECT_RATIO = 3f / 4f // width:height (matches 3:4)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
override val lifecycle: Lifecycle
|
|
@@ -111,6 +112,9 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
111
112
|
addView(overlayView, 1) // Add at index 1 (front)
|
|
112
113
|
Log.d(TAG, "[INIT] OverlayView added, childCount: $childCount")
|
|
113
114
|
|
|
115
|
+
// Match camera UI look with letterboxing when preview doesn't fill the view.
|
|
116
|
+
setBackgroundColor(android.graphics.Color.BLACK)
|
|
117
|
+
|
|
114
118
|
Log.d(TAG, "╔════════════════════════════════════════╗")
|
|
115
119
|
Log.d(TAG, "║ DocumentScannerView INIT COMPLETE ║")
|
|
116
120
|
Log.d(TAG, "╚════════════════════════════════════════╝")
|
|
@@ -137,11 +141,35 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
137
141
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
138
142
|
super.onLayout(changed, left, top, right, bottom)
|
|
139
143
|
if (changed) {
|
|
144
|
+
layoutPreviewAndOverlay(right - left, bottom - top)
|
|
140
145
|
Log.d(TAG, "[LAYOUT] View size: ${right - left}x${bottom - top}, PreviewView: ${previewView.width}x${previewView.height}")
|
|
141
146
|
cameraController?.refreshTransform()
|
|
142
147
|
}
|
|
143
148
|
}
|
|
144
149
|
|
|
150
|
+
private fun layoutPreviewAndOverlay(viewWidth: Int, viewHeight: Int) {
|
|
151
|
+
if (viewWidth <= 0 || viewHeight <= 0) return
|
|
152
|
+
|
|
153
|
+
val targetWidth: Int
|
|
154
|
+
val targetHeight: Int
|
|
155
|
+
val aspectHeight = (viewWidth / PREVIEW_ASPECT_RATIO).toInt()
|
|
156
|
+
if (aspectHeight <= viewHeight) {
|
|
157
|
+
targetWidth = viewWidth
|
|
158
|
+
targetHeight = aspectHeight
|
|
159
|
+
} else {
|
|
160
|
+
targetWidth = (viewHeight * PREVIEW_ASPECT_RATIO).toInt()
|
|
161
|
+
targetHeight = viewHeight
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
val left = (viewWidth - targetWidth) / 2
|
|
165
|
+
val top = if (targetHeight < viewHeight) 0 else (viewHeight - targetHeight) / 2
|
|
166
|
+
val right = left + targetWidth
|
|
167
|
+
val bottom = top + targetHeight
|
|
168
|
+
|
|
169
|
+
previewView.layout(left, top, right, bottom)
|
|
170
|
+
overlayView.layout(left, top, right, bottom)
|
|
171
|
+
}
|
|
172
|
+
|
|
145
173
|
private fun initializeCameraWhenReady() {
|
|
146
174
|
// If view is already laid out, start camera immediately
|
|
147
175
|
if (width > 0 && height > 0) {
|
|
@@ -213,17 +241,26 @@ class DocumentScannerView(context: ThemedReactContext) : FrameLayout(context), L
|
|
|
213
241
|
lastDetectedImageHeight = imageHeight
|
|
214
242
|
}
|
|
215
243
|
|
|
216
|
-
val
|
|
244
|
+
val previewWidth = previewView.width
|
|
245
|
+
val previewHeight = previewView.height
|
|
246
|
+
|
|
247
|
+
val rectangleOnScreen = if (rectangle != null && previewWidth > 0 && previewHeight > 0) {
|
|
217
248
|
cameraController?.mapRectangleToView(rectangle, imageWidth, imageHeight)
|
|
218
|
-
?: DocumentDetector.transformRectangleToViewCoordinates(
|
|
249
|
+
?: DocumentDetector.transformRectangleToViewCoordinates(
|
|
250
|
+
rectangle,
|
|
251
|
+
imageWidth,
|
|
252
|
+
imageHeight,
|
|
253
|
+
previewWidth,
|
|
254
|
+
previewHeight
|
|
255
|
+
)
|
|
219
256
|
} else {
|
|
220
257
|
null
|
|
221
258
|
}
|
|
222
|
-
val smoothedRectangleOnScreen = smoothRectangle(rectangleOnScreen,
|
|
259
|
+
val smoothedRectangleOnScreen = smoothRectangle(rectangleOnScreen, previewWidth, previewHeight)
|
|
223
260
|
lastRectangleOnScreen = smoothedRectangleOnScreen
|
|
224
261
|
val quality = when {
|
|
225
|
-
smoothedRectangleOnScreen != null &&
|
|
226
|
-
DocumentDetector.evaluateRectangleQualityInView(smoothedRectangleOnScreen,
|
|
262
|
+
smoothedRectangleOnScreen != null && previewWidth > 0 && previewHeight > 0 ->
|
|
263
|
+
DocumentDetector.evaluateRectangleQualityInView(smoothedRectangleOnScreen, previewWidth, previewHeight)
|
|
227
264
|
rectangle != null -> DocumentDetector.evaluateRectangleQuality(rectangle, imageWidth, imageHeight)
|
|
228
265
|
else -> RectangleQuality.TOO_FAR
|
|
229
266
|
}
|