VUE-----人脸识别功能实现

<template>
  <div id="faceIdentification">
    <div class="face-body">
      <div v-show="showContainer" class="face-capture" id="face-capture">
        <p class="tip">请保持人像在取景框内</p>
        <div @click='getCountDown' class="count_down" v-if="iscountDown">{{ count_down }}</div>
        <video  id="video" :width="vwidth" :height="vheight" playsinline webkit-playsinline ></video>
        <canvas id="refCanvas" :width="cwidth" :height="cheight"></canvas>
        <p class="contentp">{{ scanTip }}</p>
      </div>
      <div v-if="!showContainer" class="img-face">
              <img class="imgurl" :src="imgUrl" />
      </div>
    </div>
  </div>
</template>

<script>
import storage from "store";
import { USERINFO } from "@/utils/mutation-types";
import { Toast } from "vant";
import tracking from "@/pages/checkout/assets/data/tracking-min";
import "@/pages/checkout/assets/data/face-min.js";
import "@/pages/checkout/assets/data/eye-min.js";
import "@/pages/checkout/assets/data/mouth-min.js";
export default {
  data() {
    return {
      screenSize: {
        width: window.screen.width,
        height: window.screen.height,
      },
      URL: null,
      streamIns: null, // 视频流
      showContainer: true, // 显示
      tracker: null,
      tipFlag: false, // 提示用户已经检测到
      flag: false, // 判断是否已经拍照
      context: null, // canvas上下文
      profile: [], // 轮廓
      removePhotoID: null, // 停止转换图片
      scanTip: "人脸识别中...", // 提示文字
      imgUrl: "",
      canvas: null,
      trackertask: null,
      vwidth: "266",
      vheight: "266",
      cwidth: "266",
      cheight: "266",
      userInfo: {},
      orderData: {},
      count_down: 5,
      iscountDown: false,
      imgbase64:''
    };
  },
  mounted() {
    const scale = this.screenSize.width / 375;
    this.vwidth = 266 * scale;
    this.vheight = 266 * scale;
    this.cwidth = 266 * scale;
    this.cheight = 266 * scale;
    this.playVideo();


  },
  created() {
    
  },
  methods: {
    
    playVideo() {
      this.getUserMedia({
        //摄像头拍摄的区域
        video: {
          width: 500,
          height: 500,
          facingMode: "user",
        } /* 前置优先 */,
        },
          this.success,
          this.error
      );
    },

    // 访问用户媒体设备
    getUserMedia(constrains, success, error) {
      if (navigator.mediaDevices.getUserMedia) {
        //最新标准API
        navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
      } else if (navigator.webkitGetUserMedia) {
        //webkit内核浏览器
        navigator.webkitGetUserMedia(constrains).then(success).catch(error);
      } else if (navigator.mozGetUserMedia) {
        //Firefox浏览器
        navagator.mozGetUserMedia(constrains).then(success).catch(error);
      } else if (navigator.getUserMedia) {
        //旧版API
        navigator.getUserMedia(constrains).then(success).catch(error);
      } else {
        this.scanTip = "你的浏览器不支持访问用户媒体设备";
      }
    },
    success(stream) {
      this.streamIns = stream;
      const video = document.getElementById("video");
      // webkit内核浏览器
      this.URL = window.URL || window.webkitURL;
      if ("srcObject" in video) {
        video.srcObject = stream;
      } else {
        video.src = this.URL.createObjectURL(stream);
      }
      // 苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
      setTimeout(() => {
        video.play();
        this.initTracker();// 人脸捕捉
      }, 100);
    },
    error(e) {
      this.scanTip = "访问用户媒体失败";
    },

    initTracker() {
      this.context = document.getElementById("refCanvas").getContext("2d"); // 画布
      this.canvas = document.getElementById("refCanvas");
      this.tracker = new window.tracking.ObjectTracker("face"); // tracker实例
      this.tracker.setInitialScale(4);
      this.tracker.setStepSize(2); // 设置步长
      this.tracker.setEdgesDensity(0.1);
      try {
        this.trackertask = window.tracking.track("#video", this.tracker); // 开始追踪
      } catch (e) {
        this.scanTip = "访问用户媒体失败,请重试";
      }
      this.tracker.on("track", (e) => {
        //画布描绘之前清空画布
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
        if (e.data.length === 0) {
          this.scanTip = "未检测到人脸";
        } else {
          e.data.forEach((rect) => {
              //设置canvas 方框的颜色大小
              this.context.strokeStyle = "#42e365";
              this.context.lineWidth = 2;
              this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
            });
            if (!this.tipFlag) {
              this.scanTip = "检测成功,正在拍照,请保持不动5秒";
            }
            // 1.5秒后拍照,仅拍一次 给用户一个准备时间
            // falg 限制一直捕捉人脸,只要拍照之后就停止检测
            if (!this.flag) {
              this.scanTip = "拍照中...";
              this.flag = true;
              this.getCountDown()
              
              //  this.removePhotoID = setTimeout(() => {
              //   this.tackPhoto();
              //   document.getElementById("video").pause();
              //   this.tipFlag = true;
              // }, 1500);
              console.log( this.removePhotoID)

            }
          }
        });
      },
      //到计数
      getCountDown(){
        this.iscountDown= true; 
        if (this.count_down > 1) {
            setTimeout(() => {
              this.count_down--;
              this.getCountDown()
            }, 1000);
        }else{
          this.iscountDown= false;
          this.removePhotoID = setTimeout(() => {
              this.tackPhoto();
              document.getElementById("video").pause();
              this.tipFlag = true;
            }, 1500);
        }
      },

       tackPhoto() {
        // 在画布上面绘制拍到的照片
        this.context.drawImage(
          document.getElementById("video"),
          0,
          0,
          this.vwidth,
          this.vwidth
        );
        this.imgUrl = this.saveAsPNG(document.getElementById("refCanvas"));
        // this.compare(imgUrl)
        //判断图片大小
        this.imgSize();
        //this.faceToTengXun(); // 人脸比对
        this.close();
      },
      imgSize() {
        if (this.imgUrl) {
          // 获取base64图片byte大小
          const equalIndex = this.imgUrl.indexOf("="); // 获取=号下标
          let size;
          if (equalIndex > 0) {
            const str = this.imgUrl.substring(0, equalIndex); // 去除=号
            const strLength = str.length;
            const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
            size = Math.floor(fileLength / 1024); // 向下取整
            console.log("size", size + "KB");
          } else {
            const strLength = this.imgUrl.length;
            const fileLength = strLength - (strLength / 8) * 2;
            size = Math.floor(fileLength / 1024); // 向下取整
            console.log("size", size + "KB");
          }
          if (size > 1024) {
          // 图片超过1M 按比例压缩
          this.imgUrl = document.getElementById("refCanvas").toDataURL("image/png", 1024 / size);
          }
          console.log(this.imgUrl)
          this.imgbase64 = this.imgUrl.substring(22)
          console.log(this.imgbase64)
          this.$emit("getbase64",this.imgbase64)

          //this.$emit('getDetail',  this.imgbase64)
         

        }
      },
      //保存照片
      getBlobBydataURI(dataURI, type) {
        var binary = window.atob(dataURI.split(",")[1]);
        var array = [];
        for (var i = 0; i < binary.length; i++) {
          array.push(binary.charCodeAt(i));
        }
        return new Blob([new Uint8Array(array)], {
          type: type,
        });
      },
      // 保存为png,base64格式图片
      saveAsPNG(c) {
        return c.toDataURL("image/png", 0.4);
      },
      close() {
        this.flag = false;
        this.tipFlag = false;
        this.showContainer = false;
        this.context = null;
        this.scanTip = "人脸识别中...";
        clearTimeout(this.removePhotoID);
        if (this.streamIns) {
          this.streamIns.enabled = false;
          this.streamIns.getTracks()[0].stop();
          this.streamIns.getVideoTracks()[0].stop();
        }
          this.streamIns = null;
          this.trackertask.stop();
          this.tracker = null;
      }
  },
};
</script>


