zoukankan      html  css  js  c++  java
  • 基于浏览器的人脸识别标记

    最近,Chrome在Chrome中集成了一套与图形识别相关的 API——Shape Detector API,只需要少量代码就能够实现人脸识别、二维码/条形码识别和文本识别。虽然这些 API 还处于实验阶段,只要条件允许,还是可以一下的。在 Windows 10 Chrome Canary 和 安卓 Chrome 等应用中,开启 chrome://flags/#enable-experimental-web-platform-features 功能,在控制台下输入

    window.FaceDetector
    
    window.BarcodeDetector
    
    window.TextDetector

    若得到“[native code]”,那么就可以使用这些 API 了。亲测 Windows7  Chrome 61 开启了 enable-experimental-web-platform-features 之后,虽然 window 包含以上三个 API,但调用 new FaceDetector.detector 时会报错:Face detection service unavailable.

    本来只是想简单的体验一下这个 API,没想到一不小心就写了这么多。。。

    Demo1

    demo代码

    在这个 demo 中,一共有三个类:

      (1)FaceTag:所有的逻辑操作(人脸识别、搜索面部、面部标记)都在该类中实现。

      (2)ShowImg:实现图片按原比例绘制、缩放图片、获取 base64、清除功能。

      (3)SignTool:实现绘制方框、绘制文字、清除功能。

    实际上,ShowImg 完全可以用 <img> 代替的,我只是为了能够缩小图片,缩短人脸检测的时间。由于图片一旦确定后,在检测和标记阶段是不变的,为了方便操作,ShowImg 和 SignTool 各包含一个 canvas。为了减少代码的重复,将创建 canvas 和获取 ctx 的任务交给了 FaceTag 。

    面部检测

    FaceDetector 真的很简单。

    创建实例的时候,可以给它传递 FaceDetectorOptions,这个对象只含两个有效参数:
      maxDetectedFaces:检测人脸的最大数目。
      fastMode:是否优先考虑速度。

    而它只提供了 detect(img) 一个方法,传入待检测的图像,结果以 Promise 的形式返回,是包含一组 DetectedFace 元素的数组,若检测不到则返回一个空数组。DetectedFace 的相关信息:x,y,left,top,right,bottom 等都包含在 boundingBox 中。

    至于检测结果的话,正脸的精确度挺高的,但是侧脸基本检测不到。
    class FaceTag {

      constructor(opt={}){
        ...
        this.detector = new FaceDetector(orignOpt.detectorOpt);
        ...
      }
      ...
      faceDetector(aspect = 1) {     return new Promise((resolve, reject) => {       let img = this.img.getImage();       this.detector.detect(img)         .then(faces => {           if(faces.length === 0) {             reject('检测不到面部哦');           }else {             this.faces = faces;             resolve(this.faces);           }         })         .catch(err => {           reject(err);         })     })   }
      ...
    }

    根据检测到的结果信息在 signTool 的 canvas 中绘制出来。

    class SignTool {
      ...
      /*
       * 标识面部
       * param {Array} faces 需要标识的部分
       */
      drawFaces(faces=[], aspect = 1) {
        faces.length > 0 && faces.forEach(face => {
          this.drawRect(face.boundingBox, aspect);
        })
      }
    
      /*
       * 绘制 rect
       * param {object} rect 需要绘制的 rect
       */
      drawRect(rect={}, aspect = 1) {
        this.ctx.save()
        this.ctx.beginPath();
        this.ctx.lineWidth = rect.lineWidth || 2;
        this.ctx.strokeStyle = rect.color || 'red';
        this.ctx.rect(Math.floor(rect.x * aspect),
          Math.floor(rect.y * aspect),
          Math.floor(rect.width * aspect),
          Math.floor(rect.height * aspect)
        );
        this.ctx.stroke();
        this.ctx.restore();
      }
      ...
    }

    选择面部

    本 demo 中为 signTool  的 canvas添加点击事件,通过鼠标坐标与检测到的所有面部信息进行比较,判断是否选中面部。选中之后,重新绘制一遍所有的面部,在将选中面部高亮。

    class FaceTag {
      ...  
      /*
       * signTool 的 canvas 添加点击事件
       */
      addEvent() {
        if(!this.signTool.canvas) {
          return;
        }
    
        let canvas = this.signTool.canvas;
        let canvasBox = canvas.getBoundingClientRect();
    
        canvas.addEventListener('click', e => {
        // 计算鼠标在canvas中的位置
        let eX = e.clientX - canvasBox.left;
        let eY = e.clientY - canvasBox.top;
    
        this.findFace(this.img.getImage(), eX, eY)
          .then(face => {
            this.signTool.drawFaces(this.faces);
            this.heightLighRect(face);
            // 保存选中的面部                            
            this.beTagFace = face;
          })
        })
      }  
      ... }

    最后,beTagFace 的信息,将文字标注在方框附近 ,对面部的标记就完成啦。

    Demo2

    demo代码

    本 demo 结合了 WebRTC,并将 面部检测部分移到了  Web Worker 中计算。

    /*
     * 获取视频流
     * @param {Object} opt video 的参数
     */
    getUserMedia(opt) {
      navigator.mediaDevices.getUserMedia({
        video: opt
      }).then(stream => {
        this.createVideo(stream);
         resizeCanvas();
      }).catch(err => {
        alert(err);
      })
    }
    
    /*
     * 创建 video 标签
     * @param {Object} stream 视频流
     */
    createVideo(stream) {
      this.video = document.createElement('video');
      this.video.autoplay = 'autoplay';
      this.video.src = window.URL.createObjectURL(stream);
      this.container.appendChild(this.video);
    }

    通过 navigator.mediaDevices.getUserMedia 获取权限打开摄像头,获取视频流。

    由于 detect()需要传入 ImageBitmapSource 对象,因此将视频流绘制到 canvas 中,通过 canvas 获取图像信息,将图像信息传递给 Web Worker,进行面部检测。之后的就和 demo1 大致相同。

  • 相关阅读:
    HTTP报文详解
    常用的HTTP协议
    URL详解
    log4net工作原理(2)
    《Linux内核设计与实现》读书笔记(十七)- 设备与模块
    《Linux内核设计与实现》读书笔记(十六)- 页高速缓存和页回写
    《Linux内核设计与实现》读书笔记(十五)- 进程地址空间(kernel 2.6.32.60)
    《Linux内核设计与实现》读书笔记(十四)- 块I/O层
    随手记代码
    记录一下WPF中自寄宿asp.net服务添加urlacl的问题
  • 原文地址:https://www.cnblogs.com/xxhuan/p/7593757.html
Copyright © 2011-2022 走看看