Skip to content

Commit b2a9caf

Browse files
authored
Merge pull request #50 from bagussubagja/develop
Release 1.0.8 Changelog : 📦 Add packagingOptions with useLegacyPackaging for Android compatibility 🛠️ Fix InputImageConverterError for unsupported image formats User Face Not Found #45 📷 Add configurable camera resolution preset (cameraResolution parameter) ⚡ Improved error handling for ML Kit face detection 🔧 Platform-specific image format optimization (NV21 for Android, BGRA8888 for iOS) PlatformException: Face Detection Not Working #46
2 parents a2f79d7 + 62aef5a commit b2a9caf

File tree

12 files changed

+180
-41
lines changed

12 files changed

+180
-41
lines changed

.github/workflows/ci-cd.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
analyze:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Setup Flutter
16+
uses: subosito/flutter-action@v2
17+
with:
18+
flutter-version: '3.24.5'
19+
channel: 'stable'
20+
21+
- name: Get dependencies
22+
run: flutter pub get
23+
24+
- name: Analyze code
25+
run: flutter analyze --fatal-infos
26+
27+
- name: Run tests
28+
run: flutter test
29+
30+
build-and-release:
31+
needs: analyze
32+
runs-on: ubuntu-latest
33+
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
34+
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- name: Setup Flutter
39+
uses: subosito/flutter-action@v2
40+
with:
41+
flutter-version: '3.24.5'
42+
channel: 'stable'
43+
44+
- name: Setup Java
45+
uses: actions/setup-java@v4
46+
with:
47+
distribution: 'zulu'
48+
java-version: '17'
49+
50+
- name: Get plugin dependencies
51+
run: flutter pub get
52+
53+
- name: Get example dependencies
54+
working-directory: example
55+
run: flutter pub get
56+
57+
- name: Build Android APK
58+
working-directory: example
59+
run: flutter build apk --release
60+
61+
- name: Extract version from pubspec.yaml
62+
id: version
63+
run: |
64+
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //')
65+
echo "version=$VERSION" >> $GITHUB_OUTPUT
66+
echo "Version: $VERSION"
67+
68+
- name: Create Release
69+
id: create_release
70+
uses: actions/create-release@v1
71+
env:
72+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73+
with:
74+
tag_name: v${{ steps.version.outputs.version }}
75+
release_name: Release v${{ steps.version.outputs.version }}
76+
body: |
77+
## Flutter Liveness Detection Plugin v${{ steps.version.outputs.version }}
78+
79+
### Demo App
80+
Download the demo app to try the liveness detection features:
81+
- Android APK: See assets below
82+
83+
### Changes
84+
See [CHANGELOG.md](CHANGELOG.md) for detailed changes.
85+
draft: false
86+
prerelease: false
87+
88+
- name: Upload APK to Release
89+
uses: actions/upload-release-asset@v1
90+
env:
91+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92+
with:
93+
upload_url: ${{ steps.create_release.outputs.upload_url }}
94+
asset_path: example/build/app/outputs/flutter-apk/app-release.apk
95+
asset_name: flutter-liveness-detection-demo-v${{ steps.version.outputs.version }}.apk
96+
asset_content_type: application/vnd.android.package-archive

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.0.8 🚀
2+
3+
- 📦 Add packagingOptions with useLegacyPackaging for Android compatibility
4+
- 🛠️ Fix InputImageConverterError for unsupported image formats
5+
- 📷 Add configurable camera resolution preset (cameraResolution parameter)
6+
- ⚡ Improved error handling for ML Kit face detection
7+
- 🔧 Platform-specific image format optimization (NV21 for Android, BGRA8888 for iOS)
8+
19
## 1.0.7 🚀
210

311
- ⚡ Update google_mlkit_face_detection for better compability to newest flutter version

android/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ android {
3636
targetCompatibility = JavaVersion.VERSION_1_8
3737
}
3838

39+
packagingOptions {
40+
jniLibs {
41+
useLegacyPackaging true
42+
}
43+
}
44+
3945
kotlinOptions {
4046
jvmTarget = JavaVersion.VERSION_1_8
4147
}

example/android/app/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ android {
3030
versionName = flutter.versionName
3131
}
3232

