ReactJS集成face-api.js出现toNetInput错误:媒体类型不符合预期问题求助
解决face-api.js图片上传检测的类型错误问题
嘿,我一眼就看出问题所在了——你直接把File对象传给face-api的检测方法了,但它需要的是加载完成的HTMLImageElement、HTMLVideoElement这类媒体元素,根本不认原始的File!这就是你看到toNetInput报错的核心原因。
先给你梳理下关键问题点,再给你改好的代码:
核心问题拆解
- 你在
fileChangeHandler里拿到的e.target.files[0]是File对象,不是可直接用于AI检测的媒体元素 - 直接用File去调用
faceapi.createCanvas(file)和detectF(file),类型完全不匹配 - 组件顶层的
displaySize依赖file.width/height,初始时file为空会导致错误 - 用
useState管理canvas不如用useRef合适,因为canvas是DOM元素,ref能直接操作
修改后的完整代码
import React, { useRef, useEffect, useState } from "react"; import * as faceapi from 'face-api.js'; function CompName() { const canvasRef = useRef(null); // 用ref管理canvas,避免状态异步问题 const [selectedFile, setSelectedFile] = useState(null); const [isModelsLoaded, setIsModelsLoaded] = useState(false); // 加载模型:放在useEffect里,只执行一次 useEffect(() => { async function loadModels() { await Promise.all([ faceapi.nets.tinyFaceDetector.loadFromUri('/models'), faceapi.nets.ssdMobilenetv1.loadFromUri('/models'), faceapi.nets.faceLandmark68Net.loadFromUri('/models'), faceapi.nets.faceRecognitionNet.loadFromUri('/models'), faceapi.nets.faceExpressionNet.loadFromUri('/models') ]); setIsModelsLoaded(true); console.log('models loaded'); } loadModels(); }, []); // 文件选择处理:只存File对象,后续转成img元素 function fileChangeHandler(event) { const file = event.target.files[0]; if (file) { setSelectedFile(file); } } // 当文件变化且模型加载完成时,处理图片检测 useEffect(() => { if (!selectedFile || !isModelsLoaded) return; // 1. 创建HTMLImageElement,把File转成可加载的URL const img = new Image(); img.src = URL.createObjectURL(selectedFile); // 2. 必须等图片加载完成后再调用face-api img.onload = async () => { const displaySize = { width: img.width, height: img.height }; // 初始化canvas并添加到页面 if (!canvasRef.current) { canvasRef.current = faceapi.createCanvasFromMedia(img); document.body.append(canvasRef.current); } faceapi.matchDimensions(canvasRef.current, displaySize); // 3. 现在用加载完成的img元素去检测 const detections = await faceapi.detectAllFaces(img, new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks(); const resizedDetections = faceapi.resizeResults(detections, displaySize); // 绘制关键点(可选) faceapi.draw.drawFaceLandmarks(canvasRef.current, resizedDetections); // 提取关键点数组,供后续使用 if (resizedDetections.length > 0) { const landmarkPositions = resizedDetections[0].landmarks.positions; console.log('人脸关键点数组:', landmarkPositions); // 这里可以把landmarkPositions存到状态里,供后续流程使用 } // 记得释放URL,避免内存泄漏 URL.revokeObjectURL(img.src); }; }, [selectedFile, isModelsLoaded]); return ( <> <div> <input type="file" accept="image/*" onChange={fileChangeHandler} /> </div> </> ); } export default CompName;
关键修改说明
- 用
URL.createObjectURL把File转成图片URL:这样才能创建可加载的HTMLImageElement,face-api才能识别 - 等待图片
onload事件触发后再检测:确保图片完全加载,避免“media has not finished loading”的错误 - 用
useRef管理canvas:避免useState的异步更新问题,直接操作DOM元素更可靠 - 模型加载状态控制:确保模型加载完成后再执行检测,避免未加载完成就调用API的错误
- 清理URL:用
URL.revokeObjectURL释放临时URL,防止内存泄漏
这样修改后,你就能正确获取人脸关键点数组啦,后续直接用landmarkPositions就行~
内容的提问来源于stack exchange,提问作者subjectObject77
相关产品推荐
相关产品推荐

