React Native:如何通过expo-camera定时裁剪人脸并接入TensorFlow.js?
可行实现技巧
我之前在开发类似的Expo + TensorFlow.js人脸处理应用时,也遇到过这个问题——Expo Camera官方文档确实没给出直接的人脸裁剪+TFJS处理的流程,但有几个实用的技巧可以帮你实现需求,分享给你:
方法一:利用Expo Camera内置人脸检测+定时器裁剪
Expo Camera自带的人脸检测会返回人脸的边界框坐标,我们可以结合定时器,每隔1秒基于这些坐标裁剪人脸,再传入TFJS模型:
启用Camera人脸检测
首先在Camera组件中配置人脸检测参数,确保能拿到人脸的bounding box:import { Camera } from 'expo-camera'; <Camera ref={cameraRef} style={styles.camera} type={Camera.Constants.Type.front} faceDetectorSettings={{ mode: Camera.Constants.FaceDetector.Mode.fast, detectLandmarks: Camera.Constants.FaceDetector.Landmarks.none, runClassifications: Camera.Constants.FaceDetector.Classifications.none, }} onFacesDetected={handleFacesDetected} />添加定时器控制处理频率
用setInterval每隔1秒触发一次人脸裁剪,但要注意防抖——比如只有当检测到人脸时才执行,避免空操作:const [faces, setFaces] = useState([]); let processTimer = null; const handleFacesDetected = ({ faces }) => { setFaces(faces); }; useEffect(() => { processTimer = setInterval(async () => { if (faces.length === 0) return; await processFaces(faces); }, 1000); return () => clearInterval(processTimer); }, [faces]);转换坐标并裁剪人脸
Expo Camera返回的人脸坐标是相对于预览画面的,需要转换成实际图片的坐标,再用expo-image-manipulator裁剪:import * as ImageManipulator from 'expo-image-manipulator'; const processFaces = async (faces) => { // 先捕获当前相机帧(低分辨率,减少性能消耗) const photo = await cameraRef.current.takePictureAsync({ quality: 0.3, base64: true, skipProcessing: true, }); // 转换人脸坐标:预览画面 -> 实际图片 const { width: cameraWidth, height: cameraHeight } = await cameraRef.current.getCameraPreviewSizeAsync(); const { width: imageWidth, height: imageHeight } = photo; const scaleX = imageWidth / cameraWidth; const scaleY = imageHeight / cameraHeight; for (const face of faces) { const adjustedFace = { x: face.bounds.origin.x * scaleX, y: face.bounds.origin.y * scaleY, width: face.bounds.size.width * scaleX, height: face.bounds.size.height * scaleY, }; // 裁剪人脸区域 const croppedImage = await ImageManipulator.manipulateAsync( photo.uri, [{ crop: adjustedFace }], { base64: true } ); // 转换为TFJS张量并传入模型处理 const tensor = await tf.browser.fromPixelsAsync(croppedImage.base64); // 这里替换成你的模型推理逻辑 const result = await yourModel.predict(tensor); // 记得清理张量,避免内存泄漏 tensor.dispose(); result.dispose(); } };
方法二:直接捕获相机帧+TFJS人脸检测
如果Expo Camera内置的人脸检测不够灵活,你可以直接每隔1秒捕获相机帧,用TFJS自带的人脸检测模型(比如BlazeFace)检测并裁剪,再传入自定义模型:
捕获低分辨率相机帧
同样用takePictureAsync每隔1秒获取帧,优先用低分辨率减少计算压力:useEffect(() => { const timer = setInterval(async () => { const photo = await cameraRef.current.takePictureAsync({ quality: 0.2, base64: true, }); await processFrameWithTFJS(photo); }, 1000); return () => clearInterval(timer); }, []);用TFJS检测并裁剪人脸
先加载TFJS的BlazeFace模型,检测人脸后用tf.image.cropAndResize裁剪:import * as tf from '@tensorflow/tfjs'; import * as faceDetection from '@tensorflow-models/blazeface'; let faceModel = null; useEffect(() => { // 初始化TFJS和人脸检测模型 const initModels = async () => { await tf.ready(); faceModel = await faceDetection.load(); }; initModels(); }, []); const processFrameWithTFJS = async (photo) => { if (!faceModel) return; // 将base64图片转换为TFJS张量 const imageTensor = await tf.browser.fromPixelsAsync(`data:image/jpeg;base64,${photo.base64}`); // BlazeFace需要的输入尺寸是128x128,先缩放 const resizedTensor = tf.image.resizeBilinear(imageTensor, [128, 128]); // 检测人脸 const predictions = await faceModel.estimateFaces(resizedTensor); resizedTensor.dispose(); for (const pred of predictions) { // 将BlazeFace返回的归一化坐标转换为实际图片坐标 const x = pred.topLeft[0] * photo.width; const y = pred.topLeft[1] * photo.height; const width = pred.bottomRight[0] - x; const height = pred.bottomRight[1] - y; // 裁剪人脸张量 const croppedTensor = tf.image.cropAndResize( imageTensor.expandDims(0), // 增加batch维度 [[y / photo.height, x / photo.width, (y + height)/photo.height, (x + width)/photo.width]], // 归一化的裁剪框 [0], // 索引 [224, 224] // 输出尺寸(根据你的模型要求调整) ); // 传入自定义模型处理 const modelResult = await yourCustomModel.predict(croppedTensor); // 清理张量 croppedTensor.dispose(); modelResult.dispose(); } imageTensor.dispose(); };
关键性能优化点
- 降低捕获分辨率:用
quality参数减少图片大小,避免TFJS处理时卡顿。 - 及时清理张量:每次处理完都用
tf.dispose()或tf.tidy()清理,防止内存泄漏。 - 防抖处理:如果连续两帧的人脸坐标变化很小,可以跳过重复处理,节省资源。
内容的提问来源于stack exchange,提问作者Mertcan Seğmen
相关产品推荐
相关产品推荐

