使用 Node.js 和 OpenCV 构建人脸检测应用程序

老规矩,先给出下载地址;

https://url25.ctfile.com/f/34512525-576987904-6e1e27?p=7054 (访问密码:7054)

人类天生就有检测、处理和识别面孔的能力——我们与生俱来。计算机也可以做到——它只需要一些聪明的算法、大量的代码和一些算法的训练。

人脸检测是在数字图像中识别人脸的过程。它不应该与面部识别相混淆——即试图从照片中找出某人是谁——但这是该过程的第一部分。面部识别又是一个巨大的话题,但面部检测是本文的主题。

为了说明这个过程,这里有一个示例图像:

…这就是人脸检测的作用:

(来自维基百科的原始图片)

人脸检测的应用

人脸检测有很多应用。一些现代生物识别系统检测面部,然后使用面部识别将这些面部与其数据库中的图像进行比较,以便尝试识别某人而无需求助于老式密码。一些相机使用面部检测进行自动对焦。和很多东西一样,它也有营销方面的应用。

出于本教程的目的,我们将复制一个功能,如果您是 Facebook 用户,您可能会自己使用该功能。当您上传朋友的照片时,Facebook 经常将其显示给您,并突出显示所有面孔,以提示您“标记”其中的人。我们将构建类似的东西。

一点背景

在深入研究代码之前,让我们先看看我们将要使用的一些工具和概念。

OpenCV 和 Viola-Jones 目标检测算法

OpenCV(开源计算机视觉)是一个包含数百种计算机视觉算法的开源库。尽管 OpenCV 是用 C++ 编写的,但我们可以在 Node.js 应用程序中使用它,这要归功于opencv包。

在 OpenCV 中实现的算法中有Viola-Jones 对象检测框架,用于检测图像中的特征。

人脸检测只是特征(对象)检测的一个子集,但该算法专门针对检测人脸所涉及的挑战。

当然,当我们在这种情况下谈论特征检测时,它与 Modernizr 和 yepnope 等库提供的那种特征检测无关!

Paul Viola 和 Michael J. Jones 在 2004 年的一篇文章中首次提出,这种方法已成为人脸检测的事实标准。

您将在本教程后面的进一步阅读下列出的框架上找到一些其他资源。

级联和分类器

Viola-Jones 算法的一个重要方面是分类器的级联,它被描述为“使用类似 haar 的特征的增强分类器的级联”。实际上,这意味着它是 OpenCV 已经“训练”在图像中寻找的一组视觉特征,以识别特定类型的对象——在我们的例子中是人脸。您将在文档中找到有关级联和分类器的更多信息。为我们提供了专门为识别面部而设计的级联,我们将在查看实现时看到。

安装

在我们开始使用人脸检测之前,我们需要安装一些先决条件。

启动并运行的最简单(也是推荐)的方法是使用 Vagrant。您将在本文随附的存储库中找到必要的配置和供应脚本。如果您使用这种方法,则无需执行这些安装步骤。

安装 OpenCV

Linux(基于 Debian 的系统)

OpenCV 本身有许多先决条件,我们可以使用以下命令进行安装apt-get:

sudo apt-get install build-essential
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

还有一些可选的依赖项:

sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

安装 OpenCV 本身的最简单方法是使用apt-get:

sudo apt-get install libopencv-dev

在撰写本文时,它安装了 2.4.8 版本,尽管最新的 2.x 版本是 2.4.11,并且目前有 3.0.0 版本。但是,当前版本 3.0.0 上的 Node.js 包装器存在问题,所以这个版本很好。

从源头构建

如果你想从源代码构建,首先安装上面列出的依赖项,然后从下载页面下载并提取文件。

如上所述,3.0.0 目前存在问题。结合 Node.js 模块,所以最好下载 2.4.11 版本。

现在我们需要构建它:

cd ~/opencv-2.4.11
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install

请注意,最后一步可能需要一段时间!

视窗

如果您使用的是 Windows,安装就像从网站下载并运行可执行文件一样简单。您可以在此处找到指向最新版本(在撰写本文时)的直接链接。

Mac OSX

在 OSX 上安装最简单的方法是使用Homebrew:

brew tap homebrew/science
brew install opencv

您可以在此处找到进一步的说明。

图像魔术

您还需要 Imagemagick,它是我们将要使用的图像处理库的依赖项。

基于 Debian 的系统

apt-get install imagemagick 

Mac OSX