<style scoped lang="less">
#faceIdentification{
  position: absolute;
  height: 100%;
  width: 100%;
  background: #f0f0f0;
  top: 0px;
  .face-body{
    position: relative;
    height: 100%;
    width: 100%;
    .count_down{
      position: absolute;
      font-size: 35px;
      top: 220px;
      color: #fff;
      display: inline-block;
      z-index: 99999;
      opacity: 0.5;
    }

  }
  
}
  #video{
    z-index: 9999;
  }
  .face-capture {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .tip {
    position: fixed;
    top: 48px;
    z-index: 5;
    font-size: 18px;
    font-weight: 500;
    color: #333333;
    line-height: 25px;
  }

  .face-capture{
    video{
      position: fixed;
      top: 117.5px;
      object-fit: cover;
      z-index: 2;
      background-repeat: no-repeat;
      background-size: 100% 100%;
    }

  }
 .face-capture{
    canvas {
      position: fixed;
      top: 117.5px;
      object-fit: cover;
      z-index: 2;
      background-repeat: no-repeat;
      background-size: 100% 100%;
    }

  } 

  .face-capture {
    .img-cover {
      position: fixed;
      top: 63px;
      width: 375px;
      height: 375px;
      object-fit: cover;
      z-index: 3;
      background-repeat: no-repeat;
      background-size: 100% 100%;
    }
  }
  .face-capture {
    .contentp {
      position: fixed;
      top: 438px;
      font-size: 18px;
      font-weight: 500;
      color: #333333;
    }
  }
  .face-capture {
    .rect {
      border: 2px solid #0aeb08;
      position: fixed;
      z-index: 4;
    }
  }
  .img-face {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .img-face{
    .imgurl {
      position: fixed;
      top: 117.5px;
      width: 266px;
      height: 266px;
      border-radius: 133px;
    }

  } 


</style>
转载与简书:原文连接https://www.jianshu.com/p/e53592bbedea
posted @   爬坑之路任重而道远  阅读(597)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示