capacitor-plugin-camera-forked 3.0.103 → 3.0.111

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.
@@ -11,8 +11,11 @@ Pod::Spec.new do |s|
11
11
  s.author = package['author']
12
12
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
13
  s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.resources = ['ios/Plugin/*.tflite']
14
15
  s.ios.deployment_target = '13.0'
15
16
  s.dependency 'Capacitor'
16
- s.dependency 'TensorFlowLiteSwift'
17
+ s.dependency 'TensorFlowLiteSwift', '~> 2.17.0'
18
+ s.dependency 'TensorFlowLiteC', '~> 2.17.0'
19
+ s.static_framework = true
17
20
  s.swift_version = '5.1'
18
21
  end
package/README.md CHANGED
@@ -105,6 +105,7 @@ document.documentElement.style.setProperty('--ion-background-color', 'transparen
105
105
  * [`startCamera()`](#startcamera)
106
106
  * [`stopCamera()`](#stopcamera)
107
107
  * [`takeSnapshot(...)`](#takesnapshot)
108
+ * [`detectBlur(...)`](#detectblur)
108
109
  * [`saveFrame()`](#saveframe)
109
110
  * [`takeSnapshot2(...)`](#takesnapshot2)
110
111
  * [`takePhoto(...)`](#takephoto)
@@ -289,7 +290,7 @@ stopCamera() => Promise<void>
289
290
  ### takeSnapshot(...)
290
291
 
291
292
  ```typescript
292
- takeSnapshot(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string; blurScore?: number; }>
293
+ takeSnapshot(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string; isBlur?: boolean; }>
293
294
  ```
294
295
 
295
296
  take a snapshot as base64.
@@ -298,7 +299,24 @@ take a snapshot as base64.
298
299
  | ------------- | ------------------------------------------------------- |
299
300
  | **`options`** | <code>{ quality?: number; checkBlur?: boolean; }</code> |
300
301
 
301
- **Returns:** <code>Promise&lt;{ base64: string; blurScore?: number; }&gt;</code>
302
+ **Returns:** <code>Promise&lt;{ base64: string; isBlur?: boolean; }&gt;</code>
303
+
304
+ --------------------
305
+
306
+
307
+ ### detectBlur(...)
308
+
309
+ ```typescript
310
+ detectBlur(options: { image: string; }) => Promise<{ isBlur: boolean; blurConfidence: number; sharpConfidence: number; }>
311
+ ```
312
+
313
+ analyze an image for blur detection with detailed confidence scores.
314
+
315
+ | Param | Type |
316
+ | ------------- | ------------------------------- |
317
+ | **`options`** | <code>{ image: string; }</code> |
318
+
319
+ **Returns:** <code>Promise&lt;{ isBlur: boolean; blurConfidence: number; sharpConfidence: number; }&gt;</code>
302
320
 
303
321
  --------------------
304
322
 
@@ -336,14 +354,14 @@ take a snapshot on to a canvas. Web Only
336
354
  ### takePhoto(...)
337
355
 
338
356
  ```typescript
339
- takePhoto(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob; blurScore?: number; }>
357
+ takePhoto(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob; isBlur?: boolean; }>
340
358
  ```
341
359
 
342
360
  | Param | Type |
343
361
  | ------------- | -------------------------------------------------------------- |
344
362
  | **`options`** | <code>{ pathToSave?: string; includeBase64?: boolean; }</code> |
345
363
 
346
- **Returns:** <code>Promise&lt;{ path?: string; base64?: string; blob?: any; blurScore?: number; }&gt;</code>
364
+ **Returns:** <code>Promise&lt;{ path?: string; base64?: string; blob?: any; isBlur?: boolean; }&gt;</code>
347
365
 
348
366
  --------------------
349
367
 
@@ -520,9 +538,34 @@ measuredByPercentage: 0 in pixel, 1 in percent
520
538
 
521
539
  ## Blur Detection
522
540
 
523
- The plugin includes blur detection capabilities using the Laplacian variance algorithm, providing consistent results across all platforms.
541
+ The plugin includes blur detection capabilities using TensorFlow Lite models with Laplacian variance fallback, providing consistent results across all platforms.
542
+
543
+ #### Analyze Existing Images
524
544
 
525
- #### Basic Usage
545
+ Use the `detectBlur` method to analyze any base64 image with detailed confidence scores:
546
+
547
+ ```typescript
548
+ // Analyze an existing image (base64 string or data URL)
549
+ const result = await CameraPreview.detectBlur({
550
+ image: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD..."
551
+ // or just the base64 string: "/9j/4AAQSkZJRgABAQEASABIAAD..."
552
+ });
553
+
554
+ console.log('Is Blurry:', result.isBlur); // boolean: true/false
555
+ console.log('Blur Confidence:', result.blurConfidence); // 0.0-1.0 (higher = more blurry)
556
+ console.log('Sharp Confidence:', result.sharpConfidence); // 0.0-1.0 (higher = more sharp)
557
+
558
+ // Use confidence scores for advanced logic
559
+ if (result.blurConfidence > 0.7) {
560
+ console.log('High confidence this image is blurry');
561
+ } else if (result.sharpConfidence > 0.8) {
562
+ console.log('High confidence this image is sharp');
563
+ } else {
564
+ console.log('Uncertain blur status - manual review needed');
565
+ }
566
+ ```
567
+
568
+ #### Basic Usage (Capture + Detection)
526
569
 
527
570
  ```typescript
528
571
  // Take a snapshot with blur detection
@@ -562,8 +605,15 @@ const resultWithBlur = await CameraPreview.takeSnapshot({
562
605
  });
563
606
  ```
564
607
 
565
- #### Understanding Blur Scores
608
+ #### Understanding Blur Results
609
+
610
+ **New `detectBlur` Method (Recommended):**
611
+ - Returns standardized **confidence scores** (0.0-1.0 range) across all platforms
612
+ - `blurConfidence`: Higher values indicate more blur (>0.7 = likely blurry)
613
+ - `sharpConfidence`: Higher values indicate more sharpness (>0.8 = likely sharp)
614
+ - `isBlur`: Simple boolean result based on confidence thresholds
566
615
 
616
+ **Legacy `takeSnapshot` Method:**
567
617
  - **Higher values** = Sharper images
568
618
  - **Lower values** = Blurrier images
569
619
  - **Threshold guidelines**:
@@ -571,6 +621,20 @@ const resultWithBlur = await CameraPreview.takeSnapshot({
571
621
  - Android/Web: Consider values below `50-100` as blurry
572
622
  - Adjust thresholds based on your specific quality requirements
573
623
 
624
+ #### When to Use Which Method
625
+
626
+ **Use `detectBlur` for:**
627
+ - Analyzing already captured images
628
+ - Batch processing multiple images
629
+ - Getting detailed confidence scores for advanced decision logic
630
+ - Post-processing workflows
631
+ - When you need consistent confidence values across platforms
632
+
633
+ **Use `takeSnapshot` with `checkBlur: true` for:**
634
+ - Real-time capture with immediate blur feedback
635
+ - Simple blur detection during image capture
636
+ - When you only need a basic blur/sharp indication
637
+
574
638
  #### Performance Impact
575
639
 
576
640
  | Platform | Without Blur Detection | With Blur Detection | Overhead |
@@ -581,8 +645,10 @@ const resultWithBlur = await CameraPreview.takeSnapshot({
581
645
 
582
646
  #### Implementation Notes
583
647
 
584
- - Uses Laplacian variance algorithm across all platforms
585
- - Pixel sampling for performance optimization
648
+ - **TensorFlow Lite models** for advanced blur detection with high accuracy
649
+ - **Laplacian variance fallback** when TFLite models unavailable
650
+ - Pixel sampling for performance optimization
586
651
  - Hardware acceleration on iOS with Core Image
587
652
  - Client-side threshold logic for maximum flexibility
588
653
  - Cross-platform algorithm consistency
654
+ - **Dual API**: `takeSnapshot` for capture+detection, `detectBlur` for analyzing existing images
@@ -67,9 +67,8 @@ public class BlurDetectionHelper {
67
67
  // Try to use GPU acceleration if available
68
68
  try {
69
69
  options.setUseXNNPACK(true);
70
- Log.d(TAG, "Using XNNPACK acceleration");
71
70
  } catch (Exception e) {
72
- Log.w(TAG, "XNNPACK not available, using CPU");
71
+ // XNNPACK not available, using CPU
73
72
  }
74
73
 
75
74
  tflite = new Interpreter(tfliteModel, options);
@@ -82,12 +81,8 @@ public class BlurDetectionHelper {
82
81
  tflite.getOutputTensor(0).dataType()
83
82
  );
84
83
 
85
- // Log tensor information and update INPUT_SIZE based on model
84
+ // Update INPUT_SIZE based on actual model input shape
86
85
  int[] inputShape = tflite.getInputTensor(0).shape();
87
- Log.d(TAG, "Input tensor shape: " + java.util.Arrays.toString(inputShape));
88
- Log.d(TAG, "Input tensor type: " + tflite.getInputTensor(0).dataType());
89
- Log.d(TAG, "Output tensor shape: " + java.util.Arrays.toString(tflite.getOutputTensor(0).shape()));
90
- Log.d(TAG, "Output tensor type: " + tflite.getOutputTensor(0).dataType());
91
86
 
92
87
  // Update INPUT_SIZE based on actual model input shape
93
88
  // Expected format: [batch, height, width, channels] or [batch, channels, height, width]
@@ -95,29 +90,23 @@ public class BlurDetectionHelper {
95
90
  // Assume format is [batch, height, width, channels]
96
91
  int modelInputSize = inputShape[1]; // height dimension
97
92
  if (modelInputSize != INPUT_SIZE) {
98
- Log.w(TAG, "Model expects input size " + modelInputSize + " but code was configured for " + INPUT_SIZE);
99
93
  INPUT_SIZE = modelInputSize;
100
- Log.i(TAG, "Updated INPUT_SIZE to " + INPUT_SIZE + " to match model");
101
94
 
102
95
  // Recreate image processor with correct size
103
96
  imageProcessor = new ImageProcessor.Builder()
104
97
  .add(new ResizeWithCropOrPadOp(INPUT_SIZE, INPUT_SIZE))
105
98
  .add(new ResizeOp(INPUT_SIZE, INPUT_SIZE, ResizeOp.ResizeMethod.BILINEAR))
106
99
  .build();
107
- Log.d(TAG, "Recreated ImageProcessor with INPUT_SIZE = " + INPUT_SIZE);
108
100
  }
109
101
  }
110
102
 
111
103
  isInitialized = true;
112
- Log.d(TAG, "TFLite blur detection model initialized successfully");
113
104
  return true;
114
105
 
115
106
  } catch (IOException e) {
116
- Log.e(TAG, "Error loading TFLite model: " + e.getMessage());
117
107
  isInitialized = false;
118
108
  return false;
119
109
  } catch (Exception e) {
120
- Log.e(TAG, "Error initializing TFLite model: " + e.getMessage());
121
110
  isInitialized = false;
122
111
  return false;
123
112
  }
@@ -142,27 +131,17 @@ public class BlurDetectionHelper {
142
131
  inputImageBuffer.load(processedBitmap);
143
132
  inputImageBuffer = imageProcessor.process(inputImageBuffer);
144
133
 
145
- // Debug: Log buffer info
134
+ // Get tensor buffer
146
135
  ByteBuffer tensorBuffer = inputImageBuffer.getBuffer();
147
- Log.d(TAG, "TensorImage buffer size: " + tensorBuffer.remaining() + " bytes");
148
- Log.d(TAG, "TensorImage buffer capacity: " + tensorBuffer.capacity() + " bytes");
149
- Log.d(TAG, "TensorImage data type: " + inputImageBuffer.getDataType());
150
- Log.d(TAG, "Model input tensor type: " + tflite.getInputTensor(0).dataType());
151
- Log.d(TAG, "Expected buffer size: " + (INPUT_SIZE * INPUT_SIZE * 3 * 4) + " bytes");
152
136
 
153
137
  // Check if we need normalization based on data types
154
138
  ByteBuffer inferenceBuffer;
155
139
  if (inputImageBuffer.getDataType() == DataType.UINT8 && tflite.getInputTensor(0).dataType() == DataType.FLOAT32) {
156
- Log.d(TAG, "Converting UINT8 to FLOAT32 with normalization");
157
140
  inferenceBuffer = normalizeImageBuffer(tensorBuffer);
158
- Log.d(TAG, "Normalized buffer size: " + inferenceBuffer.remaining() + " bytes");
159
141
  } else if (inputImageBuffer.getDataType() == DataType.FLOAT32) {
160
- Log.d(TAG, "Using FLOAT32 buffer directly (may need normalization check)");
161
142
  // Check if values are in [0,1] range or [0,255] range
162
143
  inferenceBuffer = checkAndNormalizeFloat32Buffer(tensorBuffer);
163
- Log.d(TAG, "Final buffer size: " + inferenceBuffer.remaining() + " bytes");
164
144
  } else {
165
- Log.d(TAG, "Using buffer directly");
166
145
  inferenceBuffer = tensorBuffer;
167
146
  }
168
147
 
@@ -172,24 +151,12 @@ public class BlurDetectionHelper {
172
151
  // Get output probabilities
173
152
  float[] probabilities = outputProbabilityBuffer.getFloatArray();
174
153
 
175
- // Log probabilities for debugging
176
- Log.d(TAG, "Output probabilities length: " + probabilities.length);
177
- for (int i = 0; i < probabilities.length && i < 5; i++) {
178
- Log.d(TAG, "probabilities[" + i + "] = " + probabilities[i]);
179
- }
180
-
181
154
  // probabilities[0] = blur probability, probabilities[1] = sharp probability
182
155
  double blurConfidence = probabilities.length > 0 ? probabilities[0] : 0.0;
183
156
  double sharpConfidence = probabilities.length > 1 ? probabilities[1] : 0.0;
184
157
 
185
- // Fallback to Laplacian algorithm
186
- double laplacianScore = calculateLaplacianBlurScore(bitmap);
187
-
188
- // Determine if image is blurry TFLite confidence or Laplacian score < 50
189
- boolean isBlur = (blurConfidence > sharpConfidence && blurConfidence > 0.99) || (laplacianScore < 50 && sharpConfidence < 0.1);
190
-
191
- Log.d(TAG, String.format("TFLite Blur Detection - Blur: %.6f, Sharp: %.6f, Label: %s",
192
- blurConfidence, sharpConfidence, isBlur ? "blur" : "sharp"));
158
+ // Determine if image is blurry using TFLite confidence
159
+ boolean isBlur = (blurConfidence >= 0.99 || sharpConfidence < 0.1);
193
160
 
194
161
  // Return 1.0 for blur, 0.0 for sharp (to maintain double return type)
195
162
  return isBlur ? 1.0 : 0.0;
@@ -199,16 +166,10 @@ public class BlurDetectionHelper {
199
166
  // Fallback to Laplacian algorithm
200
167
  double laplacianScore = calculateLaplacianBlurScore(bitmap);
201
168
  boolean isBlur = laplacianScore < 150;
202
- Log.d(TAG, String.format("Laplacian Fallback - Score: %.2f, Label: %s",
203
- laplacianScore, isBlur ? "blur" : "sharp"));
204
169
  return isBlur ? 1.0 : 0.0;
205
170
  }
206
171
  }
207
172
 
208
-
209
-
210
-
211
-
212
173
  /**
213
174
  * Check if float32 buffer needs normalization and normalize if needed
214
175
  * @param float32Buffer Input buffer with float32 pixel values
@@ -227,14 +188,10 @@ public class BlurDetectionHelper {
227
188
  maxSample = Math.max(maxSample, value);
228
189
  }
229
190
 
230
- Log.d(TAG, "Sample max value from buffer: " + maxSample);
231
-
232
191
  // If max value is > 1.5, assume it's in [0,255] range and needs normalization
233
192
  if (maxSample > 1.5f) {
234
- Log.d(TAG, "Buffer appears to be in [0,255] range, normalizing to [0,1]");
235
193
  return normalizeFloat32Buffer(float32Buffer);
236
194
  } else {
237
- Log.d(TAG, "Buffer appears to be already normalized [0,1]");
238
195
  float32Buffer.rewind();
239
196
  return float32Buffer;
240
197
  }
@@ -254,8 +211,6 @@ public class BlurDetectionHelper {
254
211
  float32Buffer.rewind();
255
212
  FloatBuffer sourceFloats = float32Buffer.asFloatBuffer();
256
213
 
257
- Log.d(TAG, "Normalizing float32 buffer: " + sourceFloats.remaining() + " values, target: " + pixelCount);
258
-
259
214
  while (sourceFloats.hasRemaining() && normalizedFloats.hasRemaining()) {
260
215
  float pixelValue = sourceFloats.get();
261
216
  float normalizedValue = pixelValue / 255.0f;
@@ -263,7 +218,6 @@ public class BlurDetectionHelper {
263
218
  }
264
219
 
265
220
  normalizedBuffer.rewind();
266
- Log.d(TAG, "Normalized buffer created with " + normalizedBuffer.remaining() + " bytes");
267
221
  return normalizedBuffer;
268
222
  }
269
223
 
@@ -282,8 +236,6 @@ public class BlurDetectionHelper {
282
236
  // Reset uint8 buffer position
283
237
  uint8Buffer.rewind();
284
238
 
285
- Log.d(TAG, "Converting UINT8 to FLOAT32: " + uint8Buffer.remaining() + " uint8 values to " + pixelCount + " float values");
286
-
287
239
  // Convert each uint8 pixel to normalized float32
288
240
  while (uint8Buffer.hasRemaining() && floatBuffer.hasRemaining()) {
289
241
  int pixelValue = uint8Buffer.get() & 0xFF; // Convert to unsigned int
@@ -292,7 +244,6 @@ public class BlurDetectionHelper {
292
244
  }
293
245
 
294
246
  float32Buffer.rewind();
295
- Log.d(TAG, "UINT8 to FLOAT32 conversion complete, buffer size: " + float32Buffer.remaining() + " bytes");
296
247
  return float32Buffer;
297
248
  }
298
249
 
@@ -342,6 +293,87 @@ public class BlurDetectionHelper {
342
293
  return count > 0 ? variance / count : 0.0;
343
294
  }
344
295
 
296
+ /**
297
+ * Detect blur with detailed confidence scores
298
+ * @param bitmap Input image bitmap
299
+ * @return Map with isBlur, blurConfidence, and sharpConfidence
300
+ */
301
+ public java.util.Map<String, Object> detectBlurWithConfidence(Bitmap bitmap) {
302
+ java.util.Map<String, Object> result = new java.util.HashMap<>();
303
+
304
+ if (!isInitialized || tflite == null) {
305
+ Log.w(TAG, "TFLite model not initialized, falling back to Laplacian");
306
+ double laplacianScore = calculateLaplacianBlurScore(bitmap);
307
+ boolean isBlur = laplacianScore < 150;
308
+ double normalizedScore = Math.max(0.0, Math.min(1.0, laplacianScore / 300.0));
309
+ double sharpConfidence = normalizedScore;
310
+ double blurConfidence = 1.0 - normalizedScore;
311
+
312
+ result.put("isBlur", isBlur);
313
+ result.put("blurConfidence", blurConfidence);
314
+ result.put("sharpConfidence", sharpConfidence);
315
+ return result;
316
+ }
317
+
318
+ try {
319
+ // Use the original bitmap directly (no image enhancement)
320
+ Bitmap processedBitmap = bitmap;
321
+
322
+ // Preprocess image for model (resize and potential enhancement)
323
+ inputImageBuffer.load(processedBitmap);
324
+ inputImageBuffer = imageProcessor.process(inputImageBuffer);
325
+
326
+ // Get tensor buffer
327
+ ByteBuffer tensorBuffer = inputImageBuffer.getBuffer();
328
+
329
+ // Check if we need normalization based on data types
330
+ ByteBuffer inferenceBuffer;
331
+ if (inputImageBuffer.getDataType() == DataType.UINT8 && tflite.getInputTensor(0).dataType() == DataType.FLOAT32) {
332
+ inferenceBuffer = normalizeImageBuffer(tensorBuffer);
333
+ } else if (inputImageBuffer.getDataType() == DataType.FLOAT32) {
334
+ // Check if values are in [0,1] range or [0,255] range
335
+ inferenceBuffer = checkAndNormalizeFloat32Buffer(tensorBuffer);
336
+ } else {
337
+ inferenceBuffer = tensorBuffer;
338
+ }
339
+
340
+ // Run inference
341
+ tflite.run(inferenceBuffer, outputProbabilityBuffer.getBuffer().rewind());
342
+
343
+ // Get output probabilities
344
+ float[] probabilities = outputProbabilityBuffer.getFloatArray();
345
+
346
+ // probabilities[0] = blur probability, probabilities[1] = sharp probability
347
+ double blurConfidence = probabilities.length > 0 ? probabilities[0] : 0.0;
348
+ double sharpConfidence = probabilities.length > 1 ? probabilities[1] : 0.0;
349
+
350
+ // Determine if image is blurry using TFLite confidence
351
+ boolean isBlur = (blurConfidence >= 0.99 || sharpConfidence < 0.1);
352
+
353
+ Log.d(TAG, String.format("TFLite Blur Detection with Confidence - Blur: %.6f, Sharp: %.6f, Label: %s",
354
+ blurConfidence, sharpConfidence, isBlur ? "blur" : "sharp"));
355
+
356
+ result.put("isBlur", isBlur);
357
+ result.put("blurConfidence", blurConfidence);
358
+ result.put("sharpConfidence", sharpConfidence);
359
+ return result;
360
+
361
+ } catch (Exception e) {
362
+ Log.e(TAG, "Error during TFLite inference: " + e.getMessage(), e);
363
+ // Fallback to Laplacian algorithm
364
+ double laplacianScore = calculateLaplacianBlurScore(bitmap);
365
+ boolean isBlur = laplacianScore < 150;
366
+ double normalizedScore = Math.max(0.0, Math.min(1.0, laplacianScore / 300.0));
367
+ double sharpConfidence = normalizedScore;
368
+ double blurConfidence = 1.0 - normalizedScore;
369
+
370
+ result.put("isBlur", isBlur);
371
+ result.put("blurConfidence", blurConfidence);
372
+ result.put("sharpConfidence", sharpConfidence);
373
+ return result;
374
+ }
375
+ }
376
+
345
377
  /**
346
378
  * Check if image is blurry
347
379
  * @param bitmap Input image
@@ -372,7 +404,6 @@ public class BlurDetectionHelper {
372
404
  tflite = null;
373
405
  }
374
406
  isInitialized = false;
375
- Log.d(TAG, "TFLite blur detection model closed");
376
407
  }
377
408
 
378
409
  /**
@@ -7,6 +7,7 @@ import android.content.pm.PackageManager;
7
7
  import android.content.res.Configuration;
8
8
  import android.content.res.Resources;
9
9
  import android.graphics.Bitmap;
10
+ import android.graphics.BitmapFactory;
10
11
  import android.graphics.Color;
11
12
  import android.graphics.drawable.Drawable;
12
13
  import android.net.Uri;
@@ -1372,6 +1373,60 @@ public class CameraPreviewPlugin extends Plugin {
1372
1373
  }
1373
1374
  }
1374
1375
 
1376
+ @PluginMethod
1377
+ public void detectBlur(PluginCall call) {
1378
+ String imageString = call.getString("image");
1379
+ if (imageString == null) {
1380
+ call.reject("Image parameter is required");
1381
+ return;
1382
+ }
1383
+
1384
+ try {
1385
+ // Convert base64 string to Bitmap
1386
+ String base64String = imageString;
1387
+ if (imageString.startsWith("data:")) {
1388
+ base64String = imageString.substring(imageString.indexOf(",") + 1);
1389
+ }
1390
+
1391
+ byte[] decodedBytes = Base64.decode(base64String, Base64.DEFAULT);
1392
+ Bitmap bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
1393
+
1394
+ if (bitmap == null) {
1395
+ call.reject("Invalid image data");
1396
+ return;
1397
+ }
1398
+
1399
+ // Use the new confidence detection method
1400
+ if (blurDetectionHelper != null && blurDetectionHelper.isInitialized()) {
1401
+ java.util.Map<String, Object> result = blurDetectionHelper.detectBlurWithConfidence(bitmap);
1402
+
1403
+ JSObject jsResult = new JSObject();
1404
+ jsResult.put("isBlur", result.get("isBlur"));
1405
+ jsResult.put("blurConfidence", result.get("blurConfidence"));
1406
+ jsResult.put("sharpConfidence", result.get("sharpConfidence"));
1407
+
1408
+ call.resolve(jsResult);
1409
+ } else {
1410
+ // Fallback to Laplacian algorithm with confidence scores
1411
+ double laplacianScore = calculateLaplacianBlurScore(bitmap);
1412
+ boolean isBlur = laplacianScore < 150;
1413
+ double normalizedScore = Math.max(0.0, Math.min(1.0, laplacianScore / 300.0));
1414
+ double sharpConfidence = normalizedScore;
1415
+ double blurConfidence = 1.0 - normalizedScore;
1416
+
1417
+ JSObject result = new JSObject();
1418
+ result.put("isBlur", isBlur);
1419
+ result.put("blurConfidence", blurConfidence);
1420
+ result.put("sharpConfidence", sharpConfidence);
1421
+
1422
+ call.resolve(result);
1423
+ }
1424
+
1425
+ } catch (Exception e) {
1426
+ call.reject("Failed to process image: " + e.getMessage());
1427
+ }
1428
+ }
1429
+
1375
1430
  @PluginMethod
1376
1431
  public void getOrientation(PluginCall call) {
1377
1432
  int orientation = getContext().getResources().getConfiguration().orientation;
package/dist/docs.json CHANGED
@@ -189,7 +189,7 @@
189
189
  },
190
190
  {
191
191
  "name": "takeSnapshot",
192
- "signature": "(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string; blurScore?: number; }>",
192
+ "signature": "(options: { quality?: number; checkBlur?: boolean; }) => Promise<{ base64: string; isBlur?: boolean; }>",
193
193
  "parameters": [
194
194
  {
195
195
  "name": "options",
@@ -197,12 +197,28 @@
197
197
  "type": "{ quality?: number | undefined; checkBlur?: boolean | undefined; }"
198
198
  }
199
199
  ],
200
- "returns": "Promise<{ base64: string; blurScore?: number | undefined; }>",
200
+ "returns": "Promise<{ base64: string; isBlur?: boolean | undefined; }>",
201
201
  "tags": [],
202
202
  "docs": "take a snapshot as base64.",
203
203
  "complexTypes": [],
204
204
  "slug": "takesnapshot"
205
205
  },
206
+ {
207
+ "name": "detectBlur",
208
+ "signature": "(options: { image: string; }) => Promise<{ isBlur: boolean; blurConfidence: number; sharpConfidence: number; }>",
209
+ "parameters": [
210
+ {
211
+ "name": "options",
212
+ "docs": "",
213
+ "type": "{ image: string; }"
214
+ }
215
+ ],
216
+ "returns": "Promise<{ isBlur: boolean; blurConfidence: number; sharpConfidence: number; }>",
217
+ "tags": [],
218
+ "docs": "analyze an image for blur detection with detailed confidence scores.",
219
+ "complexTypes": [],
220
+ "slug": "detectblur"
221
+ },
206
222
  {
207
223
  "name": "saveFrame",
208
224
  "signature": "() => Promise<{ success: boolean; }>",
@@ -233,7 +249,7 @@
233
249
  },
234
250
  {
235
251
  "name": "takePhoto",
236
- "signature": "(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob; blurScore?: number; }>",
252
+ "signature": "(options: { pathToSave?: string; includeBase64?: boolean; }) => Promise<{ path?: string; base64?: string; blob?: Blob; isBlur?: boolean; }>",
237
253
  "parameters": [
238
254
  {
239
255
  "name": "options",
@@ -241,7 +257,7 @@
241
257
  "type": "{ pathToSave?: string | undefined; includeBase64?: boolean | undefined; }"
242
258
  }
243
259
  ],
244
- "returns": "Promise<{ path?: string | undefined; base64?: string | undefined; blob?: any; blurScore?: number | undefined; }>",
260
+ "returns": "Promise<{ path?: string | undefined; base64?: string | undefined; blob?: any; isBlur?: boolean | undefined; }>",
245
261
  "tags": [],
246
262
  "docs": "",
247
263
  "complexTypes": [
@@ -46,7 +46,17 @@ export interface CameraPreviewPlugin {
46
46
  checkBlur?: boolean;
47
47
  }): Promise<{
48
48
  base64: string;
49
- blurScore?: number;
49
+ isBlur?: boolean;
50
+ }>;
51
+ /**
52
+ * analyze an image for blur detection with detailed confidence scores.
53
+ */
54
+ detectBlur(options: {
55
+ image: string;
56
+ }): Promise<{
57
+ isBlur: boolean;
58
+ blurConfidence: number;
59
+ sharpConfidence: number;
50
60
  }>;
51
61
  /**
52
62
  * save a frame internally. Android and iOS only.
@@ -70,7 +80,7 @@ export interface CameraPreviewPlugin {
70
80
  path?: string;
71
81
  base64?: string;
72
82
  blob?: Blob;
73
- blurScore?: number;
83
+ isBlur?: boolean;
74
84
  }>;
75
85
  toggleTorch(options: {
76
86
  on: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAuEA,MAAM,CAAN,IAAY,cAOX;AAPD,WAAY,cAAc;IACxB,yEAAmB,CAAA;IACnB,yEAAmB,CAAA;IACnB,yEAAmB,CAAA;IACnB,2EAAoB,CAAA;IACpB,qEAAiB,CAAA;IACjB,qEAAiB,CAAA;AACnB,CAAC,EAPW,cAAc,KAAd,cAAc,QAOzB","sourcesContent":["import { PluginListenerHandle } from \"@capacitor/core\";\n\nexport interface CameraPreviewPlugin {\n initialize(options?: { quality?: number }): Promise<void>;\n getResolution(): Promise<{resolution: string}>;\n setResolution(options: {resolution: number}): Promise<void>;\n getAllCameras(): Promise<{cameras: string[]}>;\n getSelectedCamera(): Promise<{selectedCamera: string}>;\n selectCamera(options: {cameraID: string; }): Promise<void>;\n setScanRegion(options: {region:ScanRegion}): Promise<void>;\n setZoom(options: {factor: number}): Promise<void>;\n setFocus(options: {x: number, y: number}): Promise<void>;\n /**\n * Web Only\n */\n setDefaultUIElementURL(url:string): Promise<void>;\n /**\n * Web Only\n */\n setElement(ele:HTMLElement): Promise<void>;\n startCamera(): Promise<void>;\n stopCamera(): Promise<void>;\n /**\n * take a snapshot as base64.\n */\n takeSnapshot(options:{quality?:number, checkBlur?:boolean}): Promise<{base64:string, blurScore?: number}>;\n /**\n * save a frame internally. Android and iOS only.\n */\n saveFrame(): Promise<{success:boolean}>;\n /**\n * take a snapshot on to a canvas. Web Only\n */\n takeSnapshot2(options:{canvas:HTMLCanvasElement,maxLength?:number}): Promise<{scaleRatio?:number}>;\n takePhoto(options: {pathToSave?:string,includeBase64?: boolean}): Promise<{path?:string,base64?:string,blob?:Blob, blurScore?: number}>;\n toggleTorch(options: {on: boolean}): Promise<void>;\n /**\n * get the orientation of the device.\n */\n getOrientation(): Promise<{\"orientation\":\"PORTRAIT\"|\"LANDSCAPE\"}>;\n startRecording(): Promise<void>;\n stopRecording(options:{includeBase64?:boolean}): Promise<{path?:string,base64?:string,blob?:Blob}>;\n setLayout(options: {top: string, left:string, width:string, height:string}): Promise<void>;\n requestCameraPermission(): Promise<void>;\n requestMicroPhonePermission(): Promise<void>;\n isOpen():Promise<{isOpen:boolean}>;\n addListener(\n eventName: 'onPlayed',\n listenerFunc: onPlayedListener,\n ): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'onOrientationChanged',\n listenerFunc: onOrientationChangedListener,\n ): Promise<PluginListenerHandle>;\n removeAllListeners(): Promise<void>;\n}\n\nexport type onPlayedListener = (result:{resolution:string}) => void;\nexport type onOrientationChangedListener = () => void;\n\n/**\n * measuredByPercentage: 0 in pixel, 1 in percent\n */\nexport interface ScanRegion{\n left: number;\n top: number;\n right: number;\n bottom: number;\n measuredByPercentage: number;\n}\n\nexport enum EnumResolution {\n RESOLUTION_AUTO = 0,\n RESOLUTION_480P = 1,\n RESOLUTION_720P = 2,\n RESOLUTION_1080P = 3,\n RESOLUTION_2K = 4,\n RESOLUTION_4K = 5\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AA2EA,MAAM,CAAN,IAAY,cAOX;AAPD,WAAY,cAAc;IACxB,yEAAmB,CAAA;IACnB,yEAAmB,CAAA;IACnB,yEAAmB,CAAA;IACnB,2EAAoB,CAAA;IACpB,qEAAiB,CAAA;IACjB,qEAAiB,CAAA;AACnB,CAAC,EAPW,cAAc,KAAd,cAAc,QAOzB","sourcesContent":["import { PluginListenerHandle } from \"@capacitor/core\";\n\nexport interface CameraPreviewPlugin {\n initialize(options?: { quality?: number }): Promise<void>;\n getResolution(): Promise<{resolution: string}>;\n setResolution(options: {resolution: number}): Promise<void>;\n getAllCameras(): Promise<{cameras: string[]}>;\n getSelectedCamera(): Promise<{selectedCamera: string}>;\n selectCamera(options: {cameraID: string; }): Promise<void>;\n setScanRegion(options: {region:ScanRegion}): Promise<void>;\n setZoom(options: {factor: number}): Promise<void>;\n setFocus(options: {x: number, y: number}): Promise<void>;\n /**\n * Web Only\n */\n setDefaultUIElementURL(url:string): Promise<void>;\n /**\n * Web Only\n */\n setElement(ele:HTMLElement): Promise<void>;\n startCamera(): Promise<void>;\n stopCamera(): Promise<void>;\n /**\n * take a snapshot as base64.\n */\n takeSnapshot(options:{quality?:number, checkBlur?:boolean}): Promise<{base64:string, isBlur?: boolean}>;\n /**\n * analyze an image for blur detection with detailed confidence scores.\n */\n detectBlur(options:{image: string}): Promise<{isBlur: boolean, blurConfidence: number, sharpConfidence: number}>;\n /**\n * save a frame internally. Android and iOS only.\n */\n saveFrame(): Promise<{success:boolean}>;\n /**\n * take a snapshot on to a canvas. Web Only\n */\n takeSnapshot2(options:{canvas:HTMLCanvasElement,maxLength?:number}): Promise<{scaleRatio?:number}>;\n takePhoto(options: {pathToSave?:string,includeBase64?: boolean}): Promise<{path?:string,base64?:string,blob?:Blob, isBlur?: boolean}>;\n toggleTorch(options: {on: boolean}): Promise<void>;\n /**\n * get the orientation of the device.\n */\n getOrientation(): Promise<{\"orientation\":\"PORTRAIT\"|\"LANDSCAPE\"}>;\n startRecording(): Promise<void>;\n stopRecording(options:{includeBase64?:boolean}): Promise<{path?:string,base64?:string,blob?:Blob}>;\n setLayout(options: {top: string, left:string, width:string, height:string}): Promise<void>;\n requestCameraPermission(): Promise<void>;\n requestMicroPhonePermission(): Promise<void>;\n isOpen():Promise<{isOpen:boolean}>;\n addListener(\n eventName: 'onPlayed',\n listenerFunc: onPlayedListener,\n ): Promise<PluginListenerHandle>;\n addListener(\n eventName: 'onOrientationChanged',\n listenerFunc: onOrientationChangedListener,\n ): Promise<PluginListenerHandle>;\n removeAllListeners(): Promise<void>;\n}\n\nexport type onPlayedListener = (result:{resolution:string}) => void;\nexport type onOrientationChangedListener = () => void;\n\n/**\n * measuredByPercentage: 0 in pixel, 1 in percent\n */\nexport interface ScanRegion{\n left: number;\n top: number;\n right: number;\n bottom: number;\n measuredByPercentage: number;\n}\n\nexport enum EnumResolution {\n RESOLUTION_AUTO = 0,\n RESOLUTION_480P = 1,\n RESOLUTION_720P = 2,\n RESOLUTION_1080P = 3,\n RESOLUTION_2K = 4,\n RESOLUTION_4K = 5\n}\n"]}
package/dist/esm/web.d.ts CHANGED
@@ -58,7 +58,14 @@ export declare class CameraPreviewWeb extends WebPlugin implements CameraPreview
58
58
  checkBlur?: boolean;
59
59
  }): Promise<{
60
60
  base64: string;
61
- blurScore?: number;
61
+ isBlur?: boolean;
62
+ }>;
63
+ detectBlur(options: {
64
+ image: string;
65
+ }): Promise<{
66
+ isBlur: boolean;
67
+ blurConfidence: number;
68
+ sharpConfidence: number;
62
69
  }>;
63
70
  takeSnapshot2(options: {
64
71
  canvas: HTMLCanvasElement;