「OpenCV  3.3.0 Face Recognition with OpenCV」への補足記事  

OpenCVには顔照合のモジュールが提供されています。

- OpenCV  3.3.0 [Face Recognition with OpenCV] 
(https://docs.opencv.org/3.3.0/da/d60/tutorial_face_main.html)

顔関係のモジュールは、cv::face という名前空間にあります。
- [cv::face Namespace Reference](https://docs.opencv.org/master/d4/d48/namespacecv_1_1face.html)



この顔照合のモジュールについては、既に以下の記事が書かれています。

- qiita [OpenCVを使って誰の顔なのかを推定する（Eigenface, Fisherface, LBPH）](https://qiita.com/hitomatagi/items/8f2f37646179aca68649)


そこで、じゃっかん異なる視点で、その記事を補おうと思います。

### ソースコードのありか
https://github.com/opencv/opencv_contrib
をgit でとりよせていると、既に以下のディレクトリに顔照合のソースコードも含まれています。

```
$ pwd
(ソースコードのダウンロード先)/opencv_contrib/modules/face/samples
$ ls
CMakeLists.txt            facerec_save_load.cpp
etc                       facerec_video.cpp
facemark_demo_aam.cpp     sampleDetectLandmarks.cpp
facemark_demo_lbf.cpp     sampleDetectLandmarksvideo.cpp
facemark_lbf_fitting.cpp  sample_config_file.xml
facerec_demo.cpp          sample_face_swapping.cpp
facerec_eigenfaces.cpp    sample_train_landmark_detector.cpp
facerec_fisherfaces.cpp   sample_train_landmark_detector2.cpp
facerec_lbph.cpp          samplewriteconfigfile.cpp
$ 

```


### Install
次のようにして、それらのデモプログラムを動かすことができます。

```
$ mkdir build
$ cd build
$ cmake ..
$ make -j5
$ ls
CMakeCache.txt  cmake_install.cmake  facerec_fisherfaces
CMakeFiles      facerec_demo         facerec_lbph
Makefile        facerec_eigenfaces   facerec_video
$
```

この結果facerec_* のパターンのファイル名のプログラムが生成される。

### Usage

これらのファイルの使い方は次のとおり。

次の３つのプログラムは
OpenCV  3.3.0 [Face Recognition with OpenCV] 
(https://docs.opencv.org/3.3.0/da/d60/tutorial_face_main.html)
にあるソースコードとほぼ同じものです。それぞれの特徴量がどのようなものであるのかを示す画像を生成するものです。
<csv.ext>には顔画像のファイル名と顔のIDの数値とが格納されたCSVファイルを指定します。

```
$ ./facerec_eigenfaces 
usage: ./facerec_eigenfaces <csv.ext> <output_folder> 
$ ./facerec_fisherfaces 
usage: ./facerec_fisherfaces <csv.ext> <output_folder> 
$ ./facerec_lbph
usage: ./facerec_lbph <csv.ext>
```

次のディレクトリは、
```
$ ./facerec_eigenfaces att.csv output_eigen
$ ls output_eigen
eigenface_0.png                   eigenface_reconstruction_175.png
eigenface_1.png                   eigenface_reconstruction_190.png
eigenface_2.png                   eigenface_reconstruction_205.png
eigenface_3.png                   eigenface_reconstruction_220.png
eigenface_4.png                   eigenface_reconstruction_235.png
eigenface_5.png                   eigenface_reconstruction_25.png
eigenface_6.png                   eigenface_reconstruction_250.png
eigenface_7.png                   eigenface_reconstruction_265.png
eigenface_8.png                   eigenface_reconstruction_280.png
eigenface_9.png                   eigenface_reconstruction_295.png
eigenface_reconstruction_10.png   eigenface_reconstruction_40.png
eigenface_reconstruction_100.png  eigenface_reconstruction_55.png
eigenface_reconstruction_115.png  eigenface_reconstruction_70.png
eigenface_reconstruction_130.png  eigenface_reconstruction_85.png
eigenface_reconstruction_145.png  mean.png
eigenface_reconstruction_160.png
$
```

opencv_contrib/modules/face/samples にある以下の３つのコードは

facerec_eigenfaces.cpp
facerec_fisherfaces.cpp
facerec_lbph.cpp

上記の OpenCV  3.3.0 [Face Recognition with OpenCV] (https://docs.opencv.org/3.3.0/da/d60/tutorial_face_main.html)　にあるC++のコードとほとんど同じです。一部、次のような違いがあります。

```
<     Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
---
>     Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
```



照合プログラムのmainは次のとおりです。

### Usage 照合プログラム

```
$ ./facerec_demo
Usage: ./facerec_demo <csv> [arg2]
	<csv> - path to config file in CSV format
	arg2 - if the 2nd argument is provided (with any value) the advanced stuff is run and shown to console.
The CSV config file consists of the following lines:
<path>;<label>[;<comment>]
	<path> - file, dir or wildcard path
	<label> - non-negative integer person label
	<comment> - optional comment string (e.g. person name)
```

このデモの中では、
- 顔照合の学習用の顔画像のファイル名と顔ラベル（非負の整数）を受け取り、
- 顔照合の訓練をして、
- その訓練結果を利用してを１枚のテスト画像に判定させています。

facerec_demoの実行後に、face-rec-model.txtが生成されています。
これには、固有顔のデータと登録済みの画像に対するラベルデータが保存されています。

```
   labels: !!opencv-matrix
      rows: 1
      cols: 399
      dt: i
      data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
          2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
          4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6,
          6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8,
          8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10,
          10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
          11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13,
          13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15,
          15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
          16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18,
          18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19,
          19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
          21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
          23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24,
          24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26,
          26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27,
          27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29,
          29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31,
          31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32,
          32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34,
          34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
          35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37,
          37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39,
          39, 39, 39, 39, 39, 39, 39, 39 ]
```

次の行は、fn_csvのファイル名csvファイルをコマンドラインから受け取り、cv::Mat型を格納したｖectorであるimages、整数のラベルを格納した labels,整数のラベルに付随した情報をmap型で格納したlabelsInfoに返します。

```
        read_csv(fn_csv, images, labels, labelsInfo);
```
次の行は固有顔に基づく顔照合器を用意しています。

```
    Ptr<EigenFaceRecognizer> model = EigenFaceRecognizer::create();
```

次の行は、画像とラベルを用いて顔照合を訓練します。

```
    model->train(images, labels);
```

以下の行で、訓練後の結果をface-rec-model.txｔに保存します。

```
    string saveModelPath = "face-rec-model.txt";
    cout << "Saving the trained model to " << saveModelPath << endl;
    model->save(saveModelPath);
```

次の行で、１枚の与えられた画像に対する予測のラベルを返しています。

```
    // The following line predicts the label of a given
    // test image:
    int predictedLabel = model->predict(testSample);
```


```
$ ./facerec_video
usage: ./facerec_video </path/to/haar_cascade> </path/to/csv.ext> </path/to/device id>
	 </path/to/haar_cascade> -- Path to the Haar Cascade for face detection.
	 </path/to/csv.ext> -- Path to the CSV file with the face database.
	 <device id> -- The webcam device id to grab frames from.


```

このデモの中でも
-  顔照合の学習用の顔画像のファイル名と顔ラベル（非負の整数）を受け取り、
- 顔照合の訓練をしています。
入力が動画であり、しかも１枚のフレームの画像に複数人の顔が写っていることを考慮したものになっています。
１枚のフレームの中から顔検出器で検出を行い、検出したすべての顔について、顔枠と推測したIDの顔ラベルの表示とを行っています。





----


## [Introduction]
以下の３種類の顔照合のエンジンを持っています。
-    Eigenfaces (see EigenFaceRecognizer::create)
-    Fisherfaces (see FisherFaceRecognizer::create)
-    Local Binary Patterns Histograms (see LBPHFaceRecognizer::create)


## Eigenfaces in OpenCV (固有顔)

固有顔は、入力画像がどのように正規化された顔画像になっているかに強く依存します。
[「実践 コンピュータビジョン」](https://www.oreilly.co.jp/books/9784873116075/)「 1.3.6　画像の主成分分析」には、十分に正規化されていない顔画像群での固有顔と、目位置で正規化された顔画像群での固有顔とを示しています。
　固有顔は平均画像を差し引いた後の固有ベクトルです。
　すべての画像は、主成分分析で生じた固有ベクトルの線形和で記述できます。
　その固有ベクトルの係数に、個々の画像の特徴が反映されます。
　主成分分析であるため、その係数は、はじめの側で大きな絶対値を持ち、後段になればなるほど、小さな絶対値を持ちます。
　そこで、ある個数より少ない範囲の係数だけに着目しても、失われるデータは少なくなります。
　そのため、顔画像の画素数だけのベクトル空間から、その少ない範囲の係数だけに着目して画像の類似性の判定をすることになります。

- 照明変動の成分も固有顔の入っています。
- 照明変動の成分のうち、平均的な顔に倒する照明変動の成分は人を照合を区別するには適していない。

人物の顔の特徴で、照合を行ってほしいのに、照明条件が同じ顔画像で上位に照合結果が返ってこないようにするには、とりわけtrainingが必要です。trainingの際には、照明条件の変動が同じように入った、ラベルが与えられた顔画像のデータ・セットです。
また、微小な顔向きの変動についてもそうです。
さらには、表情の変動についてもそうです。

それらのデータベースがあってtrainをすることによって顔照合の精度がでる枠組みになっています。



## Fisherfaces
http://www.scholarpedia.org/article/Fisherfaces から引用
> When the goal is classification rather than representation, the LS solution may not yield the most desirable results. In such cases, one wishes to find a subspace that maps the sample vectors of the same class in a single spot of the feature representation and those of different classes as far apart from each other as possible. The techniques derived to achieve this goal are known as discriminant analysis (DA).

> The most known DA is Linear Discriminant Analysis (LDA), which can be derived from an idea suggested by R.A. Fisher in 1936. When LDA is used to find the subspace representation of a set of face images, the resulting basis vectors defining that space are known as Fisherfaces.  

上記の和訳

目標が表現ではなく分類である場合、LS（最小２乗法）の解決策は最も望ましい結果を生み出すことができない。分類をめざす場合、
同じクラスのサンプルベクトルが特徴量空間の単一のスポットに写像され、
異なるクラスに属するサンプルベクトルは、その特徴量空間で可能な限り互いと離れているそのような部分空間を見つけることを望んでる。この目標を達成するために得られた技術は、判別分析（discriminant analysis: DA）として知られている。

最もよく知られているDAは線形判別分析（Linear Discriminant Analysis、LDA）であり、これはR.A.フィッシャー（Fisher）に1936年に提案された考えで導出されます。LDAを使用して顔画像の部分空間表現を見つけると、その空間を定義する結果の基底ベクトルはFisherfacesとして知られています。


http://www.scholarpedia.org/article/Fisherfaces
のFigure 1
はfisherface の個々の基底ベクトルを示しています。

人を区別する上で有効な特徴を優先的に基底ベクトルを選んでいます。
そのため
- 照明変動の成分は上位に現れない。



## Local Binary Patterns Histograms
　固有顔の場合も、FisherFaceの場合も、照明の変動の成分が原画像に入っていることの影響が残ります。
 
> You simply can't guarantee perfect light settings in your images or 10 different images of a person. So what if there's only one image for each person?

 と気にしていますね。
　
３ｘ３の領域を中央の画素との明暗の比較で、8bitのパターン　Local Binary Patterns　を使っています。


## Pythonで顔照合をしたい場合には
### 顔照合のスクリプト例
- qiita [OpenCVを使って誰の顔なのかを推定する（Eigenface, Fisherface, LBPH）](https://qiita.com/hitomatagi/items/8f2f37646179aca68649)
の記事の中で、顔照合のPythonスクリプトの例が紹介されています。

どのように、訓練・テストするかが説明されています。



### cv::Faceの名前空間に対応するcv2.faceについて
OpenCVには顔関係のライブラリが増えていて、Pythonでも利用できるようになっています。
OpenCVのPythonバインディング部分は開発の初期は頻繁に変更を生じています。
以下のようhelpを利用して使い方を確認します。

```
help(cv2.face.EigenFaceRecognizer_create)
Help on built-in function EigenFaceRecognizer_create:

EigenFaceRecognizer_create(...)
    EigenFaceRecognizer_create([, num_components[, threshold]]) -> retval
    .   @param num_components The number of components (read: Eigenfaces) kept for this Principal
    .   Component Analysis. As a hint: There's no rule how many components (read: Eigenfaces) should be
    .   kept for good reconstruction capabilities. It is based on your input data, so experiment with the
    .   number. Keeping 80 components should almost always be sufficient.
    .   @param threshold The threshold applied in the prediction.
    .   
```


------

このプログラムへの入力画像を、dlibのランドマーク検出で正規化した画像で用いるならば
それなりの性能のシステムが少ない工数で開発できそうである。




次の記事は顔照合のシステムをどのように構築するのかを示しているものです。

[Face Recognition using OpenCV and Python: A Beginner’s Guide]
(https://www.superdatascience.com/opencv-face-recognition/)


https://github.com/bytefish/facerecognition_guide/blob/master/facerec_python.pdf



------------------------------------

### 追記　OpenCVの中にこんなコードがあることに気づいた。

[OpenCVに含まれる深層学習ベースの顔検出 ](https://qiita.com/nonbiri15/items/01df1d8743767cd341a1)


注：
　OpenCVのHOG検出器には、学習済みの識別器のデータが含まれていますが、学習するためのプログラム自体は必ずしもわかりやすい状況にはなっていません(執筆時点)。別途、情報を紹介しているサイトがありますから、検索してみてください。

[Example program showing how to train your custom HOG detector using openCV](https://github.com/DaHoC/trainHOG)

[ねこと画像処理 part 2 – 猫検出 (モデル配布)](http://rest-term.com/archives/3131/)


https://github.com/benhoff/facebook_api_script

YouTube [Facial Identification/Face Recognition with Python]
(https://www.youtube.com/watch?v=6zPeIeUAhZM)

[Face Recognition – OpenCV Python | Dataset Generator](https://thecodacus.com/opencv-face-recognition-python-part1/)

YouTube [Real-time Deep face recognition based on Google's facenet](https://www.youtube.com/watch?v=T6czH6DLhC4)

https://github.com/davidsandberg/facenet


[Facial Recognition with Python 1: PCA Eigenfaces](https://www.youtube.com/watch?v=MRMbZJ1IP6c)


---
## 追記
OpenCV自体の中に顔のlandmark 検出のライブラリが加わっている。
だから、
- OpenCVの中の深層学習で顔を検出する。
- OpenCVの中の顔landmarkの検出を利用する。
- OpenCVの中で顔照合ライブラリで顔照合をする。

そういうことができるようになりだしている。

(もちろん、ライブラリをつなぎあわせて使ったときに、検出した顔に対して、landmarkを求めることができるのか、landmarkを求めた後で、顔照合ができるのかという問題は存在する。そこのこともあるので、商用ライブラリを今すぐ置き換えるとは考え難い。ただ、古い商用ライブラリを置き換えるレベルには日に日に近づきつつある。）




### https://docs.opencv.org/3.4/dc/d63/classcv_1_1face_1_1FacemarkLBF.html


---
追記
EigenFaceを元に顔を合成する話が以下のサイトに書かれています。
リンク先をたどると、そのサンプルスクリプトを入手することができます。
顔画像を主成分分析した結果を元に、主成分の比率を変えていくとどのような合成画像ができるのかをスライドを動作させながら見ていくことができました。


[Eigenface using OpenCV (C++/Python)](https://www.learnopencv.com/eigenface-using-opencv-c-python/)

https://www.youtube.com/watch?v=J0arU2PAMls&feature=youtu.be