前端人脸识别,简单的活体检测(张张嘴...),vue3使用tracking.js,face.js,face-api.js实现

news/2024/12/23 23:30:53 标签: 前端, javascript, 开发语言

实现的逻辑是先检测是否有人脸,然后再检测是否张嘴了,最后生成照片传给后端比对人脸数据。本人是在工作中的项目需要考勤,前端需要活体检测。想到了这个简单的方法,在纯前端实现这些逻辑,当然精度不高,想要更好的实现,还得靠后端使用好的检测模型。

首先下载好相关的tracking.js,face.js,face-api.js(这个可以直接npm install face-api.js),以及一些静态的资源放public/models下

html部分(提示文字、video、canvas),css部分就不展示了,样式另行设计:

<div class="content_video">
          <div class="face" :class="{ borderColor: borderColor }">
            <p class="tip" style="font-size: 24px; color: blue; text-align: center;">{{ tip }}</p>
            <div class="face-meiti" id="container">
              <video ref="video" preload autoplay loop muted playsinline :webkit-playsinline="true"></video>
              <canvas ref="canvas"> </canvas>
            </div>
          </div>
        </div>

人脸识别部分:人脸识别使用 tracking.js和face.js,检测到人脸后即可进入面部动作识别区域

const borderColor=ref(false)
// import { useRouter } from "vue-router";
const show = ref(false);
const isIndex = ref(true);
const isPhoto = ref(false);
const videoEl = ref();
const localStream = ref();
const timer = ref();
const options = ref([]);
const showPicker = ref(false);
const form = reactive({
  workerName: "",
  programName: "",
  programId: "",
});
const vedioOpen = ref(true)
const reJiance = ref(false)
const state = reactive({
  options: '',
  nose_x_anv: "",
  nose_y_anv: "",
  mouth_Y: '',
  action: "张嘴",
  isCheck: true,
})

//人脸检测
const tip = ref("请正对摄像头");
const mediaStreamTrack = ref();
const video = ref(); //播放器实例
const trackerTask = ref(); //tracking实例
const uploadLock = true; //上传锁
const faceflag = ref(false); //是否进行拍照
const src = ref();
const getUserMedia = ref("");
const canvas = ref();
const LorR = computed(() =>
  store.state.UserInfo.workerName != undefined ? true : false
);
const selectPeople = computed(() => store.state.SalarySelectPeople);
const store = useStore();
//人脸检测区域

const init = async () => {
  toast("正在申请摄像头权限", "info")
  vedioOpen.value = false;
  initTracker(canvas.value);
};
const initTracker = async () => {
  const _this = this;
  // 固定写法
  let tracker = new window.tracking.ObjectTracker("face");
  tracker.setInitialScale(4);
  tracker.setStepSize(2);
  tracker.setEdgesDensity(0.1);
  //摄像头初始化
  trackerTask.value = window.tracking.track(video.value, tracker, {
    camera: true,
  });
  tracker.on("track", async (event) => {
    if (event.data.length == 0) {
      if (!faceflag.value) {
        tip.value = "未检测到人脸";
      }
    } else if (event.data.length > 0) {
      console.log(event.data.length);
      event.data.forEach(async (rect, index) => {
        if (index != 0) {
          return;
        }
        // 防抖
        if (!faceflag.value) {
          faceflag.value = true;
          tip.value = "请张张嘴";
          //背景更改,提示作用
          borderColor.value=true
          //进入人脸动作检测方法
          initHuoti(rect)
        }
      });
    }
  });
};

面部动作识别部分:

