opencv4nodejs, face-recognition.js で顔認識のプログラムを開発してみよう

**対象読者**

- NodeJSの言語を開発しOpenCVのライブラリを使いたい人です。
- 顔認識というプログラムを作りたい人です。

**目標**

- 基本的な顔認識のプログラムを開発する方法がわかって開発するようになります。
- 機械学習（Machine Learning）のコンピュータビジョンの基本的な結構とイメージの処理の部分がわかります。

**筆者**

- コンピュータビジョン初心者。最近イメージの処理のテクノロジーが素晴らしいです。
- まだまだ分からないことが多いので、間違いの指摘などよろしくお願いします。

##1.はじめに##

まず、プログラムの稼働を説明させていただきます。そのプログラムは顔を認識することができるというプログラムです。カメラを見るときにビデオにその人の顔と名前を表示します。カメラを見る人がたくさんいるも大丈夫です。新顔をカメラを見る場合はその人の顔のイメージを集めて次回はその人の人を発見することができます。
以上の性能を記述してからプログラムの設計を見ましょう。
![Screenshot from 2019-02-17 15-53-11.png](https://qiita-image-store.s3.amazonaws.com/0/344186/675535c3-39b3-7c02-3beb-40eff08889e3.png)
以上のダイヤグラムを見るとプログラムの動く方法がわかると思います。プログラムは主な機能が2つあります。顔のデータがある場合は認識することができて新顔の場合はユーザーが名前をインプットします。それからウエブカメラを呼び出してフレームを取得してイメージを処理します。ビデオというのはイメージがいっぱい接続します。だから私たちはビデオを処理のかわりにイメージ（ビデオのフレーム）で処理しています。

##2.前提知識##

プログラムの動く方法がわかった上で必要な知識をまとめます。

機械学習についてトレーニングのグラフを見て見ましょう。
![Screenshot from 2019-02-17 15-33-22.png](https://qiita-image-store.s3.amazonaws.com/0/344186/15a251dd-8f31-35c8-9542-6ce90e9b8e8e.png)
まずはトレーニングのステプです。誰かの顔のイメージをたくさん集めてトレニングしてモデルファイルを作ります。各人の顔は、画像処理技術の抽出の特徴的な方法に基づく特徴ベクトルによって描かれます。それでトレーニングファイルが作られます。そのファイルで顔認識するときにそれぞれの人の顔のデータを取得することができます。

言語についてNodeJSとTypeScriptの基本的な使い方がわかることは大事です。それから[opencv4nodejs](https://www.npmjs.com/package/opencv4nodejs)のライブラリをインストールしてドキュメントを読むすることが必要です。そしてライブラリの中に例がありますから使い方をわかりやすいになると思います。次は[face-recognition](https://www.npmjs.com/package/face-recognition)のライブラリを調べたほうがいいと思います。そのライブラリのドキュメントは本当に役に立ちます。それはライブラリの関数でフレームから顔の認識する方法があり精度レベールもあります。例えば以下の説明を見えます。
![Screenshot from 2019-02-17 15-15-05.png](https://qiita-image-store.s3.amazonaws.com/0/344186/77098cb4-e2d5-84ba-5348-41ba78e7d88c.png)
それぞれのフレームに対して[predict]の関数を使ってモデルファイルのそれぞれの顔を比較して似ているレベールが取得することができます。誰の顔が最もビデオに似ているか知りたい場合は[predictBest]の関数を使います。説明もあるし例もあるので難しくないと思います。

##3.コードを読んでいく##
###3.1. 顔を認識する###
この機能をもっと小さな関数に分割します。 このように分割すると、コードがよりきれいになり、保守しやすくなります。
最初は顔を検出する関数です。イメージからopencvのhaar_cascadeという分類のアルゴリズムで顔検出します。出す結果は顔の周りに描かれた長方形が識別されます。以下のイメージはコードです。
![Screenshot from 2019-02-17 15-47-14.png](https://qiita-image-store.s3.amazonaws.com/0/344186/ad8296f9-8a80-6cbd-98c9-663e34e0e22e.png)
それぞれ検出した顔に応じてフレームに長方形を描きます。その関数は簡単です。ライブラリの2つの関数を使って長方形を描く関数とテキストを書く関数です。
![Screenshot from 2019-02-17 16-18-53.png](https://qiita-image-store.s3.amazonaws.com/0/344186/500e1471-40d3-4df6-3c5c-f07c282a4f34.png)
次はウエブカメラを呼び出します。その関数は[opencv4nodejs](https://www.npmjs.com/package/opencv4nodejs)のライブラリのドキュメントがありますから簡単になります。ちょっとコードに説明します。ここでウエブカメラの各フレームを読み取るためのループを作成し、各フレームでonFrame関数を処理します。 キーが押されると、ループはクリアされて停止し、プログラムは終了します。
![Screenshot from 2019-02-17 16-06-25.png](https://qiita-image-store.s3.amazonaws.com/0/344186/6f3449e4-13a0-c604-6f84-9e806fe36c6c.png)
それからフレームの処理のステプです。ちょっと長いのでもっと小さに分割させていただきます。
![Screenshot from 2019-02-17 16-14-40.png](https://qiita-image-store.s3.amazonaws.com/0/344186/8c48091c-e485-580a-b270-14e9c30dd249.png)
ほとんどはわかりやすいです。ちょっと説明してあげます。ここにthresholdの変数は最低の似ているレベールです。顔認識するときに[face-recognition](https://www.npmjs.com/package/face-recognition)のライブラリで２つの結果を取得します。クラスネーム（人の名前）を応じて間違っているレベールがあります。間違っているレベールが大きいほど、実際の顔とデータベース内の顔との間の差が大きくなります。それが最大許容値を超えると、それは受け入れられず、新しい顔として識別されます。ほかにはvarsTimeIn、varsTimeOutとflagsDetectの変数はタイムスタンプ変数と顔検出のフラグ変数です。顔検出する場合は長方形を描きます。その顔がカメラを見る時間を保存されています。
![Screenshot from 2019-02-17 16-37-27.png](https://qiita-image-store.s3.amazonaws.com/0/344186/390c1b13-3875-03c7-97ec-f05886fb0608.png)
反対の場合10秒あとでその人がカメラを見ないとスタート時間とエンド時間の結果がログされています。時々、外部の要素によってプログラムが私たちの顔をしばらく認識できなくなるでしょう。そしてどんな理由でカメラを短い時間で見なくてもいいです。以上のコードでその問題を解決しました。
###3.2. 新顔を保存する###
知らない人がカメラを見る場合は以上に説明したthresholdの変数でその人の顔の下に[unknown]というテキストがあります。だからまずはトレーニングしなければなりません。
トレーニングのために3つのファイルを準備します。これはNodeJSでファイルを処理するようなファイルです。
最初はパスをチェックするファイル。NodeJSの[path]と[filesystem(fs)]を使ってシステムにデータフォルダが存在することを確認します。ここに「データ」と「モデル」というフォルダをチェックしてなっかた場合は新しいフォルダを作成します。
![Screenshot from 2019-02-23 18-47-12.png](https://qiita-image-store.s3.amazonaws.com/0/344186/a4505943-5ff8-ecc0-0f8f-7f144c191133.png)
次は新規なイメージを作成するファイルであります。人の名前や索引やフレームから新規な顔のイメージ作られます。
![Screenshot from 2019-02-23 18-56-32.png](https://qiita-image-store.s3.amazonaws.com/0/344186/f3b313b3-ded4-40e9-3703-162ec1138271.png)
あとはトレーニングファイルです。以上の方法で作ったイメージを利用して新顔をトレーニングします。簡単に言えば、それはこの新しい顔のデータをファイルモデルに追加することです。以下のイメージでちょっと長いですがかんたんですぐに説明します。最初は顔のイメージのフォルダに新人のイメージを全部検索して[face-recognition](https://www.npmjs.com/package/face-recognition)のライブラリの「addFaces」という関数を利用して新顔のデータを含むJSONを作られてモデルファイルに追加します。
![Screenshot from 2019-02-23 19-03-05.png](https://qiita-image-store.s3.amazonaws.com/0/344186/02396664-c1d0-3672-53d3-d705aad429b1.png)
以下の3つのファイルがあったら次は主なトレーニングの機能を開発しています。
フレームを取得する方法は顔認識の機能にほとんど同じですがちょっと変更します。はじめに「grabFramesTraining」という関数を作ります。
![Screenshot from 2019-02-23 19-16-28.png](https://qiita-image-store.s3.amazonaws.com/0/344186/f06dd51e-d10a-1fde-1ef7-e85b202520c4.png)
ここで顔認識の部分の「grabFrames」の関数とちょっと違います。関数のインプットは新人の名前を追加することが大切です。キーボードのキーを押すと現在のフレームに顔があるかどうか確認してある場合はそのフレームを保存してない場合は保存しません。ここでトレーニングのため保存するイメージの数は10ですが実際、必要な画像の数は非常に多くなります。これは、画像の数が多いほど、データが多くなり、識別の精度が高くなるためです。10枚の写真がある場合は、上記で作成したトレーニングファイルからトレーニングを実行します。
次に「runVideoNewFaceTraining」を作成します。
![Screenshot from 2019-02-23 21-10-43.png](https://qiita-image-store.s3.amazonaws.com/0/344186/b3379969-b3f8-3c7c-117a-51abcbc28e22.png)
ほとんど以上のコードは顔認識の部分と同じです。少し違うところは「check_face」という変数が出現します。その変数はなんのフレームを保存するときにそのフレームに顔が検出するかどうか確認する変数です。他にはユーザーに質問を出るためにNodeJSの[readline](https://nodejs.org/api/readline.html)を利用します。
###3.3. 主なプログラムを完成する###
この部分はNodeJSの[readline](https://nodejs.org/api/readline.html)を利用して、以上の2つの機能を呼び出します。コードは以下のイメージのようにします。
![Screenshot from 2019-02-23 21-54-08.png](https://qiita-image-store.s3.amazonaws.com/0/344186/82354c86-792b-e0bc-e59d-00709ffee369.png)
これまで全部顔認識のプログラムを完了しました。
##4.デモする##
プログラム実行時の画像は以下のとおりです。
###プログラムを実行する画面###
![Screenshot from 2019-02-23 22-12-37.png](https://qiita-image-store.s3.amazonaws.com/0/344186/359d35aa-4df5-e358-b0eb-cd50eed63a33.png)
###新顔をトレーニングをする画面###
![Screenshot from 2019-02-23 22-12-50.png](https://qiita-image-store.s3.amazonaws.com/0/344186/86330719-f1d0-25e7-2bd5-62e23d25e6cc.png)
###完了する画面###
![Screenshot from 2019-02-23 22-13-28.png](https://qiita-image-store.s3.amazonaws.com/0/344186/d80b2e16-6c63-6ca7-2e3c-8f32c664ea3e.png)
###顔認識を実行する画面###
![Screenshot from 2019-02-23 22-13-55.png](https://qiita-image-store.s3.amazonaws.com/0/344186/ecffc61f-c54f-4355-32fb-819000104bcc.png)
###二人（ここで私とスマートフォンからのイメージ）がカメラを見る画面###
![Screenshot from 2019-02-23 22-22-03.png](https://qiita-image-store.s3.amazonaws.com/0/344186/fbae311e-141c-0001-65e7-b9cc9b0b0fa5.png)
###続いてhowardさんが出て私もカメラを見てhowardさんのタイミングをログする画面###
![Screenshot from 2019-02-23 22-22-50.png](https://qiita-image-store.s3.amazonaws.com/0/344186/085fc8a6-c025-ee25-c7b3-4938abb6b42a.png)
###私も出てプログラムを停止して二人の時間をタイミングをログする画面###
![Screenshot from 2019-02-23 22-23-26.png](https://qiita-image-store.s3.amazonaws.com/0/344186/3d8f335e-74f6-b5ed-688b-adfc4a843500.png)
##最後に##
最後まで私たちは簡単な顔認識プログラムを作りました。 読んでいただきありがとうございます、あなたが理解できない何かがあれば、私にメールで連絡してください。ソースコードの詳細は以下のGitHubのリポジトリで見てください。
Email: hungph.dev.ict@gmail.com
Github: https://github.com/hungph-dev-ict/face_detect_app