使用 Face-api.js 在 Web 上進行人臉檢測

碼道人 2021-08-15 20:41:58 阅读数:376

本文一共[544]字,预计阅读时长:1分钟~
使用 face-api.js face api js

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/[email protected]/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 現在包含一個結果數組。每個結果都有一個 boxscore屬性。分數錶示神經網絡對該結果確實是一張臉的自信程度。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.eyeLeftfeature.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。


翻譯自www.sitepoint.com,作者:Tim Severien

版权声明:本文为[碼道人]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815204156315v.html