react-native-rectangle-doc-scanner 3.221.0 → 3.222.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,10 +171,11 @@ 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
|
|
178
|
+
// Build Preview with lower resolution
|
|
161
179
|
preview = Preview.Builder()
|
|
162
180
|
.setTargetResolution(Size(1280, 720))
|
|
163
181
|
.setTargetRotation(rotation)
|
|
@@ -166,125 +184,90 @@ class CameraController(
|
|
|
166
184
|
it.setSurfaceProvider(previewView.surfaceProvider)
|
|
167
185
|
}
|
|
168
186
|
|
|
187
|
+
// Build ImageCapture for periodic frame capture (instead of ImageAnalysis)
|
|
188
|
+
imageCapture = ImageCapture.Builder()
|
|
189
|
+
.setTargetResolution(Size(640, 480))
|
|
190
|
+
.setTargetRotation(rotation)
|
|
191
|
+
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
|
192
|
+
.build()
|
|
193
|
+
|
|
169
194
|
val cameraSelector = if (useFrontCamera) {
|
|
170
195
|
CameraSelector.DEFAULT_FRONT_CAMERA
|
|
171
196
|
} else {
|
|
172
197
|
CameraSelector.DEFAULT_BACK_CAMERA
|
|
173
198
|
}
|
|
174
199
|
|
|
175
|
-
//
|
|
200
|
+
// Bind Preview and ImageCapture together
|
|
176
201
|
try {
|
|
177
|
-
camera = provider.bindToLifecycle(
|
|
178
|
-
lifecycleOwner,
|
|
179
|
-
cameraSelector,
|
|
180
|
-
preview
|
|
181
|
-
)
|
|
182
|
-
Log.d(TAG, "[CAMERAX-DEBUG] Preview ONLY bound successfully - NO ImageAnalysis")
|
|
183
|
-
analysisBound = false
|
|
184
|
-
|
|
185
|
-
// TODO: If Preview works, we need to find alternative for document detection
|
|
186
|
-
// Options:
|
|
187
|
-
// 1. Use ImageCapture instead of ImageAnalysis
|
|
188
|
-
// 2. Use lower resolution for ImageAnalysis (already tried)
|
|
189
|
-
// 3. Use Camera2 API directly instead of CameraX
|
|
190
|
-
|
|
191
|
-
} catch (e: Exception) {
|
|
192
|
-
Log.e(TAG, "[CAMERAX-DEBUG] Failed to bind preview", e)
|
|
193
|
-
analysisBound = false
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private fun bindImageAnalysis(provider: ProcessCameraProvider, cameraSelector: CameraSelector, rotation: Int) {
|
|
198
|
-
if (analysisBound) return
|
|
199
|
-
|
|
200
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] Starting to add ImageAnalysis...")
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
// Build ImageAnalysis with very low resolution
|
|
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
|
|
215
|
-
provider.unbindAll()
|
|
216
|
-
|
|
217
|
-
// Rebind BOTH at the same time
|
|
218
202
|
camera = provider.bindToLifecycle(
|
|
219
203
|
lifecycleOwner,
|
|
220
204
|
cameraSelector,
|
|
221
205
|
preview,
|
|
222
|
-
|
|
206
|
+
imageCapture
|
|
223
207
|
)
|
|
224
|
-
|
|
225
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] ImageAnalysis added successfully after unbind/rebind")
|
|
226
|
-
} catch (e: Exception) {
|
|
227
|
-
Log.e(TAG, "[CAMERAX-FIX-V5] Failed to add ImageAnalysis", e)
|
|
228
|
-
analysisBound = false
|
|
208
|
+
Log.d(TAG, "[CAMERAX-V6] Preview + ImageCapture bound successfully")
|
|
229
209
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
preview
|
|
236
|
-
)
|
|
237
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] Fallback: Preview only")
|
|
238
|
-
} catch (fallbackException: Exception) {
|
|
239
|
-
Log.e(TAG, "[CAMERAX-FIX-V5] Fallback also failed", fallbackException)
|
|
210
|
+
// Start periodic frame capture for analysis
|
|
211
|
+
if (detectionEnabled) {
|
|
212
|
+
isAnalysisActive = true
|
|
213
|
+
analysisHandler.postDelayed(analysisRunnable, 500) // Start after 500ms
|
|
214
|
+
Log.d(TAG, "[CAMERAX-V6] Started periodic frame capture for analysis")
|
|
240
215
|
}
|
|
216
|
+
|
|
217
|
+
} catch (e: Exception) {
|
|
218
|
+
Log.e(TAG, "[CAMERAX-V6] Failed to bind camera use cases", e)
|
|
241
219
|
}
|
|
242
220
|
}
|
|
243
221
|
|
|
244
|
-
private
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
222
|
+
private fun captureFrameForAnalysis() {
|
|
223
|
+
val capture = imageCapture ?: return
|
|
224
|
+
|
|
225
|
+
capture.takePicture(cameraExecutor, object : ImageCapture.OnImageCapturedCallback() {
|
|
226
|
+
override fun onCaptureSuccess(image: androidx.camera.core.ImageProxy) {
|
|
227
|
+
try {
|
|
228
|
+
val rotationDegrees = image.imageInfo.rotationDegrees
|
|
229
|
+
val nv21 = image.toNv21()
|
|
230
|
+
|
|
231
|
+
lastFrame.set(
|
|
232
|
+
LastFrame(
|
|
233
|
+
nv21,
|
|
234
|
+
image.width,
|
|
235
|
+
image.height,
|
|
236
|
+
rotationDegrees,
|
|
237
|
+
useFrontCamera
|
|
238
|
+
)
|
|
256
239
|
)
|
|
257
|
-
)
|
|
258
240
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
241
|
+
val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
242
|
+
image.height
|
|
243
|
+
} else {
|
|
244
|
+
image.width
|
|
245
|
+
}
|
|
264
246
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
247
|
+
val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
248
|
+
image.width
|
|
249
|
+
} else {
|
|
250
|
+
image.height
|
|
251
|
+
}
|
|
270
252
|
|
|
271
|
-
if (detectionEnabled) {
|
|
272
253
|
val rectangle = DocumentDetector.detectRectangleInYUV(
|
|
273
254
|
nv21,
|
|
274
|
-
|
|
275
|
-
|
|
255
|
+
image.width,
|
|
256
|
+
image.height,
|
|
276
257
|
rotationDegrees
|
|
277
258
|
)
|
|
278
259
|
onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
|
|
279
|
-
}
|
|
280
|
-
|
|
260
|
+
} catch (e: Exception) {
|
|
261
|
+
Log.e(TAG, "[CAMERAX-V6] Error analyzing frame", e)
|
|
262
|
+
} finally {
|
|
263
|
+
image.close()
|
|
281
264
|
}
|
|
282
|
-
} catch (e: Exception) {
|
|
283
|
-
Log.e(TAG, "[CAMERAX] Error analyzing frame", e)
|
|
284
|
-
} finally {
|
|
285
|
-
imageProxy.close()
|
|
286
265
|
}
|
|
287
|
-
|
|
266
|
+
|
|
267
|
+
override fun onError(exception: ImageCaptureException) {
|
|
268
|
+
Log.e(TAG, "[CAMERAX-V6] Frame capture for analysis failed", exception)
|
|
269
|
+
}
|
|
270
|
+
})
|
|
288
271
|
}
|
|
289
272
|
|
|
290
273
|
private fun nv21ToJpeg(nv21: ByteArray, width: Int, height: Int, quality: Int): ByteArray {
|
package/package.json
CHANGED