OpenCVを使って顔付近を抜き出す。- PCグラフィックスをWebGLでレトロPCグラフィックスを楽しむ(20)

公開:2016-04-29 15:26
更新:2017-11-15 21:58
カテゴリ:PCグラフィックス,javascript,webgl,html5,opencv,WebGLでレトロPCグラフィックスを楽しむ

ということで、OpenCVを使って動画から顔付近を抜き出してみることにした。以外に簡単にできた。

まずOpenCVのnodeバインディングを探したところ、以下のものを見つけた。

https://github.com/peterbraden/node-opencv

で、これを手順通りにインストールしようとしたが、C++コードのコンパイルでインストールに失敗した。が回避方法がIssueに載っていた。

https://github.com/peterbraden/node-opencv/issues/382

で、上の通りにソースコードを修正したところ、ビルドが通り使用できるようになった。

使えるようになれば後は簡単。ffmpegで動画をpngファイルにまず落とし、そのファイルを順に読んで顔部分を認識、一定のサイズに切り取って40x25ピクセルのpngファイルに落とすコードを書いた。

顔の部分だけが、ちゃんと抜き出せている。

"use strict"
var cv = require('I:/libs/npm/node_modules/opencv/lib/opencv');
let fs = require('fs');

function denodeify(nodeFunc) {
  var baseArgs = Array.prototype.slice.call(arguments, 1);
  return function () {
    var nodeArgs = baseArgs.concat(Array.prototype.slice.call(arguments));
    return new Promise((resolve, reject) => {
      nodeArgs.push((error, data) => {
        if (error) {
          reject(error);
        } else if (arguments.length > 2) {
          resolve(Array.prototype.slice.call(arguments, 1));
        } else {
          resolve(data);
        }
      });
      nodeFunc.apply(null, nodeArgs);
    });
  }
}

const w = 80 * 1.5;
const h = 50 * 1.5;

var readFile = denodeify(fs.readFile);
var readDir = denodeify(fs.readdir);
var writeFile = denodeify(fs.writeFile);
var readImage = denodeify(cv.readImage);
//var detectObject = denodeify(cv.detectObject);

function faceDetect() {

  return readDir('./res/outmov/')
    .then((files) => {
      return files.filter(function (file) {
        return fs.statSync('./res/outmov/' + file).isFile() && /.*\.png$/ig.test(file); //絞り込み
      })
        .sort();
      //.map(d=>'./res/outmov/' + d);
    })
    .then(files => {
      let pr = Promise.resolve(0);
      files.forEach(f => {
        pr = pr.then(() => {
          return new Promise((resolve, reject) => {
            cv.readImage('./res/outmov/' + f, function (err, im) {
              if (err) reject(err);
              resolve(im);
            })
          });
        })
          .then((im) => {
            return new Promise((resolve, reject) => {
              im.detectObject(cv.FACE_CASCADE, {}, function (err, faces) {
                if (err) reject(err);
                if (faces.length > 0) {
                  //          for(let i = 0;i < faces.length;++i){
                  let pos = faces[faces.length - 1];
                  //            console.log(f,pos.x,pos.y,pos.width,pos.height);
                  let cx = pos.x + pos.width / 2, cy = pos.y + pos.height / 2;
                  let sx = cx - w / 2, sy = cy - h / 2 + 8;
                  if (sx < 0) sx = 0;
                  if (sy < 0) sy = 0;
                  let out = im.crop(sx, sy, w, h)
                  out.resize(40, 25, 2);
                  out.save('./res/outmov2/' + f);
                  //        }
                } else {
                  var sx = im.width() / 2 - w / 2;
                  var sy = im.height() / 2 - h / 2;
                  let out = im.crop(sx, sy, w, h);
                  out.resize(40, 25, 2);
                  out.save('./res/outmov2/' + f);
                }
                resolve();
              });
            });
          });
      });
      return pr;
    });
}

module.exports = faceDetect;

動作サンプル

新しいウィンドウで開く

ソースコード・リソース

/dev/graphics/devver/20160429/css/sfstyle.css

/dev/graphics/devver/20160429/index.html

/dev/graphics/devver/20160429/js/bundle.js

/dev/graphics/devver/20160429/test.html