I'm currently using google_mlkit_face_detection in my Flutter project for face detection. However, I've run into an issue:
When only a small part of my face is visible to the camera — for example, just the jaw and mouth, or the left ear and left eye — the detector still returns landmarks for features like the right ear or nose, even though they are clearly not visible to the camera.
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.
Has anyone else experienced this issue? Is there a way to ensure that the detector only returns landmarks that are actually visible to the camera, or a way to filter out landmarks with low confidence?
Face landmark points received when only a partial face is visible to the camera (specifically the left portion, including the left ear and left eye):
[log] LEFT EYE x 732
[log] LEFT EYE y 738
[log] Right EYE x 874
[log] Right EYE y 747
[log] Left Ear x 649
[log] Left Ear y 793
[log] Right Ear y 985
[log] Right Ear x 812
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
}
Any guidance, best practices, or workarounds would be greatly appreciated.
Thanks in advance!