brew install imagemagick 

视窗

从此页面下载并运行适当的 Windows 二进制版本(它是一个可执行文件)。

构建我们的应用程序

让我们从定义一些依赖项开始:

  • 我们使用express作为我们 Web 应用程序的基础
  • Handlebars与express-handlebars一起用于模板
  • lodash实用程序库
  • multer是一个处理文件上传的中间件
  • easyimage是一个图像处理包
  • 最后,我们使用async来避免回调地狱

因此,事不宜迟,这是我们的package.json:

{
  "name": "sitepoint/face-detection",
  "version": "1.0.0",
  "description": "A simple application which demonstrates face detection in Node.js",
  "main": "index.js",  
  "author": "Lukas White",
  "license": "MIT",
  "dependencies": {
    "async": "^1.4.2",
    "busboy": "^0.2.9",
    "connect-busboy": "0.0.2",
    "easyimage": "^2.0.3",
    "express": "^4.13.3",
    "express-handlebars": "^2.0.1",
    "lodash": "^3.10.1",
    "multer": "^1.0.3",
    "opencv": "^3.0.0"
  }
}

使用npm install.

接下来,创建几个目录:

mkdir public
mkdir public/css
mkdir public/images
mkdir views
mkdir views/layouts
mkdir uploads

现在为我们的应用程序创建一个基本布局(views/layouts/default.hbs):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Face Detection Example</title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/bootstrap-theme.min.css">
    <link rel="stylesheet" href="/css/styles.css">
  </head>
  <body>

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="/">Face Detection Example</a>
        </div>
      </div>
    </nav>

    <div id="main" class="container">
      {{{body}}}
    </div>
  </body>
</html>

我引用 Bootstrap 框架来稍微美化应用程序,但这是可选的。要么自己下载文件,要么在本文随附的存储库中找到它们。

添加一些基本样式(public/css/styles.css):

#main {
  margin-top: 50px;
}

.frame {
  position: relative;  
}

.frame a {
  display: block;
  position: absolute;
  border: solid 2px #fff;
  border-radius: 50%;
}

.frame a:hover {
  background: rgba(0,0,0,0.5);
}

现在让我们实现一个框架 Express 应用程序 ( index.js):

var express   = require('express')
    , http    = require('http')
    , async   = require('async')
    , multer  = require('multer')
    , upload  = multer({ dest: 'uploads/' })
    , exphbs  = require('express-handlebars')
    , easyimg = require('easyimage')
    , _       = require('lodash')
    , cv      = require('opencv');

// MIME types for image uploads
var exts = {
  'image/jpeg': '.jpg',
  'image/png' : '.png',
  'image/gif' : '.gif'
};

var port = 8080;
var app = express();
app.use(express.static(__dirname + '/public'))

// Configure Handlebars
app.engine('.hbs', exphbs({ extname: '.hbs', defaultLayout: 'default' }));
app.set('view engine', '.hbs');

/**
 * This is a placeholder for the application code
 */

http.createServer(app)
  .listen(port, function(server) {
    console.log('Listening on port %d', port);
  });

希望这些评论能让您了解这里发生的事情。

我们还需要一个简单的GET路线:

app.get('/', function( req, res, next ) {
  return res.render('index');
});

对应的视图(views/index.hbs)本质上只是一个文件上传表单:

<div>
  <h2>Please upload an image.</h2>
  <p><em>Note: please ensure it's at least 960 x 300 pixels in size.</em></p>
</div>

<form method="post" action="/upload" enctype="multipart/form-data">
  <div class="form-group">
    <input type="file" name="file">
  </div>
  <div class="form-group">
    <input type="submit" value="Submit" class="btn btn-primary">
  </div>
</form>

在深入了解人脸检测的本质之前,我们需要构建一个简单的文件上传机制。这将允许用户上传图像,我们会将其调整为合理的大小,然后将其显示给他们。

这是代码:

