こくぶん研究室

表情認識で SNOW 的なアプリ

はじめに

身のまわりの顔認識

スマホアプリ SNOW は、カメラで顔を認識して顔にスタンプ画像を貼り付けたり、顔を変形させたり、他の人と顔を交換したりと、不思議な体験ができて人気です(iOS 用Android 用)。スマホのカメラアプリやデジタルカメラには、顔を認識してピントを合わせたり、笑顔でシャッターを切る機能がついています。パソコンには、パスワードなしにログインできる顔認証機能が付いているものもあります。顔からユーザの年令や性別を判別する自動販売機もあります。運転手が居眠りやよそ見をしていたら警報を鳴らす装置が付いている自動車もあります。ロボットの Pepper はユーザの顔や表情を見ながら対話しています。

顔認識の方法

コンピュータでカメラの映像や画像から顔を認識する方法はいくつもありますが、基本は同じです。まず画像の中から「顔っぽい」部分を探します。顔っぽい部分が見つかったらさらに、輪郭、目、眉、鼻、口などの「顔の部品っぽい」場所を探します。あらかじめ人工知能などを使ってたくさんの人の顔の特徴を学習しておいて、画像の中から顔っぽい場所や顔の部品っぽい場所を探します。そして、顔の部品の配置のパターンから、年齢、性別、個人、表情などを判別します。画像処理、人工知能、多変量解析といった技術が使われています。

さぁ 遊んでみましょう

画像処理、人工知能、多変量解析... 難しそうですが、そういう難しい部分を気にせずに顔認識プログラムを作るためのライブラリが世の中にたくさんあります。自分で作るアプリの中で顔認識が使えると、とても便利で楽しいことができます。

▲TOP

今回の内容

目標

ユーザの顔と表情を認識して、表情に応じたスタンプ画像を貼り付ける SNOW のようなアプリを作ります。顔や表情の認識には clmtrackr.js というライブラリを使います。

今回の目標
今回の目標

使うもの

ステップ

  1. 顔認識
  2. 顔にスタンプ
  3. 表情認識
  4. 表情認識スタンプ

▲TOP

1. 顔認識

まずは顔認識ライブラリ clmtrackr.js を使えるようにして、カメラの映像で顔認識してみましょう。

今から作るもの
今から作るもの

clmtrackr.js のダウンロード

パソコンでこちらのサイトを開いてください。

clmtrackr.js のダウンロード
clmtrackr.js のダウンロード

「Clone or download」というボタンをクリックして、出てきたメニューから「Download ZIP」をクリックします。すると「clmtrackr-dev.zip」というファイルがダウンロードされます。

ダウンロードが終わったら「clmtrackr-dev.zip」を開いてください(展開しないままで構いません)。たどっていくと「clmtrackr.js」というファイルがあるので、これを右クリックして「コピー」してください。C:\xampp\htdocs フォルダの中に「face1」というフォルダを作って、その中にこの clmtrackr.js を貼り付けてください。さらに「clmtrackr-dev.zip」の中の「models」というフォルダの中にある「model_pca_20_svm.js」というファイルをコピーして、face1 フォルダの中に貼り付けてください。

これで顔認識の準備ができました。今回はダウンロードしたファイルのうち上記の二つのファイルしか使いませんが、他にも様々なサンプルプログラムなどが入っています。zip ファイルは捨てないでおきましょう。

プログラミング

Visual Studio Code(または好みのエディタ)を立ち上げて、以下のコードを入力しましょう。こちらで作ったテンプレートをもとに書き始めると効率的です。ファイルは、さきほど作った C:\xampp\htdocs フォルダの中の「face1」フォルダの中に「index.html」として保存してください。それぞれのコードが何をしているのか考えながら入力していきましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>顔認識</title>
<style>
  /* video 要素の上に canvas 要素をオーバーレイするための CSS */
  #container {              /* コンテナ用の div について */
    position: relative;     /* 座標指定を相対値指定にする */
  }
  #video {                  /* カメラ映像を流す video について */
    transform: scaleX(-1);  /* 左右反転させる */
  }
  #canvas {                 /* 描画用の canvas について */
    transform: scaleX(-1);  /* 左右反転させる */
    position: absolute;     /* 座標指定を絶対値指定にして */
    left: 0;                /* X座標を0に */
    top: 0;                 /* Y座標を0に */
  }
</style>
</head>

<body>
<div id="container">  <!-- video の上に canvas をオーバーレイするための div 要素 -->
  <video id="video" width="400" height="300" autoplay></video>  <!-- カメラ映像を流す video -->
  <canvas id="canvas" width="400" height="300"></canvas>        <!-- 重ねて描画する canvas -->
</div>
<div id="dat"></div>  <!-- データ表示用 div 要素 -->

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm.js"></script>   <!-- 顔モデル(※)の読み込み -->

<script>
// もろもろの準備
var video = document.getElementById("video");           // video 要素を取得
var canvas = document.getElementById("canvas");         // canvas 要素の取得
var context = canvas.getContext("2d");                  // canvas の context の取得

