Flutter 实现人脸检测 — 使用 google_mlkit_face_detection

📖 一、前言

在很多 App 中,我们会需要检测人脸,例如:

  • 拍照前检测是否有脸;
  • 分析表情、判断是否微笑;
  • 做人脸识别前的预处理;
  • 视频或相机实时检测。

Google 的 ML Kit 提供了强大的人脸检测(Face Detection)能力,而 Flutter 社区封装了一个插件

👉 google_mlkit_face_detection

本文将带你完整了解并使用 google_mlkit_face_detection: ^0.13.1 来实现:

  • 本地图片人脸检测;
  • 实时检测思路;
  • 常见问题与优化建议。

⚙️ 二、环境与依赖配置

  1. 添加依赖

在项目的 pubspec.yaml 文件中添加:

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  google_mlkit_face_detection: ^0.13.1
  image_picker: ^1.2.0   # 用于选择图片

执行:

yaml 复制代码
flutter pub get

  1. Android 配置

✅ 修改 Gradle 配置

在 android/app/build.gradle 中:

groovy 复制代码
android {
    compileSdkVersion 35
    defaultConfig {
        minSdkVersion 21
    }
}

✅ 添加权限

在 android/app/src/main/AndroidManifest.xml 中:

xml 复制代码
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

  1. iOS 配置

✅ 修改 Podfile

最低支持版本应为:

platform :ios, '15.5'

✅ 添加相机 / 相册权限说明

在 ios/Runner/Info.plist 中:

xml 复制代码
<key>NSCameraUsageDescription</key>
<string>用于检测人脸</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>用于选择人脸图片</string>

🧠 三、插件原理简介

google_mlkit_face_detection 的底层并不是 Dart 自己检测人脸,而是通过 平台通道 (Platform Channel) 调用 Android / iOS 的原生 ML Kit。

工作流程:

matlab 复制代码
Flutter (Dart)
   ↓  Platform Channel
Native (Android / iOS)
   ↓  调用 Google ML Kit
   ↓  返回检测结果
Flutter 绘制 UI

这种方式的好处是:

  • 检测在设备端执行,速度快;
  • 无需联网;
  • 精度高、稳定性好。

💡 四、FaceDetectorOptions 参数说明

dart 复制代码
final options = FaceDetectorOptions(
  enableContours: true,      // 是否检测人脸轮廓(眼睛、嘴唇等)
  enableLandmarks: true,     // 是否检测关键点(眼睛、鼻尖、耳朵等)
  enableClassification: true,// 是否识别表情(如微笑、闭眼)
  performanceMode: FaceDetectorMode.accurate, // 准确模式(相对较慢)
  minFaceSize: 0.1,          // 最小人脸占图片比例
);

这些参数的取舍决定性能与检测精度的平衡。


🧩 五、完整示例:检测图片中的人脸

以下是一个完整的 Flutter 示例页面:

dart 复制代码
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
import 'package:image_picker/image_picker.dart';

class FaceDetectionPage extends StatefulWidget {
  @override
  _FaceDetectionPageState createState() => _FaceDetectionPageState();
}

class _FaceDetectionPageState extends State<FaceDetectionPage> {
  File? _image;
  List<Face>? _faces;
  late FaceDetector _faceDetector;
  bool _busy = false;

  @override
  void initState() {
    super.initState();
    final options = FaceDetectorOptions(
      enableContours: true,
      enableLandmarks: true,
      enableClassification: true,
      performanceMode: FaceDetectorMode.accurate,
    );
    _faceDetector = FaceDetector(options: options);
  }

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

  Future<void> _pickAndDetect() async {
    final picker = ImagePicker();
    final XFile? file = await picker.pickImage(source: ImageSource.gallery);
    if (file == null) return;

    setState(() {
      _busy = true;
      _image = File(file.path);
      _faces = null;
    });

    final inputImage = InputImage.fromFile(_image!);
    try {
      final faces = await _faceDetector.processImage(inputImage);
      setState(() => _faces = faces);
    } catch (e) {
      print('检测失败: $e');
    } finally {
      setState(() => _busy = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('人脸检测 Demo')),
      body: Center(
        child: _busy
            ? CircularProgressIndicator()
            : Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  if (_image != null)
                    SizedBox(
                      width: 300,
                      height: 300,
                      child: Stack(
                        fit: StackFit.expand,
                        children: [
                          Image.file(_image!, fit: BoxFit.cover),
                          if (_faces != null)
                            CustomPaint(painter: FacePainter(_faces!)),
                        ],
                      ),
                    ),
                  const SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: _pickAndDetect,
                    child: Text('选择图片并检测'),
                  ),
                  const SizedBox(height: 10),
                  Text(_faces == null
                      ? '未检测'
                      : '检测到 ${_faces!.length} 张人脸'),
                ],
              ),
      ),
    );
  }
}

