1. 程式人生 > >在瀏覽器中使用tensorflow.js進行人臉識別的JavaScript API

在瀏覽器中使用tensorflow.js進行人臉識別的JavaScript API

640?wx_fmt=gif

作者 | Vincent Mühle

編譯 | 姍姍

出品 | 人工智慧頭條(公眾號ID:AI_Thinker)

【導讀】隨著深度學習方法的應用,瀏覽器呼叫人臉識別技術已經得到了更廣泛的應用與提升。在實際過程中也具有其特有的優勢,通過整合與人臉檢測與識別相關的API,通過更為簡單的coding就可以實現。今天將為大家介紹一個用於人臉檢測、人臉識別和人臉特徵檢測的 JavaScript API,通過在瀏覽器中利用 tensorflow.js 進行人臉檢測和人臉識別。大家不僅可以更快速學習這個,對有人臉識別技術需求的 JS 開發者來說更是一件值得開心的事。

前言

對於 JS 開發者來說這將是一件很開心的事,那就是終於可以在瀏覽器中進行人臉識別了!通過接下來的這篇文章,將為大家介紹  face-api.js,一個構建在 tensorflow.js core 上的 javascript 模組,實現了人臉檢測、人臉識別和人臉特徵檢測三種 CNNs (卷積神經網路)。

我們將通過研究一個簡單的程式碼示例,只用幾行程式碼就可以試著使用這個包。

第一個人臉識別包 face-recognition.js,現在又來了一個包?

如果讀過我的另一篇關於人臉識別的文章 Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning,你可能會了解到在不久前,我組裝了一個類似的包, face-recognition.js,用 nodejs 來進行人臉識別。

起初,我沒有想到在 javascript 社群對人臉識別包的需求會如此之高。對很多人來說,face-recognition.js  就像微軟或亞馬遜所提供的,似乎是一個不錯的可免費使用且開源的替代付費服務的人臉識別服務。但我經常也會被問到一個問題,在瀏覽器中是或否可以完全執行完整的人臉識別管道。

對此要感謝 tensorflow.js !我使用 tfjs-core 實現了部分類似的工具,得到與 face-recognition.js 幾乎相同的結果,但,是在瀏覽器中實現的!而最棒的一點是,它不需要設定任何外部依賴關係,就可以直接使用。還有一個意外的獎勵 —— 在 WebGL 上執行操作 ,GPU 的加速。

這足以讓我相信,javascript 社群需要這樣一個包!這也將留給你們足夠的想象空間,你們可以用它來構建各種各樣的應用。

如何用深度學習解決人臉識別問題

如果你是希望儘快開始,你可以跳過這一部分,直接跳到編碼中。但是為了更好地理解 face-api.js 使用的方法。要實現人臉識別,強烈建議參與一起學習,因為我經常會被問到這個問題。

簡單來說,我們實際上想要實現的是,識別給出的一個人的面部影象,用作輸入影象(input image)。

我們的方法是,給出想識別的那個人的一張或多張圖片,並給此人的名字打上標籤,用作參考資料(reference data)。現在將輸入圖片與引用資料進行對比並找出最相似的參考圖片。如果兩個影象足夠相似,我們將會輸出此人的名字,否則我們輸出結果為 “unknow”。

聽起來不錯吧!然而這其中還存在兩個問題。首先,如果一張照片中有多人並且我們想把所有人都識別出來該怎麼辦?其次,我們需要能夠計算出兩張人臉影象的相似度度量,以便比較它們。

人臉檢測

對於第一個問題的答案是通過人臉檢測來解決。簡單地說,我們首先定位輸入影象中的所有面孔。人臉檢測,face-api.js 實現了一個 SSD 演算法,它基本上是基於 MobileNetV1 的 CNN,在網路的頂部有一些額外的盒預測層。

網路返回每張面孔的邊界框與相應的分數,即顯示面孔的每個邊界框的概率。這些分數用於篩選邊界區域,因為影象中可能根本不包含任何面孔。注意,即使只有一個人要檢索邊界框,人臉檢測也應該執行。

640?wx_fmt=jpeg

人臉特徵檢測和人臉對齊

第一個問題解決了!但是,我們想要對齊邊界框,這樣我們就可以在傳遞給人臉識別網路之前,在每個區域的人臉中心提取出影象,這將使人臉識別更加準確!

為此 face-api.js 實現了一個簡單的 CNN 網路,此網路返回給定人臉影象的 68 個點的面部特徵。

640?wx_fmt=png

根據特徵點的位置,邊界區域可以集中在面部中心。在下圖中你可以看到人臉檢測的結果(左)與對齊的人臉影象(右)

640?wx_fmt=jpeg

人臉識別

現在我們可以將提取和對齊的人臉影象輸入到人臉識別網路中,該網路是基於類似 ResNet-34 的架構,基本上對應於 dlib 中實現的架構。該網路已經被訓練學習將人臉的特徵對映到人臉描述符(一個有128個值的向量)中,通常也被稱為人臉嵌入。

現在回到比較兩個人臉時的原始問題:我們將使用提取的每張人臉影象的描述符,並將它們與參考資料的人臉描述符進行比較。更準確地說,我們可以計算兩張人臉描述符之間的歐式距離,根據閾值判斷兩個人臉是否相似(對於 150×150 幅人臉影象,0.6 是一個很好的閾值)。使用歐幾里得距離方法非常有效,當然你也可以選擇任意型別的分類器。下面的 gif 影象例子就是通過歐幾里得距離來比較的兩張人臉影象:

