react-native-rectangle-doc-scanner 3.220.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,123 +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-FIX-V5] Preview bound, waiting 2 seconds before adding analysis...")
|
|
183
|
-
|
|
184
|
-
// Step 2: Add ImageAnalysis after a longer delay to let Preview session fully stabilize
|
|
185
|
-
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
186
|
-
bindImageAnalysis(provider, cameraSelector, rotation)
|
|
187
|
-
}, 2000)
|
|
188
|
-
|
|
189
|
-
} catch (e: Exception) {
|
|
190
|
-
Log.e(TAG, "[CAMERAX-FIX-V5] Failed to bind preview", e)
|
|
191
|
-
analysisBound = false
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
private fun bindImageAnalysis(provider: ProcessCameraProvider, cameraSelector: CameraSelector, rotation: Int) {
|
|
196
|
-
if (analysisBound) return
|
|
197
|
-
|
|
198
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] Starting to add ImageAnalysis...")
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
// Build ImageAnalysis with very low resolution
|
|
202
|
-
imageAnalysis = ImageAnalysis.Builder()
|
|
203
|
-
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
204
|
-
.setTargetRotation(rotation)
|
|
205
|
-
.setTargetResolution(Size(480, 360))
|
|
206
|
-
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
|
|
207
|
-
.build()
|
|
208
|
-
.also {
|
|
209
|
-
it.setAnalyzer(cameraExecutor, DocumentAnalyzer())
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// IMPORTANT: Unbind all first, then rebind together to avoid session reconfiguration timeout
|
|
213
|
-
provider.unbindAll()
|
|
214
|
-
|
|
215
|
-
// Rebind BOTH at the same time
|
|
216
202
|
camera = provider.bindToLifecycle(
|
|
217
203
|
lifecycleOwner,
|
|
218
204
|
cameraSelector,
|
|
219
205
|
preview,
|
|
220
|
-
|
|
206
|
+
imageCapture
|
|
221
207
|
)
|
|
222
|
-
|
|
223
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] ImageAnalysis added successfully after unbind/rebind")
|
|
224
|
-
} catch (e: Exception) {
|
|
225
|
-
Log.e(TAG, "[CAMERAX-FIX-V5] Failed to add ImageAnalysis", e)
|
|
226
|
-
analysisBound = false
|
|
208
|
+
Log.d(TAG, "[CAMERAX-V6] Preview + ImageCapture bound successfully")
|
|
227
209
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
preview
|
|
234
|
-
)
|
|
235
|
-
Log.d(TAG, "[CAMERAX-FIX-V5] Fallback: Preview only")
|
|
236
|
-
} catch (fallbackException: Exception) {
|
|
237
|
-
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")
|
|
238
215
|
}
|
|
216
|
+
|
|
217
|
+
} catch (e: Exception) {
|
|
218
|
+
Log.e(TAG, "[CAMERAX-V6] Failed to bind camera use cases", e)
|
|
239
219
|
}
|
|
240
220
|
}
|
|
241
221
|
|
|
242
|
-
private
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
)
|
|
254
239
|
)
|
|
255
|
-
)
|
|
256
240
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
241
|
+
val frameWidth = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
242
|
+
image.height
|
|
243
|
+
} else {
|
|
244
|
+
image.width
|
|
245
|
+
}
|
|
262
246
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
247
|
+
val frameHeight = if (rotationDegrees == 90 || rotationDegrees == 270) {
|
|
248
|
+
image.width
|
|
249
|
+
} else {
|
|
250
|
+
image.height
|
|
251
|
+
}
|
|
268
252
|
|
|
269
|
-
if (detectionEnabled) {
|
|
270
253
|
val rectangle = DocumentDetector.detectRectangleInYUV(
|
|
271
254
|
nv21,
|
|
272
|
-
|
|
273
|
-
|
|
255
|
+
image.width,
|
|
256
|
+
image.height,
|
|
274
257
|
rotationDegrees
|
|
275
258
|
)
|
|
276
259
|
onFrameAnalyzed?.invoke(rectangle, frameWidth, frameHeight)
|
|
277
|
-
}
|
|
278
|
-
|
|
260
|
+
} catch (e: Exception) {
|
|
261
|
+
Log.e(TAG, "[CAMERAX-V6] Error analyzing frame", e)
|
|
262
|
+
} finally {
|
|
263
|
+
image.close()
|
|
279
264
|
}
|
|
280
|
-
} catch (e: Exception) {
|
|
281
|
-
Log.e(TAG, "[CAMERAX] Error analyzing frame", e)
|
|
282
|
-
} finally {
|
|
283
|
-
imageProxy.close()
|
|
284
265
|
}
|
|
285
|
-
|
|
266
|
+
|
|
267
|
+
override fun onError(exception: ImageCaptureException) {
|
|
268
|
+
Log.e(TAG, "[CAMERAX-V6] Frame capture for analysis failed", exception)
|
|
269
|
+
}
|
|
270
|
+
})
|
|
286
271
|
}
|
|
287
272
|
|
|
288
273
|
private fun nv21ToJpeg(nv21: ByteArray, width: Int, height: Int, quality: Int): ByteArray {
|
package/package.json
CHANGED