// getUserMedia によるカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({       // メディアデバイスを取得
  video: {facingMode: "user"},                          // カメラの映像を使う(スマホならインカメラ)
  audio: false                                          // マイクの音声は使わない
});
media.then((stream) => {                                // メディアデバイスが取得できたら
  video.srcObject = stream;                             // video 要素にストリームを渡す
});

// clmtrackr の開始
var tracker = new clm.tracker();  // tracker オブジェクトを作成
tracker.init(pModel);             // tracker を所定のフェイスモデル(※)で初期化
tracker.start(video);             // video 要素内でフェイストラッキング開始

// 描画ループ
function drawLoop() {
  requestAnimationFrame(drawLoop);                      // drawLoop 関数を繰り返し実行
  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得
  showData(positions);                                  // データの表示
  context.clearRect(0, 0, canvas.width, canvas.height); // canvas をクリア
  tracker.draw(canvas);                                 // canvas にトラッキング結果を描画
}
drawLoop();                                             // drawLoop 関数をトリガー

// 顔部品(特徴点)の位置データを表示する showData 関数
function showData(pos) {
  var str = "";                                         // データの文字列を入れる変数
  for(var i = 0; i < pos.length; i++) {                 // 全ての特徴点(71個)について
    str += "特徴点" + i + ": ("
         + Math.round(pos[i][0]) + ", "                 // X座標(四捨五入して整数に)
         + Math.round(pos[i][1]) + ")<br>";             // Y座標(四捨五入して整数に)
  }
  var dat = document.getElementById("dat");             // データ表示用div要素の取得
  dat.innerHTML = str;                                  // データ文字列の表示
}
</script>
</body>
</html>

動作確認

パソコンで動作確認

今回はまずはパソコンで動作確認します。

XAMPP Control Panel で Apache を Start させてください(不明な場合はこちらを参考にしてください)。

パソコンのブラウザ(Chrome か Firefox)を開き、アドレス欄に以下のように入力します。

パソコン内蔵のカメラまたは外付けの Web カメラに自分の顔全体が入るようにしてください。しばらくすると、顔を見つけて、顔の形や部品にフィットするように緑色の線が描かれます。

顔認識できた
顔認識できた

画面の下のほうには「特徴点?:」のあとに数値が表示されていますね。特徴点?の?の部分の数値は、以下の図にような顔の部位の番号です。( ) 内に表示されている値は、ぞれぞれの部位の座標 (X, Y) です。

顔の部位の番号
顔の部位の番号

スマホで動作確認

カメラの映像を使うアプリは、プライバシー保護のために、スマホでテストするのは少しコツがいります。パソコンで動かしているサーバ(Apache)ではスマホでは動作確認できません。

https 接続が可能なサーバやホスティングサービスにアップして動作確認しましょう。パソコンで Netlify Drop などの無料ホスティングサービスにアクセスします。例えば Netlify Drop であれば、そのトップページに「face1」フォルダをドラッグ&ドロップしてください。

Netlify Drop にアップロード
Netlify Drop にアップロード

アップロードされたら、表示されたアドレスを Android スマホの Chrome で開きます。この時、アドレスの先頭に「https://」と入力してください

この手順についてよく分からない場合はこちらを参考にしてください。

スマホは横向きにします。また、スマホでは顔認識の処理が追いつかずにモッサリした動作になりますが、あまり動かずに待っていれば認識されます。

Android スマホの Chrome で
Android スマホの Chrome で

解説

顔認識をしてくれる clmtrackr.js を使うためにまず 32行目で clmtrackr.js を読み込んでいます。また、clmtrackr.js は顔を認識するために、あらかじめたくさんの人の顔で学習した「フェイスモデル」というファイルを使います。33行目で model_pca_20_svm.js という名前のフェイスモデルを読み込んでいます。

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm.js"></script>   <!-- 顔モデル(※)の読み込み -->

42~48行目はカメラを使うための処理です。42行目の getUserMedia() メソッドでカメラを起動し、カメラの準備ができたら 46行目で映像を video 要素に流し込んでいます。

// getUserMedia によるカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({       // メディアデバイスを取得
  video: {facingMode: "user"},                          // カメラの映像を使う(スマホならインカメラ)
  audio: false                                          // マイクの音声は使わない
});
media.then((stream) => {                                // メディアデバイスが取得できたら
  video.srcObject = stream;                             // video 要素にストリームを渡す
});

51~53行目で clmtrackr で顔認識を開始しています。51行目でメインとなる tracker オブジェクトを作ります。52行目で init(pModel) でフェイスモデルを設定しています。model_pca_20_svm.js ファイルの中で pModel という名前でフェイスモデルが定義されています。53行目で、カメラの映像を流し込んでいる video 要素を渡して顔認識を開始しています。

// clmtrackr の開始
var tracker = new clm.tracker();  // tracker オブジェクトを作成
tracker.init(pModel);             // tracker を所定のフェイスモデル(※)で初期化
tracker.start(video);             // video 要素内でフェイストラッキング開始

顔の中の様々な部位を検出するのが 58行目の getCurrentPositions() メソッドです。

  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得

getCurrentPositions() メソッドで得られた値 positions は 2次元の配列です。positions[部位番号][0/1] のように添え字を指定して値を取得できます。例えば鼻先の X 座標は positions[62][0]、Y 座標は positions[62][1] です。同じようにして計 71個の部位の座標を取得することができます。

