基於瀏覽器的人臉識別標記

最近,Chrome在Chrome中集成了一套與圖形識別相關的 API——Shape Detector API,只須要少許代碼就可以實現人臉識別、二維碼/條形碼識別和文本識別。雖然這些 API 還處於實驗階段,只要條件容許,仍是能夠一下的。在 Windows 10 Chrome Canary 和 安卓 Chrome 等應用中,開啓 chrome://flags/#enable-experimental-web-platform-features 功能,在控制檯下輸入html

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.git

 

原本只是想簡單的體驗一下這個 API,沒想到一不當心就寫了這麼多。。。github

 

Demo1

demo代碼web

 

在這個 demo 中,一共有三個類:chrome

  (1)FaceTag:全部的邏輯操做(人臉識別、搜索面部、面部標記)都在該類中實現。canvas

  (2)ShowImg:實現圖片按原比例繪製、縮放圖片、獲取 base6四、清除功能。api

  (3)SignTool:實現繪製方框、繪製文字、清除功能。數組

實際上,ShowImg 徹底能夠用 <img> 代替的,我只是爲了可以縮小圖片,縮短人臉檢測的時間。因爲圖片一旦肯定後,在檢測和標記階段是不變的,爲了方便操做,ShowImg 和 SignTool 各包含一個 canvas。爲了減小代碼的重複,將建立 canvas 和獲取 ctx 的任務交給了 FaceTag 。app

 

面部檢測ide

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 大體相同。

相關文章
相關標籤/搜索