local_face_detection 0.0.1 copy "local_face_detection: ^0.0.1" to clipboard
local_face_detection: ^0.0.1 copied to clipboard

High-performance on-device face detection with ONNX. Detect faces, landmarks, and scores—100% offline, no cloud APIs. Privacy-first and blazing fast.

example/lib/main.dart

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:local_face_detection/local_face_detection.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _plugin = LocalFaceDetection();
  String _platformVersion = 'Unknown';
  bool _initializing = false;
  bool _detecting = false;
  Uint8List? _imageBytes;
  FaceDetectionResult? _result;
  String? _error;

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

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    setState(() => _initializing = true);
    try {
      final version =
          await _plugin.getPlatformVersion() ?? 'Unknown platform version';
      await _plugin.initialize();
      if (!mounted) return;
      setState(() {
        _platformVersion = version;
        _error = null;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() => _error = 'Init failed: $e');
    } finally {
      if (mounted) setState(() => _initializing = false);
    }
  }

  Future<void> _pickImage() async {
    setState(() => _error = null);
    try {
      final picker = ImagePicker();
      final file = await picker.pickImage(source: ImageSource.gallery);
      if (file == null) return;
      final bytes = await file.readAsBytes();
      setState(() {
        _imageBytes = bytes;
        _result = null;
      });
      await _runDetection(bytes);
    } catch (e) {
      setState(() => _error = 'Image pick failed: $e');
    }
  }

  Future<void> _runDetection(Uint8List bytes) async {
    setState(() => _detecting = true);
    try {
      final res = await _plugin.detectFaces(
        bytes,
        scoreThreshold: 0.55,
        nmsThreshold: 0.4,
      );
      if (!mounted) return;
      setState(() => _result = res);
    } catch (e) {
      if (!mounted) return;
      setState(() => _error = 'Detection failed: $e');
    } finally {
      if (mounted) setState(() => _detecting = false);
    }
  }

  @override
  void dispose() {
    _plugin.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final imageBytes = _imageBytes;
    final result = _result;
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Local Face Detection Demo')),
        body: Padding(
          padding: const EdgeInsets.all(12),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Platform: $_platformVersion'),
              if (_initializing) const LinearProgressIndicator(),
              const SizedBox(height: 8),
              Row(
                children: [
                  ElevatedButton.icon(
                    onPressed: _initializing ? null : initPlatformState,
                    icon: const Icon(Icons.play_circle_fill),
                    label: const Text('Initialize'),
                  ),
                  const SizedBox(width: 8),
                  ElevatedButton.icon(
                    onPressed:
                        (_initializing || _detecting) ? null : _pickImage,
                    icon: const Icon(Icons.photo_library),
                    label: const Text('Pick Image'),
                  ),
                ],
              ),
              if (_detecting)
                const Padding(
                  padding: EdgeInsets.only(top: 8.0),
                  child: LinearProgressIndicator(),
                ),
              if (_error != null)
                Padding(
                  padding: const EdgeInsets.only(top: 8.0),
                  child: Text(
                    _error!,
                    style: const TextStyle(color: Colors.red),
                  ),
                ),
              const SizedBox(height: 12),
              Expanded(
                child: Center(
                  child:
                      imageBytes == null
                          ? const Text('Select an image to run detection.')
                          : AspectRatio(
                            aspectRatio: _computeAspectRatio(result) ?? 1,
                            child: Stack(
                              fit: StackFit.expand,
                              children: [
                                Image.memory(imageBytes, fit: BoxFit.contain),
                                if (result != null && result.hasFaces)
                                  CustomPaint(
                                    painter: _FaceOverlayPainter(result),
                                  ),
                              ],
                            ),
                          ),
                ),
              ),
              if (result != null) Text('Faces: ${result.detections.length}'),
            ],
          ),
        ),
      ),
    );
  }

  double? _computeAspectRatio(FaceDetectionResult? res) {
    if (res == null || res.originalWidth == 0 || res.originalHeight == 0)
      return null;
    return res.originalWidth / res.originalHeight;
  }
}

class _FaceOverlayPainter extends CustomPainter {
  _FaceOverlayPainter(this.result);
  final FaceDetectionResult result;

  @override
  void paint(Canvas canvas, Size size) {
    if (!result.hasFaces) return;
    final scaleX = size.width / result.originalWidth;
    final scaleY = size.height / result.originalHeight;

    final boxPaint =
        Paint()
          ..color = Colors.greenAccent
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2.0;
    final landmarkPaint =
        Paint()
          ..color = Colors.yellowAccent
          ..style = PaintingStyle.fill;

    for (final face in result.detections) {
      final rect = Rect.fromLTRB(
        face.boundingBox.left * scaleX,
        face.boundingBox.top * scaleY,
        face.boundingBox.right * scaleX,
        face.boundingBox.bottom * scaleY,
      );
      canvas.drawRect(rect, boxPaint);
      for (final lm in face.landmarks) {
        canvas.drawCircle(
          Offset(lm.dx * scaleX, lm.dy * scaleY),
          3,
          landmarkPaint,
        );
      }
    }
  }

  @override
  bool shouldRepaint(covariant _FaceOverlayPainter oldDelegate) =>
      oldDelegate.result != result;
}
3
likes
0
points
24
downloads

Publisher

unverified uploader

Weekly Downloads

High-performance on-device face detection with ONNX. Detect faces, landmarks, and scores—100% offline, no cloud APIs. Privacy-first and blazing fast.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, flutter_onnxruntime, path_provider, plugin_platform_interface

More

Packages that depend on local_face_detection

Packages that implement local_face_detection