顔の部位の番号
顔の部位の番号

61 行目の draw() は clmtrackr にはじめから用意されているメソッドで、認識結果を緑色の線で canvas 要素に描画します。

  tracker.draw(canvas);                                 // canvas にトラッキング結果を描画

なお、7~21行目の <style> は、video 要素の上に canvas 要素を重ねて表示するための CSS のテクニックです。video 要素には映像を流すことはできますが、直接なにかを描くことはできません。そこで、canvas 要素を video 要素の上に重ねて置くことで、canvas 要素の上に認識結果を表示させています。これらの CSS の意味は自分で調べてみましょう。

<style>
  /* video 要素の上に canvas 要素をオーバーレイするための CSS */
  #container {              /* コンテナ用の div について */
    position: relative;     /* 座標指定を相対値指定にする */
  }
  #video {                  /* カメラ映像を流す video について */
    transform: scaleX(-1);  /* 左右反転させる */
  }
  #canvas {                 /* 描画用の canvas について */
    transform: scaleX(-1);  /* 左右反転させる */
    position: absolute;     /* 座標指定を絶対値指定にして */
    left: 0;                /* X座標を0に */
    top: 0;                 /* Y座標を0に */
  }
</style>

参考

完成品をこちらに置いてありますから、パソコン(Chrome か Firefox)または Android スマホ(Chrome)で開いてみてください。

補足

カメラを使う getUserMedia は、もし悪用されるとプライバシーが侵害される可能性があります。あるホームページにアクセスしたら映像が撮れてしまうので、裏技を使えばいろいろ個人情報を盗み出す方法もあり得ます。そのため多くのブラウザでは、https 接続(http より安全な接続)が可能なサイトでないと getUserMedia を使ってカメラで撮影できないようにしています。このサイトの記事では XAMPP(Apache)を自分のパソコンにインストールして http 接続でアプリのテストをしています。Apache で https 接続を可能にする方法もありますが、個人レベルでは簡単ではありません。そこで、もっとも簡単に https 接続が可能な環境を用意してテストするために、Netlify Drop などのホスティングサービスを利用する方法を紹介しています。

▲TOP

2. 顔にスタンプ

顔を認識して、顔の様々な部位の座標が測れました。ということは、SNOW の「顔認識スタンプ」機能が作れそうですね。さっそく作ります。

今から作るもの
今から作るもの

プログラミング

Visual Studio Code(または好みのエディタ)を立ち上げて、さきほどの face1 の index.html をもとに以下のコードを入力しましょう。<body> タグ ~ </script> タグ の部分のみを掲載します(このままコピペするだけでは動きません)。ファイルは、C:\xampp\htdocs フォルダの中に「face2」というフォルダを作って、その中に「index.html」として保存してください。コメントに★が付いている部分が新しい部分です。それぞれのコードが何をしているのか考えながら入力していきましょう。

<body>
<div id="container">  <!-- video の上に canvas をオーバーレイするための div 要素 -->
  <video id="video" width="400" height="300" autoplay></video>  <!-- カメラ映像を流す video -->
  <canvas id="canvas" width="400" height="300"></canvas>        <!-- 重ねて描画する canvas -->
</div>
<div id="dat"></div>  <!-- データ表示用 div 要素 -->

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm.js"></script>   <!-- 顔モデル(※)の読み込み -->

<script>
// もろもろの準備
var video = document.getElementById("video");           // video 要素を取得
var canvas = document.getElementById("canvas");         // canvas 要素の取得
var context = canvas.getContext("2d");                  // canvas の context の取得
var stampNose = new Image();                            // ★鼻のスタンプ画像を入れる Image オブジェクト
var stampEars = new Image();                            // ★耳のスタンプ画像を入れる Image オブジェクト
stampNose.src = "nose.png";                             // ★鼻のスタンプ画像のファイル名
stampEars.src = "ears.png";                             // ★耳のスタンプ画像のファイル名

// getUserMedia によるカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({       // メディアデバイスを取得
  video: {facingMode: "user"},                          // カメラの映像を使う(スマホならインカメラ)
  audio: false                                          // マイクの音声は使わない
});
media.then((stream) => {                                // メディアデバイスが取得できたら
  video.srcObject = stream;                             // video 要素にストリームを渡す
});

// clmtrackr の開始
var tracker = new clm.tracker();  // tracker オブジェクトを作成
tracker.init(pModel);             // tracker を所定のフェイスモデル(※)で初期化
tracker.start(video);             // video 要素内でフェイストラッキング開始

// 描画ループ
function drawLoop() {
  requestAnimationFrame(drawLoop);                      // drawLoop 関数を繰り返し実行
  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得
  context.clearRect(0, 0, canvas.width, canvas.height); // canvas をクリア
  //tracker.draw(canvas);                                 // canvas にトラッキング結果を描画
  drawStamp(positions, stampNose, 62, 2.5, 0.0, 0.0);   // ★鼻のスタンプを描画
  drawStamp(positions, stampEars, 33, 3.0, 0.0, -1.8);  // ★耳のスタンプを描画
}
drawLoop();                                             // drawLoop 関数をトリガー

