使用 Face-api.js 在 Web 上进行人脸检测( 二 )


下载模型后,我们可以加载它:
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);

使用 Face-api.js 在 Web 上进行人脸检测

文章插图
 
/ 放置表情符号 /
你可以在附带的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 放在一个数组中,然后对它们进行迭代,对每只眼睛执行同样的代码 。剩下的就是在画布上画出表情符号了!


推荐阅读