机器学习实战:用nodejs实现人脸识别

 

在本文中,我将向你展示如何使用face-recognition.js执行可靠的人脸检测和识别 。 我曾经试图找一个能够精确识别人脸的Node.js库,但是没有找到,因此,我决定自己搞一个! 

这个npm包基于dlib实现,因为我发现dlib的识别精度很高。 dlib库使用深度学习方法,并附带一些预训练的模型,这些预置的模型,在LFW人脸识别基准测试上可以达到惊人的准确度:99.38% 。

为什么要搞这个东西?

最近我一直在尝试使用Node.js来构建一个人脸识别应用程序,以便从《生活大爆炸》剧中来提取和识别人物的面部。最初,我想用OpenCV的人脸识别器来实现,思路类似于我在教程Node.js + OpenCV for Face Recognition中的类似。

但是,虽然这些人脸识别器能够提供快速的预测结果,但是我发现它们不够健壮。 更确切地说,虽然他们适用于正面的人脸图像,但只要人脸姿势略有不同,就会产生相当不靠谱的预测结果。

因此,我就开始寻找替代方案,然后发现了dlib这个 C ++库,我用它的Python API试了一下,结果给我留下了深刻的印象,最后我决定: 用Node.js来实现这个功能! 因此,我创建了这个npm包,提供一个简化的Node.js 的API用于人脸识别。

face-recognition.js是什么?

我开发face-recognitiontion.js的目的,是想提供一个这样的npm包:

  • 有一个简单的API,可以让人快速上手人脸识别
  • 如果需要的话仍然允许更细粒度的识别控制
  • 易于设置(最好敲一行命令就能安装,例如npm install)

虽然这个软件包还在开发中,现在已经可以用它做点事情了:

人脸检测

你可以使用深度神经网络进行人脸检测,也可以使用简单的正面人脸识别器进行快速但但不那么可靠的检测:

人脸识别

人脸识别器是一个深度神经网络,它使用我提到的模型来计算一个独特的人脸描述符。 这个人脸识别器用标注过的人脸图像进行训练后,就可以预测输入人脸图像的标签:

人脸特征点检测

你也可以使用这个包来检测面部的特征点(5个或68个):

实战效果!

好吧,正如我所说的,我开始时尝试用OpenCV,但没能完成这个任务。 现在我有一堆150x150大小的人脸图像(分别来自Sheldon 、Raj 、 Lennard 、Howard和Stuart),我将向你展示使用这些数据来训练人脸识别器、识别新面孔是多么的简单。 这个例子的代码可以在这个仓库上找到。

准备数据

剧中每个角色不同的姿态分别采集大概20张图像:

我们将分别使用每个人的10个图像来训练识别器,其余部分来评估识别器的准确性:

const path = require('path')
const fs = require('fs')
const fr = require('face-recognition')

const dataPath = path.resolve('./data/faces')
const classNames = ['sheldon', 'lennard', 'raj', 'howard', 'stuart']

const allFiles = fs.readdirSync(dataPath)
const imagesByClass = classNames.map(c =>
  allFiles
    .filter(f => f.includes(c))
    .map(f => path.join(dataPath, f))
    .map(fp => fr.loadImage(fp))
)

const numTrainingFaces = 10
const trainDataByClass = imagesByClass.map(imgs => imgs.slice(0, numTrainingFaces))
const testDataByClass = imagesByClass.map(imgs => imgs.slice(numTrainingFaces))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

每个脸部图像的文件名中包含了人名,所以我们可以简单地实现图像和其分类的映射:

['sheldon', 'lennard', 'raj', 'howard', 'stuart']
  • 1

你可以使用fr.loadImage(fp)读取给定文件路径的图像。

检测人脸

正如我所说的,图像已经提取为150x150的大小,这是我事先用opencv4nodejs完成的。 但是你也可以用下面的代码来检测和提取人脸、保存并进行标注:

const image = fr.loadImage('image.png')
const detector = fr.FaceDetector()
const targetSize = 150
const faceImages = detector.detectFaces(image, targetSize)
faceImages.forEach((img, i) => fr.saveImage(img, `face_${i}.png`))
  • 1
  • 2
  • 3
  • 4
  • 5

训练识别器

现在我们有了数据,可以开始训练识别器:

const recognizer = fr.FaceRecognizer()

