# Estimace pózy rukou a obličeje člověka
Estimace 2D klíčových bodů rukou a obličeje člověka ve statickém izolovaném snímku. Subprojekt projektu [Interpretace znakové řeči](../README.md). Součástí projektu je definice modelů konvolučních neuronových sítí (CNN) sloužící jako estimátory pózy, jejich naučení, evaluace a vizualizace.

Estimace pózy je pojata jako úloha regrese teplotních map jednotlivých klíčových bodů konvolučními neuronovými sítěmi (CNN). Jako dataset pro nauční estimátoru pózy obličeje je zvolen dataset [Wider Facial Landmarks in-the-wild (WFLW)](https://wywu.github.io/projects/LAB/WFLW.html), který obsahuje anotace pro 98 klíčových bodů obličeje. Pro estimaci klíčových bodů obličeje bylo experimentováno s předem naučeným modelem [InterNet](https://github.com/facebookresearch/InterHand2.6M) na datasetu [InterHand 2.6M](https://mks0601.github.io/InterHand2.6M/), který estimuje 3D pózu rukou, ovšem experimentálně bylo zjištěno, že model není vhodný pro použití v reálných podmínkách, proto byl fokus přesunut na 2D estimaci pózy rukou. Jako datasety pro naučení jsou zvoleny datasety [COCO-Wholebody](https://github.com/jin-s13/COCO-WholeBody), [Halpe Full-Body Human Keypoints](https://github.com/Fang-Haoshu/Halpe-FullBody), ručně anotované vzorky z datasetu publikace [Hand Keypoint Detection in Single Images using Multiview Bootstrapping](http://www.cs.cmu.edu/~tsimon/projects/mvbs.html) a [Rendered Handpose Dataset](https://lmb.informatik.uni-freiburg.de/resources/datasets/RenderedHandposeDataset.en.html). Všechny datasety obsahují anotace pro 21 2D klíčových bodů pro každou anotovanou ruku. Před procesy učení jsou datasety serializovány do `TFRecord` formátu, definovaného knihovnou `Tensorflow`.

Adresářová struktura projektu je složená ze skriptů, knihoven, dat a vah. Spustitelné skripty jsou umístěny v adresářovém kořenu, knihovny, data a váhy jsou umístěny v samostatných adresářích `./modules/`, `./data/` a `./weights/`.

<p align="center">
  <img src="./data/readme/example.jpg" width="500"/>
</p>

## Spustitelné skripty
Stručný popis funkcionality jednotlivých skriptů, podrobnější informace jsou uvedeny přímo ve skriptech.

### `convert_weights.py`
Konverze dostupných vah pro model `InterNet` do `Tensorflow` formátu. Váhy jsou dostupné ke stažení na [oficiálním InterHand 2.6M repozitáři](https://github.com/facebookresearch/InterHand2.6M#pre-trained-internet).

### `construct_tfrecords.py`
Skript pro konverzi datasetů do serializovaného formátu `TFRecord` pro účely efektivního podávání dat modelům při procesu učení a evaluace. Obsahuje hlavní funkce:
- `interhand()` - konstrukce `TFRecord` souborů z datasetu `InterHand 2.6M`
- `wflw()` - konstrukce `TFRecord` souborů z datasetu `Wider Facial Landmarks in-the-wild (WFLW)`
- `hands()` - konstrukce `TFRecord` souborů z použitých datasetů pro estimaci pózy rukou

### `visualize_dataset.py`
Skript pro vizualizaci použitých datasetů a jejich anotací přímo ze zdrojových adresářů datasetů nebo čtením generovaných `TFRecord` souborů. Obsahuje hlavní funkce:
- `interhand_from_files()` - vizualizace `InterHand 2.6M` datasetu z adresářových souborů
- `interhand_from_tfrecords()`vizualizace `InterHand 2.6M` datasetu z `TFRecord` souboru
- `wflw_from_files()` - vizualizace `WFLW` datasetu z adresářových souborů
- `wflw_from_tfrecords()` - vizualizace `WFLW` datasetu z `TFRecord` souboru
- `coco_from_files()` - vizualizace `COCO-Wholebody` datasetu z adresářových souborů
- `halpe_from_files()` - vizualizace `Halpe Full-Body Human Keypoints` datasetu z adresářových souborů
- `mwb_from_files()` - vizualizace datasetu z `Hand Keypoint Detection in Single Images using Multiview Bootstrapping` z adresářových souborů
- `rhd_from_files()` - vizualizace `Rendered Handpose Dataset` datasetu z adresářových souborů
- `hands_from_tfrecords()` - vizualizace kombinovaného datasetu pro estimaci klíčových bodů rukou z `TFRecord` souboru

### `train.py`
Skript pro naučení CNN modelů. Při učení všech modelů je využito předem naučených vah na ImageNet datasetu. Obsahuje hlavní funkce:
- `train_face_estimator(backbone)` - naučení modelu na `WFLW` datasetu
- `train_hand_estimator(backbone)` - naučení modelu na kombinovaném datasetu pro estimaci pózy rukou
- `find_optimal_delta(deltas)` - hledání optimální hodnoty delta pro dekódování výstupních map

Argumenty `backbone` definují extraktor příznaků použitý v modelu, použité jsou varianty `"efficientnet-b0"`, `"efficientnet-b1"` a `"efficientnet-b2"`.

### `evaluate.py`
Skript pro evaluaci naučených modelů na testovacích subsetech jednotlivých datasetů. Obsahuje hlavní funkce:
- `evaluate_pretrained_interhand(handedness_thresholds, log_residuals)` - Evaluace předem naučeného modelu `InterNet`. Argument `handedness_thresholds` definuje prahy pro klasifikaci prezence levé a pravé ruky, argument `log_residuals` je flag pro logování reziduí predikcí modelu do souboru
- `evaluate_residuals()` - Evaluace a vizualizace logovaných reziduí `InterNet` modelu. Tato funkce využívá knihovnu `MatPlotLib`, která není příliš efektivní při zobrazování většího množství dat. Stejnou funkcionalitu plní přiložený MATLAB skript `interhand_residuals_viz.m`.
- `evaluate_face_keypoints(backbone, decode_method)` - Evaluace modelu estimující pózu obličeje
- `evaluate_hand_keypoints(backbone, decode_method)` - Evaluace modelu estimující pózu rukou

Argumenty `decode_method` definují metodu dekódování výstupních map estimátorů - jedno z `"argmax"` nebo `"compensated"`.

### `performance.py`
Skript pro měření FLOPs (*floating point operations*), počet parametrů a latence inference jednotlivých modelů. Latence je měřena průměrnou latencí z 50-ti inferenčních průchodů. Obsahuje hlavní funkce:
- `interhand()` - měření `InterNet` modelu
- `faces(backbone)` - měření estimátoru pózy obličeje
- `hands(backbone)` - měření estimátoru pózy rukou

### `visualize_outputs.py`
Skript pro vizualizaci výstupních map modelů. Obsahuje hlavní funkce:
- `wflw_heatmaps(backbone)` - Vizualizace výstupních map modelu estimující pózu obličeje
- `hand_heatmaps(backbone)` - Vizualizace výstupních map modelu estimující pózu rukou

### `webcam.py`
Skript pro inferenci naučených modelů na webkameře. Obsahuje hlavní funkce:
- `interhand()` - Inference předučeného `InterNet` modelu
- `face()` - Inference modelu estimující pózu obličeje
- `hands()` - Inference modelu estimující pózu rukou


## Vlastní knihovny
Stručný popis vlastně vytvořených knihoven, podrobnější popis funkcionality je uveden přímo ve zdrojových souborech. Všechny knihovny jsou umístěny v adresáři `./modules/`.

- `config.py` - Konfigurační soubor obsahující primárně informace o adresářové struktuře a umístění jednotlivých souborů. Obsahuje také parametry pro učení a inferenci modelů.
- `coco_utils.py` - Knihovna obsahující nástroje pro práci s `COCO-Wholebody` datasetem
- `estimation_utils.py` - Knihovna obsahující obecné nástroje používané pro estimaci pózy rukou i obličeje
- `face_model.py` - Knihovna obsahující definici modelu pro estimaci pózy obličeje
- `halpe_utils.py` - Knihovna obsahující nástroje pro práci s `Halpe` datasetem
- `hands_model.py` - Knihovna obsahující definici modelu pro estimaci pózy rukou
- `hands_utils.py` - Knihovna obsahující nástroje pro práci s obecně všemi datasety pro estimaci pźy rukou
- `inference_utils.py` - Knihovna obsahující nástroje pro inferenci modelů.
- `interhand_utils.py` - Knihovna obsahující nástroje pro práci s `InterHand 2.6M` datasetem
- `interhand.py` - Knihovna definující definici `InterNet` modelu
- `metrics.py` - Knihovna obsahující definice metrik pro evaluace použitých modelů
- `mwb_utils.py` - Knihovna obsahující nástroje pro práci s `MWB` datasetem
- `rhd_utils.py` - Knihovna obsahující nástroje pro práci s `Rendered Handpose Dataset` datasetem
- `wflw_utils.py` - Knihovna obsahující nástroje pro práci s `WFLW` datasetem 

## Doporučený postup pro replikaci výsledků

### Předpokládaný adresářový strom a prerekvizity
Prerekvizitou je stažení použitých datasetů
- [Wider Facial Landmarks in-the-wild](https://wywu.github.io/projects/LAB/WFLW.html)
- [COCO-Wholebody](https://github.com/jin-s13/COCO-WholeBody)
- [Halpe-FullBody](https://github.com/Fang-Haoshu/Halpe-FullBody)
- [Hand Keypoint Detection in Single Images
using Multiview Bootstrapping](http://www.cs.cmu.edu/~tsimon/projects/mvbs.html)
- [Rendered Handpose Dataset](https://lmb.informatik.uni-freiburg.de/resources/datasets/RenderedHandposeDataset.en.html)

Volitelné je stažení datasetu `InterHand 2.6M` a předem naučených vah modelu `InterNet`, ale vzhledem k tomu, že model není využit ve finální pipeline, není toho potřeba. Pozn.: Dataset `InterHand 2.6M` je také extrémně objemný pro stažení a v tomto projektu jsou data použita jen pro vizualizaci a evaluaci, proto stahování není doporučeno.
- [InterNet váhy](https://github.com/facebookresearch/InterHand2.6M#pre-trained-internet)
- [InterHand 2.6 Dataset](https://mks0601.github.io/InterHand2.6M/)

Předpokládaný adresářový strom pro stažená data je pak ve tvaru:
```
pose_estimation
├── data
│   ├── coco
│   │   ├── annotations
│   │   │   ├──  coco_wholebody_train_v1.0.json
│   │   │   └──  coco_wholebody_val_v1.0.json
│   │   ├── images
│   │   │   ├──  train2017
│   │   │   │    ├──  <>.jpg
│   │   │   │    └──  ...
│   │   │   └──  val2017
│   │   │        ├──  <>.jpg
│   │   │        └──  ...
│   │   └──  skeleton.txt
│   ├── halpe
│   │   ├── images
│   │   │   ├──  train2015
│   │   │   │    ├──  <>.jpg
│   │   │   │    └──  ...
│   │   │   └──  test2015
│   │   │        ├──  <>.jpg
│   │   │        └──  ...
│   │   └──  halpe_train_v1.json
│   ├── mwb
│   │   ├── manual_test
│   │   │   ├──  <>.jpg
│   │   │   ├──  <>.json
│   │   │   └──  ...
│   │   └── manual_train
│   │       ├──  <>.jpg
│   │       ├──  <>.json
│   │       └──  ...
│   ├── rhd
│   │   ├── evaluation
│   │   │   ├──  color
│   │   │   │    ├──  <>.png
│   │   │   │    └──  ...
│   │   │   └──  anno_evaluation.pickle
│   │   ├── training
│   │   │   ├──  color
│   │   │   │    ├──  <>.png
│   │   │   │    └──  ...
│   │   │   └──  anno_training.pickle
│   │   └── skeleton.txt
│   ├── WFLW
│   │   ├── WFLW_annotations
│   │   │   ├──  list_98pt_rect_attr_train_test
│   │   │   │    ├──  list_98pt_rect_attr_test.txt
│   │   │   │    └──  list_98pt_rect_attr_train.txt
│   │   │   └──  list_98pt_test
│   │   │        ├──  list_98pt_test.txt
│   │   │        ├──  list_98pt_test_blur
│   │   │        ├──  list_98pt_test_expression.txt
│   │   │        ├──  list_98pt_test_illumination.txt
│   │   │        ├──  list_98pt_test_largepose.txt
│   │   │        ├──  list_98pt_test_makeup.txt
│   │   │        └──  list_98pt_test_occlusion.txt
│   │   └── WFLW_images
│   │       ├──  0--Parade
│   │       │    ├──  <>.jpg
│   │       │    └──  ...
│   │       └──  ...
│   │ 
│   │  (volitelné)
│   ├── InterHand
│   │   ├── annotations
│   │   │   ├──  all
│   │   │   │    ├──  InterHand2.6M_test_camera.json
│   │   │   │    ├──  InterHand2.6M_test_data.json
│   │   │   │    ├──  InterHand2.6M_test_joint_3d.json
│   │   │   │    ├──  InterHand2.6M_test_MANO_NeuralAnnot.json
│   │   │   │    ├──  InterHand2.6M_train_camera.json
│   │   │   │    ├──  InterHand2.6M_train_data.json
│   │   │   │    ├──  InterHand2.6M_train_joint_3d.json
│   │   │   │    └──  InterHand2.6M_train_MANO_NeuralAnnot.json
│   │   │   ├──  human_annot
│   │   │   │    ├──  InterHand2.6M_test_camera.json
│   │   │   │    ├──  InterHand2.6M_test_data.json
│   │   │   │    ├──  InterHand2.6M_test_joint_3d.json
│   │   │   │    ├──  InterHand2.6M_test_MANO_NeuralAnnot.json
│   │   │   │    ├──  InterHand2.6M_train_camera.json
│   │   │   │    ├──  InterHand2.6M_train_data.json
│   │   │   │    ├──  InterHand2.6M_train_joint_3d.json
│   │   │   │    └──  InterHand2.6M_train_MANO_NeuralAnnot.json
│   │   │   ├──  machine_annot
│   │   │   │    ├──  InterHand2.6M_test_camera.json
│   │   │   │    ├──  InterHand2.6M_test_data.json
│   │   │   │    ├──  InterHand2.6M_test_joint_3d.json
│   │   │   │    ├──  InterHand2.6M_test_MANO_NeuralAnnot.json
│   │   │   │    ├──  InterHand2.6M_train_camera.json
│   │   │   │    ├──  InterHand2.6M_train_data.json
│   │   │   │    ├──  InterHand2.6M_train_joint_3d.json
│   │   │   │    ├──  InterHand2.6M_train_MANO_NeuralAnnot.json
│   │   │   │    ├──  InterHand2.6M_val_camera.json
│   │   │   │    ├──  InterHand2.6M_val_data.json
│   │   │   │    ├──  InterHand2.6M_val_joint_3d.json
│   │   │   │    └──  InterHand2.6M_val_MANO_NeuralAnnot.json
│   │   │   └──  subject.txt
│   │   ├── images
│   │   │   ├──  test
│   │   │   │    └──  ...
│   │   │   ├──  train
│   │   │   │    └──  ...
│   │   │   └──  val
│   │   │        └──  ...
│   │   └──  skeleton.txt
│   └── ...
├── weights
│   └── interhand.tar
├── ...
```

### 1. Konstrukce `TFRecord` souborů
Ve skriptu `construct_tfrecords.py` je potřeba povolit volání konstrukce datasetů rukou a obličejů a volitelně volání konstrukce `InterHand` datasetů
```python
if __name__ == '__main__':
    # interhand()  # volitelně
    wflw()
    hands()
```
```
$ python ./pose_estimation/construct_tfrecords.py
```
Výsledkem je vytvoření `TFRecord` souborů pro jednotlivé subsety datasetů
```
pose_estimation
├── data
│   ├── tfrecords
│   │   ├── wflw_train.tfrecord
│   │   ├── wflw_val.tfrecord
│   │   ├── wflw_test.tfrecord
│   │   ├── hands_train.tfrecord
│   │   ├── hands_val.tfrecord
│   │   ├── hands_test.tfrecord
│   │   ├── interhand_train.tfrecord
│   │   ├── interhand_val.tfrecord
│   │   └── interhand_test.tfrecord
│   └── ...
├── ...
```

### 2. Konverze `InterNet` vah do formátu projektu (volitelné)
```
$ python ./pose_estimation/convert_weights.py
```
Výsledkem je vytvoření vah v `.tf` formátu
```
pose_estimation
├── weights
│   ├── interhand.tf.index
│   ├── interhand.tf.data-00000-of-00001
│   └── ...
├── ...
```

### 3. Naučení modelů
Ve skriptu `train.py` povolte volání učení modelů se specifikovaným extraktorem příznaků a volitelně volání hledání optimální delta hodnoty pro potřeby dekódování výstupních map kompenzační metodou
```python
if __name__ == '__main__':
    train_hand_estimator('efficientnet-b1')
    train_face_estimator('efficientnet-b1')
    # find_optimal_delta([0, 1, 2, 3, 4, 5])  # volitelně
```
```
$ python ./pose_estimation/train.py
```
Výsledkem jsou naučené váhy modelů
```
pose_estimation
├── weights
│   ├── effnetb1_hands.tf.index
│   ├── effnetb1_hands.tf.data-00000-of-00001
│   ├── effnetb1_wflw.tf.index
│   ├── effnetb1_wflw.tf.data-00000-of-00001
│   └── ...
├── ...
```

### 4. Evaluace naučených modelů
Ve skriptu `evaluate.py` povolte volání evaluace naučených modelů a volitelně evaluaci `InterNet` modelu, např.:
```python
if __name__ == '__main__':
    # evaluate_pretrained_interhand([0.9999], True)  # volitelně
    # evaluate_residuals()  # volitelně
    evaluate_face_keypoints('efficientnet-b1', decode_method='argmax')
    evaluate_hand_keypoints('efficientnet-b1', decode_method='compensated')
```
```
$ python ./pose_estimation/evaluate.py
```
Výstupem jsou výsledky evaluací v konzoli.