koa+vue+百度云实现人脸识别

本文介绍了如何结合Koa后端和Vue前端,利用百度智能云的人脸识别服务。首先,需要在百度智能云注册并创建人脸识别应用以获取API Key和Secret Key。在Koa后端配置跨域并实现获取Access Token及人脸比对的接口。前端部分则展示完整代码,包括页面逻辑、提示页面、axios请求封装以及接口调用,实现了人脸识别后的自动截图和关闭摄像头功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

百度智能云

1、先去百度智能云注册一个百度智能云账号

2、创建人脸识别应用

进入页面后,在侧边栏选择人脸识别,在应用列表中创建一个新的应用,填写【应用名称】和【应用描述】,其他的使用默认值就可以了

3、获取秘钥

应用创建成功后,记录下自己的API Key、Secret Key

koa 后端

1.app.ts配置跨域和引入koa

import Koa from 'koa';
import Cors from 'koa2-cors';
import { corsHandler } from './middleware/cors';
import koaBody from 'koa-body';
import router from './router/packaging/index';

const app = new Koa();

// 处理静态资源
// app.use(Static(path.join(__dirname,'./public/img')));

// 跨域请求
// console.log(router);
app.use(Cors(corsHandler));

app.use(koaBody());

// 传递到 中间件里面
app.use(router.routes()).use(router.allowedMethods());

const PORT = 4001;
app.listen(PORT,()=>{
  console.log(`http://localhost:${PORT},已启动`);
});

2.获取Access Token的接口

// 人脸识别 获取Acess Token
export const getToken = async (ctx: any) => {
  console.log('----我是人脸识别接口');
  const param = qs.stringify({
    'grant_type': 'client_credentials',
    'client_id': '百度云应用id',
    'client_secret': '百度云应用秘钥'
  });
  const axs = await axios.post('https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=iZnI34PU6myzxi1sDYQrI6Fp&client_secret=uWL8ufxX8bz2Zus2r60tlQUXBsu9PtdG&', param);
  // console.log(axs);
  ctx.body = axs.data;

};

3.人脸比对

export const checkFace = async (ctx: any) => {
  // console.log(ctx.request.body);
  // 请求接口
  const url = 'https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=' + ctx.request.body.access_token;
  // 请求的图片数据
  console.log('woshi');
  // 请求数据
  // const data = {
  //   image_type: 'BASE64',
  //   image: ctx.request.body.img,
  //   // group_id_list: 'test_add', // 之前注册人脸管理库的名字
  //   // liveness_control: 'HIGH'     // 活体监测
  // };
  const dir = path.join(__dirname, ctx.request.body.urls[0].urls);
  console.log(dir);
  const base64 = fs.readFileSync(dir, 'base64'); // 文件流并转 base64
  console.log();
  const data = [
    {
      "image": ctx.request.body.img,
      "image_type": "BASE64",
      "face_type": "LIVE",
      "quality_control": "LOW",
      "liveness_control": "HIGH"
    },
    {
      "image": base64,//要比对的照片
      "image_type": "BASE64",
      "face_type": "LIVE",
      "quality_control": "LOW",
      "liveness_control": "HIGH"
    },
  ];

  const sxs = await axios({
    method: "POST",
    // headers: { "Content-Type": "application/x-www-form-urlencoded" },
    url: url,
    data: data,
  });
  ctx.body = sxs.data;
  console.log(sxs.data);
};

前端部分

完整代码

1.主要页面

