## Подготовка

### Переменные окружения

Надо выставить переменную окружения *TF_RESEARCH_DIR* прописав туда путь к 'research' папке из репозитория Tensorflow. Например:

TF_RESEARCH_DIR='/home/vbystricky/tensorflow1.5.1/models/research'

### Базовая директория
Создаём папку (будем называть её в дальнейшем базовой) в которой будут лежать исходные данные и куда будут складываться результаты работы.

### Разметка из toloka
На выходе из toloka имеем ссылку на json вида:
{
    "classified_signs": [
        {
            "signs": [
                {
                    "answer": "ok",
                    "bbox": [
                        [759, 581],
                        [795, 618]
                    ],
                    "sign_id": "priority_give_way"
                }
            ],
            "source": "https://mrc-browser.maps.yandex.net/feature/30968/image"
        },
        {
            "signs": [
                {
                    "answer": "ok",
                    "bbox": [
                        [761, 564],
                        [822, 632]
                    ],
                    "sign_id": "warning_children"
                },
                {
                    "answer": "ok",
                    "bbox": [
                        [766, 621],
                        [812, 647]
                    ],
                    "sign_id": "information_start_zone"
                }
            ],
            "source": "https://mrc-browser.maps.yandex.net/feature/30471/image"
        },
    ]
}
Ссылок на json может быть несколько.

### Файл настроек

Создаём в базовой папке файл настроек с именем `setting.json` вида:
{
    "urls": [
        "http://storage-int.mds.yandex.net/get-maps_mrc/103268/datasets/toloka/pool-219480.json",
        "http://storage-int.mds.yandex.net/get-maps_mrc/148190/datasets/toloka/pool-221476.json",
        ...
    ],
    "classes": [
        "warning_roundabout_ahead",
        "warning_uneven_road_ahead",
        ....
     ],
    "horz_flip_map": {
        "warning_roundabout_ahead"                          : "undefined",
        "warning_uneven_road_ahead"                         : "warning_uneven_road_ahead",
        "warning_road_narrows_on_both"                      : "warning_road_narrows_on_both",
        ....
    }
    "min_amount" : 1000,
    "min_bbox_width" : 30,
    "min_bbox_height" : 30
}

1. "urls" - список ссылок на json файлы с данными разметки
2. "classes" - список классов знаков, которые мы планируем детектировать и классифицировать. Это предварительный список классов, реально тренировать
будем только на те, представителей которых в датасете наберется достаточное число (см. параметр "min_amount").
   *Поскольку, у нас названия классов знаков менялись, то возможно, что в json используются названия классов отличные от тех, что мы хотим, чтобы их
    сконвертировать в нужные добавляем их в словарь json_ds_utils.py::TRAFFIC_SIGNS_NAME_MAP*
3. "horz_flip_map" - мы расширяем тренировочный датасет отражая изображения по горизонтали, при этом знак на фотографии может либо превратиться в тыкву
т.е. перестать быть знаком (например, знак STOP при отражении по горизонтали получит зеркальную надпись и не будет знаком) какого-либо класса, либо
перейти в другой класс (например, 4.1.2 - "только направо", превратится в 4.1.3 - "только налево"), либо остаться знаком того же класса (например, 1.20.1 -
"сужение дороги"). Соответственно horz_flip_map содержит словарь преобразования классов знаков при горизонтальном отражении. В случае, если знак перестаёт
быть знаком при отображении, либо не пишем его в словарь, либо пишем ему значение "undefined".
3. "min_amount" - из списка классов из предыдущего пункта реально будут работать только те, которых в датасете наберётся больше чем min_amount
4. "min_bbox_width", "min_bbox_height" - размеченный знак попадёт в тренировочный датасет, только если либо его ширина больше "min_bbox_width", либо высота больше
"min_bbox_height". В тестовый попадёт в любом случае, но при тестах мы штрафуем только за знаки больше 30 пикселей

### Конфигурация тренировки

Object detection API из TensorFlow использует для настройки тренировки конфигурационные файлы. Мы должны создать шаблон такого файла (см.
*faster_rcnn_resnet101_traffic_signs.template.config*) и указать его в *./launch_pipeline.sh* туда будут автоматически добавлены пути к созданному
датасету, списку классов и т.п. на этапе генерации датасета, и полученная конфигурация будет сохранена в базовую директорию в файл с именем
*pipeline.config*

## Генерация датасета и тренировка

Запускаем скрипт *./launch_pipeline.sh* и передаём в него один параметр - путь к базовой папке.

### Генерация датасета

Для генерации датасета в формате TFRecord скрипт вызывает код на питоне *./code/make_ds_from_json.py*. Этот скрипт

1. Скачивает json файлы с разметкой, описанные в 'setting.json'
2. Объединяет все json файлы.
3. Удаляет записи о знаках классов не из списка из 'setting.json' или с 'answer' не равным 'ok', а также знаки с размерами меньшими допустимых.
4. Пытается скачать изображения на которых размечены знаки. Изображения кешируются в папке './images', если изображение найдено в папке, вторично оно
не скачивается. URL изображений, которые не удалось скачать, записываются в файл 'bad_urls.txt' в базовой папке.
5. Повтороно считается статистика по знакам, удаляя записи для которых не удалось скачать изображение.
6. Оставляем только те знаки, которые представлены достаточным кол-вом представителей (см. "min_amount" в 'setting.json')
7. В базовой папке генерируется два файла 'train.tfrecord' и 'test.tfrecord' - данные разметки в формате TFRecord. В первый файл попадают изображения
с вероятностью задаваемой параметром *TRAIN_PART* в файле *code/make_ds_from_json.py*, остальные идут в файл 'test.tfrecord'.
8. В базовой папке генерируется файл 'label_map.pbtxt' со списком классов для TF процедуры тренировки.
9. В базовой папке генерируется файел 'pipeline.config' с конфигурацией тренировки

### Тренировка

Процедура тренировки запускается скриптом */object_detection/train.py* из Tensorflow.

## Экспорт графа для inference

Для экспорта графа для inference сети надо запустить скрипт *export_inference_graph.sh* передав ему в качестве параметра путь к базовой папке.

Для каждой точки сохранения вида *model.ckpt-<number>.index* в базовой папке будет создана папка 'export_graphs/<number>', а в ней файл
'frozen_inference_graph.pb' с графом.


## Тестирование

Для тестов надо запустить скрипт *test.sh* передав ему в качестве параметра путь к базовой папке. Он протестирует все графы из 'export_graphs' и
создаст в базовой папке файл 'test.stat' с результатами. Результат таблица со столбцами, соответствующими GT данным + негативы, и строками,
соответствующими данными полученными от сети.