class FacePainter extends CustomPainter {
  final List<Face> faces;
  FacePainter(this.faces);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 3
      ..color = Colors.red;

    for (var face in faces) {
      canvas.drawRect(face.boundingBox, paint);
    }
  }

  @override
  bool shouldRepaint(covariant FacePainter oldDelegate) =>
      oldDelegate.faces != faces;
}

🖼️ 运行效果

  • 选择一张图片后,插件会检测其中的人脸;
  • 检测到的人脸区域会用红框标出;
  • 下方会显示检测到的人脸数量。

🎥 六、实时检测思路(Camera)

想实现类似抖音那样的实时检测,需要结合 camera 插件:

  1. 启动摄像头;
  2. 调用 controller.startImageStream();
  3. 每隔几帧或每 200ms 调用一次人脸检测;
  4. 使用 InputImage.fromBytes() 转换图像流;
  5. 在界面上叠加绘制检测框。

⚠️ 注意:

  • 每帧检测会非常耗时;
  • 建议降低帧率或检测频率;
  • 处理时要注意图像旋转、镜像、比例。

🧱 七、常见问题与解决方案

问题 可能原因 解决办法
iOS检测不到前置人脸 图像镜像 / 旋转角度错误 预处理图像方向或使用后置摄像头测试
Android 报错 removeFirst 底层依赖冲突 升级 Gradle 与 Kotlin,清理缓存
实时检测卡顿 每帧都处理 设定检测间隔、关闭部分检测功能
检测不准确 模型限制 调整 minFaceSize 或开启 accurate 模式
资源未释放 未调用 close() 页面销毁时调用 _faceDetector.close()

⚡ 八、性能优化建议

目标 建议
提高帧率 每隔 N 帧检测一次
提高精度 使用 FaceDetectorMode.accurate
降低功耗 关闭不必要的检测项
减少误检 对 boundingBox 做过滤
稳定输出 对检测结果做平滑处理

🧭 九、总结

通过 google_mlkit_face_detection: ^0.13.1,我们可以:

✅ 快速实现本地人脸检测

✅ 离线检测、响应迅速

✅ 支持检测人脸框、关键点、表情等信息

但要注意:

  • 插件依赖底层 ML Kit,不支持 Web;
  • Android/iOS 环境需正确配置;
  • 实时检测需做好性能优化。

🚀 十、后续方向

后续可以结合以下方向拓展:

  • 使用 camera 实现实时检测;
  • 将检测结果与 TensorFlow Lite 人脸识别模型结合;
  • 在检测到人脸时自动拍照或触发动画效果;
  • 实现人脸表情识别与情绪分析。

🧩 参考资料

  • google_mlkit_face_detection 官方文档
  • Google ML Kit 官方文档
  • Flutter camera 插件
相关推荐
REDcker5 小时前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
麦兜*5 小时前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
le1616165 小时前
Android 依赖种类及区别:远程仓库依赖、打包依赖、模块依赖、本地仓库依赖
android
lxysbly5 小时前
psp模拟器安卓版带金手指
android
行者965 小时前
Flutter在鸿蒙平台实现自适应步骤条组件的完整指南
flutter·harmonyos·鸿蒙
云上凯歌6 小时前
02 Spring Boot企业级配置详解
android·spring boot·后端
hqiangtai6 小时前
Android 高级专家技术能力图谱
android·职场和发展
aqi006 小时前
FFmpeg开发笔记(九十七)国产的开源视频剪辑工具AndroidVideoEditor
android·ffmpeg·音视频·直播·流媒体
stevenzqzq6 小时前
Android Koin 注入入门教程
android·kotlin
炼金术7 小时前
SkyPlayer v1.1.0 - 在线视频播放功能更新
android·ffmpeg