Closed SanketKudale closed 1 month ago
I am using face smiling and left and right eye open probability code which works in debug mode but not giving proper probability in release mode or profile mode // ignore_for_file: unused_field, unnecessary_null_comparison
import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:auto_route/auto_route.dart'; import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_face_api/face_api.dart' as face_api; import 'package:google_ml_vision/google_ml_vision.dart'; import 'package:image/image.dart' as img; import 'package:onboarding/routes/app_router.dart'; import 'package:onboarding/utils/colors.dart'; import 'package:onboarding/utils/notification/notification_manager.dart'; import 'package:onboarding/utils/utils.dart'; import 'package:onboarding/view_models/kyc/ekyc_view_model.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import '../../utils/ekyc/indian_document.dart'; double _overlayWidth = 0.0; double _overlayHeight = 0.0; @RoutePage() class LivenessScanPage extends StatefulWidget { const LivenessScanPage({Key? key}) : super(key: key); @override State<LivenessScanPage> createState() => _LivenessScanState(); } class _LivenessScanState extends State<LivenessScanPage> with WidgetsBindingObserver { CameraController? _controller; late GoogleVision _vision; bool _isBlinking = false; bool _isSmiling = false; List<CameraDescription>? cameras; final cameraResolution = ResolutionPreset.veryHigh; bool _task1Message = false; bool _task2Message = false; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _initializeCamera(); _vision = GoogleVision.instance; } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.detached: break; case AppLifecycleState.resumed: break; case AppLifecycleState.inactive: triggerNotification(); break; case AppLifecycleState.hidden: break; case AppLifecycleState.paused: break; } } void _initializeCamera() async { availableCameras().then((cameras) { CameraDescription backDescription = cameras.firstWhere( (camera) => camera.lensDirection == CameraLensDirection.front, orElse: () => throw Exception('No front camera found'), ); _controller = CameraController(backDescription, cameraResolution); _controller?.initialize().then((_) { if (!mounted) return; setState(() { _detectLiveness(); }); }).catchError((e) { if (kDebugMode) { print('Error initializing camera: $e'); } }); }).catchError((e) { if (kDebugMode) { print('Error getting available cameras: $e'); } }); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); clearCacheDirectory(); _controller!.dispose(); super.dispose(); } Widget _cameraPreviewWidget() { if (_controller == null || !_controller!.value.isInitialized) { return const Text( 'Tap a camera', style: TextStyle( color: Colors.white, fontSize: 24.0, fontWeight: FontWeight.w900, ), ); } return Stack( alignment: Alignment.center, children: <Widget>[ (_controller?.value.isInitialized ?? false) ? CameraPreview(_controller!) : Container(), LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { _overlayWidth = constraints.maxWidth; _overlayHeight = constraints.maxHeight; return CustomPaint( size: Size(constraints.maxWidth, constraints.maxHeight), painter: OverlayPainter(), ); }, ), ], ); } void _detectLiveness() async { try { String percent = "initial"; if (!(_controller?.value.isInitialized ?? false)) { if (kDebugMode) { print('Error: Camera not initialized'); } return; } XFile image = await _controller!.takePicture(); final GoogleVisionImage visionImage = GoogleVisionImage.fromFile(File(image.path)); final FaceDetector faceDetector = _vision.faceDetector( const FaceDetectorOptions( enableTracking: true, enableContours: true, enableClassification: true)); final List<Face> faces = await faceDetector.processImage(visionImage); Face face = faces.first; if (_task1Message == false) { if (face.leftEyeOpenProbability != null && face.rightEyeOpenProbability != null) { log("Left Eye - ${face.leftEyeOpenProbability} Right Eye - ${face.rightEyeOpenProbability}", level: 2000); if (face.leftEyeOpenProbability! < 0.97 && face.rightEyeOpenProbability! < 0.97) { setState(() { _isBlinking = true; _task1Message = true; _detectLiveness(); }); } else { setState(() { _isBlinking = false; }); _detectLiveness(); } } else { _detectLiveness(); } } else { if (_task2Message == false) { if (face.smilingProbability != null) { log("Face Smiling Probability - ${face.smilingProbability}", level: 2000); print("Face Smiling Probability - ${face.smilingProbability}"); if (face.smilingProbability! > 0.9) { setState(() async { _isSmiling = true; _task2Message = true; final Directory tempDir = await getTemporaryDirectory(); final String cacheDirPath = tempDir.path; String filePath = '$cacheDirPath/live_face.png'; img.Image? originalImage = img.decodeImage(await File(image.path).readAsBytes()); if (originalImage != null) { File imageFile = await File(filePath) .writeAsBytes(img.encodePng(originalImage)); detectAndCropFace(imageFile, live: true); face_api.MatchFacesImage? image1 = await _convertTempFileToMatchFacesImage( '$cacheDirPath/live_face.png'); face_api.MatchFacesImage? image2 = await _convertTempFileToMatchFacesImage( '$cacheDirPath/card_face.png'); percent = await _matchFaces(image1!, image2!); } else { percent = "Original Image Null"; } print("Sake Percent - $percent"); clearCacheDirectory(); await context.router .popAndPush(KycApprovalRoute(percent: percent.toString())); }); } else { _detectLiveness(); setState(() {}); } } else { _detectLiveness(); } } } } catch (e) { if (kDebugMode) { print('Error: $e'); _detectLiveness(); } } } Future<face_api.MatchFacesImage?> _convertTempFileToMatchFacesImage( String filePath) async { try { File file = File(filePath); if (!await file.exists()) { if (kDebugMode) { print('File does not exist'); } return null; } final bytes = await file.readAsBytes(); final bitmap = base64Encode(bytes); return face_api.MatchFacesImage() ..bitmap = bitmap ..imageType = 1 ..detectAll = true; } catch (e) { if (kDebugMode) { print('Error converting file: $e'); } return null; } } Future<face_api.MatchFacesImage> _convertXFileToMatchFacesImage( XFile file) async { final bytes = await file.readAsBytes(); final bitmap = base64Encode(bytes); return face_api.MatchFacesImage() ..bitmap = bitmap ..imageType = 1 ..detectAll = true; } Future<String> _matchFaces( face_api.MatchFacesImage image1, face_api.MatchFacesImage image2) async { if (image1.bitmap == null || image1.bitmap == "") { if (kDebugMode) { print("Image 1 null"); } return Future.value("Image 1 not found"); } if (image2.bitmap == null || image2.bitmap == "") { if (kDebugMode) { print("Image 2 null"); } return Future.value("Image 2 not found"); } var request = face_api.MatchFacesRequest(); request.images = [image1, image2]; String srcValue = await face_api.FaceSDK.matchFaces(jsonEncode(request)); var response = face_api.MatchFacesResponse.fromJson(json.decode(srcValue)); String respValue = await face_api.FaceSDK.matchFacesSimilarityThresholdSplit( jsonEncode(response!.results), 0.35); face_api.MatchFacesSimilarityThresholdSplit? split = face_api.MatchFacesSimilarityThresholdSplit.fromJson( json.decode(respValue)); if (split != null) { if (split.matchedFaces.isEmpty) { return Future.value("Matched Faces are Empty"); } else { String similarity = "${(split.matchedFaces[0]!.similarity! * 100).toStringAsFixed(2)}%"; return Future.value(similarity); } } else { return Future.value("Split is Null"); } } @override Widget build(BuildContext context) { if (!(_controller?.value.isInitialized ?? false)) { return Container(); } return PopScope( onPopInvoked: (bool value) { dispose(); }, child: Scaffold( backgroundColor: Colors.white, body: Stack(children: [ _cameraPreviewWidget(), Align( alignment: Alignment.topCenter, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (_task1Message == false) taskMessage("!! Please blink !!"), if (_task1Message == true && _task2Message == false) taskMessage("!! Please Smile !!") ], ), ), ]), ), ); } taskMessage(String msg) { return Container( padding: const EdgeInsets.all(10), margin: const EdgeInsets.only(top: 80), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow(offset: const Offset(4, 4), color: grey), BoxShadow(offset: const Offset(-2, -2), color: grey) ]), child: Text( msg, style: const TextStyle(fontSize: 23), ), ); } triggerNotification() async { /*NotificationManager.instance.scheduleNotification(5, "Onboarding KYC", "Proceed with the face scanning", "face_scan");*/ String customerId = await getCustomerId(); await incrementLoginCount(); context.read<EkycViewModel>().statusUpdateAPI(customerId, "FACE_DETECTION_FAILED"); } } class OverlayPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.green ..style = PaintingStyle.fill; final paint1 = Paint() ..color = primary ..style = PaintingStyle.stroke ..strokeWidth = 6 ..strokeJoin = StrokeJoin.miter ..strokeCap = StrokeCap.square; canvas.drawCircle( Offset(_overlayWidth * 0.5, _overlayHeight * 0.5), 160, paint1); var path = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height)); var documentCircle = Rect.fromCircle( center: Offset(_overlayWidth * 0.5, _overlayHeight * 0.5), radius: 160); path.addOval(documentCircle); path.fillType = PathFillType.evenOdd; canvas.drawPath(path, paint..color = Colors.white); } @override bool shouldRepaint(CustomPainter oldDelegate) => false; } ```
I am using face smiling and left and right eye open probability code which works in debug mode but not giving proper probability in release mode or profile mode // ignore_for_file: unused_field, unnecessary_null_comparison