// POST callback for the file upload form.
app.post('/upload', upload.single('file'), function(req, res, next){

  // Use filename generated for us, plus the appropriate extension
  var filename = req.file.filename + exts[req.file.mimetype]
  // and source and destination filepaths
  , src = __dirname + '/' + req.file.path
  , dst = __dirname + '/public/images/' + filename;

  async.waterfall(
    [
      function(callback){
        // Check the mimetype to ensure the uploaded file is an image
        if (!_.contains(['image/jpeg','image/png','image/gif'],req.file.mimetype)){
          return callback(new Error(
            'Invalid file - please upload an image (.jpg, .png, .gif).')
          )
        }

        return callback();
      },
      function(callback){
        // Get some information about the uploaded file
        easyimg.info(src).then(
          function(file){
            // Check that the image is suitably large
            if ((file.width 

我们在这里所做的只是抓取一个上传的文件,确保它是一张图片,检查它的最小尺寸,如果是,我们将其调整为 960 像素。人脸检测代码暂时被忽略了。我们稍后会谈到这一点。

我不会详细介绍这个过程,因为它不是本文的主要重点——但如果你想了解发生了什么,请查看multer和easyimage文档。

接下来我们需要使用OpenCV库读取图像。在幕后,这会将图像转换为像素矩阵,然后可以在其上运行特征检测算法。

我们用来执行此操作的方法具有以下签名:

cv.readImage(filepath, function(err, im){
  // do something with the matrix referred to by the im variable
});

因为我们正在使用async模块,所以我们可以简单地将回调作为第二个参数传递给它。第一个参数是目的地dst;即,调整大小过程的结果。所以有问题的函数看起来像这样:

function(callback){
  //Use OpenCV to read the (resized) image           
  cv.readImage(dst, callback);
},

接下来,我们需要运行特征检测算法,这是Matrix类上的一个方法。这是签名:

im.detectObject(classifier, options, function(err, faces){
  // faces contains an array of data about any faces it's found
});

一切顺利,该faces变量将包含一个哈希数组,它对应于它找到的每一张脸。每个散列将包含x和y坐标(0,0图像的左上角),以及一个width和height- 从而定义图像中被认为覆盖的区域。

集成到我们的异步“瀑布”中,它看起来像这样:

function(im, callback){
  // Run the face detection algorithm   
  im.detectObject(cv.FACE_CASCADE, {}, callback);
}

请注意,我们指定了一个cv.FACE_CASCADE专门为人脸检测设计的预构建分类器 ( )。

在我们的最后一个回调中——第二个参数async.waterfall()——如果出现问题,我们将渲染一个错误模板,否则我们渲染结果,我们传递上传图像的文件名以及我们的面部数据数组。

我们需要对index.js. 请花点时间在 GitHub 上查看已完成的文件。

我们需要做的最后一件事是定义我们剩下的两个视图。错误视图 ( views/error.hbs) 只是向用户显示消息:

<div class="alert alert-error" role="alert">
  <strong>An error has occured:</strong>
  {{ message }}
</div>

<a href="/" class="btn btn-default">← Go back and try again</a>

结果视图 ( views\result.hbs) 稍微有趣一些:

{{#if faces.length}}
  <div class="alert alert-success" role="alert">
    I found <strong>{{faces.length}}</strong> face(s).
  </div>
{{else}}
  <div class="alert alert-warning" role="alert">
    Sorry, but I couldn't find any faces...
  </div>
{{/if}}

<div class="frame">
  <img src="/images/{{ filename }}">
  {{#each faces}}
    <a href="#" style="width: {{ width }}px; 
                       height: {{ height }}px; 
                       left: {{ x }}px; 
                       top: {{ y }}px;"></a>  
  {{/each}}
</div>

<a href="/" class="btn btn-default">Go back and try another</a>

我们在这里所做的是将图像包装在<div>我们分配position: relative的 a 中,然后为每个人脸渲染一个链接。每个链接都显示为一个绝对定位的块,我们使用面部数据来定义它的位置和尺寸。

现在运行应用程序:

node index.js

请注意,您可能会看到以下警告:

libdc1394 error: Failed to initialize libdc1394

因为libdc1394我们的目的不是必需的,所以您可以按照Stackoverflow 答案中的说明使用以下命令简单地禁用它:

sudo ln /dev/null /dev/raw1394

请谨慎使用,因为它可能会影响系统上安装的其他应用程序

现在在浏览器中访问该应用程序。如果你使用 Vagrant,你会在这里找到它:

http://192.168.10.10:8080/

一切顺利,您应该会看到上传表单:

以下是成功进行人脸检测尝试的结果示例:

您可以从此处获取屏幕截图中显示的图像,或尝试使用您自己的图像。需要注意的一点是,此方法要求面部处于全视图、正面和直立状态。

总结和延伸阅读

我们对面部检测的简要介绍到此结束,在此期间,我们构建了 Facebook 的照片标记小部件克隆的基础。

举报
评论 0