<template>
  <div class="box">
    <div class="aut">
      <h2>请将脸部对准识别框</h2>
      <div class="user-icon">
        <video
          width="320"
          height="240"
          ref="videoDom"
          id="video"
          preload
          autoplay
          loop
          muted
        ></video>
        <canvas width="630" height="490" ref="canvasDOM"></canvas>
      </div>

      <div>{{ loding }}</div>
      <el-button class="button" type="warning" @click="initTracker"
        >去考试</el-button
      >
      <el-button type="primary" class="button1" @click="fan"
        >返回登录</el-button
      >
      <div class="shenfen">
        <!-- <img src="require('@')" alt=""> -->
        <p>姓名:{{ Routerdata.username }}<br /></p>
        <p>电话:{{ Routerdata.phone }}<br /></p>
        <p>班级:{{ Routerdata.ClassA }}<br /></p>
        <p>考生号:{{ Routerdata.candidate }}</p>
      </div>
    </div>
    <div class="soild_text_one">
      <fieldset>
        <legend>注意事项</legend>
        <p>1.不允许切屏、刷新页面,数量达到3次将会强制提交试卷</p>
        <p>2.不允许交头接耳</p>
        <p>3.遵守考场纪律</p>
      </fieldset>
    </div>
    <!-- <Alert /> -->
  </div>
</template>

<script>
import("tracking/build/tracking-min.js");
import("tracking/build/data/face-min.js");
import { getTopics, getToken, checkFace } from "../../api/gxf";
import { getApp, getTestListA } from "../../api/hjr";
import Alert from "./alert.vue";
import { timeToNum } from "../../utils/timechange";
// 嘴巴等特征,后期可添加
// import('tracking/build/data/mouth-min.js')
// import('tracking/build/data/eye-min.js')
// import('tracking/examples/assets/stats.min.js')
// var objects = new tracking.ObjectTracker(['face', 'eye', 'mouth']);
// console.log(objects)

