Skip to content

Issue with google_mlkit_face_detection : Detecting Invisible Landmarks When Partial Face is Visible #783

Open
@akhil-ge0rge

Description

@akhil-ge0rge

Describe your issue. If applicable, add screenshots to help explain your problem.

iam facing an issue google_mlkit_face_detection with When only part of the face is visible to the camera (e.g., just the left eye and left ear), the detector still returns landmarks for features that are not visible, such as the right eye, right ear, or nose.

LEFT EYE: x=732, y=738  
RIGHT EYE: x=874, y=747  <-- not visible  
LEFT EAR: x=649, y=793  
RIGHT EAR: x=812, y=985  <-- not visible

This is causing unexpected behavior in my app’s face detection logic. I only want to capture an image when the full face is visible to the camera.

Here's how I’ve configured the detector:

  void _initializeFaceDetector() {
    _faceDetector = FaceDetector(
      options: FaceDetectorOptions(
        enableLandmarks: true,
        performanceMode: FaceDetectorMode.accurate,
        enableTracking: true,
        minFaceSize: 0.3,
      ),
    );
  }

Here's how I’ve checking Face Position :

String _checkFacePosition(Face face) {
    final double? rotY = face.headEulerAngleY; // Left-right tilt
    final double? rotZ = face.headEulerAngleZ; // Up-down tilt

    bool isBoundingBoxValid =
        face.boundingBox.width > 120 && face.boundingBox.height > 100;

    bool isConfidenceHigh = face.trackingId != null;

    bool isFaceStraight =
        face.headEulerAngleX!.abs() < 10 &&
        rotY!.abs() < 10 &&
        rotZ!.abs() < 10;

    if (!isBoundingBoxValid) {
      return "Please adjust your position.";
    }
    if (!isConfidenceHigh) {
      return "Please adjust your position.";
    }
    if ((rotY?.abs() ?? 90) > 10) {
      return "Please adjust your position.";
    }
    if ((rotZ?.abs() ?? 90) > 10) {
      return "Please adjust your position.";
    }

    // Check landmarks for visibility
    final nose = face.landmarks[FaceLandmarkType.noseBase];
    final bottomLip = face.landmarks[FaceLandmarkType.bottomMouth];
    final chin = face.landmarks[FaceLandmarkType.leftCheek];
    final leftEar = face.landmarks[FaceLandmarkType.leftEar];
    final rightEar = face.landmarks[FaceLandmarkType.rightEar];
    final rightEye = face.landmarks[FaceLandmarkType.rightEye];
    final leftEye = face.landmarks[FaceLandmarkType.leftEye];
    final rightCheek = face.landmarks[FaceLandmarkType.rightCheek];
    final leftMouth = face.landmarks[FaceLandmarkType.leftMouth];
    final rightMouth = face.landmarks[FaceLandmarkType.rightMouth];

    if (nose == null ||
        bottomLip == null ||
        chin == null ||
        leftEar == null ||
        rightEar == null ||
        rightEye == null ||
        leftEye == null ||
        rightCheek == null ||
        leftMouth == null ||
        rightMouth == null) {
      return "Ensure your face is properly centered and all features (eyes, ears, nose, lips, chin) are fully visible.";
    }

    final double boxTop = face.boundingBox.top;
    final double boxBottom = face.boundingBox.bottom;
    final double boxLeft = face.boundingBox.left;
    final double boxRight = face.boundingBox.right;
    bool isLandmarkInBounds(Point<int>? position) {
      if (position == null) return false;
      return position.y > boxTop &&
          position.y < boxBottom &&
          position.x > boxLeft &&
          position.x < boxRight;
    }

    // Ensure landmarks are within the face bounding box
    if (!(isLandmarkInBounds(nose.position) &&
        isLandmarkInBounds(bottomLip.position) &&
        isLandmarkInBounds(chin.position) &&
        isLandmarkInBounds(leftEar.position) &&
        isLandmarkInBounds(rightEar.position) &&
        isLandmarkInBounds(leftEye.position) &&
        isLandmarkInBounds(rightEye.position) &&
        isLandmarkInBounds(rightMouth.position) &&
        isLandmarkInBounds(leftMouth.position) &&
        isLandmarkInBounds(rightCheek.position))) {
      return "Ensure your face is properly centered and all features (eyes, ears, nose, lips, chin) are fully visible.";
    }

    if (isFaceStraight) {
      return "";
    }

    return "Please adjust your position."; // Default case
  }

Steps to reproduce.

Steps to Reproduce:

Use google_mlkit_face_detection

 FaceDetector(
      options: FaceDetectorOptions(
        enableLandmarks: true,
        performanceMode: FaceDetectorMode.accurate,
        enableTracking: true,
        minFaceSize: 0.3,
      ),
    )

Partially cover the face (e.g., only left side visible).
Observe that landmarks for the non-visible (right) side are still returned.

What is the expected result?

Not return landmarks for features that are not visible or have low confidence.
Allow filtering based on landmark confidence or visibility.

want to capture an image when the full face is visible to the camera.

Did you try our example app?

Yes

Is it reproducible in the example app?

Yes

Reproducible in which OS?

iOS and Android

Flutter/Dart Version?

Flutter (Channel stable, 3.32.0, on macOS 14.7.6 23H626 darwin-arm64, locale en-US) [400ms]
    • Flutter version 3.32.0 on channel stable
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision be698c48a6 (11 days ago), 2025-05-19 12:59:14 -0700
    • Engine revision 1881800949
    • Dart version 3.8.0
    • DevTools version 2.45.1

Plugin Version?

google_mlkit_face_detection: ^0.13.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions