diff --git a/CHANGELOG.md b/CHANGELOG.md index 904c7e1..2016a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,16 @@ +## 0.1.3 + +- Updated dependencies. +- Fixed face detection issue on some Android devices. +- Changed minimum iOS requirement to 15.5.0. +- Added `setZoomLevel` control to the `FaceCameraController`. +- Added `ignoreFacePositioning` allowing developers to trigger `onCapture` even when the face is not well positioned. +- Modified `README.md`. + ## 0.1.2 - Updated dependencies. -- Fixed face detection issue on some Android devices +- Fixed face detection issue on some Android devices. **DEPRECATIONS** diff --git a/README.md b/README.md index 5e73f66..6064b8e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ face_camera: ^ ### iOS --- -* Minimum iOS Deployment Target: 11.0 +* Minimum iOS Deployment Target: 15.5.0 * Follow this link and setup `ML Kit` this is required for `face_camera` to function properly on `iOS` Add two rows to the `ios/Runner/Info.plist:` @@ -115,19 +115,22 @@ Here is a list of properties available to customize your widget: | autoDisableCaptureControl | bool | set true to disable capture control widget when no face is detected | +\ +\ Here is a list of properties available to customize your widget from the controller: -| Name | Type | Description | -|---------------------------|-------------------------|-------------------------------------------------------------------------------| -| onCapture | Function(File?) | callback invoked when camera captures image | -| onFaceDetected | Function(DetectedFace?) | callback invoked when camera detects face | -| imageResolution | ImageResolution | use this to set image resolution | -| defaultCameraLens | CameraLens | use this to set initial camera lens direction | -| defaultFlashMode | CameraFlashMode | use this to set initial flash mode | -| enableAudio | bool | set false to disable capture sound | -| autoCapture | bool | set true to capture image on face detected | -| orientation | CameraOrientation | use this to lock camera orientation | -| performanceMode | FaceDetectorMode | Use this to set your preferred performance mode | +| Name | Type | Description | +|-----------------------|-------------------------|-------------------------------------------------------------------------| +| onCapture | Function(File?) | callback invoked when camera captures image | +| onFaceDetected | Function(DetectedFace?) | callback invoked when camera detects face | +| imageResolution | ImageResolution | use this to set image resolution | +| defaultCameraLens | CameraLens | use this to set initial camera lens direction | +| defaultFlashMode | CameraFlashMode | use this to set initial flash mode | +| enableAudio | bool | set false to disable capture sound | +| autoCapture | bool | set true to capture image on face detected | +| ignoreFacePositioning | bool | set true to trigger onCapture even when the face is not well positioned | +| orientation | CameraOrientation | use this to lock camera orientation | +| performanceMode | FaceDetectorMode | Use this to set your preferred performance mode | ### Contributions diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 9c1b676..6e13cd7 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,9 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion @@ -62,7 +60,3 @@ android { flutter { source '../..' } - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/build.gradle b/example/android/build.gradle index 713d7f6..bc157bd 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bc..b3c40a8 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.2.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" \ No newline at end of file diff --git a/example/ios/Podfile b/example/ios/Podfile index 8c0332c..bd73bdf 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '15.5.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b2053fd..4716c87 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -4,62 +4,58 @@ PODS: - face_camera (0.0.1): - Flutter - Flutter (1.0.0) - - google_mlkit_commons (0.7.1): + - google_mlkit_commons (0.9.0): - Flutter - MLKitVision - - google_mlkit_face_detection (0.11.0): + - google_mlkit_face_detection (0.12.0): - Flutter - google_mlkit_commons - - GoogleMLKit/FaceDetection (~> 6.0.0) - - GoogleDataTransport (9.4.1): - - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30911.0, >= 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - - GoogleMLKit/FaceDetection (6.0.0): + - GoogleMLKit/FaceDetection (~> 7.0.0) + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) + - GoogleMLKit/FaceDetection (7.0.0): - GoogleMLKit/MLKitCore - - MLKitFaceDetection (~> 5.0.0) - - GoogleMLKit/MLKitCore (6.0.0): - - MLKitCommon (~> 11.0.0) + - MLKitFaceDetection (~> 6.0.0) + - GoogleMLKit/MLKitCore (7.0.0): + - MLKitCommon (~> 12.0.0) - GoogleToolboxForMac/Defines (4.2.1) - GoogleToolboxForMac/Logger (4.2.1): - GoogleToolboxForMac/Defines (= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (4.2.1)": - GoogleToolboxForMac/Defines (= 4.2.1) - - GoogleUtilities/Environment (7.13.3): + - GoogleUtilities/Environment (8.0.2): - GoogleUtilities/Privacy - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.13.3): + - GoogleUtilities/Logger (8.0.2): - GoogleUtilities/Environment - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (7.13.3) - - GoogleUtilities/UserDefaults (7.13.3): + - GoogleUtilities/Privacy (8.0.2) + - GoogleUtilities/UserDefaults (8.0.2): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilitiesComponents (1.1.0): - - GoogleUtilities/Logger - - GTMSessionFetcher/Core (3.4.1) - - MLImage (1.0.0-beta5) - - MLKitCommon (11.0.0): - - GoogleDataTransport (< 10.0, >= 9.4.1) + - GTMSessionFetcher/Core (3.5.0) + - MLImage (1.0.0-beta6) + - MLKitCommon (12.0.0): + - GoogleDataTransport (~> 10.0) - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - - GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0) - - GoogleUtilitiesComponents (~> 1.0) + - GoogleUtilities/Logger (~> 8.0) + - GoogleUtilities/UserDefaults (~> 8.0) - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLKitFaceDetection (5.0.0): - - MLKitCommon (~> 11.0) - - MLKitVision (~> 7.0) - - MLKitVision (7.0.0): + - MLKitFaceDetection (6.0.0): + - MLKitCommon (~> 12.0) + - MLKitVision (~> 8.0) + - MLKitVision (8.0.0): - GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1) - "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)" - GTMSessionFetcher/Core (< 4.0, >= 3.3.2) - - MLImage (= 1.0.0-beta5) - - MLKitCommon (~> 11.0) - - nanopb (2.30910.0): - - nanopb/decode (= 2.30910.0) - - nanopb/encode (= 2.30910.0) - - nanopb/decode (2.30910.0) - - nanopb/encode (2.30910.0) + - MLImage (= 1.0.0-beta6) + - MLKitCommon (~> 12.0) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) - PromisesObjC (2.4.0) DEPENDENCIES: @@ -75,7 +71,6 @@ SPEC REPOS: - GoogleMLKit - GoogleToolboxForMac - GoogleUtilities - - GoogleUtilitiesComponents - GTMSessionFetcher - MLImage - MLKitCommon @@ -98,23 +93,22 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: camera_avfoundation: 759172d1a77ae7be0de08fc104cfb79738b8a59e - face_camera: 9473f8c80e20d67bd2d946ed573ccee168d31186 + face_camera: 787c50bc183b37c9b6f9777a1416e5b384b5c234 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - google_mlkit_commons: 96aaca445520311b84a2da013dedf3427fe4cc69 - google_mlkit_face_detection: b760d6035222630f347352b3b13f4a23ea9fb994 - GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a - GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065 + google_mlkit_commons: 384e4e206e122b6dad430d3158205e0b2fac6789 + google_mlkit_face_detection: ff627695d8eba051db7e0f13f7b20d802df1f84c + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleMLKit: eff9e23ec1d90ea4157a1ee2e32a4f610c5b3318 GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8 - GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 - GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe - GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd - MLImage: 1824212150da33ef225fbd3dc49f184cf611046c - MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1 - MLKitFaceDetection: 7c0e8bf09ddd27105da32d088fca978a99fc30cc - MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 - nanopb: 438bc412db1928dac798aa6fd75726007be04262 + GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + MLImage: 0ad1c5f50edd027672d8b26b0fee78a8b4a0fc56 + MLKitCommon: 07c2c33ae5640e5380beaaa6e4b9c249a205542d + MLKitFaceDetection: 2a593db4837db503ad3426b565e7aab045cefea5 + MLKitVision: 45e79d68845a2de77e2dd4d7f07947f0ed157b0e + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 -PODFILE CHECKSUM: 0d895a65d153cd6b7f421b4d23b29c977b9f03bc +PODFILE CHECKSUM: b608f9f38d697f18324c25277a50716179fc3f67 COCOAPODS: 1.15.2 diff --git a/example/pubspec.lock b/example/pubspec.lock index 013d30b..376ee81 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: camera - sha256: "2170a943dcb67be2af2c6bcda8775e74b41d4c02d6a4eb10bdc832ee185c4eea" + sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167" url: "https://pub.dev" source: hosted - version: "0.11.0+1" + version: "0.11.0+2" camera_android_camerax: dependency: transitive description: @@ -103,7 +103,7 @@ packages: path: ".." relative: true source: path - version: "0.1.1" + version: "0.1.2" fake_async: dependency: transitive description: @@ -147,18 +147,18 @@ packages: dependency: transitive description: name: google_mlkit_commons - sha256: "27d626c66a181351a953eba5b6ff1ff123aadb891b4dab085b292118f039d6ac" + sha256: "7e9a6d6e66b44aa8cfe944bda9bc3346c52486dd890ca49e5bc98845cda40d7f" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.9.0" google_mlkit_face_detection: dependency: transitive description: name: google_mlkit_face_detection - sha256: "5b597061cafe4dfa70f66adddadd19381eb88bd3312b074528c62b246392304b" + sha256: "65988405c884fd84a4ccc8bded7b5e3e4c33362f6f4eaaa94818bdaaba7bab7d" url: "https://pub.dev" source: hosted - version: "0.11.0" + version: "0.12.0" js: dependency: transitive description: @@ -317,5 +317,5 @@ packages: source: hosted version: "13.0.0" sdks: - dart: ">=3.2.3 <4.0.0" - flutter: ">=3.16.6" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/ios/face_camera.podspec b/ios/face_camera.podspec index 07a50a8..e5733f2 100644 --- a/ios/face_camera.podspec +++ b/ios/face_camera.podspec @@ -15,7 +15,7 @@ A Flutter camera plugin that detects face in real-time. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.platform = :ios, '10.0' + s.platform = :ios, '15.5.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/lib/src/controllers/face_camera_controller.dart b/lib/src/controllers/face_camera_controller.dart index f122f46..36a86b3 100644 --- a/lib/src/controllers/face_camera_controller.dart +++ b/lib/src/controllers/face_camera_controller.dart @@ -19,6 +19,7 @@ class FaceCameraController extends ValueNotifier { this.defaultFlashMode = CameraFlashMode.auto, this.enableAudio = true, this.autoCapture = false, + this.ignoreFacePositioning = false, this.orientation = CameraOrientation.portraitUp, this.performanceMode = FaceDetectorMode.fast, required this.onCapture, @@ -40,6 +41,9 @@ class FaceCameraController extends ValueNotifier { /// Set true to capture image on face detected. final bool autoCapture; + /// Set true to trigger onCapture even when the face is not well positioned + final bool ignoreFacePositioning; + /// Use this to lock camera orientation. final CameraOrientation? orientation; @@ -117,6 +121,15 @@ class FaceCameraController extends ValueNotifier { }); } + /// The supplied [zoom] value should be between 1.0 and the maximum supported + Future setZoomLevel(double zoom) async { + final CameraController? cameraController = value.cameraController; + if (cameraController == null) { + return; + } + await cameraController.setZoomLevel(zoom); + } + Future changeCameraLens() async { value = value.copyWith( currentCameraLens: @@ -183,11 +196,12 @@ class FaceCameraController extends ValueNotifier { if (result != null) { try { - if (result.wellPositioned) { + if (result.face != null) { onFaceDetected?.call(result.face); - if (autoCapture) { - captureImage(); - } + } + if (autoCapture && + (result.wellPositioned || ignoreFacePositioning)) { + captureImage(); } } catch (e) { logError(e.toString()); diff --git a/lib/src/handlers/face_identifier.dart b/lib/src/handlers/face_identifier.dart index c78b538..d380636 100644 --- a/lib/src/handlers/face_identifier.dart +++ b/lib/src/handlers/face_identifier.dart @@ -82,7 +82,7 @@ class FaceIdentifier { metadata: InputImageMetadata( size: Size(image.width.toDouble(), image.height.toDouble()), rotation: rotation, // used only in Android - format: format, // used only in iOS + format: Platform.isIOS ? format : InputImageFormat.nv21, bytesPerRow: image.planes.first.bytesPerRow, // used only in iOS ), ); @@ -117,12 +117,12 @@ class FaceIdentifier { detectedFace = face; // Head is rotated to the right rotY degrees - if (face.headEulerAngleY! > 2 || face.headEulerAngleY! < -2) { + if (face.headEulerAngleY! > 5 || face.headEulerAngleY! < -5) { wellPositioned = false; } // Head is tilted sideways rotZ degrees - if (face.headEulerAngleZ! > 2 || face.headEulerAngleZ! < -2) { + if (face.headEulerAngleZ! > 5 || face.headEulerAngleZ! < -5) { wellPositioned = false; } @@ -157,8 +157,15 @@ class FaceIdentifier { wellPositioned = false; } } + + if (wellPositioned) { + break; + } } - return DetectedFace(wellPositioned: wellPositioned, face: detectedFace); + return DetectedFace( + wellPositioned: wellPositioned, + face: detectedFace, + ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 34a5c3a..8d49fca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,8 +10,8 @@ environment: dependencies: flutter: sdk: flutter - camera: ^0.11.0+1 - google_mlkit_face_detection: ^0.11.0 + camera: ^0.11.0+2 + google_mlkit_face_detection: ^0.12.0 dev_dependencies: flutter_test: