Web 瀏覽器日益強大,網站和 Web 應用程序的復雜性也在增加。幾十年前需要超級計算機的操作現在可以在智能手機上運行,其中之一就是人臉檢測。
檢測和分析人臉的能力非常有用,因為它能讓我們添加聰明的特征。比如自動模糊人臉(比如谷歌Maps)、移動和縮放攝像頭feed以聚焦于人(比如微軟團隊)、驗證護照、添加愚蠢的濾鏡(比如Instagram和Snapchat)等等。但在這之前,我們得先找到那張臉!
Face-api.js 是一個庫,使開發人員無需機器學習背景即可在其應用程序中使用人臉檢測。
本教程的代碼可在 GitHub 上找到。
https://github.com/sitepoint-editors/demo-face-api-js
機器學習人臉檢測
檢測物體,如人臉,是相當復雜的。想一想:也許我們可以寫一個程序,通過掃描像素來找到眼睛、鼻子和嘴巴。這是可以做到的,但要使它完全可靠,實際上是無法實現的,因為有許多因素需要考慮。想想光照條件、面部毛發、各種各樣的形狀和顏色、化妝、角度、臉部面具,以及其他許多因素。
然而,神經網絡擅長解決這類問題,并且可以推廣到大多數(如果不是全部)條件。我們可以使用流行的 JAVAScript 機器學習庫 TensorFlow.js 在瀏覽器中創建、訓練和使用神經網絡。然而,即使我們使用現成的、預訓練的模型,我們仍然會對向 TensorFlow 提供信息和解釋輸出的細節有所了解。
使用 face-api.js,它將所有這些都包裝到一個直觀的 API 中。我們可以傳遞一個 img、canvas 或 video DOM 元素,該庫將返回一個或一組結果。 Face-api.js 可以檢測人臉,但也可以估計其中的各種內容,如下所列。
- 面部檢測:獲取一張或多張人臉的邊界,這對于確定圖片中人臉的位置和大小很有用。
- 面部地標檢測:獲取眉毛、眼睛、鼻子、嘴和嘴唇以及下巴的位置和形狀。這可以用來確定朝向或在特定區域投射圖形,如鼻子和嘴唇之間的胡子。
- 面部識別:確定誰在畫面中。
- 面部表情檢測:從一個人的臉上獲得表情。
- 年齡和性別檢測:從一張臉中得到年齡和性別。請注意,在“性別”分類中,它將一張臉分為女性化或男性化,這并不一定揭示他們的性別。
在你在實驗之外使用這些東西之前,請注意,人工智能擅長放大偏見。性別分類對雙性戀者來說效果很好,但它不能檢測我的非雙性戀朋友的性別。它在大多數時候都能識別出白人,但經常無法檢測到有色人種。
在使用這項技術時要非常周到,并與不同的測試小組進行徹底的測試。
安裝
我們可以通過 npm 安裝 face-api.js:
npm install face-api.js
然而,為了跳過構建工具的設置,我將通過unpkg.org包括UMD包:
/* globals faceapi */
import 'https://unpkg.com/face-api.js@0.22.2/dist/face-api.min.js';
之后,我們需要從庫的資源庫中下載正確的預訓練模型。
確定我們想從臉部知道什么,并使用可用模型部分來確定需要哪些模型。有些功能可以使用多個模型。在這種情況下,我們必須在帶寬/性能和精度之間做出選擇。比較各種可用模型的文件大小,選擇你認為最適合你的項目的模型。
不確定你的使用需要哪些型號?你可以稍后再回到這個步驟。當我們在沒有加載所需模型的情況下使用API時,將拋出一個錯誤,說明該庫所期望的模型。
我們現在準備使用 face-api.js API。
示例
讓我們來建造一些東西吧!
對于下面的示例,我將使用此函數從 Unsplash Source 加載隨機圖像:
function loadRandomImage() {
const image = new Image();
image.crossOrigin = true;
return new Promise((resolve, reject) => {
image.addEventListener('error', (error) => reject(error));
image.addEventListener('load', () => resolve(image));
image.src = 'https://source.unsplash.com/512x512/?face,friends';
});
}
/ 裁剪圖片 /
你可以在附帶的GitHub repo中找到這個演示的代碼。
https://github.com/sitepoint-editors/demo-face-api-js/blob/main/scripts/1-image-crop.js
首先,我們要選擇并加載模型。為了裁剪圖像,我們只需要知道一個人臉的邊界框,所以人臉檢測就足夠了。我們可以用兩個模型來做。SSD Mobilenet v1模型(僅低于6MB)和Tiny Face Detector模型(低于200KB)。我們說準確性是不相干的,因為用戶也可以選擇手動裁剪。此外,讓我們假設訪問者在緩慢的網絡連接上使用這個功能。因為我們的重點是帶寬和性能,我們將選擇較小的Tiny Face Detector模型。
下載模型后,我們可以加載它:
await faceapi.nets.tinyFaceDetector.loadFromUri('/models');
我們現在可以加載圖像并將其傳遞給 face-api.js。 faceapi.detectAllFaces 默認使用 SSD Mobilenet v1 模型,因此我們必須顯式傳遞 new
faceapi.TinyFaceDetectorOptions() 以強制它使用 Tiny Face Detector 模型。
const image = await loadRandomImage();
const faces = await faceapi.detectAllFaces(image, new faceapi.TinyFaceDetectorOptions());
變量 faces 現在包含一個結果數組。每個結果都有一個 box 和 score 屬性。分數表示神經網絡對該結果確實是一張臉的自信程度。box 包含一個有臉部坐標的對象,我們可以選擇第一個結果(或者我們可以使用 faceapi.detectSingleFace()),但是如果用戶提交了一張集體照片,我們希望在裁剪后的圖片中看到所有的人。為了做到這一點,我們可以計算一個自定義的邊界框。
const box = {
// 將邊界設置為它們的逆無窮大,因此任何數字都更大/更小
bottom: -Infinity,
left: Infinity,
right: -Infinity,
top: Infinity,
// 給出邊界,我們可以計算出寬度和高度
get height() {
return this.bottom - this.top;
},
get width() {
return this.right - this.left;
},
};
// 更新 box 的邊界
for (const face of faces) {
box.bottom = Math.max(box.bottom, face.box.bottom);
box.left = Math.min(box.left, face.box.left);
box.right = Math.max(box.right, face.box.right);
box.top = Math.min(box.top, face.box.top);
}
最后,我們可以創建一個畫布并顯示結果:
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = box.height;
canvas.width = box.width;
context.drawImage(
image,
box.left,
box.top,
box.width,
box.height,
0,
0,
canvas.width,
canvas.height
);
/ 放置表情符號 /
你可以在附帶的GitHub repo中找到這個演示的代碼。
https://github.com/sitepoint-editors/demo-face-api-js/blob/main/scripts/2-emoji-eyes.js
為什么不找點樂子呢?我們可以做一個過濾器,在所有的眼睛上放一個嘴巴的表情符號()。為了找到眼睛的地標,我們需要另一個模型。這一次,我們關心的是準確性,所以我們使用SSD Mobilenet v1和68點面部地標檢測模型。
同樣,我們需要先加載模型和圖像:
await faceapi.nets.faceLandmark68Net.loadFromUri('/models');
await faceapi.nets.ssdMobilenetv1.loadFromUri('/models');
const image = await loadRandomImage();
為了獲得地標,我們必須將 withFaceLandmarks() 函數調用附加到 detectAllFaces() 中以獲得地標數據。
const faces = await faceapi
.detectAllFaces(image)
.withlandmarks();
和上次一樣, faces 包含一個結果列表。除了臉部的位置外,每個結果還包含一個地標的原始點列表。為了得到每個特征的正確地標,我們需要對點的列表進行切片。因為點的數量是固定的,所以我選擇了硬編碼的索引。
for (const face of faces) {
const features = {
jaw: face.landmarks.positions.slice(0, 17),
eyebrowLeft: face.landmarks.positions.slice(17, 22),
eyebrowRight: face.landmarks.positions.slice(22, 27),
noseBridge: face.landmarks.positions.slice(27, 31),
nose: face.landmarks.positions.slice(31, 36),
eyeLeft: face.landmarks.positions.slice(36, 42),
eyeRight: face.landmarks.positions.slice(42, 48),
lipOuter: face.landmarks.positions.slice(48, 60),
lipInner: face.landmarks.positions.slice(60),
};
// ...
}
現在我們終于可以享受一點樂趣了。有很多選擇,但讓我們用嘴巴表情符號 ()遮住眼睛。
首先,我們必須確定將表情符號放在哪里,以及它應該畫多大。為了做到這一點,讓我們寫一個輔助函數,從一個任意的點集合中創建一個盒子,這個盒子里有我們需要的所有信息。
function getBoxFromPoints(points) {
const box = {
bottom: -Infinity,
left: Infinity,
right: -Infinity,
top: Infinity,
get center() {
return {
x: this.left + this.width / 2,
y: this.top + this.height / 2,
};
},
get height() {
return this.bottom - this.top;
},
get width() {
return this.right - this.left;
},
};
for (const point of points) {
box.left = Math.min(box.left, point.x);
box.right = Math.max(box.right, point.x);
box.bottom = Math.max(box.bottom, point.y);
box.top = Math.min(box.top, point.y);
}
return box;
}
現在我們可以開始在圖片上繪制表情符號。因為我們必須對兩只眼睛都這樣做,所以我們可以把 feature.eyeLeft 和 feature.eyeRight 放在一個數組中,然后對它們進行迭代,對每只眼睛執行同樣的代碼。剩下的就是在畫布上畫出表情符號了!
for (const eye of [features.eyeLeft, features.eyeRight]) {
const eyeBox = getBoxFromPoints(eye);
const fontSize = 6 * eyeBox.height;
context.font = `${fontSize}px/${fontSize}px serif`;
context.textAlign = 'center';
context.textBaseline = 'bottom';
context.fillStyle = '#000';
context.fillText('', eyeBox.center.x, eyeBox.center.y + 0.6 * fontSize);
}
請注意,我使用了一些魔法數字來調整字體大小和確切的文本位置。因為表情符號是 unicode 并且 Web 上的排版很奇怪(至少對我來說),所以我只是調整數字,直到它們看起來正確為止。更強大的替代方法是使用圖像作為疊加層。
總結
Face-api.js是一個偉大的庫,它使人臉檢測和識別變得非常容易。不需要熟悉機器學習和神經網絡的知識。我喜歡那些可以使用的工具,而這絕對是其中之一。
根據我的經驗,Web 上的人臉識別會影響性能。我們必須在帶寬和性能或準確性之間做出選擇。較小的模型肯定不太準確,并且會在我之前提到的一些因素中遺漏人臉,例如光線不足或面部被面具覆蓋時。
Microsoft Azure、google Cloud 和其他可能的企業都在云中提供人臉檢測。因為我們避免下載大模型,基于云的檢測避免了繁重的頁面加載,隨著它的頻繁改進往往更準確,并且由于優化的硬件甚至可能更快。如果您需要高精度,您可能需要研究一個您滿意的計劃。
我絕對推薦你在業余項目、實驗中使用face-api.js,也許還可以用來做MVP。