You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 13:28:15