// ★スタンプを描く drawStamp 関数
// (顔部品の位置データ, 画像, 基準位置, 大きさ, 横シフト, 縦シフト)
function drawStamp(pos, img, bNo, scale, hShift, vShift) {
  var eyes = pos[32][0] - pos[27][0];                   // 幅の基準として両眼の間隔を求める
  var nose = pos[62][1] - pos[33][1];                   // 高さの基準として眉間と鼻先の間隔を求める
  var wScale = eyes / img.width;                        // 両眼の間隔をもとに画像のスケールを決める
  var imgW = img.width * scale * wScale;                // 画像の幅をスケーリング
  var imgH = img.height * scale * wScale;               // 画像の高さをスケーリング
  var imgL = pos[bNo][0] - imgW / 2 + eyes * hShift;    // 画像のLeftを決める
  var imgT = pos[bNo][1] - imgH / 2 + nose * vShift;    // 画像のTopを決める
  context.drawImage(img, imgL, imgT, imgW, imgH);       // 画像を描く
}
</script>

動作確認

さきほども使った clmtrackr.js と model_pca_20_svm.js を face2 フォルダにコピーしてください(face1 フォルダなどから)。

また、このプログラムの動作には以下の 2個の画像ファイルが必要です。以下の画像をパソコンでダウンロードして(右クリックして「名前をつけて画像を保存」)、face2 フォルダの中に置いてください(index.html と同じ場所)。

パソコンで動作確認

さきほどと同じように、まずはパソコンで動作確認します。

XAMPP Control Panel で Apache を Start させてください(不明な場合はこちらを参考にしてください)。

パソコンのブラウザ(Chrome か Firefox)を開き、アドレス欄に以下のように入力します。

パソコン内蔵のカメラまたは外付けの Web カメラに自分の顔全体が入るようにしてください。すると、鼻先の位置に鼻とヒゲの画像、頭のあたりに耳の画像が表示されます。

顔認識スタンプができた
顔認識スタンプができた

スマホで動作確認

パソコンで動かしているサーバ(Apache)ではスマホでは動作確認できません。https 接続が可能なサーバやホスティングサービスにアップして動作確認しましょう。パソコンで Netlify Drop などの無料ホスティングサービスにアクセスします。例えば Netlify Drop であれば、そのトップページに「face2」フォルダをドラッグ&ドロップしてください。

Netlify Drop にアップロード
Netlify Drop にアップロード

アップロードされたら、表示されたアドレスを Android スマホの Chrome で開きます。この時、アドレスの先頭に「https://」と入力してください

この手順についてよく分からない場合はこちらを参考にしてください。

スマホは横向きにします。また、スマホでは顔認識の処理が追いつかずにモッサリした動作になりますが、あまり動かずに待っていれば顔認識スタンプが表示されます。

Android スマホの Chrome で
Android スマホの Chrome で

解説

まず 17~20行目でスタンプにする画像を読み込んでいます。画像は背景を透明にした png 形式で用意しましょう。もちろん、自分で作った画像にすればもっと楽しいでしょう。

var stampNose = new Image();                            // ★鼻のスタンプ画像を入れる Image オブジェクト
var stampEars = new Image();                            // ★耳のスタンプ画像を入れる Image オブジェクト
stampNose.src = "nose.png";                             // ★鼻のスタンプ画像のファイル名
stampEars.src = "ears.png";                             // ★耳のスタンプ画像のファイル名

49~58行目が実際にスタンプ画像を描画する関数です。

// ★スタンプを描く drawStamp 関数
// (顔部品の位置データ, 画像, 基準位置, 大きさ, 横シフト, 縦シフト)
function drawStamp(pos, img, bNo, scale, hShift, vShift) {
  var eyes = pos[32][0] - pos[27][0];                   // 幅の基準として両眼の間隔を求める
  var nose = pos[62][1] - pos[33][1];                   // 高さの基準として眉間と鼻先の間隔を求める
  var wScale = eyes / img.width;                        // 両眼の間隔をもとに画像のスケールを決める
  var imgW = img.width * scale * wScale;                // 画像の幅をスケーリング
  var imgH = img.height * scale * wScale;               // 画像の高さをスケーリング
  var imgL = pos[bNo][0] - imgW / 2 + eyes * hShift;    // 画像のLeftを決める
  var imgT = pos[bNo][1] - imgH / 2 + nose * vShift;    // 画像のTopを決める
  context.drawImage(img, imgL, imgT, imgW, imgH);       // 画像を描く
}

drawStamp 関数には、顔の部位の座標データ、スタンプ画像、描画する基準の位置(部位番号)、大きさ、横方向にずらす量、縦方向にずらす量の、6個の引数を渡します。

50行目で両眼の間の距離を求めて、それを「横方向の 1 の大きさ」としています。51行目では眉間と鼻先の間の距離を求めて、それを「縦方向の 1 の大きさ」としています。

52行目は 横方向の 1 の大きさをもとに画像の幅をスケーリングするための値 wScale を求めています。この wScale と、関数に渡された scale の値をもとに、53・54行目で画像の大きさを調整しています。

55・56行目では、画像を表示する横方向の位置と縦方向の位置を決めています。

clmtrackr で様々な顔の部位の座標が分かるので、あとはどのように描画しても自由です。あくまで上記の手順は「一例」と思ってください。

あとは描画ループの中で以下のように、鼻のスタンプ画像を鼻先の位置を基準に、耳のスタンプ画像を眉間の位置を基準にして描画させています。

  drawStamp(positions, stampNose, 62, 2.5, 0.0, 0.0);   // ★鼻のスタンプを描画
  drawStamp(positions, stampEars, 33, 3.0, 0.0, -1.8);  // ★耳のスタンプを描画

参考

完成品をこちらに置いてありますから、パソコン(Chrome か Firefox)または Android スマホ(Chrome)で開いてみてください。

▲TOP

3. 表情認識

人の表情は目や口や眉毛の位置や形から推定することができます。clmtrackr 自体には表情を認識する機能は無いのですが、作者のサイトには表情を認識するサンプルがあります。これを参考にして、表情認識するプログラムを作ってみます。

今から作るもの
今から作るもの

プログラミング

Visual Studio Code(または好みのエディタ)を立ち上げて、最初の face1 の index.html をもとに以下のコードを入力しましょう。<body> タグ ~ </script> タグ の部分のみを掲載します(このままコピペするだけでは動きません)。ファイルは、C:\xampp\htdocs フォルダの中に「face3」というフォルダを作って、「index.html」として保存してください。それぞれのコードが何をしているのか考えながら入力していきましょう。新しいのは★印の部分です。

<body>
<div id="container">  <!-- video の上に canvas をオーバーレイするための div 要素 -->
  <video id="video" width="400" height="300" autoplay></video>  <!-- カメラ映像を流す video -->
  <canvas id="canvas" width="400" height="300"></canvas>        <!-- 重ねて描画する canvas -->
</div>
<div id="dat"></div>  <!-- データ表示用 div 要素 -->

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm_emotion.js"></script>   <!-- ★顔モデル(※1)の読み込み -->
<script src="emotionClassifier.js"></script>  <!-- ★感情を分類する外部関数の読み込み -->
<script src="emotionModel.js"></script>       <!-- ★感情モデル(※2)の読み込み -->

<script>
// もろもろの準備
var video = document.getElementById("video");           // video 要素を取得
var canvas = document.getElementById("canvas");         // canvas 要素の取得
var context = canvas.getContext("2d");                  // canvas の context の取得

// getUserMedia によるカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({       // メディアデバイスを取得
  video: {facingMode: "user"},                          // カメラの映像を使う(スマホならインカメラ)
  audio: false                                          // マイクの音声は使わない
});
media.then((stream) => {                                // メディアデバイスが取得できたら
  video.srcObject = stream;                             // video 要素にストリームを渡す
});

// clmtrackr の開始
var tracker = new clm.tracker();  // tracker オブジェクトを作成
tracker.init(pModel);             // tracker を所定のフェイスモデル(※1)で初期化
tracker.start(video);             // video 要素内でフェイストラッキング開始

// 感情分類の開始
var classifier = new emotionClassifier();               // ★emotionClassifier オブジェクトを作成
classifier.init(emotionModel);                          // ★classifier を所定の感情モデル(※2)で初期化

// 描画ループ
function drawLoop() {
  requestAnimationFrame(drawLoop);                      // drawLoop 関数を繰り返し実行
  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得
  var parameters = tracker.getCurrentParameters();      // ★現在の顔のパラメータを取得
  var emotion = classifier.meanPredict(parameters);     // ★そのパラメータから感情を推定して emotion に結果を入れる
  showEmotionData(emotion);                             // ★感情データを表示
  context.clearRect(0, 0, canvas.width, canvas.height); // canvas をクリア
  tracker.draw(canvas);                                 // canvas にトラッキング結果を描画
}
drawLoop();                                             // drawLoop 関数をトリガー

// ★感情データの表示
function showEmotionData(emo) {
  var str ="";                                          // データの文字列を入れる変数
  for(var i = 0; i < emo.length; i++) {                 // 全ての感情(6種類)について
    str += emo[i].emotion + ": "                        // 感情名
         + emo[i].value.toFixed(1) + "<br>";            // 感情の程度(小数第一位まで)
  }
  var dat = document.getElementById("dat");             // データ表示用div要素の取得
  dat.innerHTML = str;                                  // データ文字列の表示
}
</script>

動作確認

さきほども使った clmtrackr.js を face3 フォルダにコピーしてください(face1 フォルダなどから)。

また、このプログラムの動作には以下の 3個の js ファイルが必要です。以下のリンクを右クリックして「名前をつけてリンク先を保存」で、face3 フォルダの中に置いてください(index.html と同じ場所)。

パソコンで動作確認

さきほどと同じように、まずはパソコンで動作確認します。

XAMPP Control Panel で Apache を Start させてください(不明な場合はこちらを参考にしてください)。

パソコンのブラウザ(Chrome か Firefox)を開き、アドレス欄に以下のように入力します。

