前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >face-api.js中加入MTCNN:进一步支持使用JS实时进行人脸跟踪和识别

face-api.js中加入MTCNN:进一步支持使用JS实时进行人脸跟踪和识别

作者头像
AiTechYun
发布2018-08-06 15:12:14
2.4K0
发布2018-08-06 15:12:14
举报
文章被收录于专栏:ATYUN订阅号ATYUN订阅号

编译:yxy

出品:ATYUN订阅号

如果你现在正在阅读这篇文章,那么你可能已经阅读了我的介绍文章(JS使用者福音:在浏览器中运行人脸识别)或者之前使用过face-api.js。如果你还没有听说过face-api.js,我建议你先阅读介绍文章再回来阅读本文。

和往常一样,本文中为你准备了一个代码示例。我们将解析一个小的应用程序,这个程序将在浏览器中访问摄像头图像执行实时人脸检测和人脸识别,让我们开始吧!

使用face-api.js进行人脸检测

到目前为止,face-api.js单独实现了基于SSD Mobilenet v1的CNN进行人脸检测。虽然这个是一个非常精确的人脸检测器,但SSD并不像其他架构那么快(在推理时间方面),并且可能无法通过这个人脸检测器实现实时检测,除非你或者你的用户在他们的机器中内置了一个不错的GPU。

事实证明,你并不总是需要那么高的准确度,有时候你会宁愿用高精度换取更快的人脸检测器。

所以我们要用到MTCNN,它现在可以在face-ap .js中使用了!MTCNN是一种更轻量级的面部检测器。与SSD Mobilenet v1相比:

优点:
  • 更短的推理时间(更快的检测速度)
  • 同时检测5个面部标志点(我们“免费”获得人脸对齐)
  • 模型更小:相比对于6MB(量化的SSD Mobilenet v1权重)来说,它仅约2MB
  • 可配置性:你可以调整一些参数以提高性能以满足你的特定要求
缺点:
  • 不如SSD Mobilenet v1准确

MTCNN – 同时进行人脸检测和面部地标

MTCNN(Multi-task Cascaded Convolutional Neural Networks)是一种由3个阶段组成的算法,它可以检测图像中人脸的边界框以及它们的5个点的面部地标。每个阶段通过将其输入通过CNN逐步改善检测结果,CNN返回具有其分数的候选边界框,然后是非最大抑制。

论文:https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf

在阶段1中,输入图像被缩放多次以构建影像金字塔并且图像的每个缩放版本都通过其CNN传递。在第2阶段和第3阶段,我们为每个边界框提取图像块并调整它们的大小(第2阶段为24×24,第3 阶段为48×48),然后通过该阶段的CNN传递它们。除了边界框和分数之外,阶段3还为每个边界框计算5个面部地标点。

在修改了一些MTCNN实现之后,结果表明,与SSD Mobilenet v1相比,即使在CPU上运行推断,也可以在更低的推断时间获得相当可靠的检测结果。并且,从5个面部地标点中,我们可以免费获得面部对齐!这样,在计算面部描述符之前,我们不必执行68点面部地标检测作为中间步骤。

尽管在我看来这很有前景,我还是继续在tfjs-core中实现了这一点经过几天的努力,我终于能够找到一个有效的解决方案。

摄像头人脸跟踪和人脸识别

如前所述,我们现在将看看如何使用摄像头实现人脸跟踪和人脸识别。在这个例子中,我会使用我的摄像头再次跟踪和识别一些《生活大爆炸》主角的脸,但当然你可以使用这些代码来跟踪和识别自己。

要显示网络摄像头中的帧,只需使用如下视频元素即可。此外,我将一个绝对定位的画布放在视频元素的顶部,具有相同的高度和宽度。我们将使用画布作为透明的叠加层,稍后我们可以在上面绘制检测结果:

代码语言:javascript
复制
<div style="position: relative" class="margin">
代码语言:javascript
复制
  <video onplay="onPlay(this)" id="inputVideo" autoplay muted></video>
代码语言:javascript
复制
  <canvas id="overlay" />
代码语言:javascript
复制
</div>

加载页面后,我们将加载MTCNN模型和人脸识别模型,以计算面部描述符。此外,我们使用navigator.getUserMedia将我们的摄像头流附加到视频元素:

