capacitor-plugin-camera-forked 3.0.114 → 3.1.120
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.
- package/README.md +4 -4
- package/android/build.gradle +10 -0
- package/android/src/main/AndroidManifest.xml +7 -0
- package/android/src/main/assets/blur_detection_model.tflite +0 -0
- package/android/src/main/java/com/tonyxlh/capacitor/camera/BlurDetectionHelper.java +151 -10
- package/android/src/main/java/com/tonyxlh/capacitor/camera/CameraPreviewPlugin.java +61 -26
- package/android/src/main/java/com/tonyxlh/capacitor/camera/TextRecognitionBlurHelper.java +367 -0
- package/dist/docs.json +4 -4
- package/dist/esm/definitions.d.ts +3 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +2 -1
- package/dist/esm/web.js +2 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +2 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +2 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BlurDetectionHelper.swift +155 -20
- package/ios/Plugin/CameraPreviewPlugin.swift +39 -14
- package/ios/Plugin/TextRecognitionBlurHelper.swift +336 -0
- package/ios/Plugin/blur_detection_model.tflite +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -290,7 +290,7 @@ stopCamera() => Promise<void>
|
|
|
290
290
|
### takeSnapshot(...)
|
|
291
291
|
|
|
292
292
|
```typescript
|
|
293
|
-
takeSnapshot(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string;
|
|
293
|
+
takeSnapshot(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string; confidence?: number; boundingBoxes?: number[][]; }>
|
|
294
294
|
```
|
|
295
295
|
|
|
296
296
|
take a snapshot as base64.
|
|
@@ -299,7 +299,7 @@ take a snapshot as base64.
|
|
|
299
299
|
| ------------- | ------------------------------------------------------- |
|
|
300
300
|
| **`options`** | <code>{ quality?: number; checkBlur?: boolean; }</code> |
|
|
301
301
|
|
|
302
|
-
**Returns:** <code>Promise<{ base64: string;
|
|
302
|
+
**Returns:** <code>Promise<{ base64: string; confidence?: number; boundingBoxes?: number[][]; }></code>
|
|
303
303
|
|
|
304
304
|
--------------------
|
|
305
305
|
|
|
@@ -354,14 +354,14 @@ take a snapshot on to a canvas. Web Only
|
|
|
354
354
|
### takePhoto(...)
|
|
355
355
|
|
|
356
356
|
```typescript
|
|
357
|
-
takePhoto(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob;
|
|
357
|
+
takePhoto(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob; confidence?: number; }>
|
|
358
358
|
```
|
|
359
359
|
|
|
360
360
|
| Param | Type |
|
|
361
361
|
| ------------- | -------------------------------------------------------------- |
|
|
362
362
|
| **`options`** | <code>{ pathToSave?: string; includeBase64?: boolean; }</code> |
|
|
363
363
|
|
|
364
|
-
**Returns:** <code>Promise<{ path?: string; base64?: string; blob?: any;
|
|
364
|
+
**Returns:** <code>Promise<{ path?: string; base64?: string; blob?: any; confidence?: number; }></code>
|
|
365
365
|
|
|
366
366
|
--------------------
|
|
367
367
|
|
package/android/build.gradle
CHANGED
|
@@ -47,6 +47,13 @@ repositories {
|
|
|
47
47
|
mavenCentral()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
allprojects {
|
|
51
|
+
repositories {
|
|
52
|
+
google()
|
|
53
|
+
mavenCentral()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
|
|
51
58
|
dependencies {
|
|
52
59
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
@@ -68,4 +75,7 @@ dependencies {
|
|
|
68
75
|
implementation 'org.tensorflow:tensorflow-lite:2.14.0'
|
|
69
76
|
implementation 'org.tensorflow:tensorflow-lite-support:0.4.4'
|
|
70
77
|
implementation 'org.tensorflow:tensorflow-lite-gpu:2.14.0'
|
|
78
|
+
|
|
79
|
+
// Google ML Kit dependencies for text recognition
|
|
80
|
+
implementation 'com.google.mlkit:text-recognition:16.0.1'
|
|
71
81
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
2
|
|
|
3
3
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
4
|
+
|
|
5
|
+
<application>
|
|
6
|
+
<!-- ML Kit Text Recognition model download -->
|
|
7
|
+
<meta-data
|
|
8
|
+
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
|
9
|
+
android:value="ocr" />
|
|
10
|
+
</application>
|
|
4
11
|
</manifest>
|
|
File without changes
|
|
@@ -39,6 +39,10 @@ public class BlurDetectionHelper {
|
|
|
39
39
|
private TensorImage inputImageBuffer;
|
|
40
40
|
private TensorBuffer outputProbabilityBuffer;
|
|
41
41
|
private boolean isInitialized = false;
|
|
42
|
+
|
|
43
|
+
// Text recognition blur detection helper
|
|
44
|
+
private TextRecognitionBlurHelper textBlurHelper;
|
|
45
|
+
private boolean useTextRecognition = true;
|
|
42
46
|
|
|
43
47
|
|
|
44
48
|
public BlurDetectionHelper() {
|
|
@@ -48,6 +52,9 @@ public class BlurDetectionHelper {
|
|
|
48
52
|
.add(new ResizeWithCropOrPadOp(INPUT_SIZE, INPUT_SIZE))
|
|
49
53
|
.add(new ResizeOp(INPUT_SIZE, INPUT_SIZE, ResizeOp.ResizeMethod.BILINEAR))
|
|
50
54
|
.build();
|
|
55
|
+
|
|
56
|
+
// Initialize text recognition blur helper
|
|
57
|
+
textBlurHelper = new TextRecognitionBlurHelper();
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/**
|
|
@@ -81,6 +88,9 @@ public class BlurDetectionHelper {
|
|
|
81
88
|
tflite.getOutputTensor(0).dataType()
|
|
82
89
|
);
|
|
83
90
|
|
|
91
|
+
// Initialize text recognition helper
|
|
92
|
+
textBlurHelper.initialize(context);
|
|
93
|
+
|
|
84
94
|
// Update INPUT_SIZE based on actual model input shape
|
|
85
95
|
int[] inputShape = tflite.getInputTensor(0).shape();
|
|
86
96
|
|
|
@@ -113,15 +123,51 @@ public class BlurDetectionHelper {
|
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
/**
|
|
116
|
-
* Detect blur in image using TFLite
|
|
126
|
+
* Detect blur in image using hybrid approach (Text Recognition + TFLite)
|
|
117
127
|
* @param bitmap Input image bitmap
|
|
118
128
|
* @return Blur confidence score (0.0 = sharp, 1.0 = very blurry)
|
|
119
129
|
*/
|
|
120
130
|
public double detectBlur(Bitmap bitmap) {
|
|
131
|
+
// First try text recognition if enabled
|
|
132
|
+
if (useTextRecognition && textBlurHelper != null && textBlurHelper.isInitialized()) {
|
|
133
|
+
try {
|
|
134
|
+
java.util.Map<String, Object> textResult = textBlurHelper.detectBlurWithConfidence(bitmap);
|
|
135
|
+
Boolean hasText = (Boolean) textResult.get("hasText");
|
|
136
|
+
|
|
137
|
+
if (hasText != null && hasText) {
|
|
138
|
+
// Image contains text, use text recognition result
|
|
139
|
+
Boolean isBlur = (Boolean) textResult.get("isBlur");
|
|
140
|
+
Double textConfidence = (Double) textResult.get("textConfidence");
|
|
141
|
+
Integer wordCount = (Integer) textResult.get("wordCount");
|
|
142
|
+
Integer readableWords = (Integer) textResult.get("readableWords");
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if (isBlur != null) {
|
|
146
|
+
double blurConfidence = isBlur ? 1.0 : 0.0;
|
|
147
|
+
return blurConfidence;
|
|
148
|
+
} else {
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
}
|
|
152
|
+
} catch (Exception e) {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fallback to TFLite model
|
|
157
|
+
return detectBlurWithTFLite(bitmap);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Detect blur in image using TFLite model only
|
|
162
|
+
* @param bitmap Input image bitmap
|
|
163
|
+
* @return Blur confidence score (0.0 = sharp, 1.0 = very blurry)
|
|
164
|
+
*/
|
|
165
|
+
public double detectBlurWithTFLite(Bitmap bitmap) {
|
|
121
166
|
if (!isInitialized || tflite == null) {
|
|
122
|
-
|
|
123
|
-
return
|
|
167
|
+
double laplacianScore = calculateLaplacianBlurScore(bitmap);
|
|
168
|
+
return laplacianScore;
|
|
124
169
|
}
|
|
170
|
+
|
|
125
171
|
|
|
126
172
|
try {
|
|
127
173
|
// Use the original bitmap directly (no image enhancement)
|
|
@@ -158,11 +204,11 @@ public class BlurDetectionHelper {
|
|
|
158
204
|
// Determine if image is blurry using TFLite confidence
|
|
159
205
|
boolean isBlur = (blurConfidence >= 0.99 || sharpConfidence < 0.1);
|
|
160
206
|
|
|
207
|
+
|
|
161
208
|
// Return 1.0 for blur, 0.0 for sharp (to maintain double return type)
|
|
162
209
|
return isBlur ? 1.0 : 0.0;
|
|
163
210
|
|
|
164
211
|
} catch (Exception e) {
|
|
165
|
-
Log.e(TAG, "Error during TFLite inference: " + e.getMessage(), e);
|
|
166
212
|
// Fallback to Laplacian algorithm
|
|
167
213
|
double laplacianScore = calculateLaplacianBlurScore(bitmap);
|
|
168
214
|
boolean isBlur = laplacianScore < 150;
|
|
@@ -294,24 +340,82 @@ public class BlurDetectionHelper {
|
|
|
294
340
|
}
|
|
295
341
|
|
|
296
342
|
/**
|
|
297
|
-
* Detect blur with detailed confidence scores
|
|
343
|
+
* Detect blur with detailed confidence scores using hybrid approach
|
|
298
344
|
* @param bitmap Input image bitmap
|
|
299
|
-
* @return Map with
|
|
345
|
+
* @return Map with comprehensive blur detection results
|
|
300
346
|
*/
|
|
301
347
|
public java.util.Map<String, Object> detectBlurWithConfidence(Bitmap bitmap) {
|
|
302
348
|
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
|
303
349
|
|
|
350
|
+
// Try text recognition first if enabled
|
|
351
|
+
if (useTextRecognition && textBlurHelper != null && textBlurHelper.isInitialized()) {
|
|
352
|
+
try {
|
|
353
|
+
java.util.Map<String, Object> textResult = textBlurHelper.detectBlurWithConfidence(bitmap);
|
|
354
|
+
Boolean hasText = (Boolean) textResult.get("hasText");
|
|
355
|
+
|
|
356
|
+
if (hasText != null && hasText) {
|
|
357
|
+
// Image contains text, use text recognition result
|
|
358
|
+
Boolean isBlur = (Boolean) textResult.get("isBlur");
|
|
359
|
+
Double textConfidence = (Double) textResult.get("textConfidence");
|
|
360
|
+
|
|
361
|
+
result.put("method", "text_recognition");
|
|
362
|
+
result.put("isBlur", isBlur);
|
|
363
|
+
result.put("textConfidence", textConfidence);
|
|
364
|
+
result.put("wordCount", textResult.get("wordCount"));
|
|
365
|
+
result.put("readableWords", textResult.get("readableWords"));
|
|
366
|
+
result.put("hasText", true);
|
|
367
|
+
result.put("boundingBoxes", textResult.get("boundingBoxes"));
|
|
368
|
+
|
|
369
|
+
// Set blur/sharp confidence based on text recognition result
|
|
370
|
+
if (isBlur != null && textConfidence != null) {
|
|
371
|
+
if (isBlur) {
|
|
372
|
+
// Image is blurry - high blur confidence, low sharp confidence
|
|
373
|
+
result.put("blurConfidence", 1.0);
|
|
374
|
+
result.put("sharpConfidence", 0.0);
|
|
375
|
+
} else {
|
|
376
|
+
// Image is sharp - low blur confidence, high sharp confidence
|
|
377
|
+
result.put("blurConfidence", 1.0 - textConfidence);
|
|
378
|
+
result.put("sharpConfidence", textConfidence);
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
// Default values if confidence not available
|
|
382
|
+
result.put("blurConfidence", isBlur != null && isBlur ? 1.0 : 0.0);
|
|
383
|
+
result.put("sharpConfidence", isBlur != null && !isBlur ? 1.0 : 0.0);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
} catch (Exception e) {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Fallback to TFLite model
|
|
394
|
+
return detectBlurWithTFLiteConfidence(bitmap);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Detect blur with detailed confidence scores using TFLite only
|
|
399
|
+
* @param bitmap Input image bitmap
|
|
400
|
+
* @return Map with isBlur, blurConfidence, and sharpConfidence
|
|
401
|
+
*/
|
|
402
|
+
public java.util.Map<String, Object> detectBlurWithTFLiteConfidence(Bitmap bitmap) {
|
|
403
|
+
java.util.Map<String, Object> result = new java.util.HashMap<>();
|
|
404
|
+
|
|
304
405
|
if (!isInitialized || tflite == null) {
|
|
305
|
-
Log.w(TAG, "TFLite model not initialized, falling back to Laplacian");
|
|
306
406
|
double laplacianScore = calculateLaplacianBlurScore(bitmap);
|
|
307
407
|
boolean isBlur = laplacianScore < 150;
|
|
308
408
|
double normalizedScore = Math.max(0.0, Math.min(1.0, laplacianScore / 300.0));
|
|
309
409
|
double sharpConfidence = normalizedScore;
|
|
310
410
|
double blurConfidence = 1.0 - normalizedScore;
|
|
311
411
|
|
|
412
|
+
result.put("method", "laplacian");
|
|
312
413
|
result.put("isBlur", isBlur);
|
|
313
414
|
result.put("blurConfidence", blurConfidence);
|
|
314
415
|
result.put("sharpConfidence", sharpConfidence);
|
|
416
|
+
result.put("laplacianScore", laplacianScore);
|
|
417
|
+
result.put("hasText", false);
|
|
418
|
+
result.put("boundingBoxes", new java.util.ArrayList<>());
|
|
315
419
|
return result;
|
|
316
420
|
}
|
|
317
421
|
|
|
@@ -350,16 +454,14 @@ public class BlurDetectionHelper {
|
|
|
350
454
|
// Determine if image is blurry using TFLite confidence
|
|
351
455
|
boolean isBlur = (blurConfidence >= 0.99 || sharpConfidence < 0.1);
|
|
352
456
|
|
|
353
|
-
Log.d(TAG, String.format("TFLite Blur Detection with Confidence - Blur: %.6f, Sharp: %.6f, Label: %s",
|
|
354
|
-
blurConfidence, sharpConfidence, isBlur ? "blur" : "sharp"));
|
|
355
457
|
|
|
356
458
|
result.put("isBlur", isBlur);
|
|
357
459
|
result.put("blurConfidence", blurConfidence);
|
|
358
460
|
result.put("sharpConfidence", sharpConfidence);
|
|
461
|
+
result.put("boundingBoxes", new java.util.ArrayList<>());
|
|
359
462
|
return result;
|
|
360
463
|
|
|
361
464
|
} catch (Exception e) {
|
|
362
|
-
Log.e(TAG, "Error during TFLite inference: " + e.getMessage(), e);
|
|
363
465
|
// Fallback to Laplacian algorithm
|
|
364
466
|
double laplacianScore = calculateLaplacianBlurScore(bitmap);
|
|
365
467
|
boolean isBlur = laplacianScore < 150;
|
|
@@ -370,6 +472,7 @@ public class BlurDetectionHelper {
|
|
|
370
472
|
result.put("isBlur", isBlur);
|
|
371
473
|
result.put("blurConfidence", blurConfidence);
|
|
372
474
|
result.put("sharpConfidence", sharpConfidence);
|
|
475
|
+
result.put("boundingBoxes", new java.util.ArrayList<>());
|
|
373
476
|
return result;
|
|
374
477
|
}
|
|
375
478
|
}
|
|
@@ -395,6 +498,40 @@ public class BlurDetectionHelper {
|
|
|
395
498
|
return isBlurry(bitmap) ? 100.0 : 0.0;
|
|
396
499
|
}
|
|
397
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Enable or disable text recognition for blur detection
|
|
503
|
+
* @param enable true to enable text recognition, false to use only TFLite
|
|
504
|
+
*/
|
|
505
|
+
public void setTextRecognitionEnabled(boolean enable) {
|
|
506
|
+
this.useTextRecognition = enable;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Enable or disable dictionary check in text recognition
|
|
511
|
+
* @param enable true to enable dictionary check
|
|
512
|
+
*/
|
|
513
|
+
public void setDictionaryCheckEnabled(boolean enable) {
|
|
514
|
+
if (textBlurHelper != null) {
|
|
515
|
+
textBlurHelper.setDictionaryCheckEnabled(enable);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Check if text recognition is enabled
|
|
521
|
+
* @return true if text recognition is enabled
|
|
522
|
+
*/
|
|
523
|
+
public boolean isTextRecognitionEnabled() {
|
|
524
|
+
return useTextRecognition;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Get text recognition helper instance
|
|
529
|
+
* @return TextRecognitionBlurHelper instance
|
|
530
|
+
*/
|
|
531
|
+
public TextRecognitionBlurHelper getTextBlurHelper() {
|
|
532
|
+
return textBlurHelper;
|
|
533
|
+
}
|
|
534
|
+
|
|
398
535
|
/**
|
|
399
536
|
* Clean up resources
|
|
400
537
|
*/
|
|
@@ -403,6 +540,10 @@ public class BlurDetectionHelper {
|
|
|
403
540
|
tflite.close();
|
|
404
541
|
tflite = null;
|
|
405
542
|
}
|
|
543
|
+
if (textBlurHelper != null) {
|
|
544
|
+
textBlurHelper.close();
|
|
545
|
+
textBlurHelper = null;
|
|
546
|
+
}
|
|
406
547
|
isInitialized = false;
|
|
407
548
|
}
|
|
408
549
|
|
|
@@ -125,7 +125,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
125
125
|
desiredJpegQuality = call.getInt("quality");
|
|
126
126
|
// Ensure quality is within valid range
|
|
127
127
|
desiredJpegQuality = Math.max(1, Math.min(100, desiredJpegQuality));
|
|
128
|
-
Log.d("Camera", "Initialized with JPEG quality: " + desiredJpegQuality);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
previewView = new PreviewView(getContext());
|
|
@@ -144,7 +143,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
144
143
|
// Initialize TFLite blur detection helper
|
|
145
144
|
blurDetectionHelper = new BlurDetectionHelper();
|
|
146
145
|
boolean tfliteInitialized = blurDetectionHelper.initialize(getContext());
|
|
147
|
-
Log.d("Camera", "TFLite blur detection initialized: " + tfliteInitialized);
|
|
148
146
|
|
|
149
147
|
cameraProviderFuture.addListener(() -> {
|
|
150
148
|
try {
|
|
@@ -153,7 +151,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
153
151
|
.requireLensFacing(CameraSelector.LENS_FACING_BACK).build();
|
|
154
152
|
// Auto-optimize for photo capture on initialization with specified quality
|
|
155
153
|
setupUseCases(false); // Always use photo-optimized mode
|
|
156
|
-
Log.d("Camera", "Initialized with photo capture optimization and quality: " + desiredJpegQuality);
|
|
157
154
|
call.resolve();
|
|
158
155
|
} catch (ExecutionException | InterruptedException e) {
|
|
159
156
|
e.printStackTrace();
|
|
@@ -172,10 +169,9 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
172
169
|
Preview.Builder previewBuilder = new Preview.Builder();
|
|
173
170
|
if (resolution != null) {
|
|
174
171
|
previewBuilder.setTargetResolution(resolution);
|
|
175
|
-
Log.d("Camera", "Using optimal resolution: " + resolution.getWidth() + "x" + resolution.getHeight());
|
|
176
172
|
} else {
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
// Fallback: let CameraX choose the best resolution automatically
|
|
174
|
+
Log.d("Camera", "Using CameraX auto-resolution selection");
|
|
179
175
|
}
|
|
180
176
|
preview = previewBuilder.build();
|
|
181
177
|
preview.setSurfaceProvider(previewView.getSurfaceProvider());
|
|
@@ -183,7 +179,7 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
183
179
|
// Enhanced ImageAnalysis setup
|
|
184
180
|
ImageAnalysis.Builder imageAnalysisBuilder = new ImageAnalysis.Builder();
|
|
185
181
|
if (resolution != null) {
|
|
186
|
-
|
|
182
|
+
imageAnalysisBuilder.setTargetResolution(resolution);
|
|
187
183
|
}
|
|
188
184
|
imageAnalysisBuilder.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
189
185
|
.setImageQueueDepth(1); // Optimize for latest frame
|
|
@@ -224,9 +220,26 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
224
220
|
// Only detect blur if checkBlur option is true
|
|
225
221
|
boolean shouldCheckBlur = takeSnapshotCall.getBoolean("checkBlur", false);
|
|
226
222
|
if (shouldCheckBlur) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
// Get blur detection result with bounding boxes in one call
|
|
224
|
+
if (blurDetectionHelper != null && blurDetectionHelper.isInitialized()) {
|
|
225
|
+
java.util.Map<String, Object> blurResult = blurDetectionHelper.detectBlurWithConfidence(bitmap);
|
|
226
|
+
|
|
227
|
+
Double blurConfidence = (Double) blurResult.get("blurConfidence");
|
|
228
|
+
if (blurConfidence != null) {
|
|
229
|
+
result.put("confidence", blurConfidence);
|
|
230
|
+
}
|
|
231
|
+
if (blurResult.containsKey("boundingBoxes")) {
|
|
232
|
+
Object boundingBoxesObj = blurResult.get("boundingBoxes");
|
|
233
|
+
result.put("boundingBoxes", boundingBoxesObj);
|
|
234
|
+
} else {
|
|
235
|
+
result.put("boundingBoxes", new java.util.ArrayList<>());
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
// Fallback to Laplacian algorithm
|
|
239
|
+
double confidence = calculateBlurConfidence(bitmap);
|
|
240
|
+
result.put("confidence", confidence);
|
|
241
|
+
result.put("boundingBoxes", new java.util.ArrayList<>());
|
|
242
|
+
}
|
|
230
243
|
} else {
|
|
231
244
|
Log.d("Camera", "Blur detection disabled for performance");
|
|
232
245
|
}
|
|
@@ -317,15 +330,13 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
317
330
|
|
|
318
331
|
// Return the first (highest quality) option - CameraX will adapt if not supported
|
|
319
332
|
Size optimalResolution = preferredResolutions[0];
|
|
320
|
-
Log.d("Camera", "Selected optimal resolution: " + optimalResolution.getWidth() + "x" + optimalResolution.getHeight());
|
|
321
333
|
return optimalResolution;
|
|
322
334
|
|
|
323
335
|
} catch (Exception e) {
|
|
324
|
-
|
|
336
|
+
Log.e("Camera", "Error selecting optimal resolution: " + e.getMessage());
|
|
325
337
|
}
|
|
326
338
|
|
|
327
339
|
// Fallback: return null to let CameraX auto-select
|
|
328
|
-
Log.d("Camera", "Using CameraX auto-resolution selection as fallback");
|
|
329
340
|
return null;
|
|
330
341
|
}
|
|
331
342
|
|
|
@@ -350,8 +361,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
350
361
|
|
|
351
362
|
// Enable continuous auto-focus by starting a background focus monitoring
|
|
352
363
|
startContinuousAutoFocus();
|
|
353
|
-
|
|
354
|
-
Log.d("Camera", "Initialized responsive auto-focus with continuous monitoring");
|
|
355
364
|
} catch (Exception e) {
|
|
356
365
|
Log.e("Camera", "Failed to initialize responsive auto-focus: " + e.getMessage());
|
|
357
366
|
}
|
|
@@ -454,7 +463,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
454
463
|
useCaseGroup = null;
|
|
455
464
|
recorder = null;
|
|
456
465
|
currentRecording = null;
|
|
457
|
-
Log.d("Camera", "Camera stopped and all references cleared.");
|
|
458
466
|
call.resolve();
|
|
459
467
|
} catch (Exception e) {
|
|
460
468
|
call.reject(e.getMessage());
|
|
@@ -660,7 +668,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
660
668
|
|
|
661
669
|
// If focus failed, try a backup focus attempt to reduce need for multiple taps
|
|
662
670
|
if (!result.isFocusSuccessful()) {
|
|
663
|
-
Log.d("Camera", "Initial focus failed, attempting backup focus");
|
|
664
671
|
performBackupFocus(previewX, previewY);
|
|
665
672
|
} else {
|
|
666
673
|
// If manual focus was successful, maintain it with a follow-up action
|
|
@@ -809,7 +816,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
809
816
|
.build();
|
|
810
817
|
|
|
811
818
|
camera.getCameraControl().startFocusAndMetering(adaptiveAction);
|
|
812
|
-
Log.d("Camera", "Adaptive focus at point: " + (currentPointIndex + 1));
|
|
813
819
|
}
|
|
814
820
|
} catch (Exception e) {
|
|
815
821
|
Log.d("Camera", "Adaptive focus failed: " + e.getMessage());
|
|
@@ -1128,9 +1134,15 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1128
1134
|
if (call.getBoolean("includeBase64", false)) {
|
|
1129
1135
|
String base64 = Base64.encodeToString(convertFileToByteArray(file), Base64.DEFAULT);
|
|
1130
1136
|
result.put("base64", base64);
|
|
1137
|
+
|
|
1138
|
+
// Detect blur if base64 is included
|
|
1139
|
+
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
|
|
1140
|
+
if (bitmap != null) {
|
|
1141
|
+
double confidence = calculateBlurConfidence(bitmap);
|
|
1142
|
+
result.put("confidence", confidence);
|
|
1143
|
+
}
|
|
1131
1144
|
}
|
|
1132
1145
|
result.put("path", file.getAbsolutePath());
|
|
1133
|
-
call.resolve(result);
|
|
1134
1146
|
}
|
|
1135
1147
|
|
|
1136
1148
|
@Override
|
|
@@ -1186,9 +1198,7 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1186
1198
|
Consumer<VideoRecordEvent> captureListener = new Consumer<VideoRecordEvent>() {
|
|
1187
1199
|
@Override
|
|
1188
1200
|
public void accept(VideoRecordEvent videoRecordEvent) {
|
|
1189
|
-
Log.d("Camera",videoRecordEvent.toString());
|
|
1190
1201
|
if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
|
|
1191
|
-
Log.d("Camera","finalize");
|
|
1192
1202
|
Uri uri = ((VideoRecordEvent.Finalize) videoRecordEvent).getOutputResults().getOutputUri();
|
|
1193
1203
|
String path = uri.getPath();
|
|
1194
1204
|
|
|
@@ -1301,7 +1311,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1301
1311
|
useCaseGroup = null;
|
|
1302
1312
|
recorder = null;
|
|
1303
1313
|
currentRecording = null;
|
|
1304
|
-
Log.d("Camera", "handleOnPause: Camera stopped and references cleared.");
|
|
1305
1314
|
}
|
|
1306
1315
|
|
|
1307
1316
|
// Clean up TFLite resources
|
|
@@ -1334,7 +1343,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1334
1343
|
public void requestCameraPermission(PluginCall call) {
|
|
1335
1344
|
boolean hasCameraPerms = getPermissionState(CAMERA) == PermissionState.GRANTED;
|
|
1336
1345
|
if (hasCameraPerms == false) {
|
|
1337
|
-
Log.d("Camera","no camera permission. request permission.");
|
|
1338
1346
|
String[] aliases = new String[] { CAMERA };
|
|
1339
1347
|
requestPermissionForAliases(aliases, call, "cameraPermissionsCallback");
|
|
1340
1348
|
}else{
|
|
@@ -1356,7 +1364,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1356
1364
|
public void requestMicroPhonePermission(PluginCall call) {
|
|
1357
1365
|
boolean hasCameraPerms = getPermissionState(MICROPHONE) == PermissionState.GRANTED;
|
|
1358
1366
|
if (hasCameraPerms == false) {
|
|
1359
|
-
Log.d("Camera","no microphone permission. request permission.");
|
|
1360
1367
|
String[] aliases = new String[] { MICROPHONE };
|
|
1361
1368
|
requestPermissionForAliases(aliases, call, "microphonePermissionsCallback");
|
|
1362
1369
|
}else{
|
|
@@ -1456,6 +1463,36 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1456
1463
|
return laplacianScore < 50;
|
|
1457
1464
|
}
|
|
1458
1465
|
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Calculate blur confidence score (0-1 where 1 is sharp, 0 is blurry)
|
|
1469
|
+
*/
|
|
1470
|
+
private double calculateBlurConfidence(Bitmap bitmap) {
|
|
1471
|
+
if (bitmap == null) return 0.0;
|
|
1472
|
+
|
|
1473
|
+
// Use TFLite model if available for detailed confidence
|
|
1474
|
+
if (blurDetectionHelper != null && blurDetectionHelper.isInitialized()) {
|
|
1475
|
+
java.util.Map<String, Object> result = blurDetectionHelper.detectBlurWithConfidence(bitmap);
|
|
1476
|
+
Boolean isBlur = (Boolean) result.get("isBlur");
|
|
1477
|
+
Double blurConfidence = (Double) result.get("blurConfidence");
|
|
1478
|
+
Double sharpConfidence = (Double) result.get("sharpConfidence");
|
|
1479
|
+
|
|
1480
|
+
// Return the actual blurConfidence value for more nuanced results
|
|
1481
|
+
if (blurConfidence != null) {
|
|
1482
|
+
return blurConfidence;
|
|
1483
|
+
} else if (isBlur != null) {
|
|
1484
|
+
// Fallback to boolean if confidence not available
|
|
1485
|
+
return isBlur ? 1.0 : 0.0;
|
|
1486
|
+
} else {
|
|
1487
|
+
return 0.0;
|
|
1488
|
+
}
|
|
1489
|
+
} else {
|
|
1490
|
+
// Fallback to Laplacian algorithm with confidence calculation
|
|
1491
|
+
double laplacianScore = calculateLaplacianBlurScore(bitmap);
|
|
1492
|
+
// Normalize to 0-1 range (higher score = sharper image)
|
|
1493
|
+
return Math.max(0.0, Math.min(1.0, laplacianScore / 300.0));
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1459
1496
|
|
|
1460
1497
|
/**
|
|
1461
1498
|
* Original Laplacian blur detection (fallback)
|
|
@@ -1586,7 +1623,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1586
1623
|
try {
|
|
1587
1624
|
FocusMeteringResult backupResult = backupFuture.get();
|
|
1588
1625
|
if (backupResult.isFocusSuccessful()) {
|
|
1589
|
-
Log.d("Camera", "Backup focus successful");
|
|
1590
1626
|
maintainFocusAtPoint(previewX, previewY);
|
|
1591
1627
|
} else {
|
|
1592
1628
|
Log.d("Camera", "Backup focus also failed");
|
|
@@ -1639,7 +1675,6 @@ public class CameraPreviewPlugin extends Plugin {
|
|
|
1639
1675
|
.build();
|
|
1640
1676
|
|
|
1641
1677
|
camera.getCameraControl().startFocusAndMetering(maintainAction);
|
|
1642
|
-
Log.d("Camera", "Maintaining focus at tapped point");
|
|
1643
1678
|
}
|
|
1644
1679
|
} catch (Exception e) {
|
|
1645
1680
|
Log.d("Camera", "Focus maintenance failed: " + e.getMessage());
|