640?wx_fmt=gif

在學過了人臉識別的理論之後,我們開始 coding  ~~

編碼

在這個簡短的示例中,我們將逐步看到如何在下面這張多人的輸入影象上進行人臉識別:

640?wx_fmt=jpeg

指令碼

首先,從 dist / face - api .js上或者 dist/face-ap.min.js 的minifed版本中獲取  latest build  ,包括指令碼:

<script src="face-api.js"></script>

如果用 npm :

npm i face-api.js

載入模型資料

根據你的應用需求,可以專門載入需要的模型,但是要執行一個完整的端到端示例,我們需要載入人臉檢測、人臉特徵檢測和人臉識別這三個模型。模型檔案在 repo 上可用,可在此找到。

連結:

https://github.com/justadudewhohacks/faceapi.js/tree/master/weights

和原始模型相比,已經量化了模型的權重,模型檔案大小減少了75%,以允許客戶端只加載所需要的最小資料。此外,模型的權重被分割成最大 4 MB的組塊,使瀏覽器可以快取這些檔案,這樣只需要載入一次。

模型檔案可以作為靜態資源來提供在給 web 應用程式,也可以在其他地方託管它們,通過指定檔案的路徑或 url 來載入它們。假設在 public/models 下一個模型目錄中提供它們。

const MODEL_URL = '/models'await faceapi.loadModels(MODEL_URL)

如果你只想載入特殊模組:

const MODEL_URL = '/models'

await faceapi.loadFaceDetectionModel(MODEL_URL)
await faceapi.loadFaceLandmarkModel(MODEL_URL)
await faceapi.loadFaceRecognitionModel(MODEL_URL)

從輸入影象接收所有面孔的完整描述

神經網路接受 HTML 影象、畫布或視訊元素作為輸入。簡單地說,要檢測輸入的人臉的邊界,只需使 Score > minScore



const minConfidence = 0.8const fullFaceDescriptions = await faceapi.allFaces(input, minConfidence)

完整的臉部描述包含檢測結果(邊界框+分數)、臉部特徵和計算描述符。faceapi.js 做了之前討論所有的問題。也可以手動獲取人臉位置和特徵。github repo上有這樣的示例。

注意,邊界和特徵與原始影象/媒體大小相關。如果顯示的影象大小與原始影象大小不一致,可以調整它們的大小:

const resized = fullFaceDescriptions.map(fd => fd.forSize(width, height))

可以通過畫出邊界區域來視覺化檢測結果

fullFaceDescription.forEach((fd, i) => {  faceapi.drawDetection(canvas, fd.detection, { withScore: true })})

640?wx_fmt=jpeg

人間特徵按照下面的方法顯示出來

fullFaceDescription.forEach((fd, i) => {
  faceapi.drawLandmarks(canvas, fd.landmarks, { drawLines: true })
})

640?wx_fmt=jpeg

人臉識別

知道了如何在輸入影象中檢索所有人臉的位置和描述符,我們將得到一些分別顯示一個人的影象,並計算人臉描述符。這些描述符就是我們的參考資料。

假設我們有一些示例影象,我們首先用 url 獲取影象。

然後使用 faceapi.bufferToImage 從資料快取區中建立 HTML 影象元素:



// fetch images from url as blobsconst blobs = await Promise.all(  ['sheldon.png' 'raj.png''leonard.png''howard.png'].map(    uri => (await fetch(uri)).blob()  ))// convert blobs (buffers) to HTMLImage elementsconst images = await Promise.all(blobs.map(  blob => await faceapi.bufferToImage(blob)))

接下來,對於每個影象,定位被試的臉並計算面部描述符,就像之前對輸入影象所做的處理那樣:

const refDescriptions = await Promsie.all(images.map(
  img => (await faceapi.allFaces(img))[0]
))

const refDescriptors = refDescriptions.map(fd => fd.descriptor)

現在,剩下要做的就是迴圈遍歷輸入影象的人臉描述,找到與參考資料歐式距離最小的描述符:

const sortAsc = (a, b) => a - bconst labels = ['sheldon''raj''leonard''howard']const results = fullFaceDescription.map((fd, i) => {  const bestMatch = refDescriptors.map(    refDesc => ({      label: labels[i],      distance: faceapi.euclideanDistance(fd.descriptor, refDesc)    })  ).sort(sortAsc)[0]  return {    detection: fd.detection,    label: bestMatch.label,    distance: bestMatch.distance  }})

正如前面講到的,在這裡用效果很好的歐氏距離作為相似度度量。最終得到了在輸入影象中每個人的最佳匹配。

最後可以將邊界框和它們的標籤一起畫出來並顯示結果:

// 0.6 is a good distance threshold value to judge
// whether the descriptors match or not
const maxDistance = 0.6

results.forEach(result => {
  faceapi.drawDetection(canvas, result.detection, { withScorefalse })

  const text = `${result.distance < maxDistance ? result.className : 'unkown'} (${result.distance})`
  const { x, y, height: boxHeight } = detection.getBox()
  faceapi.drawText(
    canvas.getContext('2d'),
    x,
    y + boxHeight,
    text
  )
})

640?wx_fmt=jpeg

這就是這次的學習之旅!至此希望大家已經學會如何使用這個 api,並且建議大家看一下 repo 中的其他示例。

——【完】——

AI公開課精彩預告
時間:7月12日 20:00-21:00
掃描海報二維碼,免費報名
新增微信csdnai,備註:公開課,加入課程交流群

640?wx_fmt=jpeg