パソコン内蔵のカメラまたは外付けの Web カメラに自分の顔全体が入るようにしてください。顔が認識されたら、笑顔、驚いた顔、泣き顔など、大げさに表情を作ってみてください。angry(怒り)、disgusted(嫌悪)、fear(恐怖)、sad(悲しみ)、surprised(驚き)、happy(喜び)という 6個の表情(感情)について、それぞれの程度の強さが 0~1 で表示されます。

表情認識の様子
表情認識の様子

スマホで動作確認

パソコンで動かしているサーバ(Apache)ではスマホでは動作確認できません。https 接続が可能なサーバやホスティングサービスにアップして動作確認しましょう。パソコンで Netlify Drop などの無料ホスティングサービスにアクセスします。例えば Netlify Drop であれば、そのトップページに「face3」フォルダをドラッグ&ドロップしてください。

Netlify Drop にアップロード
Netlify Drop にアップロード

アップロードされたら、表示されたアドレスを Android スマホの Chrome で開きます。この時、アドレスの先頭に「https://」と入力してください

この手順についてよく分からない場合はこちらを参考にしてください。

スマホは横向きにします。また、スマホでは顔認識の処理が追いつかずにモッサリした動作になりますが、あまり動かずに待っていれば表情の分析が始まります。

解説

9行目の clmtrackr.js の読み込みはこれまでと変わりません。10行目で、これまでとは違う顔モデルを読み込んでいます。作者によれば、感情の推定用に眉毛の動きをよりよく認識できるモデルなのだそうです。また、11行目で読み込んでいる emotionClassifier.js が表情を識別するためのプログラムを含んだファイルです。このプログラムの中から、12行目で指定した表情パターンのファイル emotionModel.js(6個の表情の顔のパターンが入っている)を参照します。

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm_emotion.js"></script>   <!-- ★顔モデル(※1)の読み込み -->
<script src="emotionClassifier.js"></script>  <!-- ★感情を分類する外部関数の読み込み -->
<script src="emotionModel.js"></script>       <!-- ★感情モデル(※2)の読み込み -->

次に、35・36行目で感情の識別を開始しています。35行目で emotionClassifier.js 内にある emotionClassifier オブジェクトのインスタンスを作成しています。さらに、emotionModel.js 内にある表情パターン emotionModel を渡して初期設定しています。

// 感情分類の開始
var classifier = new emotionClassifier();               // ★emotionClassifier オブジェクトを作成
classifier.init(emotionModel);                          // ★classifier を所定の感情モデル(※2)で初期化

表情の識別自体は42・43行目で行っています。42行目で getCurrentParameters で表情を判別するための値(パラメータ)を取り出しています。43行目で meanPredict() 関数にそのパラメータを渡すことで、6個の表情それぞれについてどの程度当てはまるかの値を得て、変数 emotion の中に入れています。

  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得
  var parameters = tracker.getCurrentParameters();      // ★現在の顔のパラメータを取得
  var emotion = classifier.meanPredict(parameters);     // ★そのパラメータから感情を推定して emotion に結果を入れる

表情の値 emotion は配列変数で、以下の表のような順に値が入っています。

meanPredict() で得られる表情
index emotion 日本語
0 anger 怒り
1 disgusted 嫌悪
2 fear 恐れ
3 sad 悲しみ
4 surprised 驚き
5 happy 喜び

.emotion というプロパティに表情の名前(anger / disgusted / fear / sad / surprised / happy)が入っています。また、.value というプロパティの中にそれぞれの表情の強さが 0~1 の値で入っています。51行目以降の drawEmotionData() 関数でこれらを表示させています。

参考

完成品をこちらに置いてありますから、パソコン(Chrome か Firefox)または Android スマホ(Chrome)で開いてみてください。

▲TOP

4. 表情認識スタンプ

最後に、表情認識にもとづいて、その表情に適したスタンプを描画するプログラムを作ります。

今から作るもの
今から作るもの

プログラミング

Visual Studio Code(または好みのエディタ)を立ち上げて、最初の face3 と face2 の index.html をもとに以下のコードを入力しましょう。<body> タグ ~ </script> タグ の部分のみを掲載します(このままコピペするだけでは動きません)。ファイルは、C:\xampp\htdocs フォルダの中に「face4」というフォルダを作って、「index.html」として保存してください。それぞれのコードが何をしているのか考えながら入力していきましょう。新しいのは★印の部分です。

<body>
<div id="container">  <!-- video の上に canvas をオーバーレイするための div 要素 -->
  <video id="video" width="400" height="300" autoplay></video>  <!-- カメラ映像を流す video -->
  <canvas id="canvas" width="400" height="300"></canvas>        <!-- 重ねて描画する canvas -->
</div>
<div id="dat"></div>  <!-- データ表示用 div 要素 -->

<!-- clmtrackr 関連ファイルの読み込み -->
<script src="clmtrackr.js"></script>          <!-- clmtrackr のメインライブラリの読み込み -->
<script src="model_pca_20_svm_emotion.js"></script>   <!-- 顔モデル(※1)の読み込み -->
<script src="emotionClassifier.js"></script>  <!-- 感情を分類する外部関数の読み込み -->
<script src="emotionModel.js"></script>       <!-- 感情モデル(※2)の読み込み -->

