在,終于有可能在瀏覽器中運行人臉識別了!通過本文,我將介紹face-api,它是一個構建在tensorflow.js core的JavaScript模塊,它實現了人臉檢測、人臉識別和人臉地標檢測三種類型的CNN。
我們首先研究一個簡單的代碼示例,以用幾行代碼立即開始使用該包。
第一個face-recognition.js,現在又是一個包?
如果你讀過關于人臉識別與其他的NodeJS文章:(https://medium.com/@muehler.v/node-js-face-recognition-js-simple-and-robust-face-recognition-using-deep-learning-ea5ba8e852),你可能知道,不久前,我組裝了一個類似的包(face-recognition.js)。
起初,我沒有想到在javascript社區中對臉部識別軟件包的需求如此之高。對于很多人來說,face-recognition.js似乎是一個不錯的免費使用且開源的替代付費服務的人臉識別服務,就像微軟或亞馬遜提供的一樣。其中很多人問,是否可以在瀏覽器中完全運行完整的人臉識別管道。
在這里,我應該感謝tensorflow.js!我設法使用tfjs-core實現了部分類似的工具,這使你可以在瀏覽器中獲得與face-recognition.js幾乎相同的結果!最好的部分是,不需要設置任何外部依賴關系,可以直接使用。并且,它是GPU加速的,在WebGL上運行操作。
這使我相信,JavaScript社區需要這樣的瀏覽器包!你可以用這個來構建自己的各種各樣的應用程序。;)
如何用深度學習解決人臉識別問題
如果你希望盡快開始,也可以直接去編碼。但想要更好地理解face-api.js中用于實現人臉識別的方法,我強烈建議你看一看,這里有很多我經常被問到的問題。
簡單地說,我們真正想要實現的是,識別一個人的面部圖像(input image)。我們這樣做的方式是為每個我們想要識別的人提供一個(或多個)圖像,并標注人名(reference data)。現在我們將它們進行比較,并找到最相似的參考圖像。如果兩張圖片足夠相似,我們輸出該人的姓名,否則我們輸出“unknown”。
聽起來不錯吧!然而,還是存在兩個問題。首先,如果我們有一張顯示多個人的圖片,我們想要識別所有的人,該怎么辦?其次,我們需要能夠獲得這種類型的兩張人臉圖像的相似性度量,以便比較它們......
人臉檢測
第一個問題的答案是人臉檢測。簡而言之,我們將首先找到輸入圖像中的所有人臉。對于人臉檢測,face-api.js實現了SSD(Single Shot Multibox Detector),它基本上是基于MobileNetV1的CNN,只是在網絡頂部疊加了一些額外的盒預測層。
網絡返回每個人臉的邊界框及其相應的分數,即每個邊界框顯示一個人臉的可能性。分數用于過濾邊界框,因為圖像中可能根本不包含任何人臉。請注意,即使只有一個人檢索邊界框,也應執行人臉檢測。
人臉標志檢測和人臉對齊
第一個問題解決了!但是,我們希望對齊邊界框,這樣我們就可以在每個框的人臉中心提取出圖像,然后將它們傳遞給人臉識別網絡,這會使人臉識別更加準確!
為此,face-api.js實現了一個簡單的CNN,它返回給定人臉圖像的68個點的人臉標志:
從地標位置,邊界框可以準確的包圍人臉。在下圖,你可以看到人臉檢測的結果(左)與對齊的人臉圖像(右)的比較:
人臉識別
現在我們可以將提取并對齊的人臉圖像提供給人臉識別網絡,這個網絡基于類似ResNet-34的架構并且基本上與dlib中實現的架構相對應。該網絡已經被訓練學習將人臉的特征映射到人臉描述符(descriptor ,具有128個值的特征矢量),這通常也被稱為人臉嵌入。
現在回到我們最初的比較兩個人臉的問題:我們將使用每個提取的人臉圖像的人臉描述符并將它們與參考數據的人臉描述符進行比較。也就是說,我們可以計算兩個人臉描述符之間的歐氏距離,并根據閾值判斷兩個人臉是否相似(對于150 x 150大小的人臉圖像,0.6是一個很好的閾值)。使用歐幾里德距離的方法非常有效,當然,你也可以使用任何你選擇的分類器。以下gif通過歐幾里德距離將兩幅人臉圖像進行比較:
學完了人臉識別的理論,我們可以開始編寫一個示例。
編碼
在這個簡短的例子中,我們將逐步了解如何在以下顯示多人的輸入圖像上進行人臉識別:
包括腳本
首先,從 dist/face-api.js或者dist/face-api.min.js的minifed版本中獲取最新的構建且包括腳本:
<script src=“face-api.js”> </ script>
鏈接:https://github.com/justadudewhohacks/face-api.js/tree/master/dist
如果你使用npm:
npm i face-api.js
加載模型數據
根據應用程序的要求,你可以專門加載所需的模型,但要運行完整的端到端示例,我們需要加載人臉檢測,人臉標識和人臉識別模型。模型文件在repo上可用,下方鏈接中找到。
https://github.com/justadudewhohacks/face-api.js/tree/master/weights
已經量化了模型權重,將模型文件大小減少了75%,以便使你的客戶端僅加載所需的最小數據。此外,模型權重被分割成最大4MB的塊,以允許瀏覽器緩存這些文件,使得它們只需加載一次。
模型文件可以簡單地作為靜態資源(static asset)提供給你的Web應用程序,可以在其他地方托管它們,可以通過指定文件的路徑或url來加載它們。假設你在models目錄中提供它們并且資源在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.8 const fullFaceDescriptions=await faceapi.allFaces(input, minConfidence)
完整的人臉描述檢測結果(邊界框+分數)、人臉標志以及計算出的描述符。正如你所看到的,faceapi.allFaces在前面討論過的所有內容都適用于我們。但是,你也可以手動獲取人臉位置和標志。如果這是你的目標,github repo上有幾個示例。
請注意,邊界框和標志位置是相對于原始圖像/媒體大小。如果顯示的圖像尺寸與原始圖像尺寸不一致,則可以調整它們的尺寸:
const resized=fullFaceDescriptions.map(fd=> fd.forSize(width, height))
我們可以通過將邊界框繪制到畫布中來可視化檢測結果:
fullFaceDescription.forEach((fd, i)=> { faceapi.drawDetection(canvas, fd.detection, { withScore: true }) })
臉部可 以顯示如下:
fullFaceDescription.forEach((fd, i)=> { faceapi.drawLandmarks(canvas, fd.landmarks, { drawLines: true }) })
通常,我所做的可視化工作是在img元素的頂部覆蓋一個絕對定位的畫布,其寬度和高度相同(參閱github示例以獲取更多信息)。
人臉識別
現在我們知道如何在給定輸入圖像的情況下檢索所有人臉的位置和描述符,即,我們將得到一些圖像,分別顯示每個人并計算他們的人臉描述符。這些描述符將成為我們的參考數據。
假設我們有一些可用的示例圖像,我們首先從url獲取圖像,然后使用faceapi.bufferToImage從其數據緩沖區創建HTML圖像元素:
// fetch images from url as blobs const blobs=await Promise.all( ['sheldon.png' 'raj.png', 'leonard.png', 'howard.png'].map( uri=> (await fetch(uri)).blob() ) ) // convert blobs (buffers) to HTMLImage elements const 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 - b const 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, { withScore: false }) 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 ) })
以上我希望你首先了解如何使用api。另外,我建議查看repo中的其他示例。
來自:ATYUN
、什么是JavaScript中的window?
JavaScript中window是一個全局對象,代表瀏覽器中一個打開的窗口,每個窗口都是一個window對象。
2、什么是document?
document是window的一個屬性,這個屬性是一個對象;
document代表當前窗口中的整個網頁;
document對象保存了網頁上所有的內容,通過document對象就可以操作網頁上的內容。
3、什么是JavaScript中的DOM?
DOM定義了訪問和操作HTML文檔(網頁)的標準方法;
DOM全稱:Document Object Model,即文檔模型對象;
學習DOM就是學習如何通過document對象操作網頁上的內容。
4、JavaScript中如何通過選擇器獲取DOM元素?
querySelector:只會返回根據指定選擇器找到的第一個元素,例子:
let oDiv=document.querySelector("div");
console.log("oDiv"); //輸出:<div></div>
querySelectorAll:會返回指定選擇器找到的所有元素,例如:
let oDivs=document.querySelectorAll("div");
console.log("oDivs"); //輸出:NodeList由所有div元素組成的為數組。
5、JavaScript如何獲取和設置元素內容?
獲取元素內容:
1)innerHTML獲取的內容包含標簽,innerText/textContent獲取的內容不包含標簽;
2)innerHTML/textContent獲取的內容不會去除兩端的空格,innerText獲取的內容會去除兩端的空格。
設置元素內容:
共同點:無論通過innerHTML/innerText/textContent設置內容,新的內容都會覆蓋原有的內容;
區別:1)通過innerHTML設置數據,數據中包含標簽,會轉換成標簽之后再添加;
2)通過innerText/textContent設置數據,數據中包含標簽,不會轉換成標簽,會當做一個字符串直接設置。
6、什么是JavaScript中的事件? 如何給元素綁定事件?
用戶和瀏覽器之間的交互行為被稱為事件,比如:鼠標的點擊/移入/移出。
在JavaScript中所有的HTML標簽都可以添加事件;
元素.事件名稱=function(){};
當對應事件被觸發時候就會自動執行function中的代碼。
掌握了以上JavaScript,就可以做某寶里面的輪播圖片了!
自IT Next,作者:Vincent Mühler,機器之心編譯,參與:Geek AI、張倩。
本文將為大家介紹一個建立在「tensorflow.js」內核上的 javascript API——「face-api.js」,它實現了三種卷積神經網絡架構,用于完成人臉檢測、識別和特征點檢測任務,可以在瀏覽器中進行人臉識別。
號外!號外!現在人們終于可以在瀏覽器中進行人臉識別了!本文將為大家介紹「face-api.js」,這是一個建立在「tensorflow.js」內核上的 javascript 模塊,它實現了三種卷積神經網絡(CNN)架構,用于完成人臉檢測、識別和特征點檢測任務。
像往常一樣,我們將查看一個簡單的代碼示例,這將使你能立即通過短短幾行代碼中的程序包開始了解這個 API。讓我們開始吧!
我們已經有了「face-recognition.js」,現在又來了另一個同樣的程序包?
如果你閱讀過本文作者另一篇關于「node.js」環境下進行人臉識別的文章《Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning》(Node.js + face-recognition.js:通過深度學習實現簡單而魯棒的人臉識別)(https://medium.com/@muehler.v/node-js-face-recognition-js-simple-and-robust-face-recognition-using-deep-learning-ea5ba8e852),你就會知道他在之前組裝過一個類似的程序包,例如「face-recgnition.js」,從而為「node.js」引入了人臉識別功能。
起初,作者并沒有預見到 JavaScript 社區對與人臉識別程序包的需求程度如此之高。對許多人而言,「face-recognition.js」似乎是一個不錯的、能夠免費試用的開源選項,它可以替代由微軟或亞馬遜等公司提供的付費人臉識別服務。但是作者曾多次被問道:是否有可能在瀏覽器中運行完整的人臉識別的工作流水線?
多虧了「tensorflow.js」,這種設想最終變為了現實!作者設法使用「tf.js
」內核實現了部分類似的工具,它們能得到和「face-recognition.js」幾乎相同的結果,但是作者是在瀏覽器中完成的這項工作!而且最棒的是,這套工具不需要建立任何的外部依賴,使用它非常方便。并且這套工具還能通過 GPU 進行加速,相關操作可以使用 WebGL 運行。
這足以讓我相信,JavaScript 社區需要這樣的一個為瀏覽器環境而編寫的程序包!可以設想一下你能通過它構建何種應用程序。
如何利用深度學習解決人臉識別問題
如果想要盡快開始實戰部分,那么你可以跳過這一章,直接跳到代碼分析部分去。但是為了更好地理解「face-api.js」中為了實現人臉識別所使用的方法,我強烈建議你順著這個章節閱讀下去,因為我常常被人們問到這個問題。
為簡單起見,我們實際想要實現的目標是在給定一張人臉的圖像時,識別出圖像中的人。為了實現這個目標,我們需要為每一個我們想要識別的人提供一張(或更多)他們的人臉圖像,并且給這些圖像打上人臉主人姓名的標簽作為參考數據。現在,我們將輸入圖像和參考數據進行對比,找到與輸入圖像最相似的參考圖像。如果有兩張圖像都與輸入足夠相似,那么我們輸出人名,否則輸出「unknown」(未知)。
聽起來確實是個好主意!然而,這個方案仍然存在兩個問題。首先,如果我們有一張顯示了多人的圖像,并且我們需要識別出其中所有的人,將會怎樣呢?其次,我們需要建立一種相似度度量手段,用來比較兩張人臉圖像。
人臉檢測
我們可以從人臉檢測技術中找到第一個問題的答案。簡單地說,我們將首先定位輸入圖像中的所有人臉。「face-api.js」針對人臉檢測工作實現了一個 SSD(Single Shot Multibox Detector)算法,它本質上是一個基于 MobileNetV1 的卷積神經網絡(CNN),在網絡的頂層加入了一些人臉邊框預測層。
該網絡將返回每張人臉的邊界框,并返回每個邊框相應的分數,即每個邊界框表示一張人臉的概率。這些分數被用于過濾邊界框,因為可能存在一張圖片并不包含任何一張人臉的情況。請注意,為了對邊界框進行檢索,即使圖像中僅僅只有一個人,也應該執行人臉檢測過程。
人臉特征點檢測及人臉對齊
在上文中,我們已經解決了第一個問題!然而,我想要指出的是,我們需要對齊邊界框,從而抽取出每個邊界框中的人臉居中的圖像,接著將其作為輸入傳給人臉識別網絡,因為這樣可以使人臉識別更加準確!
為了實現這個目標,「face-api.js」實現了一個簡單的卷積神經網絡(CNN),它將返回給定圖像的 68 個人臉特征點:
從特征點位置上看,邊界框可以將人臉居中。你可以從下圖中看到人臉檢測結果(左圖)與對齊后的人臉圖像(右圖)的對比:
人臉識別
現在,我們可以將提取出的對齊后的人臉圖像輸入到人臉識別網絡中,該網絡基于一個類似于 ResNet-34 的架構,基本上與 dlib(https://github.com/davisking/dlib/blob/master/examples/dnn_face_recognition_ex.cpp)中實現的架構一致。該網絡已經被訓練去學習出人臉特征到人臉描述符的映射(一個包含 128 個值的特征向量),這個過程通常也被稱為人臉嵌入。
現在讓我們回到最初對比兩張人臉圖像的問題:我們將使用每張抽取出的人臉圖像的人臉描述符,并且將它們與參考數據的人臉描述符進行對比。更確切地說,我們可以計算兩個人臉描述符之間的歐氏距離,并根據閾值判斷兩張人臉圖像是否相似(對于 150*150 的圖像來說,0.6 是一個很好的閾值)。使用歐氏距離的效果驚人的好,當然,你也可以選用任何一種分類器。下面的 gif 動圖可視化了通過歐氏距離比較兩張人臉圖像的過程:
至此,我們已經對人臉識別的理論有所了解。接下來讓我們開始編寫一個代碼示例。
是時候開始編程了!
在這個簡短的示例中,我們將看到如何一步步地運行人臉識別程序,識別出如下所示的輸入圖像中的多個人物:
導入腳本
首先,從 dist/face-api.js 獲得最新的版本(https://github.com/justadudewhohacks/face-api.js/tree/master/dist),或者從 dist/face-api.min.js 獲得縮減版,并且導入腳本:
<script src="face-api.js"></script>
如果你使用 npm 包管理工具,可以輸入如下指令:
npm i face-api.js
加載模型數據
你可以根據應用程序的要求加載你需要的特定模型。但是如果要運行一個完整的端到端的示例,我們還需要加載人臉檢測、人臉特征點檢測和人臉識別模型。相關的模型文件可以在代碼倉庫中找到,鏈接如下:https://github.com/justadudewhohacks/face-api.js/tree/master/weights。
其中,模型的權重已經被量化,文件大小相對于初始模型減小了 75%,使你的客戶端僅僅需要加載所需的最少的數據。此外,模型的權重被分到了最大為 4 MB 的數據塊中,使瀏覽器能夠緩存這些文件,這樣它們就只需要被加載一次。
模型文件可以直接作為你的 web 應用中的靜態資源被使用,或者你可以將它們存放在另外的主機上,通過指定的路徑或文件的 url 鏈接來加載。假如你將它們與你在 public/models 文件夾下的資產共同存放在一個 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 圖像、畫布、視頻元素或張量(tensor)作為輸入。為了檢測出輸入圖像中分數(score)大于最小閾值(minScore)的人臉邊界框,我們可以使用下面的簡單操作:
const minConfidence=0.8
const fullFaceDescriptions=await faceapi.allFaces(input, minConfidence)
一個完整的人臉描述符包含了檢測結果(邊界框+分數),人臉特征點以及計算出的描述符。正如你所看到的,「faceapi.allFaces」在底層完成了本文前面的章節所討論的所有工作。然而,你也可以手動地獲取人臉定位和特征點。如果這是你的目的,你可以參考 github repo 中的幾個示例。
請注意,邊界框和特征點的位置與原始圖像/媒體文件的尺寸有關。當顯示出的圖像尺寸與原始圖像的尺寸不相符時,你可以簡單地通過下面的方法重新調整它們的大小:
const resized=fullFaceDescriptions.map(fd=> fd.forSize(width, height))
我們可以通過將邊界框在畫布上繪制出來對檢測結果進行可視化:
fullFaceDescription.forEach((fd, i)=> {
faceapi.drawDetection(canvas, fd.detection, { withScore: true })
})
可以通過下面的方法將人臉特征點顯示出來:
fullFaceDescription.forEach((fd, i)=> {
faceapi.drawLandmarks(canvas, fd.landmarks, { drawLines: true })
})
通常,我會在 img 元素的頂層覆蓋一個具有相同寬度和高度的絕對定位的畫布(想獲取更多信息,請參閱 github 上的示例)。
人臉識別
當我們知道了如何得到給定的圖像中所有人臉的位置和描述符后,我們將得到一些每張圖片顯示一個人的圖像,并且計算出它們的人臉描述符。這些描述符將作為我們的參考數據。
假設我們有一些可以用的示例圖片,我們首先從一個 url 鏈接處獲取圖片,然后使用「faceapi.bufferToImage」從它們的數據緩存中創建 HTML 圖像元素:
// fetch images from url as blobs
const blobs=await Promise.all(
['sheldon.png' 'raj.png', 'leonard.png', 'howard.png'].map(
uri=> (await fetch(uri)).blob()
)
)
// convert blobs (buffers) to HTMLImage elements
const 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 - b
const 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, { withScore: false })
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
)
})
至此,我希望你對如何使用這個 API 有了一個初步的認識。同時,我也建議你看看文中給出的代碼倉庫中的其它示例。好好地把這個程序包玩個痛快吧!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。