纯前端实现人脸识别和对比

360 阅读2分钟

前言

闲来无事,学点东西充实一下自己。随手记录一下。本文使用了百度云的人脸对比的API,多个申请步骤。为什么要用纯前端来实现呢,懒呗还有啥。就想简单的实现一下,就不搞弯弯绕了。本次实现的主要是前端人脸识别并保存图片,然后调用百度云是API来完成对比,通过返回的相似度值来判断是否对比成功。文末附源码。其实也可以不用API,可以用python来完成模型训练,但那玩意我忘了,有时间再捡捡吧,社区搜搜应该有的。

页面布局:没啥好说的,就一个摄像头几个按钮

image.png

主要技术:vue2 + elementUI + tracking.js

本来是想用vue3的,但是好像有奇奇怪怪的bug,果断用了vue2。

创建项目

自己创建呗还等啥

然后npm i elementui啊axios啊

gan jin qu dong shou a

下载 trackingjs,别用npm,有坑!信我!戳这里=> 真的不骗你! (trackingjs.com)

image.png

下载完成之后解压有这么一个文件

image.png

选中它! 然后 ctrl+c 去到你项目的 src 目录下面的 asst 文件夹,然后ctrl+v

image.png

舒服了

接下来就是把这段源码放到你的页面中就可以了

<!--
  🚀 Tip   : tracking.js + 百度云 ===> 完成人脸识别
  🥇 Author: LanYi
  ✨ Email : xiaoqi990727@163.com
  ✍ Data  : 2023-03-22 14:24:21
-->
<template>
  <div>
    <div class="video-box">
      <video id="video" width="320" height="240" preload autoplay loop muted></video>
      <canvas id="canvas" width="320" height="240"></canvas>
    </div>
    <canvas id="screenshotCanvas" width="320" height="240"></canvas>
    <div class="switch-button">
      <el-row>
        <el-button type="primary" @click="destroyed">关闭摄像头</el-button>
        <el-button type="primary" @click="init">开始识别</el-button>
        <el-button type="primary" @click="dayin">打印初始图</el-button>
        <el-button type="primary" @click="dayinya">打印对比图</el-button>
        <el-button type="primary" @click="getSimilarity">获取相似度</el-button>
      </el-row>
    </div>
    <div>
      <img :src="img" alt="">
      <img :src="img1" alt="">
    </div>
  </div>
</template>

<script>
import '@/assets/tracking/build/tracking-min.js';
import '@/assets/tracking/build/data/face-min.js';
import axios from 'axios';

