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

基于ReactJS的人脸检测前端实现问题求助

解决方案:React前端实现摄像头人脸检测

一、先修复现有代码的基础Bug

原代码存在几处影响功能的问题,先修正:

  1. 第一个「start」按钮未绑定启动摄像头逻辑,原startup函数依赖window.load触发,改为按钮点击触发
  2. Canvas元素ID不匹配:代码中用document.getElementById('hidden'),但JSX里是<canvas id="canvas" hidden>,统一修正为id="hidden"
  3. 未定义d()函数,后续补充人脸检测逻辑

修正后的基础代码:

import { useEffect } from "react";

const App = () => {
  const videoStyle = {
    border: '1px solid black',
    width: '650px',
    height: '440px',
    marginRight: '600px',
  }

  const buttonStyle = {
    border: '1px solid black',
    width: '400px',
    height: '40px',
    marginRight: '600px',
    margin: '10px 0',
  }

  const imageStyle = {
    border: '1px solid black',
    width: '650px',
    height: '440px',
    marginRight: '600px',
  }

  useEffect(() => {
    let width = 320; 
    let height = 0; 
    let streaming = false;
    let video = null;
    let canvas = null;
    let photo = null;
    let startbutton = null;
    let detectBtn = null;
    let faceCascade = null;

    // 初始化OpenCV(后续补充)
    function initOpenCV() {
      // 待实现
    }

    function startup() { 
      video = document.getElementById('video');
      canvas = document.getElementById('hidden');
      photo = document.getElementById('photo');
      startbutton = document.getElementById('startbutton');
      detectBtn = document.getElementById("detect")
      
      navigator.mediaDevices.getUserMedia({ video: true, audio: false })
        .then(function(stream) {
          video.srcObject = stream;
          video.play();
        })
        .catch(function(err) {
          alert("摄像头启动失败: " + err);
        });

      video.addEventListener('canplay', function() {
        if (!streaming) {
          height = video.videoHeight / (video.videoWidth / width);
          if (isNaN(height)) {
            height = width / (4 / 3);
          }
          video.setAttribute('width', width);
          video.setAttribute('height', height);
          canvas.setAttribute('width', width);
          canvas.setAttribute('height', height);
          streaming = true;
        }
      }, false);

      startbutton?.addEventListener('click', function(ev) {
        takepicture();
        ev.preventDefault();
      }, false);

      detectBtn?.addEventListener('click', function(ev) {
        detectFaces();
        ev.preventDefault();
      }, false);

      clearphoto();
    }

    function clearphoto() {
      const context = canvas.getContext('2d');
      context.fillStyle = "#AAA";
      context.fillRect(0, 0, canvas.width, canvas.height);
      const data = canvas.toDataURL('image/png');
      photo.setAttribute('src', data);
    }

    function takepicture() {
      const context = canvas.getContext('2d');
      if (width && height) {
        canvas.width = width;
        canvas.height = height;
        context.drawImage(video, 0, 0, width, height);
        const data = canvas.toDataURL('image/png');
        photo.setAttribute('src', data);
      } else {
        clearphoto();
      }
    }

    // 人脸检测函数(后续补充)
    function detectFaces() {
      // 待实现
    }

    // 绑定启动按钮事件
    document.getElementById('start-camera')?.addEventListener('click', startup);

    // 加载OpenCV
    const script = document.createElement('script');
    script.src = 'https://docs.opencv.org/3.4.0/opencv.js';
    script.onload = initOpenCV;
    document.body.appendChild(script);

    return () => {
      script.remove();
      document.getElementById('start-camera')?.removeEventListener('click', startup);
    }
  }, [])

  return (  
    <div className="contentarea">
      <button id="start-camera" style={buttonStyle}>启动摄像头</button>
      <div className="camera">
        <video id="video" style={videoStyle} ></video>
      </div>
      <div><button id="startbutton" style={buttonStyle}>捕获图像</button></div>
      
      <canvas id="hidden" hidden></canvas>
      <div className="output">
        <img id="photo" style={imageStyle} alt="捕获的图像将显示在这里"/> 
        <button id="detect" style={buttonStyle}>检测人脸</button>
      </div>
    </div>
  );
}

