机器学习实战:用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. 【LOJ】#2446. 「NOI2011」 NOI 嘉年华

    题解 一道神奇的dp 我们发现关于两个东西的记录很难办,但是我们发现在固定时间区间内,如果A场地举办的活动数是一定的,那么B场地肯定举办的活动越多越好 我们预处理一个\(num[i][j]\)表示时间 ...

  2. shell变量定义中的特殊符号

    今天要写一个shell语句来输出数据库的v$database的信息 定义bb为一个接收sql返回值的变量 需要注意的是: select * from v$database ;  语句 由于其中用到了$ ...

  3. 21:包含min函数的栈

    import java.util.Stack; /** * 面试题21:包含min函数的栈 * 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数. */ public class ...

  4. redis配置新端口

    为redis分配一个8888端口,操作步骤如下:1.$REDIS_HOME/redis.conf重新复制一份,重命名为redis8888.conf.2.打开redis8888.conf配置文件,找到p ...

  5. ES6-const注意

    注意要点 const 如何做到变量在声明初始化之后不允许改变的?其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动.此时,你可能已经想到,简单类型和复合类型 ...

  6. 海康威视 - 萤石云开放平台 js 版

    开放平台 https://open.ys7.com/mobile/download.html API http://open.ys7.com/doc/zh/uikit/uikit_javascript ...

  7. beeshell —— 开源的 React Native 组件库

    介绍 beeshell 是一个 React Native 应用的基础组件库,基于 0.53.3 版本,提供一整套开箱即用的高质量组件,包含 JavaScript(以下简称 JS)组件和复合组件(包含 ...

  8. java main class not found

    1.确保 所有jar都存在, 清理所有不存在的jar 2.确保src以外没有java类

  9. 日报 18/07/15 Java 性能优化

    尽量指定类和方法的final修饰符 带有final修饰符的类是不可派生的 在java核心api中 有许多应用final的例子 例如 java.lang.string整个类都是final的 为类指定fi ...

  10. MySQL数据库sql语句

    零.用户管理: 1.新建用户: >CREATE USER name IDENTIFIED BY 'ssapdrow'; 2.更改密码: >SET PASSWORD FOR name=PAS ...