Auto Byte

专注未来出行及智能汽车科技

微信扫一扫获取更多资讯

Science AI

关注人工智能与其他前沿技术、基础学科的交叉研究与融合发展

微信扫一扫获取更多资讯

Geek AI 张倩编译

face-api.js:一个在浏览器中进行人脸识别的 JavaScript 接口

本文将为大家介绍一个建立在「tensorflow.js」内核上的 javascript API——「face-api.js」,它实现了三种卷积神经网络架构,用于完成人脸检测、识别和特征点检测任务,可以在浏览器中进行人脸识别。


号外!号外!现在人们终于可以在浏览器中进行人脸识别了!本文将为大家介绍「face-api.js」,这是一个建立在「tensorflow.js」内核上的 javascript 模块,它实现了三种卷积神经网络(CNN)架构,用于完成人脸检测、识别和特征点检测任务。

  • face-api.js:https://github.com/justadudewhohacks/face-api.js

  • TensorFlow.js:https://github.com/tensorflow/tfjs-core

像往常一样,我们将查看一个简单的代码示例,这将使你能立即通过短短几行代码中的程序包开始了解这个 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 有了一个初步的认识。同时,我也建议你看看文中给出的代码仓库中的其它示例。好好地把这个程序包玩个痛快吧!

原文链接:https://itnext.io/face-api-js-javascript-api-for-face-recognition-in-the-browser-with-tensorflow-js-bcc2a6c4cf07

工程浏览器人脸识别API
92
相关数据
深度学习技术

深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。 深度学习是机器学习中一种基于对数据进行表征学习的算法,至今已有数种深度学习框架,如卷积神经网络和深度置信网络和递归神经网络等已被应用在计算机视觉、语音识别、自然语言处理、音频识别与生物信息学等领域并获取了极好的效果。

权重技术

线性模型中特征的系数,或深度网络中的边。训练线性模型的目标是确定每个特征的理想权重。如果权重为 0,则相应的特征对模型来说没有任何贡献。

人脸识别技术

广义的人脸识别实际包括构建人脸识别系统的一系列相关技术,包括人脸图像采集、人脸定位、人脸识别预处理、身份确认以及身份查找等;而狭义的人脸识别特指通过人脸进行身份确认或者身份查找的技术或系统。 人脸识别是一项热门的计算机技术研究领域,它属于生物特征识别技术,是对生物体(一般特指人)本身的生物特征来区分生物体个体。

TensorFlow技术

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任软件DistBelief。

张量技术

张量是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 维空间内,有 个分量的一种量,其中每个分量都是坐标的函数,而在坐标变换时,这些分量也依照某些规则作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无关系)。 在数学里,张量是一种几何实体,或者说广义上的“数量”。张量概念包括标量、矢量和线性算子。张量可以用坐标系统来表达,记作标量的数组,但它是定义为“不依赖于参照系的选择的”。张量在物理和工程学中很重要。例如在扩散张量成像中,表达器官对于水的在各个方向的微分透性的张量可以用来产生大脑的扫描图。工程上最重要的例子可能就是应力张量和应变张量了,它们都是二阶张量,对于一般线性材料他们之间的关系由一个四阶弹性张量来决定。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

卷积神经网络技术

卷积神经网路(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。卷积神经网路由一个或多个卷积层和顶端的全连通层(对应经典的神经网路)组成,同时也包括关联权重和池化层(pooling layer)。这一结构使得卷积神经网路能够利用输入数据的二维结构。与其他深度学习结构相比,卷积神经网路在图像和语音识别方面能够给出更好的结果。这一模型也可以使用反向传播算法进行训练。相比较其他深度、前馈神经网路,卷积神经网路需要考量的参数更少,使之成为一种颇具吸引力的深度学习结构。 卷积网络是一种专门用于处理具有已知的、网格状拓扑的数据的神经网络。例如时间序列数据,它可以被认为是以一定时间间隔采样的一维网格,又如图像数据,其可以被认为是二维像素网格。

映射技术

映射指的是具有某种特殊结构的函数,或泛指类函数思想的范畴论中的态射。 逻辑和图论中也有一些不太常规的用法。其数学定义为:两个非空集合A与B间存在着对应关系f,而且对于A中的每一个元素x,B中总有有唯一的一个元素y与它对应,就这种对应为从A到B的映射,记作f:A→B。其中,y称为元素x在映射f下的象,记作:y=f(x)。x称为y关于映射f的原象*。*集合A中所有元素的象的集合称为映射f的值域,记作f(A)。同样的,在机器学习中,映射就是输入与输出之间的对应关系。

大佬,face-api.js 是不是没法在小程序中调用。在小程序中调用会遇到这样的问题: Error: getEnv - environment is not defined, check isNodejs() and isBrowser()
在微信网页内可以使用,你可以用webview嵌入,但是发现在安卓微信内,不同手机的表现不一致,比如华为p40识别人脸会直接崩溃,所以生产环境还是慎用比较好。