33+
packagingOptions {
34+
jniLibs {
35+
useLegacyPackaging true
36+
}
37+
}
38+
3339
buildTypes {
3440
release {
3541
// TODO: Add your own signing config for the release build.

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@
498498
"$(inherited)",
499499
"@executable_path/Frameworks",
500500
);
501-
MARKETING_VERSION = 1.0.7;
501+
MARKETING_VERSION = 1.0.8;
502502
PRODUCT_BUNDLE_IDENTIFIER = com.bagussubagja.flutterlivenessdetection;
503503
PRODUCT_NAME = "$(TARGET_NAME)";
504504
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -692,7 +692,7 @@
692692
"$(inherited)",
693693
"@executable_path/Frameworks",
694694
);
695-
MARKETING_VERSION = 1.0.7;
695+
MARKETING_VERSION = 1.0.8;
696696
PRODUCT_BUNDLE_IDENTIFIER = com.bagussubagja.flutterlivenessdetection;
697697
PRODUCT_NAME = "$(TARGET_NAME)";
698698
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -720,7 +720,7 @@
720720
"$(inherited)",
721721
"@executable_path/Frameworks",
722722
);
723-
MARKETING_VERSION = 1.0.7;
723+
MARKETING_VERSION = 1.0.8;
724724
PRODUCT_BUNDLE_IDENTIFIER = com.bagussubagja.flutterlivenessdetection;
725725
PRODUCT_NAME = "$(TARGET_NAME)";
726726
PROVISIONING_PROFILE_SPECIFIER = "";

