google_ml_kit_flutter icon indicating copy to clipboard operation
google_ml_kit_flutter copied to clipboard

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

Open akhil-ge0rge opened this issue 6 months ago • 1 comments

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

akhil-ge0rge avatar May 31 '25 04:05 akhil-ge0rge

I also have the same issue, the library accepts detection even when only a partial face is recognized.

Image

0x01001 avatar Jun 05 '25 16:06 0x01001

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Jul 06 '25 12:07 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Jul 21 '25 12:07 github-actions[bot]