react-native-rectangle-doc-scanner 3.221.0 → 3.223.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.
|
@@ -15,6 +15,8 @@ import androidx.camera.core.AspectRatio
|
|
|
15
15
|
import androidx.camera.core.Camera
|
|
16
16
|
import androidx.camera.core.CameraSelector
|
|
17
17
|
import androidx.camera.core.ImageAnalysis
|
|
18
|
+
import androidx.camera.core.ImageCapture
|
|
19
|
+
import androidx.camera.core.ImageCaptureException
|
|
18
20
|
import androidx.camera.core.Preview
|
|
19
21
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
|
20
22
|
import androidx.camera.view.PreviewView
|
|
@@ -37,6 +39,7 @@ class CameraController(
|
|
|
37
39
|
private var cameraProvider: ProcessCameraProvider? = null
|
|
38
40
|
private var preview: Preview? = null
|
|
39
41
|
private var imageAnalysis: ImageAnalysis? = null
|
|
42
|
+
private var imageCapture: ImageCapture? = null
|
|
40
43
|
private var camera: Camera? = null
|
|
41
44
|
private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
|
|
42
45
|
private val lastFrame = AtomicReference<LastFrame?>()
|
|
@@ -45,6 +48,18 @@ class CameraController(
|
|
|
45
48
|
private var useFrontCamera = false
|
|
46
49
|
private var detectionEnabled = true
|
|
47
50
|
|
|
51
|
+
// For periodic frame capture
|
|
52
|
+
private var isAnalysisActive = false
|
|
53
|
+
private val analysisHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
|
54
|
+
private val analysisRunnable = object : Runnable {
|
|
55
|
+
override fun run() {
|
|
56
|
+
if (isAnalysisActive && onFrameAnalyzed != null) {
|
|
57
|
+
captureFrameForAnalysis()
|
|
58
|
+
analysisHandler.postDelayed(this, 200) // Capture every 200ms
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
var onFrameAnalyzed: ((Rectangle?, Int, Int) -> Unit)? = null
|
|
49
64
|
|
|
50
65
|
companion object {
|
|
@@ -63,12 +78,12 @@ class CameraController(
|
|
|
63
78
|
useFrontCam: Boolean = false,
|
|
64
79
|
enableDetection: Boolean = true
|
|
65
80
|
) {
|
|
66
|
-
Log.d(TAG, "[CAMERAX] startCamera called")
|
|
81
|
+
Log.d(TAG, "[CAMERAX-V6] startCamera called")
|
|
67
82
|
this.useFrontCamera = useFrontCam
|
|
68
83
|
this.detectionEnabled = enableDetection
|
|
69
84
|
|
|
70
85
|
if (!hasCameraPermission()) {
|
|
71
|
-
Log.e(TAG, "[CAMERAX] Camera permission not granted")
|
|
86
|
+
Log.e(TAG, "[CAMERAX-V6] Camera permission not granted")
|
|
72
87
|
return
|
|
73
88
|
}
|
|
74
89
|
|
|
@@ -81,13 +96,15 @@ class CameraController(
|
|
|
81
96
|
cameraProvider = cameraProviderFuture?.get()
|
|
82
97
|
bindCameraUseCases()
|
|
83
98
|
} catch (e: Exception) {
|
|
84
|
-
Log.e(TAG, "[CAMERAX] Failed to get camera provider", e)
|
|
99
|
+
Log.e(TAG, "[CAMERAX-V6] Failed to get camera provider", e)
|
|
85
100
|
}
|
|
86
101
|
}, ContextCompat.getMainExecutor(context))
|
|
87
102
|
}
|
|
88
103
|
|
|
89
104
|
fun stopCamera() {
|
|
90
|
-
Log.d(TAG, "[CAMERAX] stopCamera called")
|
|
105
|
+
Log.d(TAG, "[CAMERAX-V6] stopCamera called")
|
|
106
|
+
isAnalysisActive = false
|
|
107
|
+
analysisHandler.removeCallbacks(analysisRunnable)
|
|
91
108
|
cameraProvider?.unbindAll()
|
|
92
109
|
analysisBound = false
|
|
93
110
|
}
|
|
@@ -119,10 +136,10 @@ class CameraController(
|
|
|
119
136
|
}
|
|
120
137
|
bitmap.recycle()
|
|
121
138
|
|
|
122
|
-
Log.d(TAG, "[CAMERAX] Photo capture succeeded: ${photoFile.absolutePath}")
|
|
139
|
+
Log.d(TAG, "[CAMERAX-V6] Photo capture succeeded: ${photoFile.absolutePath}")
|
|
123
140
|
onImageCaptured(photoFile)
|
|
124
141
|
} catch (e: Exception) {
|
|
125
|
-
Log.e(TAG, "[CAMERAX] Photo capture failed", e)
|
|
142
|
+
Log.e(TAG, "[CAMERAX-V6] Photo capture failed", e)
|
|
126
143
|
onError(e)
|
|
127
144
|
}
|
|
128
145
|
}
|
|
@@ -154,17 +171,15 @@ class CameraController(
|
|
|
154
171
|
val provider = cameraProvider ?: return
|
|
155
172
|
provider.unbindAll()
|
|
156
173
|
analysisBound = false
|
|
174
|
+
isAnalysisActive = false
|
|
157
175
|
|
|
158
176
|
val rotation = previewView.display?.rotation ?: Surface.ROTATION_0
|
|
159
177
|
|
|
160
|
-
// Build Preview ONLY
|
|
178
|
+
// Build Preview ONLY - this device cannot handle 2 simultaneous surfaces
|
|
161
179
|
preview = Preview.Builder()
|
|
162
180
|
.setTargetResolution(Size(1280, 720))
|
|
163
181
|
.setTargetRotation(rotation)
|
|
164
182
|
.build()
|
|
165
|
-
.also {
|
|
166
|
-
it.setSurfaceProvider(previewView.surfaceProvider)
|
|
167
|
-
}
|
|
168
183
|
|
|
169
184
|
val cameraSelector = if (useFrontCamera) {
|
|
170
185
|
CameraSelector.DEFAULT_FRONT_CAMERA
|
|
@@ -172,119 +187,128 @@ class CameraController(
|
|
|
172
187
|
CameraSelector.DEFAULT_BACK_CAMERA
|
|
173
188
|
}
|
|
174
189
|
|
|
175
|
-
//
|
|
190
|
+
// Bind Preview ONLY first
|
|
176
191
|
try {
|
|
177
192
|
camera = provider.bindToLifecycle(
|
|
178
193
|
lifecycleOwner,
|
|
179
194
|
cameraSelector,
|
|
180
195
|
preview
|
|
181
196
|
)
|
|
182
|
-
Log.d(TAG, "[CAMERAX-DEBUG] Preview ONLY bound successfully - NO ImageAnalysis")
|
|
183
|
-
analysisBound = false
|
|
184
197
|
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
// Set surface provider AFTER binding
|
|
199
|
+
preview?.setSurfaceProvider(previewView.surfaceProvider)
|
|
200
|
+
|
|
201
|
+
Log.d(TAG, "[CAMERAX-V7] Preview ONLY bound successfully")
|
|
202
|
+
|
|
203
|
+
// Wait for preview to stabilize, then try adding ImageCapture
|
|
204
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
205
|
+
tryAddImageCapture(provider, cameraSelector, rotation)
|
|
206
|
+
}, 3000)
|
|
190
207
|
|
|
191
208
|
} catch (e: Exception) {
|
|
192
|
-
Log.e(TAG, "[CAMERAX-
|
|
193
|
-
analysisBound = false
|
|
209
|
+
Log.e(TAG, "[CAMERAX-V7] Failed to bind preview", e)
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
212
|
|
|
197
|
-
private fun
|
|
198
|
-
|
|
213
|
+
private fun tryAddImageCapture(provider: ProcessCameraProvider, cameraSelector: CameraSelector, rotation: Int) {
|
|
214
|
+
Log.d(TAG, "[CAMERAX-V7] Attempting to add ImageCapture...")
|
|
199
215
|
|
|
200
|
-
|
|
216
|
+
// Build ImageCapture with minimal resolution
|
|
217
|
+
imageCapture = ImageCapture.Builder()
|
|
218
|
+
.setTargetResolution(Size(320, 240))
|
|
219
|
+
.setTargetRotation(rotation)
|
|
220
|
+
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
|
221
|
+
.build()
|
|
201
222
|
|
|
202
223
|
try {
|
|
203
|
-
//
|
|
204
|
-
imageAnalysis = ImageAnalysis.Builder()
|
|
205
|
-
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
206
|
-
.setTargetRotation(rotation)
|
|
207
|
-
.setTargetResolution(Size(480, 360))
|
|
208
|
-
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
|
|
209
|
-
.build()
|
|
210
|
-
.also {
|
|
211
|
-
it.setAnalyzer(cameraExecutor, DocumentAnalyzer())
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// IMPORTANT: Unbind all first, then rebind together to avoid session reconfiguration timeout
|
|
224
|
+
// Unbind and rebind with both
|
|
215
225
|
provider.unbindAll()
|
|
216
226
|
|
|
217
|
-
// Rebind BOTH at the same time
|
|
218
227
|
camera = provider.bindToLifecycle(
|
|
219
228
|
lifecycleOwner,
|
|
220
229
|
cameraSelector,
|
|
221
230
|
preview,
|
|
222
|
-
|
|
231
|
+
imageCapture
|
|
223
232
|
)
|
|
224
|
-
|
|
225
|
-
|
|
233
|
+
|
|
234
|
+
preview?.setSurfaceProvider(previewView.surfaceProvider)
|
|
235
|
+
|
|
236
|
+
Log.d(TAG, "[CAMERAX-V7] ImageCapture added successfully")
|
|
237
|
+
|
|
238
|
+
// Start periodic frame capture
|
|
239
|
+
if (detectionEnabled) {
|
|
240
|
+
isAnalysisActive = true
|
|
241
|
+
analysisHandler.postDelayed(analysisRunnable, 500)
|
|
242
|
+
Log.d(TAG, "[CAMERAX-V7] Started periodic frame capture")
|
|
243
|
+
}
|
|
244
|
+
|
|
226
245
|
} catch (e: Exception) {
|
|
227
|
-
Log.e(TAG, "[CAMERAX-
|
|
228
|
-
analysisBound = false
|
|
246
|
+
Log.e(TAG, "[CAMERAX-V7] Failed to add ImageCapture, keeping Preview only", e)
|
|
229
247
|
|
|
230
|
-
// Fallback:
|
|
248
|
+
// Fallback: Keep preview only and disable detection
|
|
231
249
|
try {
|
|
232
250
|
camera = provider.bindToLifecycle(
|
|
233
251
|
lifecycleOwner,
|
|
234
252
|
cameraSelector,
|
|
235
253
|
preview
|
|
236
254
|
)
|
|
237
|
-
|
|
255
|
+
preview?.setSurfaceProvider(previewView.surfaceProvider)
|
|
256
|
+
Log.d(TAG, "[CAMERAX-V7] Fallback: Preview only mode (detection disabled)")
|
|
238
257
|
} catch (fallbackException: Exception) {
|
|
239
|
-
Log.e(TAG, "[CAMERAX-
|
|
258
|
+
Log.e(TAG, "[CAMERAX-V7] Fallback failed", fallbackException)
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
261
|
}
|
|
243
262
|
|
|
244
|
-
private
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
private fun captureFrameForAnalysis() {
|
|
264
|
+
val capture = imageCapture ?: return
|
|
265
|
+
|
|
266
|
+
capture.takePicture(cameraExecutor, object : ImageCapture.OnImageCapturedCallback() {
|
|
267
|
+
override fun onCaptureSuccess(image: androidx.camera.core.ImageProxy) {
|
|
268
|
+
try {
|
|
269
|
+
val rotationDegrees = image.imageInfo.rotationDegrees
|
|
270
|
+
val nv21 = image.toNv21()
|
|
271
|
+
|
|
272
|
+
lastFrame.set(
|
|
273
|
+
LastFrame(
|
|
274
|
+
nv21,
|
|
275
|
+
image.width,
|
|
276
|
+
image.height,
|
|
277
|
+
rotationDegrees,
|
|
278
|
+
useFrontCamera
|
|
279
|
+
)
|
|
256
280
|
)
|
|
257
|
-
)
|
|
258
281
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
282
|
+
val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
283
|
+
image.height
|
|
284
|
+
} else {
|
|
285
|
+
image.width
|
|
286
|
+
}
|
|
264
287
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
289
|
+
image.width
|
|
290
|
+
} else {
|
|
291
|
+
image.height
|
|
292
|
+
}
|
|
270
293
|
|
|
271
|
-
if (detectionEnabled) {
|
|
272
294
|
val rectangle = DocumentDetector.detectRectangleInYUV(
|
|
273
295
|
nv21,
|
|
274
|
-
|
|
275
|
-
|
|
296
|
+
image.width,
|
|
297
|
+
image.height,
|
|
276
298
|
rotationDegrees
|
|
277
299
|
)
|
|
278
300
|
onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
|
|
279
|
-
}
|
|
280
|
-
|
|
301
|
+
} catch (e: Exception) {
|
|
302
|
+
Log.e(TAG, "[CAMERAX-V6] Error analyzing frame", e)
|
|
303
|
+
} finally {
|
|
304
|
+
image.close()
|
|
281
305
|
}
|
|
282
|
-
} catch (e: Exception) {
|
|
283
|
-
Log.e(TAG, "[CAMERAX] Error analyzing frame", e)
|
|
284
|
-
} finally {
|
|
285
|
-
imageProxy.close()
|
|
286
306
|
}
|
|
287
|
-
|
|
307
|
+
|
|
308
|
+
override fun onError(exception: ImageCaptureException) {
|
|
309
|
+
Log.e(TAG, "[CAMERAX-V6] Frame capture for analysis failed", exception)
|
|
310
|
+
}
|
|
311
|
+
})
|
|
288
312
|
}
|
|
289
313
|
|
|
290
314
|
private fun nv21ToJpeg(nv21: ByteArray, width: Int, height: Int, quality: Int): ByteArray {
|
package/package.json
CHANGED