react-native-mlkit
react-native-mlkit copied to clipboard
Can't detect faces in an image on iOS device
Hi there,
So I have been using this library for Android with no issues, it detects face with ease, however, on iOS device, the faces property is just an empty array, its not able to detect any face whatsoever.
Here's the screen file
//facial-scan.tsx
import React, { useRef, useState } from 'react';
import { Button, Image, View, StyleSheet, Dimensions, Text } from 'react-native';
import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
import Svg, { Rect, Mask } from 'react-native-svg';
import { useRouter } from 'expo-router';
import { layout } from '@/styles/common';
import { Button as ButtonComponent, Header } from '@/components';
import { typography } from '@/styles/typography';
import { useIsFocused } from '@react-navigation/native';
import { useFacesInPhoto, useFaceDetection } from '@infinitered/react-native-mlkit-face-detection';
const { width, height } = Dimensions.get('window');
const horizontalPadding = 20;
const maskMarginTop = 30;
const frameWidth = width - horizontalPadding * 2;
const frameHeight = frameWidth;
const frameX = (width - width * 0.9) / 2;
const frameY = maskMarginTop;
export default function FaceRecognitionScreen() {
const model = useFaceDetection();
const device = useCameraDevice('front');
const router = useRouter();
const [photoUri, setPhotoUri] = useState<string | null>(null);
const { hasPermission, requestPermission } = useCameraPermission();
// This hook returns `true` if the screen is focused, `false` otherwise
const isFocused = useIsFocused();
// const appState = useAppState()
const isActive = isFocused;
const cameraRef = useRef<Camera>(null);
const { faces, error } = useFacesInPhoto(photoUri ?? undefined);
const takePicture = async () => {
if (!cameraRef.current) return;
try {
const photo = await cameraRef.current?.takePhoto({ flash: 'off' });
try {
const result = await model.detectFaces(`file://${photo.path}`);
// Comment below line prior to submitting new release APK
// router.replace('/(auth)/review-info');
console.log('result: ', result)
// Uncomment below line prior during development with physical device
if (!result || !faces.length) {
// Handle undefined result case
alert('No faces detected!');
return;
}
else if(faces.length === 1) {
router.navigate('/(auth)/review-info');
}
alert(`Faces detected!, ${faces.length}, ${JSON.stringify(faces)}`);
} catch (error) {
// Handle error case
console.error('Face detection failed:', error);
}
} catch (err) {
console.error('Error taking photo:', err);
}
};
return (
<View style={layout.fill}>
<Header onPressCallback={() => {
router.back();
}} />
<View
style={styles.cameraContainer}
>
{/* Camera */}
{device && (
<View style={styles.roundedCameraWrapper}>
{!hasPermission ? (
<View>
<Text style={styles.permissionPromptText}>
We need your permission to show the camera
</Text>
<Button onPress={requestPermission} title="grant permission" />
</View>
) : <Camera
style={StyleSheet.absoluteFill}
isActive={isActive}
device={device}
resizeMode="cover"
ref={cameraRef}
photo={true}
/>}
</View>
)}
{/* Masked Overlay */}
<Svg style={StyleSheet.absoluteFill}>
<Mask id="mask">
<Rect x="0" y="0" width={width} height={height} fill="white" />
<Rect
x={frameX}
y={frameY}
width={'90%'}
height={frameHeight}
rx={10}
ry={10}
fill="black"
/>
</Mask>
<Rect
y="0"
width={'100%'}
height={height * 0.75}
fill="#000000C7"
mask="url(#mask)"
rx={10}
ry={10}
/>
</Svg>
{/* Text inside frame */}
<View style={styles.textInFrameContainer}>
<Image source={require('@/assets/images/facial.png')} />
<Text style={styles.title}>Face Recognition</Text>
<Text style={styles.subtitle}>Try to keep your face within the frame</Text>
</View>
</View>
<View style={layout.buttonContainer}>
<ButtonComponent title="Submit" onPressCallback={takePicture} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
borderWidth: 3,
borderColor: 'red'
},
backButton: {},
title: {
fontSize: 18,
fontWeight: 'bold',
color: '#00C2CB',
},
subtitle: {
fontSize: 14,
color: '#fff',
marginTop: 5,
borderWidth: 2,
borderColor: 'red'
},
submitButton: {
position: 'absolute',
bottom: 40,
alignSelf: 'center',
backgroundColor: '#00C2CB',
// borderRadius: 999,
paddingVertical: 12,
paddingHorizontal: 40,
},
submitText: {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',
},
textInFrameContainer: {
position: 'absolute',
alignItems: 'center',
justifyContent: 'space-evenly',
top: frameHeight * 0.65,
left: frameX - 20,
width: frameWidth,
height: 150
},
camera: {
width: '99.9%',
height: height * 0.75,
overflow: 'hidden',
borderColor: 'green',
borderWidth: 2
},
cameraContainer: {
marginTop: 20,
overflow: 'hidden',
flex: 1,
},
roundedCameraWrapper: {
width: '99.9%',
height: height * 0.75,
borderRadius: 10, // whatever radius you want
overflow: 'hidden',
alignSelf: 'center', // optional, if needed
borderWidth: 2,
borderColor: 'green', // optional
},
permissionPromptText: {
...typography.body,
color: 'white',
marginBottom: 10
},
});
and here is output from
await model.detectFaces(`file://${photo.path}`):
{"faces": [], "imagePath": "file://file///private/var/mobile/Containers/Data/Application/AD8AF7CF-043A-4125-970F-5ECB662C9016/tmp/9B9AEF52-9647-4259-9A3F-AA3BAFC6EB6B.jpg"}
Though it doesn't mention anything about real iOS physical device, it only says that it won't work on simulators. Mine is a physical iOS device.
Versions installed
"@infinitered/react-native-mlkit-core": "^3.1.0",
"@infinitered/react-native-mlkit-face-detection": "^3.1.0"
and I am using Expo.
Tried on a different iOS device, same issue, face property is an empty array.
Here is the reproducible code:
import React, { useEffect, useRef, useState } from 'react';
import { Alert, Button, StyleSheet, View, Text } from 'react-native';
import { Camera, useCameraDevices, useCameraPermission, PhotoFile } from 'react-native-vision-camera';
import { useFaceDetection } from '@infinitered/react-native-mlkit-face-detection';
export default function FullScreenCamera() {
const { hasPermission, requestPermission } = useCameraPermission();
const devices = useCameraDevices();
const device = devices[1];
const camera = useRef<Camera>(null);
const [photoPath, setPhotoPath] = useState<string | null>(null);
const model = useFaceDetection();
useEffect(() => {
if (!hasPermission) {
Alert.alert(
'Camera Permission Required',
'We need access to your camera to continue.',
[
{
text: 'Okay',
onPress: requestPermission,
style: 'default',
},
],
{ cancelable: false }
);
}
}, [hasPermission]);
const takePhoto = async () => {
if (camera.current == null) return;
try {
const photo: PhotoFile = await camera.current.takePhoto({
// qualityPrioritization: 'speed',
});
const res = await model.detectFaces(photo.path);
console.log('Face detection result:', res);
// if (!res) {
// console.error('Error detecting faces:', res);
// return;
// }
// const faces = res.faces;
// const hasFaces = faces.length > 0;
// console.log('Faces detected:', faces);
// console.log('Has faces:', hasFaces);
console.log('Photo taken at path:', photo.path);
setPhotoPath(photo.path); // This will trigger useFaceDetection automatically
} catch (error) {
console.error('Error taking photo:', error);
}
};
if (!device || !hasPermission) {
return <View style={styles.blackScreen} />;
}
return (
<View style={styles.container}>
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
photo={true}
/>
<View style={styles.captureButton}>
<Button title="Capture" onPress={takePhoto} />
</View>
{/* {photoPath && (
<View style={styles.faceResult}>
<Text style={{ color: 'white' }}>
</Text>
<Button title="Reset" onPress={takePhoto} />
</View>
)} */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
blackScreen: {
flex: 1,
backgroundColor: 'black',
},
captureButton: {
position: 'absolute',
bottom: 40,
alignSelf: 'center',
backgroundColor: 'white',
borderRadius: 8,
paddingHorizontal: 20,
paddingVertical: 10,
},
faceResult: {
position: 'absolute',
top: 60,
alignSelf: 'center',
backgroundColor: 'rgba(0,0,0,0.6)',
padding: 10,
borderRadius: 10,
},
});
Any updates?
Hey @mgulfam0722 - sorry for the delay here, no updates as of yet but we will take a look!
@mgulfam0722 can you just let us know your Expo and RN version please?
Hi, quick update: the last time I tested this issue was on April 24th, 2025. I’ve since removed the reproducible test project I had set up, so I currently don't have an environment to re-verify the behaviour.