sprintfacedetection

Fast, native face detection for Flutter applications powered by Google ML Kit (Android) and Apple Vision (iOS).

Features

  • ⚡ High Performance Native Stream Analysis: Directly processes CameraImage video stream frames in Kotlin (Android) and Swift (iOS) with minimal bridge overhead.
  • 👤 Facial Attributes & Landmarks: Extracts head pose angles (Pitch, Yaw, Roll), eye openness (left/right), smile probability, and mouth openness.
  • 💡 Luminance Detection: Calculates real-time ambient lighting levels directly from image buffers.
  • 🖼️ File Face Detection: Analyzes static image files (detectFaceInImageFile) using native platform ML Kit / Vision algorithms.
  • 🔒 Privacy-First On-Device AI: Zero network calls, 100% on-device processing.

Installation

Add sprintfacedetection to your pubspec.yaml:

dependencies:
  sprintfacedetection: ^0.1.0
  camera: ^0.12.1

Platform Setup

Android (android/app/src/main/AndroidManifest.xml)

Add camera permissions:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

iOS (ios/Runner/Info.plist)

Add camera usage description:

<key>NSCameraUsageDescription</key>
<string>Camera access is required for real-time face detection.</string>

Usage

1. Real-Time Camera Stream Analysis

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:sprintfacedetection/sprintfacedetection.dart';

class LiveFaceDetectionExample extends StatefulWidget {
  const LiveFaceDetectionExample({super.key});

  @override
  State<LiveFaceDetectionExample> createState() => _LiveFaceDetectionExampleState();
}

class _LiveFaceDetectionExampleState extends State<LiveFaceDetectionExample> {
  final FaceDetectionService _service = FaceDetectionService();
  CameraController? _controller;
  CustomFace? _detectedFace;
  bool _isProcessing = false;

  @override
  void initState() {
    super.initState();
    _initCamera();
  }

  Future<void> _initCamera() async {
    final cameras = await availableCameras();
    if (cameras.isEmpty) return;

    final frontCam = cameras.firstWhere(
      (c) => c.lensDirection == CameraLensDirection.front,
      orElse: () => cameras.first,
    );

    _controller = CameraController(
      frontCam,
      ResolutionPreset.medium,
      enableAudio: false,
    );

    await _controller!.initialize();

    _controller!.startImageStream((CameraImage image) async {
      if (_isProcessing) return;
      _isProcessing = true;

      try {
        final CustomFace? face = await _service.analyse(image, frontCam);
        if (mounted) {
          setState(() => _detectedFace = face);
        }
      } finally {
        _isProcessing = false;
      }
    });
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_controller == null || !_controller!.value.isInitialized) {
      return const Center(child: CircularProgressIndicator());
    }

    return Column(
      children: [
        Expanded(child: CameraPreview(_controller!)),
        if (_detectedFace != null) ...[
          Text('Face Detected! ID: ${_detectedFace!.trackingId}'),
          Text('Pitch: ${_detectedFace!.headEulerAngleX?.toStringAsFixed(1)}°'),
          Text('Yaw: ${_detectedFace!.headEulerAngleY?.toStringAsFixed(1)}°'),
          Text('Left Eye Open: ${(_detectedFace!.leftEyeOpenProbability ?? 0) * 100}%'),
          Text('Smile Probability: ${(_detectedFace!.smilingProbability ?? 0) * 100}%'),
        ],
      ],
    );
  }
}

2. Detect Face in Static Image File

import 'package:sprintfacedetection/sprintfacedetection.dart';

Future<void> analyzeImageFile(String filePath) async {
  final service = FaceDetectionService();
  final Rect? faceBoundingBox = await service.detectFaceInImageFile(filePath);

  if (faceBoundingBox != null) {
    print('Face detected at bounding box: $faceBoundingBox');
    print('Width: ${faceBoundingBox.width}, Height: ${faceBoundingBox.height}');
  } else {
    print('No face found in image file.');
  }
}

CustomFace Data Class

When a face is detected in a stream, analyse() returns a CustomFace object containing:

Property Type Description
trackingId int? Unique face tracking ID assigned by native ML Kit / Vision detector
boundingBox Rect Bounding box rectangle of the detected face in image coordinates
headEulerAngleX double? Head pitch angle (tilt up/down) in degrees
headEulerAngleY double? Head yaw angle (turn left/right) in degrees
headEulerAngleZ double? Head roll angle (tilt side) in degrees
leftEyeOpenProbability double? Probability that the left eye is open ($0.0 \dots 1.0$)
rightEyeOpenProbability double? Probability that the right eye is open ($0.0 \dots 1.0$)
smilingProbability double? Probability that the face is smiling ($0.0 \dots 1.0$)
mouthOpenProbability double? Probability that the mouth is open ($0.0 \dots 1.0$)
luminance double? Average lighting level computed directly from frame buffer

Example Project

A complete runnable example project is available in the example/ folder.