export default {
  components: {
    // Alert,
  },
  name: "testTracking",
  data() {
    return {
      // 记录拍照到了几次
      count: 0,
      isdetected: "请您保持脸部在画面中央",
      loding: "",
      access_token: "",
      dialogVisible: false,
      fullscreenLoading: false,
      Routerdata: [],
      datalist: [],
      TestResultData:[],
      AppData:[]
    };
  },
  methods: {
    // 初始化racker
    initTracker() {
      // alert('进来了')
      // alert(navigator.mediaDevices)
      // 启用摄像头,这一个是原生调用摄像头的功能,不写的话有时候谷歌浏览器调用摄像头会失败
      navigator.mediaDevices
        .getUserMedia({ video: true, audio: true })
        .then(this.getMediaStreamSuccess)
        .catch(this.getMediaStreamError);

      this.context = this.canvas.getContext("2d");

      // 初始化tracking参数
      this.tracker = new tracking.ObjectTracker("face");
      this.tracker.setInitialScale(4);
      this.tracker.setStepSize(2);
      this.tracker.setEdgesDensity(0.1);
      this.tracker.on("track", (event) => {
        this.onTracked(event);
      });

      // tracking启用摄像头,这里我选择调用原生的摄像头
      // tracking.track(this.video, this.tracker, { camera: true });

      // 如果是读取视频,可以用trackerTask.stop trackerTask.run来暂停、开始视频
      this.trackerTask = tracking.track(this.video, this.tracker);
    },
    // 监听中
    onTracked(event) {
      // 判断终止条件, stop是异步的,不返回的话,还会一直截图
      if (this.count >= 21) {
        this.onStopTracking();
        return;
      }

      // 画框框
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      event.data.forEach((rect) => {
        this.context.lineWidth = 1;
        this.context.strokeStyle = "#a64ceb";
        //'#a64ceb';
        this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
        this.context.font = "11px Helvetica";
        this.context.fillStyle = "#fff";
        // 截图

        if (event.data.length > 0 && this.count <= 20) {
          if (this.count < 0) {
            this.count = 0;
          }
          this.count += 1;
          if (this.count > 20) {
            this.isdetected = "已检测到人脸,正在识别";
            this.getPhoto();
          }
        } else {
          this.count -= 1;
          if (this.count < 0) {
            this.isdetected = "请您保持脸部在画面中央";
          }
        }
      });
      // 视频中心展示文字
      this.context.fillText(this.isdetected, 100, 30);
    },
    // 停止监听
    onStopTracking() {
      this.trackerTask.stop();
      this.video.pause();
      // 关闭摄像头
      this.video.srcObject = null;
      window.stream.getTracks().forEach((track) => track.stop());
    },
    // 获取人脸照片
    getPhoto() {
      console.log(this.isdetected);
      this.isdetected = "";
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      let video = document.getElementById("video");
      this.context.drawImage(
        video,
        0,
        0,
        this.canvas.width,
        this.canvas.height
      );
      let dataUrl = this.canvas.toDataURL("image/jpeg", 1);
      // console.log(dataUrl)
      this.imgbase64 = dataUrl.replace(/^data:image\/\w+;base64,/, "");
      // 开始人脸识别
      this.postFace();
    },
    // 初始化调用Token
    getToken() {
      getToken().then((res) => {
        this.access_token = res.access_token;

        console.log(res);
        console.log(this.access_token);
      });
    },
    // 人脸验证
    postFace() {
      const loading = this.$loading({
        lock: true,
        text: "正在识别中,请稍后................",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)",
      });
      setTimeout(() => {
        loading.close();
      }, 2000);

      checkFace({
        access_token: this.access_token,
        img: this.imgbase64,
        urls: this.datalist,
      }).then((res) => {
        console.log(res);
        this.loding = "";
        if (res.error_code !== 0) {
          if (res.error_code == 223120) {
            this.video.srcObject = null;
            window.stream.getTracks().forEach((track) => track.stop());
            this.$message({
              message: "检测失败,请从新检测",
              type: "warning",
            });
            this.count = 0;
            this.onStopTracking();
          }
        } else {
          if (res.result.score > 80) {
            this.$message({
              message: "人脸识别成功,3秒后跳转",
              type: "success",
            });
          
      
            setTimeout(() => {
              localStorage.setItem("wxToken", "1");
              this.$router.push({
                path: "/",
                query: this.Routerdata,
              });
            }, 3000);
          } else {
            this.$message({
              message: "人脸识别失败,未找到",
              type: "warning",
            });
            this.count = 0;
            this.onStopTracking();
          }
        }
      });
      // this.$axios
      //   .post("http://192.168.50.35:3000/checkFace", {})
      //   .then((res) => {

      //   });
    },
    // openFullScreen2() {

    // },
    // 视频流启动
    getMediaStreamSuccess(stream) {
      window.stream = stream;
      this.video.srcObject = stream;
    },
    // 视频媒体流失败
    getMediaStreamError(error) {
      alert("视频媒体流获取错误" + error);
    },
    fan() {
      this.$router.push("/login");
    },
  },
  mounted() {
    this.dialogVisible = true;
    this.video = this.$refs.videoDom;
    this.canvas = this.$refs.canvasDOM;
    window.addEventListener("beforeunload", localStorage.setItem('leaves',0), false);
    //初始化获取tonken
    this.getToken();
    let url = this.$route.fullPath;
    // const {username,classA,phone,candidate} = this.$route.query;
    this.Routerdata = this.$route.query;
    getApp({ a: 1, b: 2 }).then((res) => {
      let a = res.filter((item, index) => {
        return this.Routerdata.phone === item.phone;
      });
      // console.log(a);
      this.datalist = a;
    });
        getTestListA().then((TestResult) => {
              this.TestResultData = TestResult
            });
            getApp().then((AppResult)=>{
              this.AppData = AppResult
            })
  },
  destroyed() {},
};
</script>

<style scoped>
.soild_text_one {
  width: 100%;
  margin: 0 auto;
}

