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

avatar
作者
猴君
阅读量:2

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

首先下载好相关的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); }

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!