const jiance = ref();
const initHuoti = async (rect) => {
  await faceApi.nets.tinyFaceDetector.loadFromUri('/models');
  await faceApi.loadFaceLandmarkModel("/models");
  state.options = new faceApi.TinyFaceDetectorOptions({
    inputSize: 320,
    scoreThreshold: 0.4
  });

  //设定定时器,重复获取当前摄像头的图片
  jiance.value = setInterval(() => {
    let context = canvas.value.getContext("2d", { willReadFrequently: true });
    context.strokeStyle = "#a64ceb";
    context.strokeRect(rect.x, rect.y, rect.width, rect.height);
    canvas.value.width = video.value.videoWidth;
    canvas.value.height = video.value.videoHeight;
    context.drawImage(video.value, 0, 0, canvas.value.width, canvas.value.height);
    let base64Img = canvas.value.toDataURL("image/jpeg");
    face_test(base64Img)
  }, 400)
}
  //使用faceapi检测人脸图片的特征值
const face_test = async (base64Img) => {
  const detections1 = await faceApi.detectSingleFace(canvas.value, state.options).withFaceLandmarks();
  if (detections1) {
    const landmarks = detections1.landmarks
    const jawOutline = landmarks.getJawOutline()
    const nose = landmarks.getNose()
    const mouth = landmarks.getMouth()
    const leftEye = landmarks.getLeftEye()
    const rightEye = landmarks.getRightEye()
    const leftEyeBbrow = landmarks.getLeftEyeBrow()
    const rightEyeBrow = landmarks.getRightEyeBrow()
    //将特征值给这个方法检测是否张嘴了
    isOpenMouth(mouth, base64Img)
    const resizedDetections = faceApi.resizeResults(detections1, { width: 280, height: 280 })
    faceApi.draw.drawDetections(canvas.value, resizedDetections)
  }
}
//是否张嘴,精度取决于其中的检测方法
const isOpenMouth = (mouth, base64Img) => {
  const mouth_Y_list = mouth.map((item) => {
    return item.y
  })
  const max = Math.max(...mouth_Y_list)
  const min = Math.min(...mouth_Y_list)
  const _y = max - min
  if (state.mouth_Y === "") {
    state.mouth_Y = _y
  }
  if (Math.abs(state.mouth_Y - _y) > 10) {
    clearTimeout(jiance.value);
    toast('检测成功,正在拍照', 'info', 1000);
    tip.value = "正在拍照,请勿乱动";
    borderColor.value=false
    //上传照片
    uploadimg(base64Img);
  }
  state.mouth_Y = _y
}

更好的获取摄像头方法,在tracking.js中做修改tracking.initUserMedia_方法,使用window.stream = MediaStream;存储相关流,方便后面页面关闭。

function getMediaDevices() {
    
    try {
        navigator.mediaDevices.enumerateDevices().then(function (devices) {
            devices.forEach(function (device) {
                switch (device?.kind) {
                    case 'audioinput':
                        console.log('音频输入设备(麦克风|话筒):', device);
                        break;
                    case 'audiooutput':
                        console.log('音频输出设备(扬声器|音响):', device);
                        break;
                    case 'videoinput':
                        console.log('视频输入设备(摄像头|相机):', device);
                        break;
                    default:
                        console.log('当前可用的媒体设备: ', device);
                        break;
                }
            });
        }).catch(function (err) {
            console.error(err);
        });
    } catch (err) {
        console.error(err);
    } finally {
        if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
            console.log("不支持mediaDevices.enumerateDevices(), 未识别到多媒体设备!");
        }
    }
};

  tracking.initUserMedia_ = function(element, opt_options) {
    getMediaDevices();
    try {
      var options = {
          audio: true,    // 注:这里 audio、video 默认都为false【一定要确保有麦克风或摄像头(有啥设备就开启啥设备)硬件设备的情况下才设为true 或 {...},否则会报DOMException: Requested device not found 等错!!】
          video: true,  // 获取视频 默认video: { facingMode: 'user' } }前置相机
      }
      if (navigator.mediaDevices.getUserMedia) {
          // 访问用户媒体设备的 新标准API
          navigator.mediaDevices.getUserMedia(options).then(function (MediaStream) {
            element.srcObject=MediaStream
            window.stream = MediaStream;
          }).catch(function (err) {
              console.error("访问用户媒体设备失败:权限被拒绝 或 未识别到多媒体设备!", err);

          }).finally(() => {
              console.log('navigator.mediaDevices.getUserMedia API')
          });
      }
      else if (navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia) {
          try {
              //访问用户媒体设备的 旧标准API 兼容方法
              navigator.getUserMedia(options, function (MediaStream) {
                element.srcObject=MediaStream
                window.stream = MediaStream;
              }, function (err) {
                  console.error("访问用户媒体设备失败:权限被拒绝 或 未识别到多媒体设备!", err);
              })
          } catch (error) {
              console.error(err);
          }
          finally {
              console.log('navigator.getUserMedia API')
          };
      } else {
          if (0 > location.origin.indexOf('https://')) {
              console.error("提示:请尝试在本地localhost域名 或 https协议 的Web服务器环境中重新运行!");
          }
      }
  } catch (error) {
      console.error("访问用户媒体设备失败:", error);
  }

  };

