Статьи

Создайте приложение для обнаружения лиц, используя Node.js и OpenCV

Люди обладают врожденной способностью распознавать, обрабатывать и распознавать лица — мы рождены с этим. Компьютеры тоже могут это делать — для этого нужны просто умные алгоритмы, много кода и некоторая подготовка алгоритмов.

Распознавание лиц — это процесс идентификации лиц на цифровых изображениях. Это не должно быть перепутано с распознаванием лица — то есть попыткой понять, кто кто-то из фотографии — но это первая часть процесса. Распознавание лиц — огромная тема для другого времени, но обнаружение лица — предмет этой статьи.

Чтобы проиллюстрировать процесс, вот пример изображения:

NASA Astronaut Group 15 - исходное изображение до обнаружения лица

… и вот что делает распознавание лиц:

NASA Astronaut Group 15 - лица, выделенные после обнаружения лица

(Исходное изображение из Википедии )

Применение обнаружения лица

Есть множество применений распознавания лиц. Некоторые современные биометрические системы обнаруживают лица и затем используют распознавание лиц для сравнения этих лиц с изображениями в своих базах данных, чтобы попытаться идентифицировать кого-либо, не прибегая к старым добрым паролям. Некоторые камеры используют функцию распознавания лиц для автофокусировки. И, как и многие другие вещи, он также имеет приложения в маркетинге.

Для целей данного руководства мы воспроизведем функцию, которую вы могли бы использовать самостоятельно, если вы являетесь пользователем Facebook. Когда вы загружаете фотографию своих друзей, Facebook часто показывает ее вам с выделенными лицами, чтобы предложить вам «пометить» людей в ней. Мы собираемся построить нечто подобное.

Немного фона

Прежде чем мы углубимся в код, давайте рассмотрим некоторые инструменты и концепции, которые мы собираемся использовать.

OpenCV и алгоритм обнаружения объектов Виолы-Джонса

OpenCV (Open Source Computer Vision) — это библиотека с открытым исходным кодом из сотен алгоритмов компьютерного зрения. Хотя OpenCV написан на C ++, мы можем использовать его в приложениях Node.js благодаря пакету opencv .

Среди алгоритмов, реализованных в OpenCV, есть инфраструктура обнаружения объектов Viola-Jones , которая используется для обнаружения функций на изображениях.

Обнаружение лица — это просто подмножество обнаружения объектов (объектов), но алгоритм ориентирован на конкретные задачи, связанные с определением лиц.

Конечно, когда мы говорим об обнаружении функций в этом контексте, это не имеет ничего общего с типом обнаружения функций, предоставляемым такими библиотеками, как Modernizr и yepnope!

Впервые представленный в статье 2004 года Полом Виола и Майклом Дж. Джонсом, этот подход стал стандартом де-факто для обнаружения лиц.

Вы найдете некоторые дополнительные ресурсы на платформе, перечисленной в разделе « Дальнейшее чтение», позже в этом руководстве.

Каскады и классификаторы

Важным аспектом алгоритма Виолы-Джонса является каскад классификаторов, который описывается как «каскад повышенных классификаторов, работающих с особенностями типа хаара». С практической точки зрения это означает, что это набор визуальных функций, которые 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. Однако в настоящее время существуют проблемы с оболочкой Node.js в версии 3.0.0., Так что эта версия в порядке.

Здание из источника

Если вы хотите собрать из исходного кода, начните с установки зависимостей, перечисленных выше, затем загрузите и извлеките файлы со страницы загрузок .

Как отмечено выше, в настоящее время существуют проблемы с 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

Если вы используете Windows, установка так же проста, как загрузка и запуск исполняемого файла с веб-сайта . Вы найдете прямую ссылку на последнюю версию (на момент написания) прямо здесь .

Mac OS X

Самый простой способ установки на OSX — это использовать Homebrew :

 brew tap homebrew/science brew install opencv 

Вы найдете дальнейшие инструкции здесь .

Imagemagick

Вам также понадобится Imagemagick, который является зависимостью от библиотеки обработки изображений, которую мы будем использовать.

Системы на основе Debian

 apt-get install imagemagick 

Mac OS X

 brew install imagemagick 

Windows

Загрузите и запустите соответствующий двоичный выпуск Windows, который является исполняемым файлом, с этой страницы .

Создание нашего приложения

Напоминаем, что весь исходный код этого руководства доступен на Github .

Давайте начнем с определения нескольких зависимостей:

  • Мы используем экспресс в качестве основы нашего веб-приложения
  • Рули для шаблонов, наряду с экспресс-рулем
  • Библиотека утилит 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": , "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); }, 

Далее нам нужно запустить функцию обнаружения признаков algorthim, которая является методом класса 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() — втором аргументе 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">&larr; 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 , затем отрисовываем ссылку для каждого лица. Каждая ссылка отображается как абсолютно позиционированный блок, и мы используем данные лица, чтобы определить ее положение и размеры.

Теперь запустите приложение:

 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.

Если вам нужно действительно подробное руководство по реализации инфраструктуры обнаружения объектов Viola-Jones, вас может заинтересовать этот тезис, подготовленный Уле Хельвигом Йенсеном из Технического университета Дании. Вы также можете найти это видео YouTube полезным фоном.

В OpenCV есть еще много всего интересного; хорошее начало — документация и, в частности, учебные пособия ; заметьте, однако, что они в основном сфокусированы на C ++.

Какие еще приложения вы можете придумать? Дай мне знать в комментариях.