You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中使用Google ML Kit显示人脸轮廓

人脸检测后绘制面部轮廓的实现方案

问题描述

已完成人脸检测功能,但当前代码仅能绘制人脸的 bounding box,需要实现**面部轮廓(如五官轮廓、脸部轮廓)**的绘制,现有代码如下:

class FacePainter extends CustomPainter {
  ui.Image image;
  List<Face> faces;
  List<Rect> rects = [];

  FacePainter(this.image, this.faces) {
    for (var i = 0; i < faces.length; i++) {
      rects.add(faces[i].boundingBox);
    }
    print("object");
  }
  @override
  void paint(ui.Canvas canvas, ui.Size size) {
    final Paint paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2.0
      ..color = Colors.blue;
    canvas.drawImage(image, Offset.zero, paint);
    for (var i = 0; i < faces.length; i++) {
      canvas.drawRect(rects[i], paint);
    }
  }

  @override
  bool shouldRepaint(FacePainter oldDelegate) {
    return image != oldDelegate.image || image != oldDelegate.faces;
  }
}

实现思路

人脸检测库返回的Face对象包含FaceLandmark属性,存储了面部关键特征点(如下巴、脸颊、额头、眼睛、鼻子等)。通过连接这些特征点,就能绘制出面部轮廓。

修改后的代码

import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';

class FacePainter extends CustomPainter {
  final ui.Image image;
  final List<Face> faces;
  final Paint _outlinePaint = Paint()
    ..style = PaintingStyle.stroke
    ..strokeWidth = 2.0
    ..color = Colors.red;

  FacePainter(this.image, this.faces);

  @override
  void paint(ui.Canvas canvas, ui.Size size) {
    // 绘制原图
    canvas.drawImage(image, Offset.zero, Paint());

    for (var face in faces) {
      // 1. 绘制脸部轮廓(连接下巴、脸颊、额头特征点)
      final faceContourPoints = <Offset>[];
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.chin]?.position, face.boundingBox));
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.leftCheek]?.position, face.boundingBox));
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.leftForehead]?.position, face.boundingBox));
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.rightForehead]?.position, face.boundingBox));
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.rightCheek]?.position, face.boundingBox));
      faceContourPoints.add(_convertOffset(face.landmarks[FaceLandmarkType.chin]?.position, face.boundingBox));

      if (faceContourPoints.every((point) => point != null)) {
        final path = Path()..addPolygon(faceContourPoints.cast<Offset>(), false);
        canvas.drawPath(path, _outlinePaint);
      }

      // 2. 绘制眼睛轮廓(示例:左眼)
      final leftEye = face.landmarks[FaceLandmarkType.leftEye]?.position;
      if (leftEye != null) {
        canvas.drawCircle(Offset(leftEye.x.toDouble(), leftEye.y.toDouble()), 8.0, _outlinePaint);
      }

      // 3. 绘制鼻子轮廓
      final noseBase = face.landmarks[FaceLandmarkType.noseBase]?.position;
      final noseBridge = face.landmarks[FaceLandmarkType.noseBridge]?.position;
      if (noseBase != null && noseBridge != null) {
        final path = Path()
          ..moveTo(noseBase.x.toDouble(), noseBase.y.toDouble())
          ..lineTo(noseBridge.x.toDouble(), noseBridge.y.toDouble());
        canvas.drawPath(path, _outlinePaint);
      }
    }
  }

  // 转换特征点坐标到画布坐标系
  Offset? _convertOffset(Point<int>? point, Rect boundingBox) {
    if (point == null) return null;
    // 若画布与原图尺寸不一致,需添加比例转换逻辑
    return Offset(point.x.toDouble(), point.y.toDouble());
  }

  @override
  bool shouldRepaint(FacePainter oldDelegate) {
    return image != oldDelegate.image || faces != oldDelegate.faces;
  }
}

关键说明

  • 特征点获取:通过face.landmarks[FaceLandmarkType.xxx]调用不同部位的关键点,FaceLandmarkType包含下巴、脸颊、额头、眼睛、鼻子、嘴巴等多种类型。
  • 坐标适配:如果画布尺寸和原图尺寸不同,需要根据比例调整特征点坐标,避免轮廓错位。
  • 路径绘制:用Path连接多个特征点实现连续轮廓;单个关键点(如眼睛中心)可通过drawCircle标记。
  • 细节扩展:可添加更多特征点(如嘴巴上下唇关键点),让轮廓更精细。

内容的提问来源于stack exchange,提问作者Jeevan Shrestha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:42:51