mediapipe_face_mesh 1.2.2 copy "mediapipe_face_mesh: ^1.2.2" to clipboard
mediapipe_face_mesh: ^1.2.2 copied to clipboard

Flutter plugin for MediaPipe Face Mesh inference on Android/iOS, supporting RGBA and NV21 inputs via an FFI-powered TFLite core.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:image/image.dart' as img;
import 'package:mediapipe_face_mesh/mediapipe_face_mesh.dart';

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

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

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

class _FaceMeshPainter extends CustomPainter {
  _FaceMeshPainter({required this.landmarks});

  final List<FaceMeshLandmark> landmarks;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.redAccent
      ..style = PaintingStyle.fill;

    for (final lm in landmarks) {
      final double x = lm.x.clamp(0.0, 1.0) * size.width;
      final double y = lm.y.clamp(0.0, 1.0) * size.height;
      canvas.drawCircle(Offset(x, y), 1.0, paint);
    }
  }

  @override
  bool shouldRepaint(covariant _FaceMeshPainter oldDelegate) {
    return !identical(oldDelegate.landmarks, landmarks);
  }
}

class _MyAppState extends State<MyApp> {
  FaceMeshProcessor? _faceMeshProcessor;
  String _status = 'Initializing...';
  FaceMeshResult? _result;
  Uint8List? _sourcePngBytes;
  Uint8List? _displayBytes;
  int? _displayWidth;
  int? _displayHeight;

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

  Future<void> _init() async {
    try {
      final mesh = await FaceMeshProcessor.create(
        delegate: FaceMeshDelegate.xnnpack,
        enableRoiTracking: false,
      );
      setState(() {
        _faceMeshProcessor = mesh;
        _status = 'Engine ready. Tap the button to run a dummy inference.';
      });
    } catch (error) {
      setState(() {
        _status =
            'Initialization failed (expected until a proper TFLite runtime/model is bundled): $error';
      });
      debugPrint(error.toString());
    }
  }

  Future<void> _runOnce() async {
    final mesh = _faceMeshProcessor;
    if (mesh == null) {
      return;
    }
    try {
      // Load the PNG asset into memory (compressed PNG bytes),
      // decode it to an image so pixels are available,
      // then re-encode those pixels into raw RGBA bytes for the native API.
      final ByteData byteData = await rootBundle.load('assets/img.png');
      final Uint8List pngByte = byteData.buffer.asUint8List(
        byteData.offsetInBytes,
        byteData.lengthInBytes,
      );
      final img.Image? decodedData = img.decodeImage(pngByte);
      if (decodedData == null) {
        throw Exception('Failed to decode mesh PNG asset');
      }
      final img.Image rgba = img.copyResize(
        decodedData,
        width: decodedData.width,
        height: decodedData.height,
      );
      final Uint8List rgbaBytes = Uint8List.fromList(
        rgba.convert(numChannels: 4).getBytes(order: img.ChannelOrder.rgba),
      );

      _sourcePngBytes = pngByte;
      _displayBytes = rgbaBytes;
      _displayWidth = rgba.width;
      _displayHeight = rgba.height;

      final FaceMeshImage image = FaceMeshImage(
        pixels: rgbaBytes,
        width: rgba.width,
        height: rgba.height,
      );
      final FaceMeshResult result = mesh.process(image);
      debugPrint(
        'First 5 landmarks: ${result.landmarks.take(5).map((lm) => '(${lm.x.toStringAsFixed(3)}, ${lm.y.toStringAsFixed(3)}, ${lm.z.toStringAsFixed(3)})').join(', ')}',
      );
      setState(() {
        _result = result;
        _status =
            'Landmarks: ${result.landmarks.length}, score: ${result.score.toStringAsFixed(2)}';
      });
    } catch (error) {
      setState(() {
        _status = 'Inference failed: $error';
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('MediaPipe Face Mesh')),
        body: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(_status, style: Theme.of(context).textTheme.bodyLarge),
                const SizedBox(height: 12),
                ElevatedButton(
                  onPressed: _faceMeshProcessor == null ? null : _runOnce,
                  child: const Text('Run process()'),
                ),
                const SizedBox(height: 12),
                if (_result != null)
                  Text(
                    'First landmark: '
                    'x=${_result!.landmarks.first.x.toStringAsFixed(3)}, '
                    'y=${_result!.landmarks.first.y.toStringAsFixed(3)}',
                    style: Theme.of(context).textTheme.bodyMedium,
                  ),
                const SizedBox(height: 12),
                if (_displayBytes != null && _displayWidth != null) ...[
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const Text('Input image'),
                            const SizedBox(height: 8),
                            AspectRatio(
                              aspectRatio:
                                  _displayWidth! / (_displayHeight ?? 1),
                              child: Image.memory(
                                _sourcePngBytes ?? _displayBytes!,
                                fit: BoxFit.contain,
                              ),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(width: 12),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const Text('Output with landmarks'),
                            const SizedBox(height: 8),
                            AspectRatio(
                              aspectRatio:
                                  _displayWidth! / (_displayHeight ?? 1),
                              child: Stack(
                                fit: StackFit.expand,
                                children: [
                                  Image.memory(
                                    _sourcePngBytes ?? _displayBytes!,
                                    fit: BoxFit.contain,
                                  ),
                                  if (_result != null)
                                    Positioned.fill(
                                      child: IgnorePointer(
                                        child: CustomPaint(
                                          painter: _FaceMeshPainter(
                                            landmarks: _result!.landmarks,
                                          ),
                                        ),
                                      ),
                                    ),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ],
                const SizedBox(height: 16),
                const Text(
                  'Provide a valid mediapipe_face_mesh.tflite and TensorFlow Lite '
                  'runtime libraries to get real predictions.',
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
0
likes
160
points
185
downloads

Publisher

verified publishercornpip.dev

Weekly Downloads

Flutter plugin for MediaPipe Face Mesh inference on Android/iOS, supporting RGBA and NV21 inputs via an FFI-powered TFLite core.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

ffi, flutter, plugin_platform_interface

More

Packages that depend on mediapipe_face_mesh

Packages that implement mediapipe_face_mesh