<script>
// もろもろの準備
var video = document.getElementById("video");           // video 要素を取得
var canvas = document.getElementById("canvas");         // canvas 要素の取得
var context = canvas.getContext("2d");                  // canvas の context の取得
var stampNose = new Image();                            // 鼻のスタンプ画像を入れる Image オブジェクト
var stampEars = new Image();                            // 耳のスタンプ画像を入れる Image オブジェクト
var stampTear = new Image();                            // ★涙のスタンプ画像を入れる Image オブジェクト
var stampSurp = new Image();                            // ★驚きのスタンプ画像を入れる Image オブジェクト
var stampEyes = new Image();                            // ★ハートのスタンプ画像を入れる Image オブジェクト
stampNose.src = "nose.png";                             // 鼻のスタンプ画像のファイル名
stampEars.src = "ears.png";                             // 耳のスタンプ画像のファイル名
stampTear.src = "tear.png";                             // ★涙のスタンプ画像のファイル名
stampSurp.src = "surp.png";                             // ★驚きのスタンプ画像のファイル名
stampEyes.src = "eyes.png";                             // ★ハートのスタンプ画像のファイル名

// getUserMedia によるカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({       // メディアデバイスを取得
  video: {facingMode: "user"},                          // カメラの映像を使う(スマホならインカメラ)
  audio: false                                          // マイクの音声は使わない
});
media.then((stream) => {                                // メディアデバイスが取得できたら
  video.srcObject = stream;                             // video 要素にストリームを渡す
});

// clmtrackr の開始
var tracker = new clm.tracker();  // tracker オブジェクトを作成
tracker.init(pModel);             // tracker を所定のフェイスモデル(※1)で初期化
tracker.start(video);             // video 要素内でフェイストラッキング開始

// 感情分類の開始
var classifier = new emotionClassifier();               // emotionClassifier オブジェクトを作成
classifier.init(emotionModel);                          // classifier を所定の感情モデル(※2)で初期化

// 描画ループ
function drawLoop() {
  requestAnimationFrame(drawLoop);                      // drawLoop 関数を繰り返し実行
  var positions = tracker.getCurrentPosition();         // 顔部品の現在位置の取得
  var parameters = tracker.getCurrentParameters();      // 現在の顔のパラメータを取得
  var emotion = classifier.meanPredict(parameters);     // そのパラメータから感情を推定して emotion に結果を入れる
  context.clearRect(0, 0, canvas.width, canvas.height); // canvas をクリア
  //tracker.draw(canvas);                                 // canvas にトラッキング結果を描画
  drawStamp(positions, stampNose, 62, 2.5, 0.0, 0.0);   // 鼻のスタンプを描画
  drawStamp(positions, stampEars, 33, 3.0, 0.0, -1.8);  // 耳のスタンプを描画
  if(emotion[3].value > 0.4) {                          // ★感情 sad の値が一定値より大きければ
    drawStamp(positions, stampTear, 23, 0.4, 0.0, 0.8); // ★涙のスタンプを描画(右目尻)
    drawStamp(positions, stampTear, 28, 0.4, 0.0, 0.8); // ★涙のスタンプを描画(左目尻)
  }
  if(emotion[4].value > 0.8) {                          // ★感情 surprised の値が一定値より大きければ
    drawStamp(positions, stampSurp, 14, 1.0, 0.7, 0.0); // ★驚きのスタンプを描画(顔の左)
  }
  if(emotion[5].value > 0.4) {                          // ★感情 happy の値が一定値より大きければ
    drawStamp(positions, stampEyes, 27, 0.6, 0.0, 0.0); // ★ハートのスタンプを描画(右目)
    drawStamp(positions, stampEyes, 32, 0.6, 0.0, 0.0); // ★ハートのスタンプを描画(左目)
  }
}
drawLoop();                                             // drawLoop 関数をトリガー

// スタンプを描く drawStamp 関数
// (顔部品の位置データ, 画像, 基準位置, 大きさ, 横シフト, 縦シフト)
function drawStamp(pos, img, bNo, scale, hShift, vShift) {
  var eyes = pos[32][0] - pos[27][0];                   // 幅の基準として両眼の間隔を求める
  var nose = pos[62][1] - pos[33][1];                   // 高さの基準として眉間と鼻先の間隔を求める
  var wScale = eyes / img.width;                        // 両眼の間隔をもとに画像のスケールを決める
  var imgW = img.width * scale * wScale;                // 画像の幅をスケーリング
  var imgH = img.height * scale * wScale;               // 画像の高さをスケーリング
  var imgL = pos[bNo][0] - imgW / 2 + eyes * hShift;    // 画像のLeftを決める
  var imgT = pos[bNo][1] - imgH / 2 + nose * vShift;    // 画像のTopを決める
  context.drawImage(img, imgL, imgT, imgW, imgH);       // 画像を描く
}
</script>

動作確認

さきほども使った clmtrackr.js を face4 フォルダにコピーしてください(face1 フォルダなどから)。