//在使用页面关闭摄像头的方法
const stopMediaStreamTrack = function () {
  if (typeof window.stream === "object") {
    window.stream.getTracks().forEach(track => track.stop());
  }
  clearTimeout(jiance.value);
}

http://www.niftyadmin.cn/n/5797111.html

相关文章

ChatGPT生成测试用例的最佳实践(四)

通常情况下还应该进行测试用例外不评审。将已完成的基于百度关键字搜索业务的功能和安全测试用例集的存放位置告知项目团队成员&#xff0c;需要预留出一定的时间&#xff0c;便于项目组研发、产品人员阅读&#xff0c;以免在项目团队测试用例评审会议上占用过多时间熟悉相关测…

Windows11 家庭版安装配置 Docker

1. 安装WSL WSL 是什么&#xff1a; WSL 是一个在 Windows 上运行 Linux 环境的轻量级工具&#xff0c;它可以让用户在 Windows 系统中运行 Linux 工具和应用程序。Docker 为什么需要 WSL&#xff1a; Docker 依赖 Linux 内核功能&#xff0c;WSL 2 提供了一个高性能、轻量级的…

Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门hello world输出【入门一】

开发环境搭建&#xff1a;Linux-Ubuntu下搭建ESP32的开发环境的步骤&#xff0c;使用乐鑫最新稳定版的esp-idf-CSDN博客 一、安装好开发环境后&#xff0c;在esp目录下再创建一个esp32的目录【用于编程测试demo】 二、进入esp32目录&#xff0c;打开终端【拷贝esp-idf的hello工…

mapStateToProps

mapStateToProps 是 React 应用中与 Redux 结合使用时的一个重要概念。它是一个函数&#xff0c;用于将 Redux store 中的状态映射到 React 组件的 props 上。通过这个函数&#xff0c;你可以选择组件需要订阅的 state 部分&#xff0c;并在 Redux store 更新时自动更新组件的 …

yolov5 yolov6 yolov7 yolov8 yolov9目标检测、目标分类 目标切割 性能对比

文章目录 YOLOv1-YOLOv8之间的对比如下表所示&#xff1a;一、YOLO算法的核心思想1. YOLO系列算法的步骤2. Backbone、Neck和Head 二、YOLO系列的算法1.1 模型介绍1.2 网络结构1.3 实现细节1.4 性能表现 2. YOLOv2&#xff08;2016&#xff09;2.1 改进部分2.2 网络结构 3. YOL…

渗透测试实战—某医院安全评估测试

免责声明&#xff1a;文章来源于真实渗透测试&#xff0c;已获得授权&#xff0c;且关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本…

SEO初学者-SEO基础

SEO 基础SEO 初期设置SEO怎么做如何跟踪 SEO 效果免费的 SEO 工具 如果您希望人们通过 Google 找到您的网站&#xff0c;您需要了解 SEO 的基础知识。这些知识比您想象的要简单。 什么是 SEO&#xff1f; 搜索引擎优化 (SEO) 是增加网站自然搜索流量的过程。在这一过程中&am…