fieldset {
  height: 500px;
  border-left: none;
  border-right: none;
  border-bottom: none;
  color: gray;
  border-color: #e7f8f7f8;
  height: 20px;
  margin-top: 60px;
}
legend {
  text-align: center;
  padding: 0 10px;
  font-size: 18px;
}
.aut {
  width: 70%;
  height: 651px;
  margin: 0 auto;
}
.box {
  width: 100%;
  height: 850px;
}
h2 {
  font-family: "宋体";
  font-weight: bold;
  text-align: center;
}
.shenfen {
  width: 300px;
  height: 300px;
  border: 1px solid rgb(235, 223, 223);
  padding: 20px;
  margin-left: 57px;
  margin-top: 30px;
}
.user-icon {
  position: relative;
  margin: 0 auto;
  margin-top: 10px;
  width: 890px;
  height: 489px;
  background: #000;
  float: right;
}
.button1 {
  width: 300px;
  height: 50px;
  line-height: 50px;
  margin: 0 auto;
  float: left;
  font-size: 16px;
  position: absolute;
  position: absolute;
  left: 70%;
  top: 630px; /*参照物是父容器*/
  transform: translate(-50%, -50%); /*百分比的参照物是自身*/
}
.button {
  width: 300px;
  height: 50px;
  line-height: 50px;
  margin: 0 auto;
  float: left;
  font-size: 16px;
  position: absolute;
  position: absolute;
  left: 50%;
  top: 630px; /*参照物是父容器*/
  transform: translate(-50%, -50%); /*百分比的参照物是自身*/
}
video,
canvas {
  width: 630px;
  height: 100%;
  position: absolute;
  left: 50%;
  top: 50%; /*参照物是父容器*/
  transform: translate(-50%, -50%); /*百分比的参照物是自身*/
}
</style>

2.提示页面

<template>
  <div class="divAlert"></div>
  <!-- <el-button type="text" @click="">Click to open the Message Box</el-button> -->
</template>

<script lang="ts">
import {onMounted} from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import type { Action } from 'element-plus'
export default {
  props: {},
  setup(props) {
    const open = () => {
      ElMessageBox.alert("即将进行人脸识别", "提示", {
        confirmButtonText: "OK",
        callback: (action: Action) => {
        },
      });
    };
    onMounted(()=>{open()}) 
    return {
      open
    };
  },
};
</script>

<style>
</style>

3.axios请求封装

import axios from 'axios';
// import { message } from 'antd';

// const apiV1 = 'http://localhost:4001/';
// const apiV1 = 'http://192.168.5.104:5003/';
const apiV1='/api'
export const httpProvider = axios.create({
  // baseURL:process.env.NODE_ENV === 'production'? apiV1+'/api/v1':'/api',
  baseURL: apiV1,
  timeout: 6000
});
// 拦截 : 请求拦截
httpProvider.interceptors.request.use(
  (config: any) => {
    const token = window.localStorage.getItem('token');
    if (token) { // 有token,请求头里Authorization: Bearer "1"
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  () => {
    throw new Error('发起请求出错');
  }
);

httpProvider.interceptors.response.use(
  (res) => {
    return res.data;
  },
  (err) => {
    if (err && err.response && err.response.status) {
      const status = err.response.status;
      switch (status) {
        case 504:
        case 500:
        case 404:
          // message.error('服务器异常');
          break;
        case 401:
          // message.error('未授权');
          break;
        case 403:
          // message.error('无权访问');
          break;
        case 422:
          // message.error('参数出错');
          break;
        // case 400: // 请求出错
        //   message.error('未授权');
        //   break;
        default:
          // message.error('未知错误');
      }
    }
  }
);

4.接口

import { httpProvider } from "../utils/request";
// 人脸识别 获取Acess Token
export function getToken(data:any):any{
  console.log(data)
  return httpProvider.request({
    method:'POST',
    url:'/getToken',
    data
  });
}

// 监测人脸
export function checkFace(data:any):any{
  console.log(data)
  return httpProvider.request({
    method:'POST',
    url:'/checkFace',
    data
  });
}
export function getTestListA(data:any):any{
  return httpProvider.request({
    method:'POST', 
    url:'/getTestListA',
    data
  });
}
export function getApp(data:any):any{
  return httpProvider.request({
    method:'POST', 
    url:'/getApp',
    data
  

完整的代码中包含了,人脸识别认证后自动截图,自动关闭摄像头等功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值