代码语言:javascript
复制
$(document).ready(function() {
代码语言:javascript
复制
  run()
代码语言:javascript
复制
})
代码语言:javascript
复制
代码语言:javascript
复制
asyncfunction run() {
代码语言:javascript
复制
  // load the models
代码语言:javascript
复制
  await faceapi.loadMtcnnModel('/')
代码语言:javascript
复制
  await faceapi.loadFaceRecognitionModel('/')
代码语言:javascript
复制
代码语言:javascript
复制
  // try to access users webcam and stream the images
代码语言:javascript
复制
  // to the video element
代码语言:javascript
复制
  const videoEl = document.getElementById('inputVideo')
代码语言:javascript
复制
  navigator.getUserMedia(
代码语言:javascript
复制
    { video: {} },
代码语言:javascript
复制
    stream => videoEl.srcObject = stream,
代码语言:javascript
复制
    err => console.error(err)
代码语言:javascript
复制
  )
代码语言:javascript
复制
}

现在程序应该会请求授予浏览器访问摄像头的权限。在我们为视频元素指定的onPlay回调中,我们将处理每个帧的实际加工。注意,一旦视频开始播放,就会触发onplay事件。

人脸检测

正如我所说,我们可以在这里配置一些检测参数。默认参数:

代码语言:javascript
复制
const mtcnnForwardParams = {
代码语言:javascript
复制
  // number of scaled versions of the input image passed through the CNN
代码语言:javascript
复制
  // of the first stage, lower numbers will result in lower inference time,
代码语言:javascript
复制
  // but will also be less accurate
代码语言:javascript
复制
  maxNumScales: 10,
代码语言:javascript
复制
  // scale factor used to calculate the scale steps of the image
代码语言:javascript
复制
  // pyramid used in stage 1
代码语言:javascript
复制
  scaleFactor: 0.709,
代码语言:javascript
复制
  // the score threshold values used to filter the bounding
代码语言:javascript
复制
  // boxes of stage 1, 2 and 3
代码语言:javascript
复制
  scoreThresholds: [0.6, 0.7, 0.7],
代码语言:javascript
复制
  // mininum face size to expect, the higher the faster processing will be,
代码语言:javascript
复制
  // but smaller faces won't be detected
代码语言:javascript
复制
  minFaceSize: 20
代码语言:javascript
复制
}

要跟踪摄像头中的人脸,我们会将minFaceSize增加到至少200px仅检测较大尺寸的人脸使我们获得更短的推理时间,因为网络将图像缩小的系数更大:

代码语言:javascript
复制
const mtcnnForwardParams = {
代码语言:javascript
复制
  // limiting the search space to larger faces for webcam detection
代码语言:javascript
复制
  minFaceSize: 200
代码语言:javascript
复制
}
代码语言:javascript
复制
代码语言:javascript
复制
const mtcnnResults = await faceapi.mtcnn(document.getElementById('inputVideo'), mtcnnForwardParams)

如你所见,我们可以简单地将视频元素提供给它,就像图像或画布元素那样。

通过MTCNN前向传递给我们一个FaceDetection数组(边界框+得分)以及每个检测到的人脸的FaceLandmark5。现在我们可以将结果绘制到叠加层上:

代码语言:javascript
复制
faceapi.drawDetection('overlay', mtcnnResults.map(res => res.faceDetection), { withScore:false })
代码语言:javascript
复制
faceapi.drawLandmarks('overlay', mtcnnResults.map(res => res.faceLandmarks), { lineWidth: 4, color:'red' })

例子如下,到目前为止,我们将得到下面的结论:

计算人脸描述符

从我之前的教程中你应该已经知道,在计算任何面部描述符之前,我们需要将人脸地标的位置与人脸边界框的位置对其。从对齐的框中,我们提取对齐的通过人脸识别网络传递的面部张量:

代码语言:javascript
复制
const alignedFaceBoxes = results.map(
代码语言:javascript
复制
  ({ faceLandmarks }) => faceLandmarks.align()
代码语言:javascript
复制
)
代码语言:javascript
复制
代码语言:javascript
复制
const alignedFaceTensors = await extractFaceTensors(input, alignedFaceBoxes)
代码语言:javascript
复制
代码语言:javascript
复制
const descriptors = await Promise.all(alignedFaceTensors.map(
代码语言:javascript
复制
  faceTensor => faceapi.computeFaceDescriptor(faceTensor)
代码语言:javascript
复制
))
代码语言:javascript
复制
代码语言:javascript
复制
// free memory
代码语言:javascript
复制
alignedFaceTensors.forEach(t => t.dispose())

