简单的说一下人脸识别的过程及前端实现

2020-05-29 09:37:14 浏览数 (1)

写在前面

最近写项目的时候在做一个登录的操作,但是我们因为是多端的,有小程序,有PC,后期可能还有公众号等,所以我们的登录是很多种不同的登录方式的,有最基本的账号密码登录,有微信扫码登录,有工号刷卡登录,有手机验证码登录,当然还有逼格比较高的人脸识别登录,手机验证码登录其实是一个很简单的,所以我可能会在后期写一下处理的过程,今天我们大概说一下人脸识别的一个过程,当然因我不是写后端的,所以这里是不能贴后端的源码的,但是前端的处理还是可以写一下的。

业务需求

最近在做登录的模块,有人问了,我怎么做项目和别人不一样啊,怎么是反着的,别人都是登录先做好,再实现别的,其实这个是没什么的,我们是先简单的实现了一个账号密码的登录的过程,然后就开始实现里面具体的一些业务了,现在里面的已经做了一部分,可以进一步的完善我们的登录模块了,登录的方式很多种,其中一种就是人脸识别登录了,就是当用户点击人脸识别登录的时候,直接打开摄像头,看到自己以后进行比对,实现登录的一个过程。

实现过程

Created with Raphaël 2.2.0开始人脸照片(func) 后端 (png)比对人脸库结束yesno

就是这样一个简单的过程(其实是markdown画流程图不熟练),好吧,就简单的这样展示一下,其实人脸识别就是一个前端给照片,后端进行比对的一个过程,所以本质上说我画的也是对的。

前端代码

我把这调用摄像头和发送给后端图片文件封装成了一个组件,下面介绍一下组件怎么使用。

代码语言:javascript复制
<template>
  <div class="videos" ref="videos">
    <video  id="video" class="vio" autoplay="autoplay" v-show="video_show"></video>
    <!--隐藏掉   为了发送照片-->
    <canvas id="canvas" width="498" height="238" v-show="canvas_show"></canvas>
  </div>
</template>

<script>
  export default {
    name: "Videos",
    props:{
      isOpen : false
    },
    data() {
      return {
        video_show : true,
        canvas_show : false,
        url : '',
        loading : true,
      }
    },
    watch:{
      isOpen(val){
        this.isOpen = val;
        console.info(val);
        if(val){
          this.camera_options();
        }
      }
    },
    methods: {
        camera_options(){
        let that = this;
        var constraints = {
          video: {
            width: 498,
            height: 238
          },
        };
        var videos = this.$refs.videos;
        console.info(videos);
        var promise = navigator.mediaDevices.getUserMedia(constraints);
        promise.then((MediaStream) => {
          /**
           * @desc MediaStream 返回参数
           * active: true
           * id: "ykCZTVor0KNVypRFZW8dFSwrFd9QuihhWmqA"
           * onactive: null
           * onaddtrack: null
           * oninactive: null
           * onremovetrack: null
           */
          console.info(MediaStream);
          video.srcObject = MediaStream;
          video.play();
        }).catch((error) => {
          console.info(error);
        });
        /**
         * @desc 倒计时以后进行拍照的操作
         */
        setTimeout(function() {
          let canvas = document.getElementById("canvas");
          console.info(canvas);
          canvas.getContext('2d').drawImage(video, 0, 0, 200, 100);
          console.info(typeof video);
          console.info(canvas.getContext('2d').drawImage(video, 0, 0, 200, 100));
          /**
           * @desc 拿到图片的base64
           * @param canvas base64
           */
          canvas = canvas.toDataURL("image/png");
          console.info(canvas);
          /**
           * @desc 拍照以后将video元素移除
           * @desc 拍照将base64转为file文件
           */
          if(canvas) {
            /**
             * 元素移除操作不进行,因为是直接发送照片,但是不进行照片的展示。
             */
            let blob = that.dataURLtoBlob(canvas);
            let file = that.blobToFile(blob, "imgName");
            if(blob){
              let url = that.url '';
              that.$axios({
                   method : ' post',
                   url : url,
                   data : {

                   }
              }).then((res)=>{
                /**
                 * 判断是不是请求验证成功,如果成功直接登录,失败的话,直接重新发送
                 */
                 console.info(res);
              }).catch((err)=>{
                 console.info(err);
              })
            }
            console.info(file);
          } else {
            /**
             *
             */
          }
        }, 1000);
      },
      /**
       * 将图片转为file格式
       * @param {Object} dataurl 将拿到的base64的数据当做参数传递
       */
      dataURLtoBlob : function(dataurl) {
        let arr = dataurl.split(','),
            mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]),
            n = bstr.length,
            u8arr = new Uint8Array(n);
        while(n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], {
            type: mime
        });
      },
      /**
       *
       * @param {Object} theBlob  文件
       * @param {Object} fileName 文件名字
       */
      blobToFile : function(theBlob, fileName) {
        theBlob.lastModifiedDate = new Date();
        theBlob.name = fileName;
        return theBlob;
      }
    },
  }
