# Detekce rukou a obličeje v obraze
Detekce rukou a obličeje člověka ve statické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 detektory, jejich naučení, evaluace a inference.

Definované modely CNN jsou `YOLOv5` a `EfficientDet`, které jsou schopny detekovat objekty různých tříd. Modely byly definované, tak aby byly kompatibilní s veřejně přístupnými vahami pro *transfer learning*. Cílem je tedy detekovat v jednom kroku lidské ruce i obličeje. Pro tyto potřeby je potřeba dataset, který obsahuje anotace pro lidské ruce i lidské obličeje. Takový dataset není k dispozici, jsou použity tedy dva samostatné datasety, jeden obsahující anotace rukou [100 Days of Hands](https://fouheylab.eecs.umich.edu/~dandans/projects/100DOH/) a druhý obsahující anotace obličejů [WIDER Faces](http://shuoyang1213.me/WIDERFACE/). Na obou datasetech jsou samostatně naučeny dva detektory, které jsou pak použity ke křížovému doplnění anotací obou datasetů (k datasetu pro detekci rukou jsou pomocí modelu přidány anotace obličejů apod.). Na výsledném datasetu jsou pak naučeny výsledné modely. 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`
Skript pro konverzi volně dostupných předučených vah `YOLOv5` a `EfficientDet` modelů pro potřeby *transfer learning*. Předem naučené váhy jsou naučené na [COCO datasetu](https://cocodataset.org/#home) a jsou volně ke stažení na oficiálních repozitářích autorů detektorů.
- `YOLOv5` - [https://github.com/ultralytics/yolov5](https://github.com/ultralytics/yolov5#pretrained-checkpoints)
- `EfficientDet` - [https://github.com/google/automl/tree/master/efficientdet](https://github.com/google/automl/tree/master/efficientdet#2-pretrained-efficientdet-checkpoints)

Výstupem skriptu jsou váhy v `Tensorflow` formátu, které jsou kompatibilní s modely definovanými v `./modules/yolov5` a `./modules/efficientdet`. Obsahuje hlavní funkce:
- `convert_yolov5_weights(model_cfg)` - konverze vah `YOLOv5` modelu, kde argument `model_cfg` určuje konfiguraci architektury, možnosti jsou `yolov5s`, `yolov5m` a `yolov5l`.
- `convert_efficientdet_weights(model_cfg)` - konverze vah `EfficientDet` modelu, kde argument `model_cfg` určuje konfiguraci architektury, možnosti jsou `efficientdet-d0`, `efficientdet-d1` až `efficientdet-d6`.

### `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. Mimo konverze datasetů, jsou také počítány rozměry anchor boxů detektorů. Obsahuje hlavní funkce:
- `hands_dataset()` - konstrukce `TFRecord` souborů z datasetu `100 Days of Hands`
- `face_dataset()` - konstrukce `TFRecord` souborů z datasetu `WIDER Face`
- `combined_dataset()` - konstrukce `TFRecord` souborů z obou datasetů a křížových anotací

### `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:
- `hands_from_files()` - vizualizace `100 Days of Hands` datasetu z adresářových souborů
- `face_from_files()` - vizualizace `WIDER Face` datasetu z adresářových souborů
- `combined_from_files()` - vizualizace kombinovaného datasetu z adresářových souborů
- `hands_from_tfrecords()` - vizualizace `100 Days of Hands` datasetu z `TFRecord` souboru
- `face_from_tfrecords()` - vizualizace `WIDER Face` datasetu z `TFRecord` souboru
- `combined_from_tfrecords()` - vizualizace kombinovaného datasetu 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 COCO datasetu. Obsahuje hlavní funkce:
- `train_hands(model_cfg)` - naučení modelu na `100 Days of Hands` datasetu.
- `train_face(model_cfg)` - naučení modelu na `WIDER Face` datasetu.
- `train_combined(model_cfg)` - naučení modelu na kombinovaném datasetu.

Argumenty `model_cfg` definují model, který bude naučen. Validní možnosti jsou `yolov5s`, `yolov5m`, `yolov5l` nebo `efficientdet-d0`, `efficientdet-d1` až `efficientdet-d6`.

### `combine_datasets.py`
Skript pro automatickou křížovou anotaci `100 Days of Hands` a `WIDER Face` datasetů pomocí naučených samostatných modelů. `100 Days of Hands` dataset obsahuje rozdělení dat do jednotlivých subsetů pro učení, validaci a testování. Autoři `WIDER Face` datasetu neposkytují anotace pro testovací subset, jsou tedy k dispozici pouze subsety pro učení a validaci. Proto je v rámci `WIDER Face` datasetu zacházeno s validačními daty jako s daty pro testování. Křížová anotace je provedena pouze pro subsety pro učení a validaci, subsety pro testování nejsou strojově anotovány, pro zachování maximální čistoty dat pro potřeby evaluace. Argumenty hlavní funkce jsou konfigurace naučených detektorů.

### `evaluate.py`
Skript pro evaluaci naučených modelů pomocí `mean average precision` metriky (mAP) na testovacích subsetech jednotlivých datasetů. Obsahuje hlavní funkce:
- `evaluate_hands(model_cfg, iou_threshold, score_threshold)` - Evaluace modelu detekující lidské ruce
- `evaluate_faces(model_cfg, iou_threshold, score_threshold)` - Evaluace modelu detekující lidské obličeje
- `evaluate_combined(model_cfg, iou_threshold, score_threshold)` - Evaluace modelu detekující lidské ruce a obličeje

Argument `model_cfg` definuje model, který bude testován. Validní možnosti jsou `yolov5s`, `yolov5m`, `yolov5l` nebo `efficientdet-d0`, `efficientdet-d1` až `efficientdet-d6`. Argument `iou_threshold` definuje jeden nebo více úrovní prahu IOU pro výpočet mAP. Argument `score_threshold` definuje jeden nebo více úrovní prahu skóre pro výpočet mAP.

### `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ů. Argumentem hlavní funkce je seznam měřených modelů.

### `webcam.py`
Skript pro inferenci naučeného modelu na webkameře. Argumentem hlavní funkce je název konfigurace modelu, který bude použit pro inferenci.

### `videos.py`
Skript pro inferenci naučeného modelu na videích. Ze seznamu vstupních videí vytvoří mozaiku vizualizace výstupů modelu. Argumenty hlavní funkce jsou název konfigurace modelu, který bude použit pro inferenci a seznam systémových cest k videím.

## 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ů detektorů.
- `dataset_utils.py` - Knihovna obsahující nástroje pro práci s použitými datasety.
- `detector_utils.py` - Knihovna obsahující nástroje pro CNN modely, které využívají konfigurace `YOLOv5` i `EfficientDet`.
- `efficientdet.py` - Knihovna obsahující definici `EfficientDet` modelů.
- `inference_utils.py` - Knihovna obsahující nástroje pro inferenci modelů.
- `mean_average_precision.py` - Knihovna obsahující definici mAP metriky pro evaluaci detektorů.
- `yolov5.py` - Knihovna obsahující definici `YOLOv5` modelů.

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

### Předpokládaný adresářový strom a prerekvizity
Prerekvizity jsou stažení použitých datasetů a předem naučených vah detektorů
- [100 Days of Hands](https://fouheylab.eecs.umich.edu/~dandans/projects/100DOH/) 
- [WIDER Faces](http://shuoyang1213.me/WIDERFACE/)
- [YOLOv5](https://github.com/ultralytics/yolov5#pretrained-checkpoints)
- [EfficientDet v H5 formátu](https://github.com/google/automl/tree/master/efficientdet#2-pretrained-efficientdet-checkpoints)

Předpokládaný adresářový strom pro stažená data je pak ve tvaru:
```
detection
├── data
│   ├── faces
│   │   ├── wider_face_split
│   │   │   ├── wider_face_test.mat
│   │   │   ├── wider_face_test_filelist.txt
│   │   │   ├── wider_face_train.mat
│   │   │   ├── wider_face_train_bbx_gt.txt
│   │   │   ├── wider_face_val.mat
│   │   │   └── wider_face_val_bbx_gt.txt
│   │   ├── WIDER_test
│   │   │   └── images
│   │   │       ├──  0--Parade
│   │   │       │    ├──  <>.jpg
│   │   │       │    └──  ...
│   │   │       └──  ...
│   │   ├── WIDER_train
│   │   │   └── images
│   │   │       ├──  0--Parade
│   │   │       │    ├──  <>.jpg
│   │   │       │    └──  ...
│   │   │       └──  ...
│   │   └── WIDER_val
│   │       └── images
│   │           ├──  0--Parade
│   │           │    ├──  <>.jpg
│   │           │    └──  ...
│   │           └──  ...
│   ├── hands
│   │   ├── Annotations
│   │   │   ├── <>.xml
│   │   │   └── ...
│   │   ├── ImageSets
│   │   │   └── Main
│   │   │       ├── test.txt
│   │   │       ├── train.txt
│   │   │       ├── trainval.txt
│   │   │       └── val.txt
│   │   └── JPEGImages
│   │       ├── <>.jpg
│   │       └── ...
│   └── ...
├── weights
│   ├── pretrained_yolov5s.tar
│   ├── pretrained_yolov5m.tar
│   ├── pretrained_yolov5l.tar
│   ├── pretrained_efficientdet-d0.h5
│   ├── pretrained_efficientdet-d1.h5
│   ├── pretrained_efficientdet-d2.h5
│   ├── pretrained_efficientdet-d3.h5
│   ├── pretrained_efficientdet-d4.h5
│   ├── pretrained_efficientdet-d5.h5
│   └── pretrained_efficientdet-d6.h5
├── ...
```


### 1. Konstrukce `TFRecord` souborů pro naučení samostatných modelů pro detekci rukou a obličejů
Ve skriptu `construct_tfrecords.py` je potřeba povolit volání konstrukce datasetů rukou a obličejů
```python
if __name__ == '__main__':
    hands_dataset()
    face_dataset()
    # combined_dataset()
```

```
$ python ./detection/construct_tfrecords.py
```
Výsledkem je vytvoření `TFRecord` souborů pro jednotlivé subsety datasetů a souborů obsahující vypočtené parametry anchor boxů
```
detection
├── data
│   ├── anchors
│   │   ├── faces
│   │   │   ├── anchors.txt
│   │   │   └── efficientdet_anchors.txt
│   │   └── hands
│   │       ├── anchors.txt
│   │       └── efficientdet_anchors.txt
│   ├── tfrecords
│   │   ├── faces_train.tfrecord
│   │   ├── faces_val.tfrecord
│   │   ├── hands_train.tfrecord
│   │   ├── hands_val.tfrecord
│   │   └── hands_test.tfrecord
│   └── ...
├── ...
```

### 2. Konverze předem naučených vah do formátu tohoto projektu
Ve skriptu `convert_weights.py` povolte volání konverze pro konfigurace modelů, které budou použity pro učení, např.:
```python
if __name__ == '__main__':
    convert_yolov5_weights('yolov5s')
    # convert_yolov5_weights('yolov5m')
    # convert_yolov5_weights('yolov5l')
    convert_efficientdet_weights('efficientdet-d0')
    # convert_efficientdet_weights('efficientdet-d1')
    # convert_efficientdet_weights('efficientdet-d2')
    # convert_efficientdet_weights('efficientdet-d3')
    # convert_efficientdet_weights('efficientdet-d4')
    # convert_efficientdet_weights('efficientdet-d5')
    # convert_efficientdet_weights('efficientdet-d6')
```

```
$ python ./detection/convert_weights.py
```
Výsledkem je vytvoření vah v `.tf` formátu
```
detection
├── weights
│   ├── pretrained_yolov5s.tf.index
│   ├── pretrained_yolov5s.tf.data-00000-of-00001
│   ├── pretrained_efficientdet_d0.tf.index
│   ├── pretrained_efficientdet_d0.tf.data-00000-of-00001
│   └── ...
├── ...
```

### 3. Naučení samostatných modelů
Ve skriptu `train.py` povolte volání učení samostatných modelů s argumentem názvu architektury modelů, které budou naučeny (jako samostatné modely jsou podporované pouze modely typu `YOLOv5`), např.:
```python
if __name__ == '__main__':
    train_face('yolov5s')
    train_hands('yolov5m')
    # train_combined('efficientdet-d0')
```

```
$ python ./detection/train.py
```
Výsledkem jsou naučené váhy zvolených modelů
```
detection
├── weights
│   ├── face_yolov5s.tf.index
│   ├── face_yolov5s.tf.data-00000-of-00001
│   ├── hands_yolov5m.tf.index
│   ├── hands_yolov5m.tf.data-00000-of-00001
│   └── ...
├── ...
```

### 4. Křížová anotace výsledného kombinovaného datasetu
Ve skriptu `combine_datasets.py` upravte argumenty hlavní funkce, tak aby souhlasily s naučenými modely.
```python
def main(faces_model_cfg, hands_model_cfg):
    ...

if __name__ == '__main__':
    main('yolov5s', 'yolov5m')
```

```
$ python ./detection/combine_datasets.py
```
Výsledkem jsou `.json` anotační soubory pro jednotlivé subsety kombinovaného datasetu
```
detection
├── data
│   ├── combined
│   │   ├── combined_test.json
│   │   ├── combined_train.json
│   │   └── combined_val.json
│   └── ...
├── ...
```

### 5. Konstrukce `TFRecord` souboru kombinovaného datasetu
Ve skriptu `construct_tfrecords.py` je potřeba povolit volání konstrukci pro kombinovaný dataset
```python
if __name__ == '__main__':
    # hands_dataset()
    # face_dataset()
    combined_dataset()
```

```
$ python ./detection/construct_tfrecords.py
```
Výsledkem je vytvoření `TFRecord` souborů pro jednotlivé subsety kombinovaného datasetu a souborů obsahující vypočtené parametry anchor boxů
```
detection
├── data
│   ├── anchors
│   │   ├── combined
│   │   │   ├── anchors.txt
│   │   │   └── efficientdet_anchors.txt
│   │   └── ...
│   ├── tfrecords
│   │   ├── combined_train.tfrecord
│   │   ├── combined_val.tfrecord
│   │   ├── combined_test.tfrecord
│   │   └── ...
│   └── ...
├── ...
```

### 6. Naučení výsledných modelů
Ve skriptu `train.py` povolte volání učení kombinovaných modelů s argumentem názvu architektury modelů, které budou naučeny, např.:
```python
if __name__ == '__main__':
    # train_face('yolov5s')
    # train_hands('yolov5m')
    train_combined('efficientdet-d0')
    train_combined('yolov5l')
```

```
$ python ./detection/train.py
```
Výsledkem jsou naučené váhy zvolených modelů, což je finální produkt tohoto projektu
```
detection
├── weights
│   ├── combined_yolov5l.tf.index
│   ├── combined_yolov5l.tf.data-00000-of-00001
│   ├── combined_efficientdet_d0.tf.index
│   ├── combined_efficientdet_d0.tf.data-00000-of-00001
│   └── ...
├── ...
```

### 7. Evaluace naučených modelů
Ve skriptu `evaluate.py` povolte volání evaluace modelů s argumenty názvy architektury modelů, IOU prahy a prahy pro skóre, např.:
```python
if __name__ == '__main__':
    iou_tholds = [0.3, 0.5, 0.7]
    score_tholds = [0.2, 0.3, 0.4, 0.5]
    evaluate_faces('yolov5s', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_hands('yolov5s', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('yolov5s', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('yolov5m', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('yolov5l', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('efficientdet-d0', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('efficientdet-d1', iou_threshold=iou_tholds, score_threshold=score_tholds)
    evaluate_combined('efficientdet-d2', iou_threshold=iou_tholds, score_threshold=score_tholds)
```
```
$ python ./detection/evaluate.py
```
Výstupem jsou výsledky evaluací v konzoli.

## Ukázka inference `YOLOv5-M` modelu
<p align="center">
  <img src="./data/readme/example.gif" width=800/>
</p>