また、このプログラムの動作にはさきほどと同じ以下の 3個の js ファイルが必要です。以下のリンクを右クリックして「名前をつけてリンク先を保存」で、face3 フォルダの中に置いてください(index.html と同じ場所)。

さらに、以下の 5個の画像ファイルが必要です。以下の画像をパソコンでダウンロードして(右クリックして「名前をつけて画像を保存」)、face4 フォルダの中に置いてください(index.html と同じ場所)。

パソコンで動作確認

さきほどと同じように、まずはパソコンで動作確認します。

XAMPP Control Panel で Apache を Start させてください(不明な場合はこちらを参考にしてください)。

パソコンのブラウザ(Chrome か Firefox)を開き、アドレス欄に以下のように入力します。

パソコン内蔵のカメラまたは外付けの Web カメラに自分の顔全体が入るようにしてください。顔が認識されたら、笑顔、驚いた顔、泣き顔など、大げさに表情を作ってみてください。

驚いた顔をすると
驚いた顔をすると
悲しい顔をすると
悲しい顔をすると
喜んだ顔をすると
喜んだ顔をすると

スマホで動作確認

パソコンで動かしているサーバ(Apache)ではスマホでは動作確認できません。https 接続が可能なサーバやホスティングサービスにアップして動作確認しましょう。パソコンで Netlify Drop などの無料ホスティングサービスにアクセスします。例えば Netlify Drop であれば、そのトップページに「face4」フォルダをドラッグ&ドロップしてください。

Netlify Drop にアップロード
Netlify Drop にアップロード

アップロードされたら、表示されたアドレスを Android スマホの Chrome で開きます。この時、アドレスの先頭に「https://」と入力してください

この手順についてよく分からない場合はこちらを参考にしてください。

スマホは横向きにします。また、スマホでは顔認識の処理が追いつかずにモッサリした動作になりますが、あまり動かずに待っていれば表情に応じたスタンプが描画されます。

解説

表情に応じてスタンプを描画しているのは 58~68行目です。

  if(emotion[3].value > 0.4) {                          // ★感情 sad の値が一定値より大きければ
    drawStamp(positions, stampTear, 23, 0.4, 0.0, 0.8); // ★涙のスタンプを描画(右目尻)
    drawStamp(positions, stampTear, 28, 0.4, 0.0, 0.8); // ★涙のスタンプを描画(左目尻)
  }
  if(emotion[4].value > 0.8) {                          // ★感情 surprised の値が一定値より大きければ
    drawStamp(positions, stampSurp, 14, 1.0, 0.7, 0.0); // ★驚きのスタンプを描画(顔の左)
  }
  if(emotion[5].value > 0.4) {                          // ★感情 happy の値が一定値より大きければ
    drawStamp(positions, stampEyes, 27, 0.6, 0.0, 0.0); // ★ハートのスタンプを描画(右目)
    drawStamp(positions, stampEyes, 32, 0.6, 0.0, 0.0); // ★ハートのスタンプを描画(左目)
  }

あとは face1~face3 で紹介したことだけで、特別なことはしていません。

参考

完成品をこちらに置いてありますから、パソコン(Chrome か Firefox)または Android スマホ(Chrome)で開いてみてください。

▲TOP

まとめ

顔認識アプリ、いかがでしたか?

顔認識のために必要な顔パターンの学習やモデルの構築などはすでに終わっていて、あとは clmtrackr.js と顔のモデルを読み込むだけで、顔や表情の認識ができました。顔の様々な部位の座標や表情が数値で得られますから、あとはアイデア次第でいろいろなことができそうですね。なお、clmtrackr が認識できる顔は「ひとり」だけです。

clmtrackr は今回紹介した顔と表情の認識以外にも、顔の変形(deformation)や、他人の顔を貼り付けるマスキング(masking)ということもできます。そこまでできると、まさに SNOW アプリですね。作者のサイトにサンプルがありますからぜひ体験してみましょう(変形のサンプルマスキングのサンプル)。また、これらのサンプルのコードは、はじめにダウンロードした clmtrackr の zip ファイルの中にあります。少しだけややこしいのでこのページでは紹介しませんでしたが、ぜひチャレンジしてみてください。

さらに clmtrackr 以外にも JavaScript で顔などを認識できるライブラリとして以下のようなものがあります。

上記の中には、顔だけでなく、手や特定の色などを検出できるものがあります。複数人の顔を検出できるものもあります。しかし、顔の詳細な部位や表情まで簡単に認識できるのが clmtrackr の良いところです。

また、JavaScript 専用でなくても、近ごろはクラウドサービス(Web API)として顔や表情認識機能を提供しているものがどんどん増えています。Web API ですから、様々な言語から利用することができます。最新の人工知能の技術を使ったサービスで、これからはこのようなクラウドの機能を自分のアプリに組み込むことがどんどん増えるでしょう。いずれも無料で試すことができますから、機会があれば試してみましょう。

「○○認識」という技術分野は今後ますます進歩するでしょう。技術としては難しそうに感じますが、ありがたいことに、使うのはどんどん簡単になってきています。恐れずにどんどん使ってみて、便利で楽しい応用を考えてみましょう!

補足

上で作ったコードはすべてこちら(GitHub)に置いてありますから、参考にしてください。

▲TOP