</script>

<style scoped>
  .videos {
    /*height: 238px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #CCCCCC;
    border-radius: 4px*/
  }
  #video{
    height: 238px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #CCCCCC;
    border-radius: 4px
  }
</style>

简单的解释一下我屎一样的代码:首先说一下我的需求,我因为是多种登录方式,所以需要注意的是用户登录的时候不确定点击哪一种登录,所以需要将扫描人脸作为其中一种验证的方式,那么用户点击别的登录方式的时候就不可以进行摄像头捕捉人像了,点击人脸识别的时候再调用,所以需要尽心父组件给子组件一个flag,来告诉他什么时候打开摄像头,什么时候关闭,所以我在开始的时候写了一个props,里面就是一个判断是否打开摄像头的flag,下面的是watch也就是一个监听事件,那么这个监听事件就是为了监听父组件是不是改变了打开摄像头的操作,下面的是methods也就是方法的实现,方法里面的注释应该写的还是很清楚的,这里就不一一的解释了。

这里需要说明几个点: 1.如果后端需要的是base64的文件,那么写到canvas = canvas.toDataURL(“image/png”);就可以结束了,那么我的代码多是应为后端需要的是一个file文件,所以我只能将base64文件转为file文件。 2.dataURLtoBlob、blobToFile这两个函数是为了转为base64转为文件用的,如果不需要转换的话,就可以直接不写这两个方法也是可以的。 3.当后端返回一个false的时候,直接重新调用该函数,这样就可以直接不停的给后端发照片,直到验证成功为止!

父组件的使用

我们引用组件的方式有多种,常见的是两种,第一是公共的引用,也就是在main.js中直接引用,另一种就是我这次用的,就是什么页面需要,就在什么页面中调用,调用的过程是:

代码语言:javascript复制
import Videos from '@/components/login/Videos'
components : {
      'videos' : Videos
    },
代码语言:javascript复制
<div class="login_sty" v-show="login_face">
        <!--扫脸登陆-->
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item prop="code">
              <el-input type="text" v-model="account.code" auto-complete="off" placeholder="请输入集团代码"></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item>
              <el-select v-model="scheduleValue" placeholder="早班">
                <el-option v-for="item in schedule" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <div style="height: 180px;text-align: center">
          <el-row :gutter="20">
            <el-col :span="24">
            <videos :isOpen="flag_camera"></videos>
            </el-col>
          </el-row>
        </div>
      </div>

我的点击事件是:

代码语言:javascript复制
<el-row :gutter="20">
        <el-col :span="6"><span @click="login_type('login_wx')">微信扫码登录</span></el-col>
        <el-col :span="6"><span @click="login_type('login_face')">人脸识别登录</span></el-col>
        <el-col :span="6"><span @click="login_type('login_iphone')">手机验证码登录</span></el-col>
        <el-col :span="6"><span @click="login_type('login_cus_num')">工号卡刷卡登录</span></el-col>
      </el-row>

ok 写到这里基本就可以实现了,别的就没什么了!

0 人点赞