export default {
  data () {
    return {
      trackerTask: null,
      mediaStreamTrack: null,
      video: null,
      screenshotCanvas: null,
      img: null,
      img1: null,
      length: null,
      token: null
    }
  },
  mounted () {
    this.init();
    this.getToken()
  },
  methods: {
    // 初始化设置
    init () {
      this.video = this.mediaStreamTrack = document.getElementById('video');
      this.screenshotCanvas = document.getElementById('screenshotCanvas');

      let canvas = document.getElementById('canvas');
      let context = canvas.getContext('2d');

      // 固定写法
      let tracker = new window.tracking.ObjectTracker('face');
      tracker.setInitialScale(4); // 设定初始比例
      tracker.setStepSize(2);  // 设置步长
      tracker.setEdgesDensity(0.1); // 设置边缘密度
      // 摄像头初始化
      this.trackerTask = window.tracking.track('#video', tracker, { camera: true });
      // 监听
      tracker.on('track', event => {
        // 检测出人脸 绘画人脸位置
        context.clearRect(0, 0, canvas.width, canvas.height);
        // rect.x, rect.y, rect.width, rect.height这四个参数表示左上角的坐标和框出来人脸的大小。
        event.data.forEach(rect => {
          context.strokeStyle = '#a64ceb';
          context.strokeRect(rect.x, rect.y, rect.width, rect.height);
          context.font = '11px Helvetica';
          context.fillStyle = "#fff";
          context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
          context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
        });
        // event.data.length长度代表检测几张人脸
        this.length = event.data.length
      });
    },
    // 获取相似度 ===>关键
    async getSimilarity () {
      const data = JSON.stringify([
        {
          "image": this.img.replace(/^data:image\/\w+;base64,/, ""),
          "image_type": "BASE64",
          "face_type": "LIVE",
          "quality_control": "LOW",
          "liveness_control": "HIGH"
        },
        {
          "image": this.img1.replace(/^data:image\/\w+;base64,/, ""),
          "image_type": "BASE64",
          "face_type": "IDCARD",
          "quality_control": "LOW",
          "liveness_control": "HIGH"
        }
      ])

      const res = await axios({
        url: `/ly/match?access_token=${this.token}`,
        method: 'POST',
        headers: { "content-type": "application/json" },
        data
      })
      console.log('人脸对比结果 ======>', res.data)
      if (!res.data.result) {
        this.$message.error('人脸对比失败')
        return
      }
      res.data.result.score > 80 && this.$message.success(`人脸对比通过,相似度为 ${res.data.result.score}`);
      res.data.result.score < 80 && this.$message.error('人脸对比失败');
    },
    // 获取百度云 token
    async getToken () {
      const postData = {
        url: `/xiaoqi/token?client_id=xxxxxx&client_secret=xxxxxx&grant_type=client_credentials`,
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        },
      }
      const res = await axios(postData)
      this.token = res.data.access_token
    },

    // 获取人脸数据
    getFaceData () {
      // 没有识别到人脸直接返回
      if (!this.length) return
      // 绘制当前帧图片转换为base64格式
      let canvas = this.screenshotCanvas;
      let video = this.video;
      let ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      let base64Img = canvas.toDataURL('image/jpeg');
      return base64Img
    },

    // 初始图
    dayin () {
      this.img = this.getFaceData()
    },

    // 对比图
    dayinya () {
      this.img1 = this.getFaceData()
    },

    //关闭摄像头
    destroyed () {
      if (!this.mediaStreamTrack) {
        return
      }
      this.mediaStreamTrack.srcObject.getTracks()[0].stop();
      this.trackerTask.stop()
    }
  }
}
</script>

<style scoped>
/* 绘图canvas 不需显示隐藏即可 */
#screenshotCanvas {
  display: none;
}

.video-box {
  position: relative;
  margin-left: 30px;
  width: 320px;
  height: 240px;
}

.switch-button {
  margin-top: 30px;
  margin-left: 30px;
}
video,
canvas {
  position: absolute;
  top: 0;
  left: 0;
  border: #000000 5px solid;
}
</style>


代码别直接跑啊,肯定会报错的。检查一下引入的路径啥的名字对不对啥的。还有,百度云的一些密钥啥的我都用 xxxxxx 来替换了。你需要去百度云拿你自己的!获取token那里留个心眼。

image.png

用下面这两个去替换一下

image.png

怎么创建我也不记得了,自己摸索吧,反正创建完要人脸识别验证一下,然后好像有一些可以白嫖的福利领一下,主要是搜索 人脸识别 然后就点进去,创建应用,就可以看见上面的页面了。好了就这样了,累了。本来想说一下具体代码的,但是发现这个好简单,还有挺多注释的,如果你看到这了就慢慢琢磨吧,加油秃头仔!

差点忘记了,还没完呢

纯前端解决会被浏览器报跨域的错误,我做了代理,直接复制粘贴过去就可以了。现在基本没什么问题了。

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,

  devServer: {
    proxy: {
      '/ly': {
        target: 'https://aip.baidubce.com/rest/2.0/face/v3', //后台接口的服务地址
        pathRewrite: { '^/ly': ''}
      },
      '/xiaoqi': {
        target: 'https://aip.baidubce.com/oauth/2.0', //后台接口的服务地址
        pathRewrite: { '^/xiaoqi': ''}
      },
    }
  }
})

意外之喜

image.png

对比不通过的话就好好反思一下自己哈,是不是太帅了让接口堵住了!