example/lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class _HomeViewState extends State<HomeView> {
6464
.livenessDetection(
6565
context: context,
6666
config: LivenessDetectionConfig(
67+
cameraResolution: ResolutionPreset
68+
.medium, // adjust the quality of image processing
6769
imageQuality: 100, // adjust your image quality result
6870
isEnableMaxBrightness:
6971
true, // enable disable max brightness when taking face photo

example/pubspec.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ packages:
169169
path: ".."
170170
relative: true
171171
source: path
172-
version: "1.0.6"
172+
version: "1.0.8"
173173
flutter_plugin_android_lifecycle:
174174
dependency: transitive
175175
description:
@@ -197,18 +197,18 @@ packages:
197197
dependency: transitive
198198
description:
199199
name: google_mlkit_commons
200-
sha256: "27d626c66a181351a953eba5b6ff1ff123aadb891b4dab085b292118f039d6ac"
200+
sha256: "8f40fbac10685cad4715d11e6a0d86837d9ad7168684dfcad29610282a88e67a"
201201
url: "https://pub.dev"
202202
source: hosted
203-
version: "0.7.1"
203+
version: "0.11.0"
204204
google_mlkit_face_detection:
205205
dependency: transitive
206206
description:
207207
name: google_mlkit_face_detection
208-
sha256: "5b597061cafe4dfa70f66adddadd19381eb88bd3312b074528c62b246392304b"
208+
sha256: f336737d5b8a86797fd4368f42a5c26aeaa9c6dcc5243f0a16b5f6f663cfb70a
209209
url: "https://pub.dev"
210210
source: hosted
211-
version: "0.11.0"
211+
version: "0.13.1"
212212
image:
213213
dependency: transitive
214214
description:

lib/src/core/utils/machine_learning_kit_helper.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,16 @@ class MachineLearningKitHelper {
1919
const maxAttempts = 3;
2020

2121
for (var attempt = 0; attempt < maxAttempts; attempt++) {
22-
final List<Face> faces = await faceDetector.processImage(imgFile);
23-
if (faces.isNotEmpty) return faces;
22+
try {
23+
final List<Face> faces = await faceDetector.processImage(imgFile);
24+
if (faces.isNotEmpty) return faces;
25+
} catch (e) {
26+
debugPrint('Face detection error (attempt ${attempt + 1}): $e');
27+
if (e.toString().contains('InputImageConverterError') ||
28+
e.toString().contains('ImageFormat is not supported')) {
29+
return [];
30+
}
31+
}
2432
}
2533

2634
return [];

lib/src/models/liveness_detection_config.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:camera/camera.dart';
12
import 'package:flutter_liveness_detection_randomized_plugin/src/models/liveness_detection_label_model.dart';
23

34
class LivenessDetectionConfig {
@@ -8,6 +9,7 @@ class LivenessDetectionConfig {
89
final LivenessDetectionLabelModel? customizedLabel;
910
final bool isEnableMaxBrightness;
1011
final int imageQuality;
12+
final ResolutionPreset cameraResolution;
1113

1214
LivenessDetectionConfig({
1315
this.startWithInfoScreen = false,
@@ -17,5 +19,6 @@ class LivenessDetectionConfig {
1719
this.customizedLabel,
1820
this.isEnableMaxBrightness = true,
1921
this.imageQuality = 100,
22+
this.cameraResolution = ResolutionPreset.high,
2023
});
2124
}

lib/src/presentation/views/liveness_detection_view.dart

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// ignore_for_file: depend_on_referenced_packages
2-
import 'package:flutter/foundation.dart';
32
import 'package:flutter_liveness_detection_randomized_plugin/index.dart';
43
import 'package:flutter_liveness_detection_randomized_plugin/src/core/constants/liveness_detection_step_constant.dart';
54
import 'package:collection/collection.dart';
@@ -260,8 +259,14 @@ class _LivenessDetectionScreenState extends State<LivenessDetectionView> {
260259

261260
void _startLiveFeed() async {
262261
final camera = availableCams[_cameraIndex];
263-
_cameraController =
264-
CameraController(camera, ResolutionPreset.high, enableAudio: false);
262+
_cameraController = CameraController(
263+
camera,
264+
widget.config.cameraResolution,
265+
enableAudio: false,
266+
imageFormatGroup: Platform.isAndroid
267+
? ImageFormatGroup.nv21
268+
: ImageFormatGroup.bgra8888,
269+
);
265270

266271
_cameraController?.initialize().then((_) {
267272
if (!mounted) return;
@@ -278,39 +283,44 @@ class _LivenessDetectionScreenState extends State<LivenessDetectionView> {
278283
}
279284

280285
Future<void> _processCameraImage(CameraImage cameraImage) async {
281-
final WriteBuffer allBytes = WriteBuffer();
282-
for (final Plane plane in cameraImage.planes) {
283-
allBytes.putUint8List(plane.bytes);
284-
}
285-
final bytes = allBytes.done().buffer.asUint8List();
286-
287-
final Size imageSize = Size(
288-
cameraImage.width.toDouble(),
289-
cameraImage.height.toDouble(),
290-
);
291-
292286
final camera = availableCams[_cameraIndex];
293287
final imageRotation =
294288
InputImageRotationValue.fromRawValue(camera.sensorOrientation);
295289
if (imageRotation == null) return;
296290

297-
final inputImageFormat =
298-
InputImageFormatValue.fromRawValue(cameraImage.format.raw);
299-
if (inputImageFormat == null) return;
300-
301-
final inputImageData = InputImageMetadata(
302-
size: imageSize,
303-
rotation: imageRotation,
304-
format: inputImageFormat,
305-
bytesPerRow: cameraImage.planes[0].bytesPerRow,
306-
);
291+
InputImage? inputImage;
307292

308-
final inputImage = InputImage.fromBytes(
309-
metadata: inputImageData,
310-
bytes: bytes,
311-
);
293+
if (Platform.isAndroid) {
294+
if (cameraImage.format.group == ImageFormatGroup.nv21) {
295+
inputImage = InputImage.fromBytes(
296+
bytes: cameraImage.planes[0].bytes,
297+
metadata: InputImageMetadata(
298+
size: Size(
299+
cameraImage.width.toDouble(), cameraImage.height.toDouble()),
300+
rotation: imageRotation,
301+
format: InputImageFormat.nv21,
302+
bytesPerRow: cameraImage.planes[0].bytesPerRow,
303+
),
304+
);
305+
}
306+
} else if (Platform.isIOS) {
307+
if (cameraImage.format.group == ImageFormatGroup.bgra8888) {
308+
inputImage = InputImage.fromBytes(
309+
bytes: cameraImage.planes[0].bytes,
310+
metadata: InputImageMetadata(
311+
size: Size(
312+
cameraImage.width.toDouble(), cameraImage.height.toDouble()),
313+
rotation: imageRotation,
314+
format: InputImageFormat.bgra8888,
315+
bytesPerRow: cameraImage.planes[0].bytesPerRow,
316+
),
317+
);
318+
}
319+
}
312320

313-
_processImage(inputImage);
321+
if (inputImage != null) {
322+
_processImage(inputImage);
323+
}
314324
}
315325

316326
Future<void> _processImage(InputImage inputImage) async {

0 commit comments

Comments
 (0)