如果你觉得这样做很麻烦,代码太多,还有一个方便快捷的函数—faceapi.allFacesMtcnn。它可以检测图像中所有人脸并计算它们的描述符(类似于faceapi.allFaces):

代码语言:javascript
复制
const fullFaceDescriptions = await faceapi.allFacesMtcnn(document.getElementById('inputVideo'), mtcnnParams)
代码语言:javascript
复制
// fullFaceDescriptions[0].detection
代码语言:javascript
复制
// fullFaceDescriptions[0].landmarks
代码语言:javascript
复制
// fullFaceDescriptions[0].descriptor
人脸识别

从现在开始,我们只需按照上一个教程中的方式进行操作。回想一下,为了识别人脸,在运行主循环之前,我们必须为每个我们想要识别(参考数据)的人的示例图像预先计算一个(至少一个)面部描述符。为了做出决策,是哪一个人坐在摄像头前面,我们将在参考数据中的面部描述符中查询人脸描述符并返回最相似的匹配:

代码语言:javascript
复制
const sortAsc = (a, b) => a - b
代码语言:javascript
复制
代码语言:javascript
复制
const results = fullFaceDescriptions.map((fd) => {
代码语言:javascript
复制
  const bestMatch = refDescriptors.map(
代码语言:javascript
复制
    ({ descriptor, label }) => ({
代码语言:javascript
复制
      label,
代码语言:javascript
复制
      distance: faceapi.euclideanDistance(fd.descriptor, descriptor)
代码语言:javascript
复制
    })
代码语言:javascript
复制
  ).sort(sortAsc)[0]
代码语言:javascript
复制
代码语言:javascript
复制
  return {
代码语言:javascript
复制
    detection: fd.detection,
代码语言:javascript
复制
    label: bestMatch.label,
代码语言:javascript
复制
    distance: bestMatch.distance
代码语言:javascript
复制
  }
代码语言:javascript
复制
})

如果你只想跟踪自己,只需自拍一张然后运行faceapi.allFaces就可以检索自己脸部的面部描述符(参考描述符)。然后,可以使用faceapi.euclideanDistance直接计算查询面部描述符的距离(从你的摄像头图像到参考描述符)

最后,我们将带有预测标签的文本和相对于边界框位置的距离再次绘制到覆盖层画布上:

代码语言:javascript
复制
// 0.6 is a good distance threshold value to judge
代码语言:javascript
复制
// whether the descriptors match or not
代码语言:javascript
复制
const maxDistance = 0.6
代码语言:javascript
复制
代码语言:javascript
复制
results.forEach(result => {
代码语言:javascript
复制
  faceapi.drawDetection(canvas, result.detection, { withScore:false })
代码语言:javascript
复制
代码语言:javascript
复制
  const text = `${result.distance < maxDistance ? result.className :'unknown'} (${result.distance})`
代码语言:javascript
复制
  const { x, y, height: boxHeight } = detection.getBox()
代码语言:javascript
复制
  faceapi.drawText(
代码语言:javascript
复制
    canvas.getContext('2d'),
代码语言:javascript
复制
    x,
代码语言:javascript
复制
    y + boxHeight,
代码语言:javascript
复制
    text
代码语言:javascript
复制
  )
代码语言:javascript
复制
})

然后,不要忘记调用onPlay继续迭代处理最近的帧。就是这些!

结语

最后要注意的是,为每一帧重新计算查询面部描述符是一种非常幼稚的方法。显然,有更有效的方法,例如每隔x帧跟踪和更新检测结果的面部描述符。通常,被跟踪的人脸的姿势不会在几帧中有剧烈地改变。但为了简单起见,这样就可以了。如果你想要从中挤出更多的fps,可以利用这一点。

示例的完整源代码:https://github.com/justadudewhohacks/face-api.js/blob/master/examples/views/mtcnnFaceRecognitionWebcam.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ATYUN订阅号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用face-api.js进行人脸检测
    • 优点:
      • 缺点:
      • MTCNN – 同时进行人脸检测和面部地标
      • 摄像头人脸跟踪和人脸识别
        • 人脸检测
          • 计算人脸描述符
            • 人脸识别
            • 结语
            相关产品与服务
            人脸识别
            腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档