export default App;

二、集成OpenCV.js实现人脸检测

1. 完善OpenCV初始化与人脸检测逻辑

在initOpenCV和detectFaces函数中添加OpenCV相关代码:

function initOpenCV() {
  // 加载预训练的Haar人脸分类器
  faceCascade = new cv.CascadeClassifier();
  // 加载内置的人脸模型(OpenCV.js自带)
  faceCascade.load(cv.samples.findFile('haarcascade_frontalface_default.xml'));
}

function detectFaces() {
  if (!faceCascade || !photo.src) return;

  // 创建OpenCV图像对象
  const img = cv.imread(photo);
  const gray = new cv.Mat();
  cv.cvtColor(img, gray, cv.COLOR_RGBA2GRAY);
  
  // 检测人脸
  const faces = new cv.RectVector();
  faceCascade.detectMultiScale(gray, faces, 1.1, 3, 0);

  // 绘制带尺寸的人脸框
  for (let i = 0; i < faces.size(); i++) {
    const face = faces.get(i);
    const point1 = new cv.Point(face.x, face.y);
    const point2 = new cv.Point(face.x + face.width, face.y + face.height);
    cv.rectangle(img, point1, point2, [255, 0, 0, 255], 2);
    // 添加尺寸文本
    cv.putText(img, `${face.width}x${face.height}`, 
      new cv.Point(face.x, face.y - 10), 
      cv.FONT_HERSHEY_SIMPLEX, 0.5, [255, 0, 0, 255], 2);
  }

  // 将结果绘制到img元素
  cv.imshow(photo, img);
  
  // 释放内存
  img.delete();
  gray.delete();
  faces.delete();
}

2. 注意事项

  • OpenCV.js加载需要一定时间,建议在按钮上添加加载状态,避免用户提前点击检测按钮
  • 如果Haar分类器加载失败,可以手动下载haarcascade_frontalface_default.xml放到public目录,修改加载路径为/haarcascade_frontalface_default.xml

三、TensorFlow Models安装问题的解决方法

如果更倾向于使用TensorFlow,先解决安装报错问题:

  1. 修正拼写错误:你提到的tensflow-models/face-landmarks-detection是拼写错误,正确包名是@tensorflow-models/face-landmarks-detection
  2. 正确安装命令:
# 先安装TensorFlow.js核心库
npm install @tensorflow/tfjs
# 安装人脸检测模型
npm install @tensorflow-models/face-landmarks-detection
  1. 排错步骤:如果安装失败,尝试清理npm缓存后重新安装:
npm cache clean --force
rm -rf node_modules package-lock.json
npm install

TensorFlow人脸检测实现示例

import * as tf from '@tensorflow/tfjs';
import * as faceDetection from '@tensorflow-models/face-landmarks-detection';

// 在useEffect中添加初始化逻辑
let detector = null;
async function initTF() {
  // 加载模型
  detector = await faceDetection.createDetector(faceDetection.SupportedModels.MediaPipeFaceDetector);
}

// 人脸检测函数
async function detectFaces() {
  if (!detector || !photo.src) return;

  const img = document.getElementById('photo');
  const predictions = await detector.estimateFaces(img);

  // 获取图像上下文绘制框
  const canvas = document.createElement('canvas');
  canvas.width = img.width;
  canvas.height = img.height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);

  predictions.forEach(prediction => {
    const box = prediction.boundingBox;
    // 绘制矩形框
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 2;
    ctx.strokeRect(box.xMin, box.yMin, box.width, box.height);
    // 绘制尺寸文本
    ctx.fillStyle = 'red';
    ctx.font = '16px Arial';
    ctx.fillText(`${Math.round(box.width)}x${Math.round(box.height)}`, box.xMin, box.yMin - 10);
  });

  // 更新img元素
  photo.src = canvas.toDataURL('image/png');
}

// 在useEffect中调用initTF
useEffect(() => {
  initTF();
  // ...其他逻辑
}, [])

内容的提问来源于stack exchange,提问作者cdt123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:54:56