trainDataByClass.forEach((faces, label) => {
  const name = classNames[label]
  recognizer.addFaces(faces, name)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

基本上这就是把每张人脸图像输入到神经网络中,神经网络输出面部的描述符并存储指定类别的所有描述符。 你还可以通过将numJitters指定为第三个参数来利用训练数据生成一些抖动后的图像,这个参数将应用旋转 、缩放和镜像等变换来创建每个输入人脸的不同版本图像。 增加抖动版本的数量可能有助于提高预测的准确性,但同时也会增加训练时间。

此外,我们可以存储识别器的状态,这样就不必在下次要用时重新进行训练了,只需要简单地从一个文件中加载训练好的模型:

保存:

const modelState = recognizer.serialize()
fs.writeFileSync('model.json', JSON.stringify(modelState))
  • 1
  • 2

加载:

const modelState = require('model.json')
recognizer.load(modelState)
  • 1
  • 2

识别新面孔

现在我们可以用剩余的样本数据来检查预测精度并记录结果:

const errors = classNames.map(_ => [])
testDataByClass.forEach((faces, label) => {
  const name = classNames[label]
  console.log()
  console.log('testing %s', name)
  faces.forEach((face, i) => {
    const prediction = recognizer.predictBest(face)
    console.log('%s (%s)', prediction.className, prediction.distance)

    // count number of wrong classifications
    if (prediction.className !== name) {
      errors[label] = errors[label] + 1
    }
  })
})

// print the result
const result = classNames.map((className, label) => {
  const numTestFaces = testDataByClass[label].length
  const numCorrect = numTestFaces - errors[label].length
  const accuracy = parseInt((numCorrect / numTestFaces) * 10000) / 100
  return `${className} ( ${accuracy}% ) : ${numCorrect} of ${numTestFaces} faces have been recognized correctly`
})
console.log('result:')
console.log(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

当前的预测是通过计算输入人脸图像的描述符向量到分类中每个描述符的欧式距离来完成的,同时也计算出所有距离的平均值 。 有人可能会说, kmeans聚类或SVM分类器将更适合这个任务,我也可能在将来实现这些算法。 但是现在使用欧几里德距离的实现,看起来执行速度很快而且效率够高。

调用predictionBest将输出具有最小距离(例如最高相似度)的结果。 输出看起来像这样:

{className:'sheldon',距离:0.5} 
  • 1

如果你想获得所有分类的脸部描述符到输入的人脸图像的距离,可以简单地使用recognitionizer.predict(image) ,它将输出一个数组,其成员包含输入图像到每个分类的距离:

  [
  {className:'sheldon',距离:0.5},
  {className:'raj',距离:0.8},
  {className:'howard',距离:0.7},
  {className:'lennard',距离:0.69},
  {className:'stuart',距离:0.75}
  ] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

结果

运行上面的例子将给出以下结果。

使用10个面孔进行训练:

sheldon ( 90.9% ) : 10 of 11 faces have been recognized correctly
lennard ( 100% ) : 12 of 12 faces have been recognized correctly
raj ( 100% ) : 12 of 12 faces have been recognized correctly
howard ( 100% ) : 12 of 12 faces have been recognized correctly
stuart ( 100% ) : 3 of 3 faces have been recognized correctly
  • 1
  • 2
  • 3
  • 4
  • 5

每个训练只使用5个面孔 :

sheldon ( 100% ) : 16 of 16 faces have been recognized correctly
lennard ( 88.23% ) : 15 of 17 faces have been recognized correctly
raj ( 100% ) : 17 of 17 faces have been recognized correctly
howard ( 100% ) : 17 of 17 faces have been recognized correctly
stuart ( 87.5% ) : 7 of 8 faces have been recognized correctly
  • 1
  • 2
  • 3
  • 4
  • 5

结论

从结果看,即使只用一小组数据进行训练,也可以获得相当准确的结果。甚至对这些我从网上抓取的小尺寸图像(一些提取的脸部非常模糊), 结果也令人非常满意。

如果你喜欢这篇文章,记得关注我的头条号:新缸中之脑!

原文:Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning

机器学习实战:用nodejs实现人脸识别的更多相关文章

  1. Nodejs开发人脸识别系统-教你实现高大上的人工智能

    Nodejs开发人脸识别系统-教你实现高大上的人工智能   一.缘起缘生 前段时间有个H5很火,上传个头像就可以显示自己穿军装的样子,无意中看到了一篇帖子叫 全民刷军装背后的AI技术及简单实现 ,里面 ...

  2. [机器学习][face recognition] 一个视频人脸识别实现

    开发环境和用到的库: Ubuntu jupyter notebook(python3.6) OpenCV Dlib face_recognition 实现效果如下(视频截图): #coding=utf ...

  3. 机器学习实战kNN之手写识别

    kNN算法算是机器学习入门级绝佳的素材.书上是这样诠释的:“存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都有标签,即我们知道样本集中每一条数据与所属分类的对应关系.输入没有标签的新数据 ...

  4. [深度应用]·实战掌握Dlib人脸识别开发教程

    [深度应用]·实战掌握Dlib人脸识别开发教程 个人网站--> http://www.yansongsong.cn/ 项目GitHub地址--> https://github.com/xi ...

  5. 基于kNN的手写字体识别——《机器学习实战》笔记

    看完一节<机器学习实战>,算是踏入ML的大门了吧!这里就详细讲一下一个demo:使用kNN算法实现手写字体的简单识别 kNN 先简单介绍一下kNN,就是所谓的K-近邻算法: [作用原理]: ...

  6. 在nodejs中的集成虹软人脸识别

    ==虹软官网地址==http://www.arcsoft.com.cn 在官网注册账号,并且申请人脸识别激活码, 选择SDK版本和运行系统(windows/linux/android/ios) ,我们 ...

  7. 【从零学习openCV】IOS7人脸识别实战

    前言 接着上篇<IOS7下的人脸检測>,我们顺藤摸瓜的学习怎样在IOS7下用openCV的进行人脸识别,实际上非常easy,因为人脸检測部分已经完毕,剩下的无非调用openCV的方法对採集 ...

  8. windows下百度离线人脸识别本地部署与使用(nodejs做客户端,c++做服务端,socket做通信)

    1.离线人脸识别本地部署 详情请阅读百度人脸识别官网 2.nodejs做socket通信的客户端 为什么不直接通过调用c++编译的exe获得人脸识别结果? 原因:exe运行时会加载很多模型而消耗很多时 ...

  9. 机器学习-svd实现人脸识别

    加载sklearn中的人脸数据集 from sklearn.datasets import fetch_lfw_people faces = fetch_lfw_people() 执行上面的第二行程序 ...

随机推荐

  1. 牛客网 江西财经大学第二届程序设计竞赛同步赛 D.绕圈游戏-(跳青蛙游戏)找数的所有因子就可以了

    链接:https://ac.nowcoder.com/acm/contest/635/D来源:牛客网 D.绕圈游戏 433为了帮ddd提升智商,决定陪他van特殊的游戏.433给定一个带有n个点的环, ...

  2. TCP 建立连接为什么要握 3 次手?

    上次已经说过,没有协议,不成方圆,计算机之间的通信更是依赖于协议.今天就重点分析一下 TCP 协议. 传输控制协议 TCP 是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 7 ...

  3. AORUS GA-Z270X-Gaming 5開箱

    「AORUS」這個品牌名稱由埃及神祇荷魯斯(戰爭與狩獵之神)的名字衍生而成.荷魯斯通常被勾勒為獵鷹的形象,因此獵鷹的頭形被用作AORUS品牌的商標於2014年,本來只做為用在電競筆電及一些週邊方面,但 ...

  4. [leetcode greedy]45. Jump Game II

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  5. HTML基础-DAY1

    HTML基础 Web的本质就是利用浏览器访问socket服务端,socket服务端收到请求回复数据提供给浏览器进行渲染显示. import socket def main(): sock = sock ...

  6. BZOJ 4945 UOJ #317 NOI2017 游戏 2-SAT 拓扑排序

    http://uoj.ac/problem/317 https://www.lydsy.com/JudgeOnline/problem.php?id=4945 我现在的程序uoj的额外数据通过不了,b ...

  7. OI刷题记录(Updating)

    [Counter] [AGC]:0/96 [ARC]:0/70 [2016年省选]:0/69 [2017年省选]:22/75 [2018年省选]:0/63 [ZROI2018]:0/30 [ZROI2 ...

  8. 【10.9校内练习赛】【搜索】【2-sat】【树链剖分】【A_star k短路】【差分约束+判负环】

    在洛谷上复制的题目! P3154 [CQOI2009]循环赛 题目描述 n队伍比赛,每两支队伍比赛一次,平1胜3负0. 给出队伍的最终得分,求多少种可能的分数表. 输入输出格式 输入格式: 第一行包含 ...

  9. 【BZOJ】1864: [Zjoi2006]三色二叉树

    1864: [Zjoi2006]三色二叉树 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 1295  Solved: 961[Submit][Status ...

  10. Html的学习随笔

    在<head>的<style>中定义样式,有#号,比如#header就是定义一种名为header的样式,后面用id=header来调用:而无#号,比如直接就是header,那后 ...