faceApi-人脸识别和人脸检测

需求:浏览器通过模型检测前方是否有人(距离和正脸),检测到之后拍照随机保存一帧

实现步骤:

  1. 获取浏览器的摄像头权限
  2. 创建video标签并通过video标签展示摄像头影像
  3. 创建canvas标签并通过canvas标签绘制摄像头影像并展示
  4. 将canvas的当前帧转成图片展示保存

pnpm install @vladmandic/face-api 下载依赖

pnpm install @vladmandic/face-api

下载model模型

将下载的model模型放到项目的public文件中 如下图

创建video和canvas标签

复制代码
      <video ref="videoRef" style="display: none"></video>
      <template v-if="!picture || picture == ''">
        <canvas ref="canvasRef" width="400" height="400"></canvas>
      </template>
      <template v-else>
        <img ref="image" :src="picture" alt="" />
      </template>
    </div>
.video 复制代码
  width: 400px;
  height: 400px;
  border-radius: 50%;
  overflow: hidden;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.video_box {
  position: fixed;
  width: 400px;
  height: 400px;
  border-radius: 50%;
  overflow: hidden;
}

@keyframes moveToTopLeft {
  0% {
    right: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }

  100% {
    right: -68px;
    top: -68px;
    transform: scale(0.5);
  }
}

.video_box {
  animation: moveToTopLeft 2s ease forwards;
}

介绍分析

video 类选择器 让视频流居中

picture变量 判断是否转成照片

video_box视频流的某一帧转成照片后 动态移动到屏幕右上角

主要逻辑代码 主要逻辑代码 主要逻辑代码!!!

import 复制代码
import * as faceApi from '@vladmandic/face-api'

const videoRef = ref()
const options = ref(null)
const canvasRef = ref(null)
let timeout = null
// 初始化人脸识别
const init = async () => {
    await faceApi.nets.ssdMobilenetv1.loadFromUri("/models") //人脸检测
    // await faceApi.nets.tinyFaceDetector.loadFromUri("/models") //人脸检测  人和摄像头距离打开
    await faceApi.nets.faceLandmark68Net.loadFromUri("/models") //特征检测 人和摄像头距离必须打开
    // await faceApi.nets.faceRecognitionNet.loadFromUri("/models") //识别人脸 
    // await faceApi.nets.faceExpressionNet.loadFromUri("/models") //识别表情,开心,沮丧,普通
    // await faceApi.loadFaceLandmarkModel("/models");

    options.value = new faceApi.SsdMobilenetv1Options({
        minConfidence: 0.5, // 0.1 ~ 0.9
    });
    await cameraOptions()
}

// 打开摄像头
const cameraOptions = async() => {
    let constraints = {
      video: true
    }
    // 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
    try {
      if (navigator.mediaDevices) {
        navigator.mediaDevices.getUserMedia(constraints).then((MediaStream) => {
          // 返回参数
          videoRef.value.srcObject = MediaStream;
          videoRef.value.play();
          recognizeFace()
        }).catch((error) => {
          console.log(error);
        });
      } else {
        console.log('浏览器不支持开启摄像头,请更换浏览器')
      }
  
    } catch (err) {
      console.log('非https访问')
    }
  }

//   检测人脸
const recognizeFace = async () => {
    if (videoRef.value.paused) return clearTimeout(timeout);
    canvasRef.value.getContext('2d', { willReadFrequently: true }).drawImage(videoRef.value, 0, 0, 400, 400);
    // 直接检测人脸  灵敏较高
    // const results = await new faceApi.DetectAllFacesTask(canvasRef.value, options.value).withFaceLandmarks();
    // if (results.length > 0) {
    //   photoShoot()
    // }
    // 计算人与摄像头距离和是否正脸
    const results = await new faceApi.detectSingleFace(canvasRef.value, options.value).withFaceLandmarks()
    if (results) {
      // 计算距离
      const { positions } = results.landmarks;
      const leftPoint = positions[0];
      const rightPoint = positions[16];
      // length 可以代替距离的判断   距离越近  length值越大
      const length = Math.sqrt(
          Math.pow(leftPoint.x - rightPoint.x, 2) +
            Math.pow(leftPoint.y - rightPoint.y, 2),
      );
        // 计算是否正脸
      const { roll, pitch, yaw } = results.angle
      //roll水平角度 pitch上下角度 yaw 扭头角度
      console.log(roll, pitch, yaw, length)
      if (roll >= -10 && roll <= 10 && pitch >= -10 && pitch <= 10 && yaw>= -20 && yaw <= 20 && length >= 90 && length <= 110) {
        
        photoShoot()

      }

    }
    

    timeout = setTimeout(() => {
      return recognizeFace()
    }, 0)
  }
  const picture = ref(null)
  const photoShoot = () => {
    // 拿到图片的base64
    let canvas = canvasRef.value.toDataURL("image/png");
    // 停止摄像头成像
    videoRef.value.srcObject.getTracks()[0].stop()
    videoRef.value.pause()
    if(canvas) {
        // 拍照将base64转为file流文件
        let blob = dataURLtoBlob(canvas);
        let file = blobToFile(blob, "imgName");
        // 将blob图片转化路径图片
        picture.value = window.URL.createObjectURL(file)

    } else {
        console.log('canvas生成失败')
    }
}
/**
         * 将图片转为blob格式
         * dataurl 拿到的base64的数据
         */
const dataURLtoBlob = (dataurl) => {
  let arr = dataurl.split(','),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
  while(n--) {
      u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], {
      type: mime
  });
}
/**
* 生成文件信息
* theBlob 文件
* fileName 文件名字
*/
const blobToFile = (theBlob, fileName) => {
  theBlob.lastModifiedDate = new Date().toLocaleDateString();
  theBlob.name = fileName;
  return theBlob;
}

// 判断是否在区间
const isInRange = (number, start, end) => {
  return number >= start && number <= end
}
export { init, videoRef, canvasRef, timeout, picture }
相关推荐
新智元3 分钟前
全球顶尖 CS 论文惊爆 AI「好评密令」!哥大等 14 所高校卷入,学术圈炸锅
人工智能·openai
l0sgAi8 分钟前
vLLM在RTX50系显卡上部署大模型-使用wsl2
linux·人工智能
DDliu8 分钟前
花半个月死磕提示词后,我发现:真正值钱的不是模板,是这套可复用的结构化思维
人工智能
腾讯云开发者8 分钟前
AI 浪潮下的锚与帆:工程师文化的变与不变 | 架构师夜生活
人工智能
JoernLee9 分钟前
机器学习算法:支持向量机SVM
人工智能·算法·机器学习
杰尼橙子13 分钟前
深度解读Karpathy说的Software 3.0时代,感觉是个人的机会很大的时代呀
人工智能·openai
我爱一条柴ya37 分钟前
【AI大模型】线性回归:经典算法的深度解析与实战指南
人工智能·python·算法·ai·ai编程
Qiuner43 分钟前
【源力觉醒 创作者计划】开源、易用、强中文:文心一言4.5或是 普通人/非AI程序员 的第一款中文AI?
人工智能·百度·开源·文心一言·gitcode
未来之窗软件服务1 小时前
chrome webdrive异常处理-session not created falled opening key——仙盟创梦IDE
前端·人工智能·chrome·仙盟创梦ide·东方仙盟·数据调式
AI街潜水的八角1 小时前
深度学习图像分类数据集—蘑菇识别分类
人工智能·深度学习·分类