From 6c6ace8e9d44e2ca7e8e22c65f57f530751bc6a4 Mon Sep 17 00:00:00 2001
From: Mathieu Beligon <mathieu@feedly.com>
Date: Sun, 10 Jan 2021 12:57:04 +0100
Subject: [PATCH] [all] merge all projects, and explore digit recognition

---
 .gitignore                                    |   1 +
 common/doc/change_venv.png                    | Bin 88731 -> 0 bytes
 .../digits/.changes                           | Bin 75097 -> 82433 bytes
 poetry.lock                                   | Bin 157358 -> 146500 bytes
 {common => polystar_cv}/README.md             |   0
 .../common => polystar_cv}/__init__.py        |   0
 {common => polystar_cv}/config/settings.toml  |   2 +
 .../polystar}/__init__.py                     |   0
 .../polystar/common}/__init__.py              |   0
 .../common/communication}/__init__.py         |   0
 .../file_descriptor_target_sender.py          |   0
 .../communication/print_target_sender.py      |   0
 .../common/communication/target_sender_abc.py |   0
 .../common/communication/usb_target_sender.py |   0
 .../polystar/common/constants.py              |   0
 .../polystar/common/dependency_injection.py   |   8 +-
 .../polystar/common/filters}/__init__.py      |   0
 .../polystar/common/filters/exclude_filter.py |   0
 .../polystar/common/filters/filter_abc.py     |   0
 .../polystar/common/filters/keep_filter.py    |   0
 .../common/filters/pass_through_filter.py     |   0
 .../common/frame_generators}/__init__.py      |   0
 .../camera_frame_generator.py                 |   0
 .../cv2_frame_generator_abc.py                |   0
 .../fps_video_frame_generator.py              |   0
 .../frame_generators/frames_generator_abc.py  |   0
 .../frame_generators/video_frame_generator.py |   0
 .../polystar/common/models}/__init__.py       |   0
 .../polystar/common/models/box.py             |   0
 .../polystar/common/models/camera.py          |   0
 .../polystar/common/models/image.py           |   9 +
 .../common/models/image_annotation.py         |   0
 .../polystar/common/models/label_map.py       |   0
 .../polystar/common/models/object.py          |  34 +--
 .../polystar/common/models/tf_model.py        |   0
 .../polystar/common/models/trt_model.py       |   0
 .../polystar/common/pipeline}/__init__.py     |   0
 .../pipeline/classification}/__init__.py      |   0
 .../classification/classification_pipeline.py |  23 +-
 .../pipeline/classification/classifier_abc.py |   0
 .../keras_classification_pipeline.py          | 179 +++++++++++++
 .../pipeline/classification/random_model.py   |   0
 .../classification/rule_based_classifier.py   |   0
 .../polystar/common/pipeline/concat.py        |   0
 .../common/pipeline/featurizers}/__init__.py  |   0
 .../pipeline}/featurizers/histogram_2d.py     |  13 +-
 .../featurizers/histogram_blocs_2d.py         |  30 +++
 .../common/pipeline/keras}/__init__.py        |   0
 .../common/pipeline/keras/classifier.py       |  54 ++++
 .../polystar/common/pipeline/keras/cnn.py     |  27 ++
 .../pipeline/keras/compilation_parameters.py  |  14 +
 .../common/pipeline/keras/data_preparator.py  |  15 ++
 .../common/pipeline/keras/distillation.py     |  58 ++++
 .../common/pipeline/keras/model_preparator.py |   6 +
 .../polystar/common/pipeline/keras/trainer.py |  41 +++
 .../pipeline/keras/transfer_learning.py       |  24 ++
 .../polystar/common/pipeline/pipe_abc.py      |   0
 .../polystar/common/pipeline/pipeline.py      |   4 +-
 .../pipeline/preprocessors}/__init__.py       |   0
 .../pipeline}/preprocessors/normalise.py      |   2 +
 .../common/pipeline}/preprocessors/resize.py  |   2 +
 .../pipeline}/preprocessors/rgb_to_hsv.py     |   0
 .../polystar/common/settings.py               |   9 +-
 .../common/target_pipeline}/__init__.py       |   0
 .../armors_descriptors}/__init__.py           |   0
 .../armors_color_descriptor.py                |   0
 .../armors_descriptor_abc.py                  |   0
 .../common/target_pipeline/debug_pipeline.py  |   0
 .../detected_objects}/__init__.py             |   0
 .../detected_objects/detected_armor.py        |   2 +-
 .../detected_objects/detected_object.py       |   0
 .../detected_objects_factory.py               |   0
 .../detected_objects/detected_robot.py        |   0
 .../object_selectors}/__init__.py             |   0
 .../closest_object_selector.py                |   0
 .../object_selectors/object_selector_abc.py   |   0
 .../scored_object_selector_abc.py             |   0
 .../objects_detectors}/__init__.py            |   0
 .../objects_detectors/objects_detector_abc.py |   0
 .../tf_model_objects_detector.py              |   0
 .../trt_model_object_detector.py              |   0
 .../objects_linker}/__init__.py               |   0
 .../objects_linker/objects_linker_abs.py      |   0
 .../objects_linker/simple_objects_linker.py   |   0
 .../objects_trackers}/__init__.py             |   0
 .../objects_trackers/object_track.py          |   0
 .../objects_trackers/objects_tracker_abc.py   |   0
 .../objects_validators}/__init__.py           |   0
 .../confidence_object_validator.py            |   0
 .../contains_box_validator.py                 |   0
 .../objects_validators/in_box_validator.py    |   0
 .../objects_validators/negation_validator.py  |   0
 .../objects_validator_abc.py                  |   0
 .../robot_color_validator.py                  |   0
 .../type_object_validator.py                  |   0
 .../common/target_pipeline/target_abc.py      |   0
 .../target_factories}/__init__.py             |   0
 .../ratio_simple_target_factory.py            |   0
 .../ratio_target_factory_abc.py               |   0
 .../target_factories/target_factory_abc.py    |   0
 .../common/target_pipeline/target_pipeline.py |   0
 .../tracking_target_pipeline.py               |   0
 .../polystar/common/utils}/__init__.py        |   0
 .../polystar/common/utils/dataframe.py        |   0
 .../polystar/common/utils/git.py              |   0
 .../polystar/common/utils/iterable_utils.py   |   0
 .../polystar/common/utils/logs.py             |   0
 .../polystar/common/utils/markdown.py         |   6 +-
 .../polystar/common/utils/misc.py             |   0
 .../polystar/common/utils/named_mixin.py      |   0
 .../polystar/common/utils/no_case_enum.py     |   2 +-
 polystar_cv/polystar/common/utils/registry.py |  18 ++
 .../polystar/common/utils/serialization.py    |  25 ++
 .../polystar/common/utils/singleton.py        |  16 ++
 .../polystar/common/utils/str_utils.py        |   0
 .../polystar/common/utils/tensorflow.py       |   0
 .../polystar/common/utils/time.py             |   0
 .../polystar/common/utils/tqdm.py             |   0
 .../common/utils/working_directory.py         |   0
 .../polystar/common/view}/__init__.py         |   0
 .../common/view/cv2_results_viewer.py         |   0
 .../common/view/plt_results_viewer.py         |   0
 .../common/view/results_viewer_abc.py         |   0
 .../research}/__init__.py                     |   0
 .../research/common}/__init__.py              |   0
 .../research/common/constants.py              |   0
 .../research/common/dataset}/__init__.py      |   0
 .../common/dataset/cleaning}/__init__.py      |   0
 .../dataset/cleaning/dataset_changes.py       |   8 +
 .../dataset/cleaning/dataset_cleaner_app.py   |   0
 .../common/dataset/improvement}/__init__.py   |   0
 .../common/dataset/improvement/zoom.py        |   0
 .../common/dataset/perturbations}/__init__.py |   0
 .../dataset/perturbations/examples/.gitignore |   0
 .../dataset/perturbations/examples/test.png   | Bin
 .../image_modifiers}/__init__.py              |   0
 .../image_modifiers/brightness.py             |   0
 .../perturbations/image_modifiers/contrast.py |   0
 .../image_modifiers/gaussian_blur.py          |   0
 .../image_modifiers/gaussian_noise.py         |   0
 .../image_modifiers/horizontal_blur.py        |   0
 .../image_modifiers/image_modifier_abc.py     |   0
 .../image_modifiers/saturation.py             |   0
 .../dataset/perturbations/perturbator.py      |   0
 .../common/dataset/perturbations/utils.py     |   0
 .../common/dataset/tensorflow_record.py       |   0
 .../common/dataset/twitch}/__init__.py        |   0
 .../dataset/twitch/aerial_view_detector.py    |   0
 .../common/dataset/twitch/mask_aerial.jpg     | Bin
 .../common/dataset/twitch/mask_detector.py    |   0
 .../common/dataset/twitch/mask_robot_view.jpg | Bin
 .../dataset/twitch/robots_views_extractor.py  |   0
 .../research/common/dataset/upload.py         |  10 +-
 .../research/common/datasets}/__init__.py     |   0
 .../research/common/datasets/dataset.py       |   0
 .../common/datasets/dataset_builder.py        |   0
 .../common/datasets/filter_dataset.py         |   0
 .../research/common/datasets/image_dataset.py |   0
 .../datasets/image_file_dataset_builder.py    |   0
 .../common/datasets/iterator_dataset.py       |   0
 .../research/common/datasets/lazy_dataset.py  |   0
 .../common/datasets/roco}/__init__.py         |   0
 .../common/datasets/roco/roco_annotation.py   |   0
 .../common/datasets/roco/roco_dataset.py      |   0
 .../datasets/roco/roco_dataset_builder.py     |   0
 .../datasets/roco/roco_dataset_descriptor.py  |   0
 .../common/datasets/roco/roco_datasets.py     |   3 +
 .../common/datasets/roco/zoo}/__init__.py     |   0
 .../research/common/datasets/roco/zoo/dji.py  |   0
 .../common/datasets/roco/zoo/dji_zoomed.py    |   0
 .../datasets/roco/zoo/roco_dataset_zoo.py     |   0
 .../common/datasets/roco/zoo/twitch.py        |   0
 .../research/common/datasets/slice_dataset.py |   0
 .../common/datasets/transform_dataset.py      |   0
 .../research/common/datasets/union_dataset.py |   0
 .../research/common/gcloud/gcloud_storage.py  |  57 ++--
 .../research/common/scripts}/__init__.py      |   0
 ...onstruct_dataset_from_manual_annotation.py |   0
 ...t_twith_datasets_from_manual_annotation.py |   0
 .../common/scripts/correct_annotations.py     |   0
 .../scripts/create_tensorflow_records.py      |   0
 .../extract_robots_views_from_video.py        |   0
 .../common/scripts/improve_roco_by_zooming.py |   0
 .../scripts/make_twitch_chunks_to_annotate.py |   0
 .../common/scripts/move_aerial_views.py       |   0
 .../common/scripts/visualize_dataset.py       |   0
 .../research/common/utils/experiment_dir.py   |  15 ++
 .../research/robots}/__init__.py              |   0
 .../research/robots/armor_color}/__init__.py  |   0
 .../robots/armor_color/benchmarker.py         |  16 ++
 .../research/robots/armor_color/datasets.py   |  49 ++++
 .../research/robots/armor_color/pipeline.py   |  11 +
 .../robots/armor_color/scripts}/__init__.py   |   0
 .../robots/armor_color/scripts/benchmark.py   |  64 +++++
 .../armor_color/scripts/hyper_tune_cnn.py     |  35 +++
 .../research/robots}/armor_digit/__init__.py  |   0
 .../robots/armor_digit/armor_digit_dataset.py |  62 +++++
 .../robots/armor_digit/digit_benchmarker.py   |  16 ++
 .../robots/armor_digit/gcloud}/__init__.py    |   0
 .../armor_digit/gcloud/gather_performances.py |  41 +++
 .../armor_digit/gcloud/hptuning_config.yaml   |  33 +++
 .../robots/armor_digit/gcloud/train.py        |  19 ++
 .../robots/armor_digit/gcloud/train_cnn.py    |  29 ++
 .../robots/armor_digit/gcloud/train_vgg16.py  |  31 +++
 .../armor_digit/gcloud/train_xception.py      |  31 +++
 .../robots/armor_digit/gcloud/trainer.sh      |  30 +++
 .../research/robots/armor_digit/pipeline.py   |  13 +
 .../robots/armor_digit/scripts}/__init__.py   |   0
 .../robots/armor_digit/scripts/benchmark.py   |  56 ++++
 .../armor_digit/scripts}/clean_datasets.py    |  19 +-
 .../armor_digit/scripts/hyper_tune_cnn.py     |  32 +++
 .../hyper_tune_distiled_vgg16_into_cnn.py     |  39 +++
 .../robots/armor_digit/scripts/train_vgg16.py |  37 +++
 .../research/robots/dataset}/__init__.py      |   0
 .../robots}/dataset/armor_dataset_factory.py  |   0
 .../dataset/armor_value_dataset_cache.py      |  23 +-
 .../dataset/armor_value_dataset_generator.py  |  19 +-
 .../dataset/armor_value_target_factory.py     |   0
 .../research/robots/demos}/__init__.py        |   0
 .../research/robots}/demos/demo_infer.py      |   4 +-
 .../research/robots}/demos/demo_pipeline.py   |  11 +-
 .../robots}/demos/demo_pipeline_camera.py     |   2 +-
 .../research/robots}/demos/utils.py           |   0
 .../research/robots/evaluation}/__init__.py   |   0
 .../research/robots/evaluation/benchmarker.py |  50 ++++
 .../research/robots/evaluation/evaluator.py   |  14 +-
 .../research/robots/evaluation/hyper_tuner.py |  34 +++
 .../robots/evaluation/metrics}/__init__.py    |   0
 .../robots}/evaluation/metrics/accuracy.py    |   4 +-
 .../research/robots}/evaluation/metrics/f1.py |   4 +-
 .../robots}/evaluation/metrics/metric_abc.py  |   2 +-
 .../robots}/evaluation/performance.py         |  15 +-
 .../research/robots/evaluation/reporter.py    | 184 +++++++------
 .../research/robots}/evaluation/set.py        |   0
 .../research/robots}/evaluation/trainer.py    |   0
 .../research/runes}/__init__.py               |   0
 .../research/runes}/constants.py              |   0
 .../research/runes/dataset}/__init__.py       |   0
 .../research/runes/dataset/blend/__init__.py  |   0
 .../runes}/dataset/blend/examples/.gitignore  |   0
 .../runes}/dataset/blend/examples/back1.jpg   | Bin
 .../runes}/dataset/blend/examples/logo.png    | Bin
 .../runes}/dataset/blend/examples/logo.xml    |   0
 .../runes}/dataset/blend/image_blender.py     |   9 +-
 .../blend/labeled_image_modifiers/__init__.py |   0
 .../labeled_image_modifier_abc.py             |   2 +-
 .../labeled_image_rotator.py                  |   7 +-
 .../labeled_image_scaler.py                   |   5 +-
 .../runes}/dataset/dataset_generator.py       |  10 +-
 .../research/runes}/dataset/labeled_image.py  |   0
 .../common/integration_tests/__init__.py      |   0
 .../integration_tests/datasets/__init__.py    |   0
 .../datasets/test_dji_dataset.py              |   0
 .../datasets/test_dji_zoomed_dataset.py       |   0
 .../datasets/test_twitch_dataset_v1.py        |   0
 .../tests/common/unittests/__init__.py        |   0
 .../roco/test_directory_dataset_zoo.py        |   0
 .../common/unittests/datasets/test_dataset.py |   0
 .../roco/test_directory_dataset_zoo.py        |   0
 .../unittests/datasets_v3/test_dataset.py     |   0
 .../unittests/filters/test_filters_abc.py     |   0
 .../test_image_classifier_pipeline.py         |   0
 .../unittests/object_validators/__init__.py   |   0
 .../test_in_box_validator.py                  |   0
 pyproject.toml                                |  26 +-
 robots-at-robots/Readme.md                    |   6 -
 robots-at-robots/config/settings.toml         |   6 -
 .../robots_at_robots/dependency_injection.py  |  16 --
 .../polystar/robots_at_robots/globals.py      |   5 -
 .../armor_color/armor_color_benchmarker.py    |  24 --
 .../armor_color/armor_color_dataset.py        |  26 --
 .../robots_at_robots/armor_color/benchmark.py |  64 -----
 .../armor_digit/armor_digit_benchmarker.py    |  31 ---
 .../armor_digit/armor_digit_dataset.py        |  32 ---
 .../robots_at_robots/armor_digit/benchmark.py | 253 ------------------
 .../research/robots_at_robots/constants.py    |   1 -
 .../robots_at_robots/evaluation/benchmark.py  |  56 ----
 robots-at-runes/Readme.md                     |   6 -
 robots-at-runes/config/settings.toml          |   5 -
 .../polystar/robots_at_runes/globals.py       |   5 -
 280 files changed, 1585 insertions(+), 756 deletions(-)
 delete mode 100644 common/doc/change_venv.png
 rename {common => polystar_cv}/README.md (100%)
 rename {common/polystar/common => polystar_cv}/__init__.py (100%)
 rename {common => polystar_cv}/config/settings.toml (67%)
 rename {common/polystar/common/communication => polystar_cv/polystar}/__init__.py (100%)
 rename {common/polystar/common/filters => polystar_cv/polystar/common}/__init__.py (100%)
 rename {common/polystar/common/frame_generators => polystar_cv/polystar/common/communication}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/communication/file_descriptor_target_sender.py (100%)
 rename {common => polystar_cv}/polystar/common/communication/print_target_sender.py (100%)
 rename {common => polystar_cv}/polystar/common/communication/target_sender_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/communication/usb_target_sender.py (100%)
 rename {common => polystar_cv}/polystar/common/constants.py (100%)
 rename {common => polystar_cv}/polystar/common/dependency_injection.py (80%)
 rename {common/polystar/common/image_pipeline => polystar_cv/polystar/common/filters}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/filters/exclude_filter.py (100%)
 rename {common => polystar_cv}/polystar/common/filters/filter_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/filters/keep_filter.py (100%)
 rename {common => polystar_cv}/polystar/common/filters/pass_through_filter.py (100%)
 rename {common/polystar/common/image_pipeline/featurizers => polystar_cv/polystar/common/frame_generators}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/frame_generators/camera_frame_generator.py (100%)
 rename {common => polystar_cv}/polystar/common/frame_generators/cv2_frame_generator_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/frame_generators/fps_video_frame_generator.py (100%)
 rename {common => polystar_cv}/polystar/common/frame_generators/frames_generator_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/frame_generators/video_frame_generator.py (100%)
 rename {common/polystar/common/image_pipeline/preprocessors => polystar_cv/polystar/common/models}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/models/box.py (100%)
 rename {common => polystar_cv}/polystar/common/models/camera.py (100%)
 rename {common => polystar_cv}/polystar/common/models/image.py (82%)
 rename {common => polystar_cv}/polystar/common/models/image_annotation.py (100%)
 rename {common => polystar_cv}/polystar/common/models/label_map.py (100%)
 rename {common => polystar_cv}/polystar/common/models/object.py (81%)
 rename {common => polystar_cv}/polystar/common/models/tf_model.py (100%)
 rename {common => polystar_cv}/polystar/common/models/trt_model.py (100%)
 rename {common/polystar/common/models => polystar_cv/polystar/common/pipeline}/__init__.py (100%)
 rename {common/polystar/common/pipeline => polystar_cv/polystar/common/pipeline/classification}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/pipeline/classification/classification_pipeline.py (81%)
 rename {common => polystar_cv}/polystar/common/pipeline/classification/classifier_abc.py (100%)
 create mode 100644 polystar_cv/polystar/common/pipeline/classification/keras_classification_pipeline.py
 rename {common => polystar_cv}/polystar/common/pipeline/classification/random_model.py (100%)
 rename {common => polystar_cv}/polystar/common/pipeline/classification/rule_based_classifier.py (100%)
 rename {common => polystar_cv}/polystar/common/pipeline/concat.py (100%)
 rename {common/polystar/common/pipeline/classification => polystar_cv/polystar/common/pipeline/featurizers}/__init__.py (100%)
 rename {common/polystar/common/image_pipeline => polystar_cv/polystar/common/pipeline}/featurizers/histogram_2d.py (51%)
 create mode 100644 polystar_cv/polystar/common/pipeline/featurizers/histogram_blocs_2d.py
 rename {common/polystar/common/target_pipeline => polystar_cv/polystar/common/pipeline/keras}/__init__.py (100%)
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/classifier.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/cnn.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/compilation_parameters.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/data_preparator.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/distillation.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/model_preparator.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/trainer.py
 create mode 100644 polystar_cv/polystar/common/pipeline/keras/transfer_learning.py
 rename {common => polystar_cv}/polystar/common/pipeline/pipe_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/pipeline/pipeline.py (81%)
 rename {common/polystar/common/target_pipeline/armors_descriptors => polystar_cv/polystar/common/pipeline/preprocessors}/__init__.py (100%)
 rename {common/polystar/common/image_pipeline => polystar_cv/polystar/common/pipeline}/preprocessors/normalise.py (74%)
 rename {common/polystar/common/image_pipeline => polystar_cv/polystar/common/pipeline}/preprocessors/resize.py (82%)
 rename {common/polystar/common/image_pipeline => polystar_cv/polystar/common/pipeline}/preprocessors/rgb_to_hsv.py (100%)
 rename {common => polystar_cv}/polystar/common/settings.py (69%)
 rename {common/polystar/common/target_pipeline/detected_objects => polystar_cv/polystar/common/target_pipeline}/__init__.py (100%)
 rename {common/polystar/common/target_pipeline/object_selectors => polystar_cv/polystar/common/target_pipeline/armors_descriptors}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/armors_descriptors/armors_color_descriptor.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/armors_descriptors/armors_descriptor_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/debug_pipeline.py (100%)
 rename {common/polystar/common/target_pipeline/objects_detectors => polystar_cv/polystar/common/target_pipeline/detected_objects}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/detected_objects/detected_armor.py (97%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/detected_objects/detected_object.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/detected_objects/detected_objects_factory.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/detected_objects/detected_robot.py (100%)
 rename {common/polystar/common/target_pipeline/objects_linker => polystar_cv/polystar/common/target_pipeline/object_selectors}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/object_selectors/closest_object_selector.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/object_selectors/object_selector_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/object_selectors/scored_object_selector_abc.py (100%)
 rename {common/polystar/common/target_pipeline/objects_trackers => polystar_cv/polystar/common/target_pipeline/objects_detectors}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_detectors/objects_detector_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_detectors/tf_model_objects_detector.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_detectors/trt_model_object_detector.py (100%)
 rename {common/polystar/common/target_pipeline/objects_validators => polystar_cv/polystar/common/target_pipeline/objects_linker}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_linker/objects_linker_abs.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_linker/simple_objects_linker.py (100%)
 rename {common/polystar/common/target_pipeline/target_factories => polystar_cv/polystar/common/target_pipeline/objects_trackers}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_trackers/object_track.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_trackers/objects_tracker_abc.py (100%)
 rename {common/polystar/common/utils => polystar_cv/polystar/common/target_pipeline/objects_validators}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/confidence_object_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/contains_box_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/in_box_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/negation_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/objects_validator_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/robot_color_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/objects_validators/type_object_validator.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/target_abc.py (100%)
 rename {common/polystar/common/view => polystar_cv/polystar/common/target_pipeline/target_factories}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/target_factories/ratio_simple_target_factory.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/target_factories/ratio_target_factory_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/target_factories/target_factory_abc.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/target_pipeline.py (100%)
 rename {common => polystar_cv}/polystar/common/target_pipeline/tracking_target_pipeline.py (100%)
 rename {common/research/common => polystar_cv/polystar/common/utils}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/dataframe.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/git.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/iterable_utils.py (100%)
 rename common/research/common/dataset/__init__.py => polystar_cv/polystar/common/utils/logs.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/markdown.py (94%)
 rename {common => polystar_cv}/polystar/common/utils/misc.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/named_mixin.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/no_case_enum.py (72%)
 create mode 100644 polystar_cv/polystar/common/utils/registry.py
 create mode 100644 polystar_cv/polystar/common/utils/serialization.py
 create mode 100644 polystar_cv/polystar/common/utils/singleton.py
 rename {common => polystar_cv}/polystar/common/utils/str_utils.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/tensorflow.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/time.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/tqdm.py (100%)
 rename {common => polystar_cv}/polystar/common/utils/working_directory.py (100%)
 rename {common/research/common/dataset/cleaning => polystar_cv/polystar/common/view}/__init__.py (100%)
 rename {common => polystar_cv}/polystar/common/view/cv2_results_viewer.py (100%)
 rename {common => polystar_cv}/polystar/common/view/plt_results_viewer.py (100%)
 rename {common => polystar_cv}/polystar/common/view/results_viewer_abc.py (100%)
 rename {common/research/common/dataset/improvement => polystar_cv/research}/__init__.py (100%)
 rename {common/research/common/dataset/perturbations => polystar_cv/research/common}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/constants.py (100%)
 rename {common/research/common/dataset/perturbations/image_modifiers => polystar_cv/research/common/dataset}/__init__.py (100%)
 rename {common/research/common/dataset/twitch => polystar_cv/research/common/dataset/cleaning}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/dataset/cleaning/dataset_changes.py (77%)
 rename {common => polystar_cv}/research/common/dataset/cleaning/dataset_cleaner_app.py (100%)
 rename {common/research/common/datasets => polystar_cv/research/common/dataset/improvement}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/dataset/improvement/zoom.py (100%)
 rename {common/research/common/datasets/roco => polystar_cv/research/common/dataset/perturbations}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/examples/.gitignore (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/examples/test.png (100%)
 rename {common/research/common/datasets/roco/zoo => polystar_cv/research/common/dataset/perturbations/image_modifiers}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/brightness.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/contrast.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/gaussian_blur.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/gaussian_noise.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/horizontal_blur.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/image_modifier_abc.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/image_modifiers/saturation.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/perturbator.py (100%)
 rename {common => polystar_cv}/research/common/dataset/perturbations/utils.py (100%)
 rename {common => polystar_cv}/research/common/dataset/tensorflow_record.py (100%)
 rename {common/research/common/scripts => polystar_cv/research/common/dataset/twitch}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/dataset/twitch/aerial_view_detector.py (100%)
 rename {common => polystar_cv}/research/common/dataset/twitch/mask_aerial.jpg (100%)
 rename {common => polystar_cv}/research/common/dataset/twitch/mask_detector.py (100%)
 rename {common => polystar_cv}/research/common/dataset/twitch/mask_robot_view.jpg (100%)
 rename {common => polystar_cv}/research/common/dataset/twitch/robots_views_extractor.py (100%)
 rename {common => polystar_cv}/research/common/dataset/upload.py (70%)
 rename {common/tests/common/integration_tests => polystar_cv/research/common/datasets}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/datasets/dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/dataset_builder.py (100%)
 rename {common => polystar_cv}/research/common/datasets/filter_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/image_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/image_file_dataset_builder.py (100%)
 rename {common => polystar_cv}/research/common/datasets/iterator_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/lazy_dataset.py (100%)
 rename {common/tests/common/integration_tests/datasets => polystar_cv/research/common/datasets/roco}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/roco_annotation.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/roco_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/roco_dataset_builder.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/roco_dataset_descriptor.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/roco_datasets.py (95%)
 rename {common/tests/common/unittests => polystar_cv/research/common/datasets/roco/zoo}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/zoo/dji.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/zoo/dji_zoomed.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/zoo/roco_dataset_zoo.py (100%)
 rename {common => polystar_cv}/research/common/datasets/roco/zoo/twitch.py (100%)
 rename {common => polystar_cv}/research/common/datasets/slice_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/transform_dataset.py (100%)
 rename {common => polystar_cv}/research/common/datasets/union_dataset.py (100%)
 rename {common => polystar_cv}/research/common/gcloud/gcloud_storage.py (58%)
 rename {common/tests/common/unittests/object_validators => polystar_cv/research/common/scripts}/__init__.py (100%)
 rename {common => polystar_cv}/research/common/scripts/construct_dataset_from_manual_annotation.py (100%)
 rename {common => polystar_cv}/research/common/scripts/construct_twith_datasets_from_manual_annotation.py (100%)
 rename {common => polystar_cv}/research/common/scripts/correct_annotations.py (100%)
 rename {common => polystar_cv}/research/common/scripts/create_tensorflow_records.py (100%)
 rename {common => polystar_cv}/research/common/scripts/extract_robots_views_from_video.py (100%)
 rename {common => polystar_cv}/research/common/scripts/improve_roco_by_zooming.py (100%)
 rename {common => polystar_cv}/research/common/scripts/make_twitch_chunks_to_annotate.py (100%)
 rename {common => polystar_cv}/research/common/scripts/move_aerial_views.py (100%)
 rename {common => polystar_cv}/research/common/scripts/visualize_dataset.py (100%)
 create mode 100644 polystar_cv/research/common/utils/experiment_dir.py
 rename {robots-at-robots/polystar/robots_at_robots => polystar_cv/research/robots}/__init__.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots/armor_color}/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/armor_color/benchmarker.py
 create mode 100644 polystar_cv/research/robots/armor_color/datasets.py
 create mode 100644 polystar_cv/research/robots/armor_color/pipeline.py
 rename {robots-at-robots/research/robots_at_robots/armor_color => polystar_cv/research/robots/armor_color/scripts}/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/armor_color/scripts/benchmark.py
 create mode 100644 polystar_cv/research/robots/armor_color/scripts/hyper_tune_cnn.py
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/armor_digit/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/armor_digit/armor_digit_dataset.py
 create mode 100644 polystar_cv/research/robots/armor_digit/digit_benchmarker.py
 rename {robots-at-robots/research/robots_at_robots/dataset => polystar_cv/research/robots/armor_digit/gcloud}/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/gather_performances.py
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/hptuning_config.yaml
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/train.py
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/train_cnn.py
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/train_vgg16.py
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/train_xception.py
 create mode 100644 polystar_cv/research/robots/armor_digit/gcloud/trainer.sh
 create mode 100644 polystar_cv/research/robots/armor_digit/pipeline.py
 rename {robots-at-robots/research/robots_at_robots/evaluation => polystar_cv/research/robots/armor_digit/scripts}/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/armor_digit/scripts/benchmark.py
 rename {robots-at-robots/research/robots_at_robots/armor_digit => polystar_cv/research/robots/armor_digit/scripts}/clean_datasets.py (62%)
 create mode 100644 polystar_cv/research/robots/armor_digit/scripts/hyper_tune_cnn.py
 create mode 100644 polystar_cv/research/robots/armor_digit/scripts/hyper_tune_distiled_vgg16_into_cnn.py
 create mode 100644 polystar_cv/research/robots/armor_digit/scripts/train_vgg16.py
 rename {robots-at-robots/research/robots_at_robots/evaluation/metrics => polystar_cv/research/robots/dataset}/__init__.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/dataset/armor_dataset_factory.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/dataset/armor_value_dataset_cache.py (73%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/dataset/armor_value_dataset_generator.py (70%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/dataset/armor_value_target_factory.py (100%)
 rename {robots-at-runes/polystar/robots_at_runes => polystar_cv/research/robots/demos}/__init__.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/demos/demo_infer.py (89%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/demos/demo_pipeline.py (90%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/demos/demo_pipeline_camera.py (97%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/demos/utils.py (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/robots/evaluation}/__init__.py (100%)
 create mode 100644 polystar_cv/research/robots/evaluation/benchmarker.py
 rename robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluator.py => polystar_cv/research/robots/evaluation/evaluator.py (83%)
 create mode 100644 polystar_cv/research/robots/evaluation/hyper_tuner.py
 rename {robots-at-runes/research/robots_at_runes/dataset => polystar_cv/research/robots/evaluation/metrics}/__init__.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/metrics/accuracy.py (59%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/metrics/f1.py (79%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/metrics/metric_abc.py (77%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/performance.py (85%)
 rename robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluation_reporter.py => polystar_cv/research/robots/evaluation/reporter.py (66%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/set.py (100%)
 rename {robots-at-robots/research/robots_at_robots => polystar_cv/research/robots}/evaluation/trainer.py (100%)
 rename {robots-at-runes/research/robots_at_runes/dataset/blend => polystar_cv/research/runes}/__init__.py (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/constants.py (100%)
 rename {robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers => polystar_cv/research/runes/dataset}/__init__.py (100%)
 create mode 100644 polystar_cv/research/runes/dataset/blend/__init__.py
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/examples/.gitignore (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/examples/back1.jpg (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/examples/logo.png (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/examples/logo.xml (100%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/image_blender.py (88%)
 create mode 100644 polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/__init__.py
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py (94%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py (85%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py (80%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/dataset_generator.py (85%)
 rename {robots-at-runes/research/robots_at_runes => polystar_cv/research/runes}/dataset/labeled_image.py (100%)
 create mode 100644 polystar_cv/tests/common/integration_tests/__init__.py
 create mode 100644 polystar_cv/tests/common/integration_tests/datasets/__init__.py
 rename {common => polystar_cv}/tests/common/integration_tests/datasets/test_dji_dataset.py (100%)
 rename {common => polystar_cv}/tests/common/integration_tests/datasets/test_dji_zoomed_dataset.py (100%)
 rename {common => polystar_cv}/tests/common/integration_tests/datasets/test_twitch_dataset_v1.py (100%)
 create mode 100644 polystar_cv/tests/common/unittests/__init__.py
 rename {common => polystar_cv}/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py (100%)
 rename {common => polystar_cv}/tests/common/unittests/datasets/test_dataset.py (100%)
 rename {common => polystar_cv}/tests/common/unittests/datasets_v3/roco/test_directory_dataset_zoo.py (100%)
 rename {common => polystar_cv}/tests/common/unittests/datasets_v3/test_dataset.py (100%)
 rename {common => polystar_cv}/tests/common/unittests/filters/test_filters_abc.py (100%)
 rename {common => polystar_cv}/tests/common/unittests/image_pipeline/test_image_classifier_pipeline.py (100%)
 create mode 100644 polystar_cv/tests/common/unittests/object_validators/__init__.py
 rename {common => polystar_cv}/tests/common/unittests/object_validators/test_in_box_validator.py (100%)
 delete mode 100644 robots-at-robots/Readme.md
 delete mode 100644 robots-at-robots/config/settings.toml
 delete mode 100644 robots-at-robots/polystar/robots_at_robots/dependency_injection.py
 delete mode 100644 robots-at-robots/polystar/robots_at_robots/globals.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_color/armor_color_benchmarker.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_color/armor_color_dataset.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_color/benchmark.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_benchmarker.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_dataset.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/armor_digit/benchmark.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/constants.py
 delete mode 100644 robots-at-robots/research/robots_at_robots/evaluation/benchmark.py
 delete mode 100644 robots-at-runes/Readme.md
 delete mode 100644 robots-at-runes/config/settings.toml
 delete mode 100644 robots-at-runes/polystar/robots_at_runes/globals.py

diff --git a/.gitignore b/.gitignore
index 6ad15ab..436330e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
 # experiments
 /experiments
 /resources/models
+/pipelines
 
 # TF models
 /models
diff --git a/common/doc/change_venv.png b/common/doc/change_venv.png
deleted file mode 100644
index 3e44e4a2ecb8359fc2a5d18ce2a945f2360aa71a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 88731
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
E59`UU-~a#s

diff --git a/dataset/dji_roco/robomaster_Final Tournament/digits/.changes b/dataset/dji_roco/robomaster_Final Tournament/digits/.changes
index 094f578582ac0df9123e392b0be3aec31d5a695b..9edf650edeb7e75cd18f7d73a48f7889cafe0cf2 100644
GIT binary patch
delta 1823
zcmYjSJ8u<76xQtSjRi>HB{q+kV4Fu_%)-uNXJ#w7e;~+0sghuBg%lzM6bUIv2w#Mt
z#hhXq3NB3q6xcFFiZrw+l6{2`qCvJ4x-^bbMMB}+Ju|a*VSV@PH|P1ynfI@={@u>f
zOLp-je1CKAm|o;s@CvTqoO2J3vi<Pao39(dEU@cG0^Axq<Tj_-0anB8r3OwWqJ)KC
z8gkBrJ3GlvXJS#5ZZN^-8wCCoQaNm2497UEt~HMF7;KIm9yvyeaQOr~ouUhOp0T5&
z$ayAY!J+r_RP!qlGUSveLb)#TJIGfB3_k^m+DU=IBZ`!W3KsT;46xictvyf4j@c~V
zqk7z(FIh241s*>lOCk&mz6rS+7+-4bTbnKB=OV|IlWe|ma{GQDSQxx(aej?(N7#ZJ
zw>`<`8vge0fm6fEE^^orb-{JR88x?t{tA@<<CzaBOPKwYPG+*i#^rxfSD1vAHTsa^
z2k7rPNKKbGi@uK~MLeBI3H?RiEve$jJ5m?e_gBZ+d?HPxXVMe`_)|XuX<_{nIc3tF
zpI`+hENkdK50PpZ{zteY>q2nXqsmo`nySLM{sA_JM^zc=i4t*isS<4^OhGBXqN0?9
znLm6e#IX>>VblrY!5uFFrFPg``Jl`0Pr+pc{9~Y3%R}E9WuiH)+Jvjp&75Fo;H;c#
zGRi86{Tcp4{Yfj-YnVV<*`n=SWr0gSOvLe;R4_A6W+;^hy^27pePC3MhVI`~c&f2E
zv-|v*H7ffVrO!0{zaBVpM{F%&bt7_kkTjaZUCFd8NPu`#9{e^<cTyV|-teu_rqMjk
zOk1}wg(?w?z`vtJnSq{&aN`3df!6XP!OW<F2Z0;9-dL1@^$UIsrh;%~GE<?zx2{r!
zFnuXdG%S2eQ4*u!#bfe4!eIlA5(&*S8ryWk3)dLv_ULS8EY|7;Xe=z<_a!3Ep9GA{
zFeiR$>H@a@4UTH)8PZ7eAxt~LVTYllApn+W+f8Z9Rstg&RA)JCT<~=vj<*(Z6gc9T
zrxkJEWmdF(MSGB#%NX-Rw4xc@7nq7Dgj=0rr<$~aR1zz9gqQH@t#7N9VdBVW6H##k
zBv!%Ymw~MsHtuYTcObUQ;_dRX$5&^W`%$7fZd}B1<60e;Vq*0}FWSVK=0O<MfZH@I
kQ+&nN=q*XCjS@J76u}F1=ErMSj_sbozaQ}bt=+di1FsDMDgXcg

delta 18
ZcmZo{VZC{ZrC|$Wuqqpuf<i4<EdW231+xGE

diff --git a/poetry.lock b/poetry.lock
index deddcd7ff49e9e80124967f26303ac8ffa6db9b9..d853b41c27feb9d0eaed9b23e008abaeed50ccfb 100644
GIT binary patch
delta 19592
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

delta 25339
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

diff --git a/common/README.md b/polystar_cv/README.md
similarity index 100%
rename from common/README.md
rename to polystar_cv/README.md
diff --git a/common/polystar/common/__init__.py b/polystar_cv/__init__.py
similarity index 100%
rename from common/polystar/common/__init__.py
rename to polystar_cv/__init__.py
diff --git a/common/config/settings.toml b/polystar_cv/config/settings.toml
similarity index 67%
rename from common/config/settings.toml
rename to polystar_cv/config/settings.toml
index 9e84f3b..c81c609 100644
--- a/common/config/settings.toml
+++ b/polystar_cv/config/settings.toml
@@ -3,6 +3,8 @@ CAMERA_WIDTH = 1920
 CAMERA_HEIGHT = 1080
 CAMERA_HORIZONTAL_FOV = 120
 
+MODEL_NAME = 'robots/TRT_ssd_mobilenet_v2_roco.bin'
+
 [development]
 
 [production]
diff --git a/common/polystar/common/communication/__init__.py b/polystar_cv/polystar/__init__.py
similarity index 100%
rename from common/polystar/common/communication/__init__.py
rename to polystar_cv/polystar/__init__.py
diff --git a/common/polystar/common/filters/__init__.py b/polystar_cv/polystar/common/__init__.py
similarity index 100%
rename from common/polystar/common/filters/__init__.py
rename to polystar_cv/polystar/common/__init__.py
diff --git a/common/polystar/common/frame_generators/__init__.py b/polystar_cv/polystar/common/communication/__init__.py
similarity index 100%
rename from common/polystar/common/frame_generators/__init__.py
rename to polystar_cv/polystar/common/communication/__init__.py
diff --git a/common/polystar/common/communication/file_descriptor_target_sender.py b/polystar_cv/polystar/common/communication/file_descriptor_target_sender.py
similarity index 100%
rename from common/polystar/common/communication/file_descriptor_target_sender.py
rename to polystar_cv/polystar/common/communication/file_descriptor_target_sender.py
diff --git a/common/polystar/common/communication/print_target_sender.py b/polystar_cv/polystar/common/communication/print_target_sender.py
similarity index 100%
rename from common/polystar/common/communication/print_target_sender.py
rename to polystar_cv/polystar/common/communication/print_target_sender.py
diff --git a/common/polystar/common/communication/target_sender_abc.py b/polystar_cv/polystar/common/communication/target_sender_abc.py
similarity index 100%
rename from common/polystar/common/communication/target_sender_abc.py
rename to polystar_cv/polystar/common/communication/target_sender_abc.py
diff --git a/common/polystar/common/communication/usb_target_sender.py b/polystar_cv/polystar/common/communication/usb_target_sender.py
similarity index 100%
rename from common/polystar/common/communication/usb_target_sender.py
rename to polystar_cv/polystar/common/communication/usb_target_sender.py
diff --git a/common/polystar/common/constants.py b/polystar_cv/polystar/common/constants.py
similarity index 100%
rename from common/polystar/common/constants.py
rename to polystar_cv/polystar/common/constants.py
diff --git a/common/polystar/common/dependency_injection.py b/polystar_cv/polystar/common/dependency_injection.py
similarity index 80%
rename from common/polystar/common/dependency_injection.py
rename to polystar_cv/polystar/common/dependency_injection.py
index fd22dcb..88eca3a 100644
--- a/common/polystar/common/dependency_injection.py
+++ b/polystar_cv/polystar/common/dependency_injection.py
@@ -1,16 +1,16 @@
+from dataclasses import dataclass
 from math import pi
 
-from dataclasses import dataclass
 from dynaconf import LazySettings
-from injector import Module, provider, singleton, multiprovider, Injector
+from injector import Injector, Module, multiprovider, provider, singleton
 
 from polystar.common.constants import LABEL_MAP_PATH
 from polystar.common.models.camera import Camera
 from polystar.common.models.label_map import LabelMap
-from polystar.robots_at_robots.globals import settings
+from polystar.common.settings import settings
 
 
-def make_common_injector() -> Injector:
+def make_injector() -> Injector:
     return Injector(modules=[CommonModule(settings)])
 
 
diff --git a/common/polystar/common/image_pipeline/__init__.py b/polystar_cv/polystar/common/filters/__init__.py
similarity index 100%
rename from common/polystar/common/image_pipeline/__init__.py
rename to polystar_cv/polystar/common/filters/__init__.py
diff --git a/common/polystar/common/filters/exclude_filter.py b/polystar_cv/polystar/common/filters/exclude_filter.py
similarity index 100%
rename from common/polystar/common/filters/exclude_filter.py
rename to polystar_cv/polystar/common/filters/exclude_filter.py
diff --git a/common/polystar/common/filters/filter_abc.py b/polystar_cv/polystar/common/filters/filter_abc.py
similarity index 100%
rename from common/polystar/common/filters/filter_abc.py
rename to polystar_cv/polystar/common/filters/filter_abc.py
diff --git a/common/polystar/common/filters/keep_filter.py b/polystar_cv/polystar/common/filters/keep_filter.py
similarity index 100%
rename from common/polystar/common/filters/keep_filter.py
rename to polystar_cv/polystar/common/filters/keep_filter.py
diff --git a/common/polystar/common/filters/pass_through_filter.py b/polystar_cv/polystar/common/filters/pass_through_filter.py
similarity index 100%
rename from common/polystar/common/filters/pass_through_filter.py
rename to polystar_cv/polystar/common/filters/pass_through_filter.py
diff --git a/common/polystar/common/image_pipeline/featurizers/__init__.py b/polystar_cv/polystar/common/frame_generators/__init__.py
similarity index 100%
rename from common/polystar/common/image_pipeline/featurizers/__init__.py
rename to polystar_cv/polystar/common/frame_generators/__init__.py
diff --git a/common/polystar/common/frame_generators/camera_frame_generator.py b/polystar_cv/polystar/common/frame_generators/camera_frame_generator.py
similarity index 100%
rename from common/polystar/common/frame_generators/camera_frame_generator.py
rename to polystar_cv/polystar/common/frame_generators/camera_frame_generator.py
diff --git a/common/polystar/common/frame_generators/cv2_frame_generator_abc.py b/polystar_cv/polystar/common/frame_generators/cv2_frame_generator_abc.py
similarity index 100%
rename from common/polystar/common/frame_generators/cv2_frame_generator_abc.py
rename to polystar_cv/polystar/common/frame_generators/cv2_frame_generator_abc.py
diff --git a/common/polystar/common/frame_generators/fps_video_frame_generator.py b/polystar_cv/polystar/common/frame_generators/fps_video_frame_generator.py
similarity index 100%
rename from common/polystar/common/frame_generators/fps_video_frame_generator.py
rename to polystar_cv/polystar/common/frame_generators/fps_video_frame_generator.py
diff --git a/common/polystar/common/frame_generators/frames_generator_abc.py b/polystar_cv/polystar/common/frame_generators/frames_generator_abc.py
similarity index 100%
rename from common/polystar/common/frame_generators/frames_generator_abc.py
rename to polystar_cv/polystar/common/frame_generators/frames_generator_abc.py
diff --git a/common/polystar/common/frame_generators/video_frame_generator.py b/polystar_cv/polystar/common/frame_generators/video_frame_generator.py
similarity index 100%
rename from common/polystar/common/frame_generators/video_frame_generator.py
rename to polystar_cv/polystar/common/frame_generators/video_frame_generator.py
diff --git a/common/polystar/common/image_pipeline/preprocessors/__init__.py b/polystar_cv/polystar/common/models/__init__.py
similarity index 100%
rename from common/polystar/common/image_pipeline/preprocessors/__init__.py
rename to polystar_cv/polystar/common/models/__init__.py
diff --git a/common/polystar/common/models/box.py b/polystar_cv/polystar/common/models/box.py
similarity index 100%
rename from common/polystar/common/models/box.py
rename to polystar_cv/polystar/common/models/box.py
diff --git a/common/polystar/common/models/camera.py b/polystar_cv/polystar/common/models/camera.py
similarity index 100%
rename from common/polystar/common/models/camera.py
rename to polystar_cv/polystar/common/models/camera.py
diff --git a/common/polystar/common/models/image.py b/polystar_cv/polystar/common/models/image.py
similarity index 82%
rename from common/polystar/common/models/image.py
rename to polystar_cv/polystar/common/models/image.py
index 29a0b13..2653d6e 100644
--- a/common/polystar/common/models/image.py
+++ b/polystar_cv/polystar/common/models/image.py
@@ -5,6 +5,8 @@ from typing import Iterable, List
 import cv2
 import numpy as np
 
+from polystar.common.constants import PROJECT_DIR
+
 Image = np.ndarray
 
 
@@ -20,6 +22,13 @@ class FileImage:
     def __array__(self) -> np.ndarray:
         return self.image
 
+    def __getstate__(self) -> str:
+        return str(self.path.relative_to(PROJECT_DIR))
+
+    def __setstate__(self, rel_path: str):
+        self.path = PROJECT_DIR / rel_path
+        self.image = load_image(self.path)
+
 
 def load_image(image_path: Path, conversion: int = cv2.COLOR_BGR2RGB) -> Image:
     return cv2.cvtColor(cv2.imread(str(image_path), cv2.IMREAD_UNCHANGED), conversion)
diff --git a/common/polystar/common/models/image_annotation.py b/polystar_cv/polystar/common/models/image_annotation.py
similarity index 100%
rename from common/polystar/common/models/image_annotation.py
rename to polystar_cv/polystar/common/models/image_annotation.py
diff --git a/common/polystar/common/models/label_map.py b/polystar_cv/polystar/common/models/label_map.py
similarity index 100%
rename from common/polystar/common/models/label_map.py
rename to polystar_cv/polystar/common/models/label_map.py
diff --git a/common/polystar/common/models/object.py b/polystar_cv/polystar/common/models/object.py
similarity index 81%
rename from common/polystar/common/models/object.py
rename to polystar_cv/polystar/common/models/object.py
index 06727be..68860b6 100644
--- a/common/polystar/common/models/object.py
+++ b/polystar_cv/polystar/common/models/object.py
@@ -11,39 +11,41 @@ ArmorNumber = NewType("ArmorNumber", int)
 
 
 class ArmorColor(NoCaseEnum):
-    Grey = auto()
-    Blue = auto()
-    Red = auto()
+    GREY = auto()
+    BLUE = auto()
+    RED = auto()
 
-    Unknown = auto()
+    UNKNOWN = auto()
 
     def __str__(self):
         return self.name.lower()
 
     @property
     def short(self) -> str:
-        return self.name[0] if self != ArmorColor.Unknown else "?"
+        return self.name[0] if self != ArmorColor.UNKNOWN else "?"
 
 
 class ArmorDigit(NoCaseEnum):  # CHANGING
     # Those have real numbers
-    HERO = 1
-    ENGINEER = 2
-    STANDARD_1 = 3
-    STANDARD_2 = 4
-    STANDARD_3 = 5
+    HERO = auto()
+    # ENGINEER = 2
+    STANDARD_1 = auto()
+    STANDARD_2 = auto()
+    # STANDARD_3 = 5
+
     # Those have symbols
-    OUTPOST = auto()
-    BASE = auto()
-    SENTRY = auto()
+    # OUTPOST = auto()
+    # BASE = auto()
+    # SENTRY = auto()
 
     UNKNOWN = auto()
     OUTDATED = auto()  # Old labelisation
 
     def __str__(self) -> str:
-        if self.value <= 5:
-            return f"{self.value} ({self.name.title()})"
-        return self.name.title()
+        # if self.value <= 5:
+        #     return f"{self.value} ({self.name.title()})"
+        # return self.name.title()
+        return f"{self.value + (self.value >= 2)} ({self.name.title()})"  # hacky, but a number is missing (2)
 
     @property
     def short(self) -> str:
diff --git a/common/polystar/common/models/tf_model.py b/polystar_cv/polystar/common/models/tf_model.py
similarity index 100%
rename from common/polystar/common/models/tf_model.py
rename to polystar_cv/polystar/common/models/tf_model.py
diff --git a/common/polystar/common/models/trt_model.py b/polystar_cv/polystar/common/models/trt_model.py
similarity index 100%
rename from common/polystar/common/models/trt_model.py
rename to polystar_cv/polystar/common/models/trt_model.py
diff --git a/common/polystar/common/models/__init__.py b/polystar_cv/polystar/common/pipeline/__init__.py
similarity index 100%
rename from common/polystar/common/models/__init__.py
rename to polystar_cv/polystar/common/pipeline/__init__.py
diff --git a/common/polystar/common/pipeline/__init__.py b/polystar_cv/polystar/common/pipeline/classification/__init__.py
similarity index 100%
rename from common/polystar/common/pipeline/__init__.py
rename to polystar_cv/polystar/common/pipeline/classification/__init__.py
diff --git a/common/polystar/common/pipeline/classification/classification_pipeline.py b/polystar_cv/polystar/common/pipeline/classification/classification_pipeline.py
similarity index 81%
rename from common/polystar/common/pipeline/classification/classification_pipeline.py
rename to polystar_cv/polystar/common/pipeline/classification/classification_pipeline.py
index 56086c9..c85c7d6 100644
--- a/common/polystar/common/pipeline/classification/classification_pipeline.py
+++ b/polystar_cv/polystar/common/pipeline/classification/classification_pipeline.py
@@ -1,6 +1,6 @@
 from abc import ABC
 from enum import IntEnum
-from typing import ClassVar, Generic, List, Sequence, Tuple, TypeVar
+from typing import ClassVar, Generic, List, Sequence, Tuple, Type, TypeVar
 
 from numpy import asarray, ndarray, pad
 
@@ -12,11 +12,19 @@ EnumT = TypeVar("EnumT", bound=IntEnum)
 
 
 class ClassificationPipeline(Pipeline, Generic[IT, EnumT], ABC):
-    enum: ClassVar[EnumT]
+    enum: ClassVar[Type[EnumT]]
+
+    classes: ClassVar[List[EnumT]]
+    n_classes: ClassVar[int]
+
+    def __init_subclass__(cls):
+        if hasattr(cls, "enum"):
+            cls.classes = [klass for klass in cls.enum if klass.name not in {"OUTDATED", "UNKNOWN"}]
+            cls.n_classes = len(cls.classes)
 
     def __init__(self, steps: List[Tuple[str, PipeABC]]):
         super().__init__(steps)
-        self.classifier.n_classes = len(self.enum)
+        self.classifier.n_classes = self.n_classes
 
     @property
     def classifier(self) -> ClassifierABC:
@@ -41,20 +49,13 @@ class ClassificationPipeline(Pipeline, Generic[IT, EnumT], ABC):
     def predict_proba_and_classes(self, x: Sequence[IT]) -> Tuple[ndarray, List[EnumT]]:
         proba = asarray(self.predict_proba(x))
         indices = proba.argmax(axis=1)
-        classes = [self.classes_[i] for i in indices]
+        classes = [self.classes[i] for i in indices]
         return proba, classes
 
     def score(self, x: Sequence[IT], y: List[EnumT], **score_params) -> float:
         """It is needed to have a proper CV"""
         return super().score(x, _labels_to_indices(y), **score_params)
 
-    @property
-    def classes_(self) -> List[EnumT]:
-        return list(self.enum)
-
-    def __init_subclass__(cls, **kwargs):
-        assert hasattr(cls, "enum"), f"You need to provide an `enum` ClassVar for {cls.__name__}"
-
 
 def _labels_to_indices(labels: List[EnumT]) -> ndarray:
     return asarray([label.value - 1 for label in labels])
diff --git a/common/polystar/common/pipeline/classification/classifier_abc.py b/polystar_cv/polystar/common/pipeline/classification/classifier_abc.py
similarity index 100%
rename from common/polystar/common/pipeline/classification/classifier_abc.py
rename to polystar_cv/polystar/common/pipeline/classification/classifier_abc.py
diff --git a/polystar_cv/polystar/common/pipeline/classification/keras_classification_pipeline.py b/polystar_cv/polystar/common/pipeline/classification/keras_classification_pipeline.py
new file mode 100644
index 0000000..388e33d
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/classification/keras_classification_pipeline.py
@@ -0,0 +1,179 @@
+from os.path import join
+from typing import Callable, Dict, List, Sequence, Tuple, Union
+
+from hypertune import HyperTune
+from tensorflow.python.keras.callbacks import Callback, EarlyStopping, TensorBoard
+from tensorflow.python.keras.losses import CategoricalCrossentropy
+from tensorflow.python.keras.metrics import categorical_accuracy
+from tensorflow.python.keras.models import Model
+from tensorflow.python.keras.optimizer_v2.adam import Adam
+
+from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
+from polystar.common.pipeline.keras.classifier import KerasClassifier
+from polystar.common.pipeline.keras.cnn import make_cnn_model
+from polystar.common.pipeline.keras.compilation_parameters import KerasCompilationParameters
+from polystar.common.pipeline.keras.data_preparator import KerasDataPreparator
+from polystar.common.pipeline.keras.distillation import DistillationLoss, DistillationMetric, Distiller
+from polystar.common.pipeline.keras.trainer import KerasTrainer
+from polystar.common.pipeline.keras.transfer_learning import make_transfer_learning_model
+from polystar.common.pipeline.preprocessors.normalise import Normalise
+from polystar.common.pipeline.preprocessors.resize import Resize
+
+
+class KerasClassificationPipeline(ClassificationPipeline):
+    @classmethod
+    def from_model(cls, model: Model, trainer: KerasTrainer, input_shape: Tuple[int, int], name: str):
+        return cls.from_pipes(
+            [Resize(input_shape), Normalise(), KerasClassifier(model=model, trainer=trainer)], name=name
+        )
+
+    @classmethod
+    def from_transfer_learning(
+        cls,
+        logs_dir: str,
+        input_size: int,
+        model_factory: Callable[..., Model],
+        dropout: float,
+        dense_size: int,
+        lr: float,
+        verbose: int = 0,
+        name: str = None,
+    ):
+        input_shape = (input_size, input_size)
+        name = name or f"{model_factory.__name__} ({input_size}) - lr {lr:.1e} - drop {dropout:.1%} - {dense_size}"
+        return cls.from_model(
+            model=make_transfer_learning_model(
+                input_shape=input_shape,
+                n_classes=cls.n_classes,
+                model_factory=model_factory,
+                dropout=dropout,
+                dense_size=dense_size,
+            ),
+            trainer=make_classification_trainer(
+                lr=lr, logs_dir=logs_dir, name=name, verbose=verbose, batch_size=32, steps_per_epoch=100
+            ),
+            name=name,
+            input_shape=input_shape,
+        )
+
+    @classmethod
+    def from_custom_cnn(
+        cls,
+        logs_dir: str,
+        input_size: int,
+        conv_blocks: Sequence[Sequence[int]],
+        dropout: float,
+        dense_size: int,
+        lr: float,
+        verbose: int = 0,
+        name: str = None,
+        batch_size: int = 32,
+        steps_per_epoch: Union[str, int] = 100,
+    ) -> ClassificationPipeline:
+        name = name or (
+            f"cnn - ({input_size}) - lr {lr:.1e} - drop {dropout:.1%} - "
+            + " ".join("_".join(map(str, sizes)) for sizes in conv_blocks)
+            + f" - {dense_size}"
+        )
+        input_shape = (input_size, input_size)
+        return cls.from_model(
+            make_cnn_model(
+                input_shape=input_shape,
+                conv_blocks=conv_blocks,
+                dense_size=dense_size,
+                output_size=cls.n_classes,
+                dropout=dropout,
+            ),
+            trainer=make_classification_trainer(
+                lr=lr,
+                logs_dir=logs_dir,
+                name=name,
+                verbose=verbose,
+                batch_size=batch_size,
+                steps_per_epoch=steps_per_epoch,
+            ),
+            name=name,
+            input_shape=input_shape,
+        )
+
+    @classmethod
+    def from_distillation(
+        cls,
+        teacher_pipeline: ClassificationPipeline,
+        logs_dir: str,
+        conv_blocks: Sequence[Sequence[int]],
+        dropout: float,
+        dense_size: int,
+        lr: float,
+        temperature: float,
+        verbose: int = 0,
+        name: str = None,
+    ):
+        input_shape: Tuple[int, int] = teacher_pipeline.named_steps["Resize"].size
+        name = name or (
+            f"distiled - temp {temperature:.1e}"
+            f" - cnn - ({input_shape[0]}) - lr {lr:.1e} - drop {dropout:.1%} - "
+            + " ".join("_".join(map(str, sizes)) for sizes in conv_blocks)
+            + f" - {dense_size}"
+        )
+        return cls.from_model(
+            model=make_cnn_model(
+                input_shape, conv_blocks=conv_blocks, dense_size=dense_size, output_size=cls.n_classes, dropout=dropout,
+            ),
+            trainer=KerasTrainer(
+                model_preparator=Distiller(temperature=temperature, teacher_model=teacher_pipeline.classifier.model),
+                compilation_parameters=KerasCompilationParameters(
+                    loss=DistillationLoss(temperature=temperature, n_classes=cls.n_classes),
+                    metrics=[DistillationMetric(categorical_accuracy, n_classes=cls.n_classes)],
+                    optimizer=Adam(lr),
+                ),
+                callbacks=make_classification_callbacks(join(logs_dir, name)),
+                verbose=verbose,
+            ),
+            name=name,
+            input_shape=input_shape,
+        )
+
+
+def make_classification_callbacks(log_dir: str) -> List[Callback]:
+    return [
+        EarlyStopping(verbose=0, patience=7, restore_best_weights=True, monitor="val_categorical_accuracy"),
+        TensorBoard(log_dir=log_dir),
+        # HyperTuneClassificationCallback(),
+    ]
+
+
+def make_classification_trainer(
+    lr: float, logs_dir: str, name: str, verbose: int, batch_size: int, steps_per_epoch: Union[str, int]
+) -> KerasTrainer:
+    return KerasTrainer(
+        compilation_parameters=KerasCompilationParameters(
+            loss=CategoricalCrossentropy(from_logits=False), metrics=[categorical_accuracy], optimizer=Adam(lr)
+        ),
+        data_preparator=KerasDataPreparator(batch_size=batch_size, steps=steps_per_epoch),
+        callbacks=make_classification_callbacks(join(logs_dir, name)),
+        verbose=verbose,
+    )
+
+
+class HyperTuneClassificationCallback(Callback):
+    def __init__(self):
+        super().__init__()
+        self.hpt = HyperTune()
+        self.best_accuracy_epoch = (0, -1)
+
+    def on_epoch_end(self, epoch: int, logs: Dict = None):
+        accuracy = logs["val_categorical_accuracy"]
+        self._report(accuracy, epoch)
+        self.best_accuracy_epoch = max(self.best_accuracy_epoch, (accuracy, epoch))
+
+    def on_train_begin(self, logs=None):
+        self.best_accuracy_epoch = (0, -1)
+
+    def on_train_end(self, logs=None):
+        self._report(*self.best_accuracy_epoch)
+
+    def _report(self, accuracy: float, epoch: int):
+        self.hpt.report_hyperparameter_tuning_metric(
+            hyperparameter_metric_tag="val_accuracy", metric_value=accuracy, global_step=epoch
+        )
diff --git a/common/polystar/common/pipeline/classification/random_model.py b/polystar_cv/polystar/common/pipeline/classification/random_model.py
similarity index 100%
rename from common/polystar/common/pipeline/classification/random_model.py
rename to polystar_cv/polystar/common/pipeline/classification/random_model.py
diff --git a/common/polystar/common/pipeline/classification/rule_based_classifier.py b/polystar_cv/polystar/common/pipeline/classification/rule_based_classifier.py
similarity index 100%
rename from common/polystar/common/pipeline/classification/rule_based_classifier.py
rename to polystar_cv/polystar/common/pipeline/classification/rule_based_classifier.py
diff --git a/common/polystar/common/pipeline/concat.py b/polystar_cv/polystar/common/pipeline/concat.py
similarity index 100%
rename from common/polystar/common/pipeline/concat.py
rename to polystar_cv/polystar/common/pipeline/concat.py
diff --git a/common/polystar/common/pipeline/classification/__init__.py b/polystar_cv/polystar/common/pipeline/featurizers/__init__.py
similarity index 100%
rename from common/polystar/common/pipeline/classification/__init__.py
rename to polystar_cv/polystar/common/pipeline/featurizers/__init__.py
diff --git a/common/polystar/common/image_pipeline/featurizers/histogram_2d.py b/polystar_cv/polystar/common/pipeline/featurizers/histogram_2d.py
similarity index 51%
rename from common/polystar/common/image_pipeline/featurizers/histogram_2d.py
rename to polystar_cv/polystar/common/pipeline/featurizers/histogram_2d.py
index adbf0e4..d20d6ca 100644
--- a/common/polystar/common/image_pipeline/featurizers/histogram_2d.py
+++ b/polystar_cv/polystar/common/pipeline/featurizers/histogram_2d.py
@@ -12,12 +12,15 @@ class Histogram2D(PipeABC):
     bins: int = 8
 
     def transform_single(self, image: Image) -> ndarray:
-        return array([self._channel_hist(image, channel) for channel in range(3)]).ravel()
-
-    def _channel_hist(self, image: Image, channel: int) -> ndarray:
-        hist = cv2.calcHist([image], [channel], None, [self.bins], [0, 256], accumulate=False).ravel()
-        return hist / hist.sum()
+        return array(
+            [calculate_normalized_channel_histogram(image, channel, self.bins) for channel in range(3)]
+        ).ravel()
 
     @property
     def name(self) -> str:
         return "hist"
+
+
+def calculate_normalized_channel_histogram(image: Image, channel: int, bins: int) -> ndarray:
+    hist = cv2.calcHist([image], [channel], None, [bins], [0, 256], accumulate=False).ravel()
+    return hist / hist.sum()
diff --git a/polystar_cv/polystar/common/pipeline/featurizers/histogram_blocs_2d.py b/polystar_cv/polystar/common/pipeline/featurizers/histogram_blocs_2d.py
new file mode 100644
index 0000000..dcef9d0
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/featurizers/histogram_blocs_2d.py
@@ -0,0 +1,30 @@
+from dataclasses import dataclass
+from itertools import chain
+from typing import Iterable
+
+from numpy import array_split
+from numpy.core._multiarray_umath import array, ndarray
+
+from polystar.common.models.image import Image
+from polystar.common.pipeline.featurizers.histogram_2d import calculate_normalized_channel_histogram
+from polystar.common.pipeline.pipe_abc import PipeABC
+
+
+@dataclass
+class HistogramBlocs2D(PipeABC):
+    bins: int = 8
+    rows: int = 2
+    cols: int = 3
+
+    def transform_single(self, image: Image) -> ndarray:
+        return array(
+            [
+                calculate_normalized_channel_histogram(bloc, channel, self.bins)
+                for channel in range(3)
+                for bloc in _split_images_in_blocs(image, self.rows, self.cols)
+            ]
+        ).ravel()
+
+
+def _split_images_in_blocs(image: Image, n_rows: int, n_cols: int) -> Iterable[Image]:
+    return chain.from_iterable(array_split(column, n_rows, axis=0) for column in array_split(image, n_cols, axis=1))
diff --git a/common/polystar/common/target_pipeline/__init__.py b/polystar_cv/polystar/common/pipeline/keras/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/__init__.py
rename to polystar_cv/polystar/common/pipeline/keras/__init__.py
diff --git a/polystar_cv/polystar/common/pipeline/keras/classifier.py b/polystar_cv/polystar/common/pipeline/keras/classifier.py
new file mode 100644
index 0000000..8568a87
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/classifier.py
@@ -0,0 +1,54 @@
+from copy import copy
+from tempfile import NamedTemporaryFile
+from typing import Dict, List, Optional, Sequence
+
+from numpy import asarray
+from tensorflow.python.keras.models import Model, load_model
+from tensorflow.python.keras.utils.np_utils import to_categorical
+
+from polystar.common.models.image import Image
+from polystar.common.pipeline.classification.classifier_abc import ClassifierABC
+from polystar.common.pipeline.keras.trainer import KerasTrainer
+from polystar.common.utils.registry import registry
+
+
+@registry.register()
+class KerasClassifier(ClassifierABC):
+    def __init__(self, model: Model, trainer: KerasTrainer):
+        self.model = model
+        self.trainer: Optional[KerasTrainer] = trainer
+
+    def fit(self, images: List[Image], labels: List[int], validation_size: int) -> "KerasClassifier":
+        assert self.trainable, "You can't train an un-pickled classifier"
+        images = asarray(images)
+        labels = to_categorical(asarray(labels), self.n_classes)
+        train_images, train_labels = images[:-validation_size], labels[:-validation_size]
+        val_images, val_labels = images[-validation_size:], labels[-validation_size:]
+
+        self.trainer.train(self.model, train_images, train_labels, val_images, val_labels)
+
+        return self
+
+    def predict_proba(self, examples: List[Image]) -> Sequence[float]:
+        return self.model.predict(asarray(examples))
+
+    def __getstate__(self) -> Dict:
+        with NamedTemporaryFile(suffix=".hdf5", delete=True) as fd:
+            self.model.save(fd.name, overwrite=True, include_optimizer=False)
+            model_str = fd.read()
+        state = copy(self.__dict__)
+        state.pop("model")
+        state.pop("trainer")
+        return {**state, "model_str": model_str}
+
+    def __setstate__(self, state: Dict):
+        self.__dict__.update(state)
+        with NamedTemporaryFile(suffix=".hdf5", delete=True) as fd:
+            fd.write(state.pop("model_str"))
+            fd.flush()
+            self.model = load_model(fd.name)
+        self.trainer = None
+
+    @property
+    def trainable(self) -> bool:
+        return self.trainer is not None
diff --git a/polystar_cv/polystar/common/pipeline/keras/cnn.py b/polystar_cv/polystar/common/pipeline/keras/cnn.py
new file mode 100644
index 0000000..8e3cecf
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/cnn.py
@@ -0,0 +1,27 @@
+from typing import Sequence, Tuple
+
+from tensorflow.python.keras import Input, Sequential
+from tensorflow.python.keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D, Softmax
+
+
+def make_cnn_model(
+    input_shape: Tuple[int, int],
+    conv_blocks: Sequence[Sequence[int]],
+    dense_size: int,
+    output_size: int,
+    dropout: float,
+) -> Sequential:
+    model = Sequential()
+    model.add(Input((*input_shape, 3)))
+
+    for conv_sizes in conv_blocks:
+        for size in conv_sizes:
+            model.add(Conv2D(size, (3, 3), activation="relu"))
+        model.add(MaxPooling2D())
+
+    model.add(Flatten())
+    model.add(Dense(dense_size))
+    model.add(Dropout(dropout))
+    model.add(Dense(output_size))
+    model.add(Softmax())
+    return model
diff --git a/polystar_cv/polystar/common/pipeline/keras/compilation_parameters.py b/polystar_cv/polystar/common/pipeline/keras/compilation_parameters.py
new file mode 100644
index 0000000..7bca53c
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/compilation_parameters.py
@@ -0,0 +1,14 @@
+from dataclasses import dataclass
+from typing import Callable, Dict, List, Optional, Union
+
+from tensorflow.python.keras.losses import Loss
+from tensorflow.python.keras.metrics import Metric
+from tensorflow.python.keras.optimizer_v2.optimizer_v2 import OptimizerV2
+
+
+@dataclass
+class KerasCompilationParameters:
+    optimizer: Union[str, OptimizerV2]
+    loss: Union[str, Callable, Loss]
+    metrics: List[Union[str, Callable, Metric]]
+    loss_weights: Optional[Dict[str, float]] = None
diff --git a/polystar_cv/polystar/common/pipeline/keras/data_preparator.py b/polystar_cv/polystar/common/pipeline/keras/data_preparator.py
new file mode 100644
index 0000000..1120ac0
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/data_preparator.py
@@ -0,0 +1,15 @@
+from typing import Any, Tuple, Union
+
+from numpy.core._multiarray_umath import ndarray
+from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
+
+
+class KerasDataPreparator:
+    def __init__(self, batch_size: int, steps: Union[str, int]):
+        self.steps = steps
+        self.batch_size = batch_size
+
+    def prepare_training_data(self, images: ndarray, labels: ndarray) -> Tuple[Any, int]:
+        train_datagen = ImageDataGenerator()
+        steps = self.steps if isinstance(self.steps, int) else len(images) / self.batch_size
+        return train_datagen.flow(images, labels, batch_size=self.batch_size, shuffle=True), steps
diff --git a/polystar_cv/polystar/common/pipeline/keras/distillation.py b/polystar_cv/polystar/common/pipeline/keras/distillation.py
new file mode 100644
index 0000000..79b42c6
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/distillation.py
@@ -0,0 +1,58 @@
+from typing import Callable
+
+from tensorflow.python.keras import Input, Model, Sequential
+from tensorflow.python.keras.layers import Softmax, concatenate
+from tensorflow.python.keras.losses import KLDivergence
+from tensorflow.python.keras.models import Model
+from tensorflow.python.ops.nn_ops import softmax
+
+from polystar.common.pipeline.keras.model_preparator import KerasModelPreparator
+
+
+class DistillationLoss(KLDivergence):
+    def __init__(self, temperature: float, n_classes: int):
+        super().__init__()
+        self.n_classes = n_classes
+        self.temperature = temperature
+
+    def __call__(self, y_true, y_pred, sample_weight=None):
+        teacher_logits, student_logits = y_pred[:, : self.n_classes], y_pred[:, self.n_classes :]
+        return super().__call__(
+            softmax(teacher_logits / self.temperature, axis=1),
+            softmax(student_logits / self.temperature, axis=1),
+            sample_weight=sample_weight,
+        )
+
+
+class DistillationMetric:
+    def __init__(self, metric: Callable, n_classes: int):
+        self.n_classes = n_classes
+        self.metric = metric
+        self.__name__ = metric.__name__
+
+    def __call__(self, y_true, y_pred):
+        teacher_logits, student_logits = y_pred[:, : self.n_classes], y_pred[:, self.n_classes :]
+        return self.metric(y_true, student_logits)
+
+
+class Distiller(KerasModelPreparator):
+    def __init__(
+        self, teacher_model: Model, temperature: float,
+    ):
+        self.teacher_model = teacher_model
+        self.temperature = temperature
+        assert isinstance(teacher_model.layers[-1], Softmax)
+
+    def prepare_model(self, model: Model) -> Model:
+        assert isinstance(model.layers[-1], Softmax)
+
+        self.teacher_model.trainable = False
+
+        inputs = Input(shape=model.input.shape[1:])
+
+        return Model(
+            inputs=inputs,
+            outputs=concatenate(
+                [Sequential(self.teacher_model.layers[:-1])(inputs), Sequential(model.layers[:-1])(inputs)]
+            ),
+        )
diff --git a/polystar_cv/polystar/common/pipeline/keras/model_preparator.py b/polystar_cv/polystar/common/pipeline/keras/model_preparator.py
new file mode 100644
index 0000000..e41e03f
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/model_preparator.py
@@ -0,0 +1,6 @@
+from tensorflow.python.keras.models import Model
+
+
+class KerasModelPreparator:
+    def prepare_model(self, model: Model) -> Model:
+        return model
diff --git a/polystar_cv/polystar/common/pipeline/keras/trainer.py b/polystar_cv/polystar/common/pipeline/keras/trainer.py
new file mode 100644
index 0000000..9db4ba7
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/trainer.py
@@ -0,0 +1,41 @@
+from dataclasses import dataclass, field
+from typing import List
+
+from numpy.core._multiarray_umath import ndarray
+from tensorflow.python.keras.callbacks import Callback
+from tensorflow.python.keras.models import Model
+
+from polystar.common.pipeline.keras.compilation_parameters import KerasCompilationParameters
+from polystar.common.pipeline.keras.data_preparator import KerasDataPreparator
+from polystar.common.pipeline.keras.model_preparator import KerasModelPreparator
+
+
+@dataclass
+class KerasTrainer:
+    compilation_parameters: KerasCompilationParameters
+    callbacks: List[Callback]
+    data_preparator: KerasDataPreparator = field(default_factory=KerasDataPreparator)
+    model_preparator: KerasModelPreparator = field(default_factory=KerasModelPreparator)
+    max_epochs: int = 300
+    verbose: int = 0
+
+    def train(
+        self,
+        model: Model,
+        train_images: ndarray,
+        train_labels: ndarray,
+        validation_images: ndarray,
+        validation_labels: ndarray,
+    ):
+        model = self.model_preparator.prepare_model(model)
+        model.compile(**self.compilation_parameters.__dict__)
+        train_data, steps = self.data_preparator.prepare_training_data(train_images, train_labels)
+
+        model.fit(
+            x=train_data,
+            validation_data=(validation_images, validation_labels),
+            steps_per_epoch=steps,
+            epochs=self.max_epochs,
+            callbacks=self.callbacks,
+            verbose=self.verbose,
+        )
diff --git a/polystar_cv/polystar/common/pipeline/keras/transfer_learning.py b/polystar_cv/polystar/common/pipeline/keras/transfer_learning.py
new file mode 100644
index 0000000..a6a7b47
--- /dev/null
+++ b/polystar_cv/polystar/common/pipeline/keras/transfer_learning.py
@@ -0,0 +1,24 @@
+from typing import Callable, Tuple
+
+from tensorflow.python.keras import Input, Model, Sequential
+from tensorflow.python.keras.layers import Dense, Dropout, Flatten, Softmax
+from tensorflow.python.keras.models import Model
+
+
+def make_transfer_learning_model(
+    input_shape: Tuple[int, int], n_classes: int, model_factory: Callable[..., Model], dropout: float, dense_size: int,
+) -> Sequential:
+    input_shape = (*input_shape, 3)
+    base_model: Model = model_factory(weights="imagenet", input_shape=input_shape, include_top=False)
+
+    return Sequential(
+        [
+            Input(input_shape),
+            base_model,
+            Flatten(),
+            Dense(dense_size, activation="relu"),
+            Dropout(dropout),
+            Dense(n_classes),
+            Softmax(),
+        ]
+    )
diff --git a/common/polystar/common/pipeline/pipe_abc.py b/polystar_cv/polystar/common/pipeline/pipe_abc.py
similarity index 100%
rename from common/polystar/common/pipeline/pipe_abc.py
rename to polystar_cv/polystar/common/pipeline/pipe_abc.py
diff --git a/common/polystar/common/pipeline/pipeline.py b/polystar_cv/polystar/common/pipeline/pipeline.py
similarity index 81%
rename from common/polystar/common/pipeline/pipeline.py
rename to polystar_cv/polystar/common/pipeline/pipeline.py
index 52ed2e2..e09d130 100644
--- a/common/polystar/common/pipeline/pipeline.py
+++ b/polystar_cv/polystar/common/pipeline/pipeline.py
@@ -6,10 +6,12 @@ from polystar.common.pipeline.classification.classifier_abc import ClassifierABC
 from polystar.common.pipeline.pipe_abc import PipeABC, get_pipes_names_without_repetitions
 from polystar.common.utils.named_mixin import NamedMixin
 
+Pipes = List[Union[PipeABC, ClassifierABC]]
+
 
 class Pipeline(Pipeline, NamedMixin):
     @classmethod
-    def from_pipes(cls, pipes: List[Union[PipeABC, ClassifierABC]], name: str = None) -> "Pipeline":
+    def from_pipes(cls, pipes: Pipes, name: str = None) -> "Pipeline":
         names = get_pipes_names_without_repetitions(pipes)
         rv = cls(list(zip(names, pipes)))
         rv.name = name or "-".join(names)
diff --git a/common/polystar/common/target_pipeline/armors_descriptors/__init__.py b/polystar_cv/polystar/common/pipeline/preprocessors/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/armors_descriptors/__init__.py
rename to polystar_cv/polystar/common/pipeline/preprocessors/__init__.py
diff --git a/common/polystar/common/image_pipeline/preprocessors/normalise.py b/polystar_cv/polystar/common/pipeline/preprocessors/normalise.py
similarity index 74%
rename from common/polystar/common/image_pipeline/preprocessors/normalise.py
rename to polystar_cv/polystar/common/pipeline/preprocessors/normalise.py
index a00c8d0..0f84029 100644
--- a/common/polystar/common/image_pipeline/preprocessors/normalise.py
+++ b/polystar_cv/polystar/common/pipeline/preprocessors/normalise.py
@@ -1,7 +1,9 @@
 from polystar.common.models.image import Image
 from polystar.common.pipeline.pipe_abc import PipeABC
+from polystar.common.utils.registry import registry
 
 
+@registry.register()
 class Normalise(PipeABC):
     def transform_single(self, image: Image) -> Image:
         return image / 255
diff --git a/common/polystar/common/image_pipeline/preprocessors/resize.py b/polystar_cv/polystar/common/pipeline/preprocessors/resize.py
similarity index 82%
rename from common/polystar/common/image_pipeline/preprocessors/resize.py
rename to polystar_cv/polystar/common/pipeline/preprocessors/resize.py
index 6afbc2b..c239e0a 100644
--- a/common/polystar/common/image_pipeline/preprocessors/resize.py
+++ b/polystar_cv/polystar/common/pipeline/preprocessors/resize.py
@@ -4,8 +4,10 @@ from cv2.cv2 import resize
 
 from polystar.common.models.image import Image
 from polystar.common.pipeline.pipe_abc import PipeABC
+from polystar.common.utils.registry import registry
 
 
+@registry.register()
 class Resize(PipeABC):
     def __init__(self, size: Tuple[int, int]):
         self.size = size
diff --git a/common/polystar/common/image_pipeline/preprocessors/rgb_to_hsv.py b/polystar_cv/polystar/common/pipeline/preprocessors/rgb_to_hsv.py
similarity index 100%
rename from common/polystar/common/image_pipeline/preprocessors/rgb_to_hsv.py
rename to polystar_cv/polystar/common/pipeline/preprocessors/rgb_to_hsv.py
diff --git a/common/polystar/common/settings.py b/polystar_cv/polystar/common/settings.py
similarity index 69%
rename from common/polystar/common/settings.py
rename to polystar_cv/polystar/common/settings.py
index 943fa5f..23b13f2 100644
--- a/common/polystar/common/settings.py
+++ b/polystar_cv/polystar/common/settings.py
@@ -17,12 +17,15 @@ class Settings(LazySettings):
 
 
 def _config_file_for_project(project_name: str) -> Path:
-    return PROJECT_DIR / project_name / "config" / "settings.toml"
+    return PROJECT_DIR / "config" / "settings.toml"
 
 
-def make_settings(project_name: str) -> LazySettings:
+def make_settings() -> LazySettings:
     return LazySettings(
         SILENT_ERRORS_FOR_DYNACONF=False,
-        SETTINGS_FILE_FOR_DYNACONF=f"{_config_file_for_project('common')},{_config_file_for_project(project_name)}",
+        SETTINGS_FILE_FOR_DYNACONF=f"{PROJECT_DIR  / 'config' / 'settings.toml'}",
         ENV_SWITCHER_FOR_DYNACONF="POLYSTAR_ENV",
     )
+
+
+settings = make_settings()
diff --git a/common/polystar/common/target_pipeline/detected_objects/__init__.py b/polystar_cv/polystar/common/target_pipeline/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/detected_objects/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/__init__.py
diff --git a/common/polystar/common/target_pipeline/object_selectors/__init__.py b/polystar_cv/polystar/common/target_pipeline/armors_descriptors/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/object_selectors/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/armors_descriptors/__init__.py
diff --git a/common/polystar/common/target_pipeline/armors_descriptors/armors_color_descriptor.py b/polystar_cv/polystar/common/target_pipeline/armors_descriptors/armors_color_descriptor.py
similarity index 100%
rename from common/polystar/common/target_pipeline/armors_descriptors/armors_color_descriptor.py
rename to polystar_cv/polystar/common/target_pipeline/armors_descriptors/armors_color_descriptor.py
diff --git a/common/polystar/common/target_pipeline/armors_descriptors/armors_descriptor_abc.py b/polystar_cv/polystar/common/target_pipeline/armors_descriptors/armors_descriptor_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/armors_descriptors/armors_descriptor_abc.py
rename to polystar_cv/polystar/common/target_pipeline/armors_descriptors/armors_descriptor_abc.py
diff --git a/common/polystar/common/target_pipeline/debug_pipeline.py b/polystar_cv/polystar/common/target_pipeline/debug_pipeline.py
similarity index 100%
rename from common/polystar/common/target_pipeline/debug_pipeline.py
rename to polystar_cv/polystar/common/target_pipeline/debug_pipeline.py
diff --git a/common/polystar/common/target_pipeline/objects_detectors/__init__.py b/polystar_cv/polystar/common/target_pipeline/detected_objects/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_detectors/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/detected_objects/__init__.py
diff --git a/common/polystar/common/target_pipeline/detected_objects/detected_armor.py b/polystar_cv/polystar/common/target_pipeline/detected_objects/detected_armor.py
similarity index 97%
rename from common/polystar/common/target_pipeline/detected_objects/detected_armor.py
rename to polystar_cv/polystar/common/target_pipeline/detected_objects/detected_armor.py
index 613385b..3bfaf2b 100644
--- a/common/polystar/common/target_pipeline/detected_objects/detected_armor.py
+++ b/polystar_cv/polystar/common/target_pipeline/detected_objects/detected_armor.py
@@ -27,7 +27,7 @@ class DetectedArmor(DetectedObject):
             self._color = ArmorColor(self.colors_proba.argmax() + 1)
             return self._color
 
-        return ArmorColor.Unknown
+        return ArmorColor.UNKNOWN
 
     @property
     def digit(self) -> ArmorDigit:
diff --git a/common/polystar/common/target_pipeline/detected_objects/detected_object.py b/polystar_cv/polystar/common/target_pipeline/detected_objects/detected_object.py
similarity index 100%
rename from common/polystar/common/target_pipeline/detected_objects/detected_object.py
rename to polystar_cv/polystar/common/target_pipeline/detected_objects/detected_object.py
diff --git a/common/polystar/common/target_pipeline/detected_objects/detected_objects_factory.py b/polystar_cv/polystar/common/target_pipeline/detected_objects/detected_objects_factory.py
similarity index 100%
rename from common/polystar/common/target_pipeline/detected_objects/detected_objects_factory.py
rename to polystar_cv/polystar/common/target_pipeline/detected_objects/detected_objects_factory.py
diff --git a/common/polystar/common/target_pipeline/detected_objects/detected_robot.py b/polystar_cv/polystar/common/target_pipeline/detected_objects/detected_robot.py
similarity index 100%
rename from common/polystar/common/target_pipeline/detected_objects/detected_robot.py
rename to polystar_cv/polystar/common/target_pipeline/detected_objects/detected_robot.py
diff --git a/common/polystar/common/target_pipeline/objects_linker/__init__.py b/polystar_cv/polystar/common/target_pipeline/object_selectors/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_linker/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/object_selectors/__init__.py
diff --git a/common/polystar/common/target_pipeline/object_selectors/closest_object_selector.py b/polystar_cv/polystar/common/target_pipeline/object_selectors/closest_object_selector.py
similarity index 100%
rename from common/polystar/common/target_pipeline/object_selectors/closest_object_selector.py
rename to polystar_cv/polystar/common/target_pipeline/object_selectors/closest_object_selector.py
diff --git a/common/polystar/common/target_pipeline/object_selectors/object_selector_abc.py b/polystar_cv/polystar/common/target_pipeline/object_selectors/object_selector_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/object_selectors/object_selector_abc.py
rename to polystar_cv/polystar/common/target_pipeline/object_selectors/object_selector_abc.py
diff --git a/common/polystar/common/target_pipeline/object_selectors/scored_object_selector_abc.py b/polystar_cv/polystar/common/target_pipeline/object_selectors/scored_object_selector_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/object_selectors/scored_object_selector_abc.py
rename to polystar_cv/polystar/common/target_pipeline/object_selectors/scored_object_selector_abc.py
diff --git a/common/polystar/common/target_pipeline/objects_trackers/__init__.py b/polystar_cv/polystar/common/target_pipeline/objects_detectors/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_trackers/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/objects_detectors/__init__.py
diff --git a/common/polystar/common/target_pipeline/objects_detectors/objects_detector_abc.py b/polystar_cv/polystar/common/target_pipeline/objects_detectors/objects_detector_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_detectors/objects_detector_abc.py
rename to polystar_cv/polystar/common/target_pipeline/objects_detectors/objects_detector_abc.py
diff --git a/common/polystar/common/target_pipeline/objects_detectors/tf_model_objects_detector.py b/polystar_cv/polystar/common/target_pipeline/objects_detectors/tf_model_objects_detector.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_detectors/tf_model_objects_detector.py
rename to polystar_cv/polystar/common/target_pipeline/objects_detectors/tf_model_objects_detector.py
diff --git a/common/polystar/common/target_pipeline/objects_detectors/trt_model_object_detector.py b/polystar_cv/polystar/common/target_pipeline/objects_detectors/trt_model_object_detector.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_detectors/trt_model_object_detector.py
rename to polystar_cv/polystar/common/target_pipeline/objects_detectors/trt_model_object_detector.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/__init__.py b/polystar_cv/polystar/common/target_pipeline/objects_linker/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/objects_linker/__init__.py
diff --git a/common/polystar/common/target_pipeline/objects_linker/objects_linker_abs.py b/polystar_cv/polystar/common/target_pipeline/objects_linker/objects_linker_abs.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_linker/objects_linker_abs.py
rename to polystar_cv/polystar/common/target_pipeline/objects_linker/objects_linker_abs.py
diff --git a/common/polystar/common/target_pipeline/objects_linker/simple_objects_linker.py b/polystar_cv/polystar/common/target_pipeline/objects_linker/simple_objects_linker.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_linker/simple_objects_linker.py
rename to polystar_cv/polystar/common/target_pipeline/objects_linker/simple_objects_linker.py
diff --git a/common/polystar/common/target_pipeline/target_factories/__init__.py b/polystar_cv/polystar/common/target_pipeline/objects_trackers/__init__.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_factories/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/objects_trackers/__init__.py
diff --git a/common/polystar/common/target_pipeline/objects_trackers/object_track.py b/polystar_cv/polystar/common/target_pipeline/objects_trackers/object_track.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_trackers/object_track.py
rename to polystar_cv/polystar/common/target_pipeline/objects_trackers/object_track.py
diff --git a/common/polystar/common/target_pipeline/objects_trackers/objects_tracker_abc.py b/polystar_cv/polystar/common/target_pipeline/objects_trackers/objects_tracker_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_trackers/objects_tracker_abc.py
rename to polystar_cv/polystar/common/target_pipeline/objects_trackers/objects_tracker_abc.py
diff --git a/common/polystar/common/utils/__init__.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/__init__.py
similarity index 100%
rename from common/polystar/common/utils/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/__init__.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/confidence_object_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/confidence_object_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/confidence_object_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/confidence_object_validator.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/contains_box_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/contains_box_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/contains_box_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/contains_box_validator.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/in_box_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/in_box_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/in_box_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/in_box_validator.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/negation_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/negation_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/negation_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/negation_validator.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/objects_validator_abc.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/objects_validator_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/objects_validator_abc.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/objects_validator_abc.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/robot_color_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/robot_color_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/robot_color_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/robot_color_validator.py
diff --git a/common/polystar/common/target_pipeline/objects_validators/type_object_validator.py b/polystar_cv/polystar/common/target_pipeline/objects_validators/type_object_validator.py
similarity index 100%
rename from common/polystar/common/target_pipeline/objects_validators/type_object_validator.py
rename to polystar_cv/polystar/common/target_pipeline/objects_validators/type_object_validator.py
diff --git a/common/polystar/common/target_pipeline/target_abc.py b/polystar_cv/polystar/common/target_pipeline/target_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_abc.py
rename to polystar_cv/polystar/common/target_pipeline/target_abc.py
diff --git a/common/polystar/common/view/__init__.py b/polystar_cv/polystar/common/target_pipeline/target_factories/__init__.py
similarity index 100%
rename from common/polystar/common/view/__init__.py
rename to polystar_cv/polystar/common/target_pipeline/target_factories/__init__.py
diff --git a/common/polystar/common/target_pipeline/target_factories/ratio_simple_target_factory.py b/polystar_cv/polystar/common/target_pipeline/target_factories/ratio_simple_target_factory.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_factories/ratio_simple_target_factory.py
rename to polystar_cv/polystar/common/target_pipeline/target_factories/ratio_simple_target_factory.py
diff --git a/common/polystar/common/target_pipeline/target_factories/ratio_target_factory_abc.py b/polystar_cv/polystar/common/target_pipeline/target_factories/ratio_target_factory_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_factories/ratio_target_factory_abc.py
rename to polystar_cv/polystar/common/target_pipeline/target_factories/ratio_target_factory_abc.py
diff --git a/common/polystar/common/target_pipeline/target_factories/target_factory_abc.py b/polystar_cv/polystar/common/target_pipeline/target_factories/target_factory_abc.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_factories/target_factory_abc.py
rename to polystar_cv/polystar/common/target_pipeline/target_factories/target_factory_abc.py
diff --git a/common/polystar/common/target_pipeline/target_pipeline.py b/polystar_cv/polystar/common/target_pipeline/target_pipeline.py
similarity index 100%
rename from common/polystar/common/target_pipeline/target_pipeline.py
rename to polystar_cv/polystar/common/target_pipeline/target_pipeline.py
diff --git a/common/polystar/common/target_pipeline/tracking_target_pipeline.py b/polystar_cv/polystar/common/target_pipeline/tracking_target_pipeline.py
similarity index 100%
rename from common/polystar/common/target_pipeline/tracking_target_pipeline.py
rename to polystar_cv/polystar/common/target_pipeline/tracking_target_pipeline.py
diff --git a/common/research/common/__init__.py b/polystar_cv/polystar/common/utils/__init__.py
similarity index 100%
rename from common/research/common/__init__.py
rename to polystar_cv/polystar/common/utils/__init__.py
diff --git a/common/polystar/common/utils/dataframe.py b/polystar_cv/polystar/common/utils/dataframe.py
similarity index 100%
rename from common/polystar/common/utils/dataframe.py
rename to polystar_cv/polystar/common/utils/dataframe.py
diff --git a/common/polystar/common/utils/git.py b/polystar_cv/polystar/common/utils/git.py
similarity index 100%
rename from common/polystar/common/utils/git.py
rename to polystar_cv/polystar/common/utils/git.py
diff --git a/common/polystar/common/utils/iterable_utils.py b/polystar_cv/polystar/common/utils/iterable_utils.py
similarity index 100%
rename from common/polystar/common/utils/iterable_utils.py
rename to polystar_cv/polystar/common/utils/iterable_utils.py
diff --git a/common/research/common/dataset/__init__.py b/polystar_cv/polystar/common/utils/logs.py
similarity index 100%
rename from common/research/common/dataset/__init__.py
rename to polystar_cv/polystar/common/utils/logs.py
diff --git a/common/polystar/common/utils/markdown.py b/polystar_cv/polystar/common/utils/markdown.py
similarity index 94%
rename from common/polystar/common/utils/markdown.py
rename to polystar_cv/polystar/common/utils/markdown.py
index 791bef0..77530fe 100644
--- a/common/polystar/common/utils/markdown.py
+++ b/polystar_cv/polystar/common/utils/markdown.py
@@ -3,6 +3,7 @@ from typing import Any, Iterable, TextIO
 
 from markdown.core import markdown
 from matplotlib.figure import Figure
+from matplotlib.pyplot import close
 from pandas import DataFrame
 from tabulate import tabulate
 from xhtml2pdf.document import pisaDocument
@@ -40,9 +41,10 @@ class MarkdownFile:
         self.paragraph(f"![{alt}]({str(relative_path).replace(' ', '%20')})")
         return self
 
-    def figure(self, figure: Figure, name: str, alt: str = "img"):
-        name = name.replace(" ", "_")
+    def figure(self, figure: Figure, name: str, alt: str = "img", close_after: bool = True):
+        name = name.replace(" ", "_").replace("%", "p")
         figure.savefig(self.markdown_path.parent / name)
+        close(figure)
         return self.image(name, alt)
 
     def table(self, data: DataFrame) -> "MarkdownFile":
diff --git a/common/polystar/common/utils/misc.py b/polystar_cv/polystar/common/utils/misc.py
similarity index 100%
rename from common/polystar/common/utils/misc.py
rename to polystar_cv/polystar/common/utils/misc.py
diff --git a/common/polystar/common/utils/named_mixin.py b/polystar_cv/polystar/common/utils/named_mixin.py
similarity index 100%
rename from common/polystar/common/utils/named_mixin.py
rename to polystar_cv/polystar/common/utils/named_mixin.py
diff --git a/common/polystar/common/utils/no_case_enum.py b/polystar_cv/polystar/common/utils/no_case_enum.py
similarity index 72%
rename from common/polystar/common/utils/no_case_enum.py
rename to polystar_cv/polystar/common/utils/no_case_enum.py
index b89a03f..b55371d 100644
--- a/common/polystar/common/utils/no_case_enum.py
+++ b/polystar_cv/polystar/common/utils/no_case_enum.py
@@ -4,4 +4,4 @@ from enum import IntEnum
 class NoCaseEnum(IntEnum):
     @classmethod
     def _missing_(cls, key):
-        return cls[key.capitalize()]
+        return cls[key.upper()]
diff --git a/polystar_cv/polystar/common/utils/registry.py b/polystar_cv/polystar/common/utils/registry.py
new file mode 100644
index 0000000..d3eb717
--- /dev/null
+++ b/polystar_cv/polystar/common/utils/registry.py
@@ -0,0 +1,18 @@
+from itertools import chain
+from typing import Dict, Sequence, Type
+
+from polystar.common.utils.singleton import Singleton
+
+
+class Registry(Dict[str, Type], Singleton):
+    def register(self, previous_names: Sequence[str] = ()):
+        def decorator(class_: Type):
+            for name in chain((class_.__name__,), previous_names):
+                assert name not in self, f"{name} is already registered"
+                self[name] = class_
+            return class_
+
+        return decorator
+
+
+registry = Registry()
diff --git a/polystar_cv/polystar/common/utils/serialization.py b/polystar_cv/polystar/common/utils/serialization.py
new file mode 100644
index 0000000..b7aff56
--- /dev/null
+++ b/polystar_cv/polystar/common/utils/serialization.py
@@ -0,0 +1,25 @@
+import logging
+import pickle
+from pathlib import Path
+from typing import Any, Type
+
+from polystar.common.utils.registry import registry
+
+
+class UnpicklerWithRegistry(pickle.Unpickler):
+    def find_class(self, module: str, name: str) -> Type:
+        try:
+            return registry[name]
+        except KeyError:
+            return super().find_class(module, name)
+
+
+def pkl_load(file_path: Path):
+    with file_path.with_suffix(".pkl").open("rb") as f:
+        return UnpicklerWithRegistry(f).load()
+
+
+def pkl_dump(obj: Any, file_path: Path):
+    file_path_with_suffix = file_path.with_suffix(".pkl")
+    file_path_with_suffix.write_bytes(pickle.dumps(obj))
+    logging.info(f"{obj} saved at {file_path_with_suffix}")
diff --git a/polystar_cv/polystar/common/utils/singleton.py b/polystar_cv/polystar/common/utils/singleton.py
new file mode 100644
index 0000000..488b92c
--- /dev/null
+++ b/polystar_cv/polystar/common/utils/singleton.py
@@ -0,0 +1,16 @@
+class SingletonMetaclass(type):
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            cls._instances[cls] = super(SingletonMetaclass, cls).__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
+class Singleton:
+    _instance = None
+
+    def __new__(cls, *args, **kwargs):
+        if not isinstance(cls._instance, cls):
+            cls._instance = object.__new__(cls, *args, **kwargs)
+        return cls._instance
diff --git a/common/polystar/common/utils/str_utils.py b/polystar_cv/polystar/common/utils/str_utils.py
similarity index 100%
rename from common/polystar/common/utils/str_utils.py
rename to polystar_cv/polystar/common/utils/str_utils.py
diff --git a/common/polystar/common/utils/tensorflow.py b/polystar_cv/polystar/common/utils/tensorflow.py
similarity index 100%
rename from common/polystar/common/utils/tensorflow.py
rename to polystar_cv/polystar/common/utils/tensorflow.py
diff --git a/common/polystar/common/utils/time.py b/polystar_cv/polystar/common/utils/time.py
similarity index 100%
rename from common/polystar/common/utils/time.py
rename to polystar_cv/polystar/common/utils/time.py
diff --git a/common/polystar/common/utils/tqdm.py b/polystar_cv/polystar/common/utils/tqdm.py
similarity index 100%
rename from common/polystar/common/utils/tqdm.py
rename to polystar_cv/polystar/common/utils/tqdm.py
diff --git a/common/polystar/common/utils/working_directory.py b/polystar_cv/polystar/common/utils/working_directory.py
similarity index 100%
rename from common/polystar/common/utils/working_directory.py
rename to polystar_cv/polystar/common/utils/working_directory.py
diff --git a/common/research/common/dataset/cleaning/__init__.py b/polystar_cv/polystar/common/view/__init__.py
similarity index 100%
rename from common/research/common/dataset/cleaning/__init__.py
rename to polystar_cv/polystar/common/view/__init__.py
diff --git a/common/polystar/common/view/cv2_results_viewer.py b/polystar_cv/polystar/common/view/cv2_results_viewer.py
similarity index 100%
rename from common/polystar/common/view/cv2_results_viewer.py
rename to polystar_cv/polystar/common/view/cv2_results_viewer.py
diff --git a/common/polystar/common/view/plt_results_viewer.py b/polystar_cv/polystar/common/view/plt_results_viewer.py
similarity index 100%
rename from common/polystar/common/view/plt_results_viewer.py
rename to polystar_cv/polystar/common/view/plt_results_viewer.py
diff --git a/common/polystar/common/view/results_viewer_abc.py b/polystar_cv/polystar/common/view/results_viewer_abc.py
similarity index 100%
rename from common/polystar/common/view/results_viewer_abc.py
rename to polystar_cv/polystar/common/view/results_viewer_abc.py
diff --git a/common/research/common/dataset/improvement/__init__.py b/polystar_cv/research/__init__.py
similarity index 100%
rename from common/research/common/dataset/improvement/__init__.py
rename to polystar_cv/research/__init__.py
diff --git a/common/research/common/dataset/perturbations/__init__.py b/polystar_cv/research/common/__init__.py
similarity index 100%
rename from common/research/common/dataset/perturbations/__init__.py
rename to polystar_cv/research/common/__init__.py
diff --git a/common/research/common/constants.py b/polystar_cv/research/common/constants.py
similarity index 100%
rename from common/research/common/constants.py
rename to polystar_cv/research/common/constants.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/__init__.py b/polystar_cv/research/common/dataset/__init__.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/__init__.py
rename to polystar_cv/research/common/dataset/__init__.py
diff --git a/common/research/common/dataset/twitch/__init__.py b/polystar_cv/research/common/dataset/cleaning/__init__.py
similarity index 100%
rename from common/research/common/dataset/twitch/__init__.py
rename to polystar_cv/research/common/dataset/cleaning/__init__.py
diff --git a/common/research/common/dataset/cleaning/dataset_changes.py b/polystar_cv/research/common/dataset/cleaning/dataset_changes.py
similarity index 77%
rename from common/research/common/dataset/cleaning/dataset_changes.py
rename to polystar_cv/research/common/dataset/cleaning/dataset_changes.py
index 3375476..bdeb474 100644
--- a/common/research/common/dataset/cleaning/dataset_changes.py
+++ b/polystar_cv/research/common/dataset/cleaning/dataset_changes.py
@@ -1,4 +1,5 @@
 import json
+from contextlib import suppress
 from pathlib import Path
 from typing import Dict, List, Set
 
@@ -6,6 +7,7 @@ from more_itertools import flatten
 
 from polystar.common.utils.git import get_git_username
 from polystar.common.utils.time import create_time_id
+from research.common.gcloud.gcloud_storage import GCStorages
 
 INVALIDATED_KEY: str = "invalidated"
 
@@ -13,6 +15,8 @@ INVALIDATED_KEY: str = "invalidated"
 class DatasetChanges:
     def __init__(self, dataset_directory: Path):
         self.changes_file: Path = dataset_directory / ".changes"
+        with suppress(FileNotFoundError):
+            GCStorages.DEV.download_file_if_missing(self.changes_file)
 
     @property
     def invalidated(self) -> Set[str]:
@@ -30,3 +34,7 @@ class DatasetChanges:
         changes[INVALIDATED_KEY][entry_id] = names
         self.changes_file.write_text(json.dumps(changes, indent=2))
         print(f"changes saved, see entry {entry_id} in file://{self.changes_file}")
+        self.upload()
+
+    def upload(self):
+        GCStorages.DEV.upload_file(self.changes_file)
diff --git a/common/research/common/dataset/cleaning/dataset_cleaner_app.py b/polystar_cv/research/common/dataset/cleaning/dataset_cleaner_app.py
similarity index 100%
rename from common/research/common/dataset/cleaning/dataset_cleaner_app.py
rename to polystar_cv/research/common/dataset/cleaning/dataset_cleaner_app.py
diff --git a/common/research/common/datasets/__init__.py b/polystar_cv/research/common/dataset/improvement/__init__.py
similarity index 100%
rename from common/research/common/datasets/__init__.py
rename to polystar_cv/research/common/dataset/improvement/__init__.py
diff --git a/common/research/common/dataset/improvement/zoom.py b/polystar_cv/research/common/dataset/improvement/zoom.py
similarity index 100%
rename from common/research/common/dataset/improvement/zoom.py
rename to polystar_cv/research/common/dataset/improvement/zoom.py
diff --git a/common/research/common/datasets/roco/__init__.py b/polystar_cv/research/common/dataset/perturbations/__init__.py
similarity index 100%
rename from common/research/common/datasets/roco/__init__.py
rename to polystar_cv/research/common/dataset/perturbations/__init__.py
diff --git a/common/research/common/dataset/perturbations/examples/.gitignore b/polystar_cv/research/common/dataset/perturbations/examples/.gitignore
similarity index 100%
rename from common/research/common/dataset/perturbations/examples/.gitignore
rename to polystar_cv/research/common/dataset/perturbations/examples/.gitignore
diff --git a/common/research/common/dataset/perturbations/examples/test.png b/polystar_cv/research/common/dataset/perturbations/examples/test.png
similarity index 100%
rename from common/research/common/dataset/perturbations/examples/test.png
rename to polystar_cv/research/common/dataset/perturbations/examples/test.png
diff --git a/common/research/common/datasets/roco/zoo/__init__.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/__init__.py
similarity index 100%
rename from common/research/common/datasets/roco/zoo/__init__.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/__init__.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/brightness.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/brightness.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/brightness.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/brightness.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/contrast.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/contrast.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/contrast.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/contrast.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/gaussian_blur.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/gaussian_blur.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/gaussian_blur.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/gaussian_blur.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/gaussian_noise.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/gaussian_noise.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/gaussian_noise.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/gaussian_noise.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/horizontal_blur.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/horizontal_blur.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/horizontal_blur.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/horizontal_blur.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/image_modifier_abc.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/image_modifier_abc.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/image_modifier_abc.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/image_modifier_abc.py
diff --git a/common/research/common/dataset/perturbations/image_modifiers/saturation.py b/polystar_cv/research/common/dataset/perturbations/image_modifiers/saturation.py
similarity index 100%
rename from common/research/common/dataset/perturbations/image_modifiers/saturation.py
rename to polystar_cv/research/common/dataset/perturbations/image_modifiers/saturation.py
diff --git a/common/research/common/dataset/perturbations/perturbator.py b/polystar_cv/research/common/dataset/perturbations/perturbator.py
similarity index 100%
rename from common/research/common/dataset/perturbations/perturbator.py
rename to polystar_cv/research/common/dataset/perturbations/perturbator.py
diff --git a/common/research/common/dataset/perturbations/utils.py b/polystar_cv/research/common/dataset/perturbations/utils.py
similarity index 100%
rename from common/research/common/dataset/perturbations/utils.py
rename to polystar_cv/research/common/dataset/perturbations/utils.py
diff --git a/common/research/common/dataset/tensorflow_record.py b/polystar_cv/research/common/dataset/tensorflow_record.py
similarity index 100%
rename from common/research/common/dataset/tensorflow_record.py
rename to polystar_cv/research/common/dataset/tensorflow_record.py
diff --git a/common/research/common/scripts/__init__.py b/polystar_cv/research/common/dataset/twitch/__init__.py
similarity index 100%
rename from common/research/common/scripts/__init__.py
rename to polystar_cv/research/common/dataset/twitch/__init__.py
diff --git a/common/research/common/dataset/twitch/aerial_view_detector.py b/polystar_cv/research/common/dataset/twitch/aerial_view_detector.py
similarity index 100%
rename from common/research/common/dataset/twitch/aerial_view_detector.py
rename to polystar_cv/research/common/dataset/twitch/aerial_view_detector.py
diff --git a/common/research/common/dataset/twitch/mask_aerial.jpg b/polystar_cv/research/common/dataset/twitch/mask_aerial.jpg
similarity index 100%
rename from common/research/common/dataset/twitch/mask_aerial.jpg
rename to polystar_cv/research/common/dataset/twitch/mask_aerial.jpg
diff --git a/common/research/common/dataset/twitch/mask_detector.py b/polystar_cv/research/common/dataset/twitch/mask_detector.py
similarity index 100%
rename from common/research/common/dataset/twitch/mask_detector.py
rename to polystar_cv/research/common/dataset/twitch/mask_detector.py
diff --git a/common/research/common/dataset/twitch/mask_robot_view.jpg b/polystar_cv/research/common/dataset/twitch/mask_robot_view.jpg
similarity index 100%
rename from common/research/common/dataset/twitch/mask_robot_view.jpg
rename to polystar_cv/research/common/dataset/twitch/mask_robot_view.jpg
diff --git a/common/research/common/dataset/twitch/robots_views_extractor.py b/polystar_cv/research/common/dataset/twitch/robots_views_extractor.py
similarity index 100%
rename from common/research/common/dataset/twitch/robots_views_extractor.py
rename to polystar_cv/research/common/dataset/twitch/robots_views_extractor.py
diff --git a/common/research/common/dataset/upload.py b/polystar_cv/research/common/dataset/upload.py
similarity index 70%
rename from common/research/common/dataset/upload.py
rename to polystar_cv/research/common/dataset/upload.py
index cc857b1..5cbc168 100644
--- a/common/research/common/dataset/upload.py
+++ b/polystar_cv/research/common/dataset/upload.py
@@ -2,6 +2,7 @@ import logging
 
 from tqdm import tqdm
 
+from research.common.dataset.cleaning.dataset_changes import DatasetChanges
 from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
 from research.common.datasets.roco.roco_datasets import ROCODatasets
 from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
@@ -13,6 +14,11 @@ def upload_all_digit_datasets(roco_datasets: ROCODatasets):
         upload_digit_dataset(roco_dataset)
 
 
+def upload_all_color_datasets(roco_datasets: ROCODatasets):
+    for roco_dataset in tqdm(roco_datasets, desc="Uploading datasets"):
+        upload_color_dataset(roco_dataset)
+
+
 def upload_digit_dataset(roco_dataset: ROCODatasetBuilder):
     _upload_armor_dataset(roco_dataset, "digits")
 
@@ -23,9 +29,11 @@ def upload_color_dataset(roco_dataset: ROCODatasetBuilder):
 
 def _upload_armor_dataset(roco_dataset: ROCODatasetBuilder, name: str):
     GCStorages.DEV.upload_directory(roco_dataset.main_dir / name, extensions_to_exclude={".changes"})
+    DatasetChanges(roco_dataset.main_dir / name).upload()
 
 
 if __name__ == "__main__":
     logging.getLogger().setLevel("INFO")
 
-    upload_all_digit_datasets(ROCODatasetsZoo.DJI)
+    upload_all_digit_datasets(ROCODatasetsZoo.TWITCH)
+    upload_digit_dataset(ROCODatasetsZoo.DJI.FINAL)
diff --git a/common/tests/common/integration_tests/__init__.py b/polystar_cv/research/common/datasets/__init__.py
similarity index 100%
rename from common/tests/common/integration_tests/__init__.py
rename to polystar_cv/research/common/datasets/__init__.py
diff --git a/common/research/common/datasets/dataset.py b/polystar_cv/research/common/datasets/dataset.py
similarity index 100%
rename from common/research/common/datasets/dataset.py
rename to polystar_cv/research/common/datasets/dataset.py
diff --git a/common/research/common/datasets/dataset_builder.py b/polystar_cv/research/common/datasets/dataset_builder.py
similarity index 100%
rename from common/research/common/datasets/dataset_builder.py
rename to polystar_cv/research/common/datasets/dataset_builder.py
diff --git a/common/research/common/datasets/filter_dataset.py b/polystar_cv/research/common/datasets/filter_dataset.py
similarity index 100%
rename from common/research/common/datasets/filter_dataset.py
rename to polystar_cv/research/common/datasets/filter_dataset.py
diff --git a/common/research/common/datasets/image_dataset.py b/polystar_cv/research/common/datasets/image_dataset.py
similarity index 100%
rename from common/research/common/datasets/image_dataset.py
rename to polystar_cv/research/common/datasets/image_dataset.py
diff --git a/common/research/common/datasets/image_file_dataset_builder.py b/polystar_cv/research/common/datasets/image_file_dataset_builder.py
similarity index 100%
rename from common/research/common/datasets/image_file_dataset_builder.py
rename to polystar_cv/research/common/datasets/image_file_dataset_builder.py
diff --git a/common/research/common/datasets/iterator_dataset.py b/polystar_cv/research/common/datasets/iterator_dataset.py
similarity index 100%
rename from common/research/common/datasets/iterator_dataset.py
rename to polystar_cv/research/common/datasets/iterator_dataset.py
diff --git a/common/research/common/datasets/lazy_dataset.py b/polystar_cv/research/common/datasets/lazy_dataset.py
similarity index 100%
rename from common/research/common/datasets/lazy_dataset.py
rename to polystar_cv/research/common/datasets/lazy_dataset.py
diff --git a/common/tests/common/integration_tests/datasets/__init__.py b/polystar_cv/research/common/datasets/roco/__init__.py
similarity index 100%
rename from common/tests/common/integration_tests/datasets/__init__.py
rename to polystar_cv/research/common/datasets/roco/__init__.py
diff --git a/common/research/common/datasets/roco/roco_annotation.py b/polystar_cv/research/common/datasets/roco/roco_annotation.py
similarity index 100%
rename from common/research/common/datasets/roco/roco_annotation.py
rename to polystar_cv/research/common/datasets/roco/roco_annotation.py
diff --git a/common/research/common/datasets/roco/roco_dataset.py b/polystar_cv/research/common/datasets/roco/roco_dataset.py
similarity index 100%
rename from common/research/common/datasets/roco/roco_dataset.py
rename to polystar_cv/research/common/datasets/roco/roco_dataset.py
diff --git a/common/research/common/datasets/roco/roco_dataset_builder.py b/polystar_cv/research/common/datasets/roco/roco_dataset_builder.py
similarity index 100%
rename from common/research/common/datasets/roco/roco_dataset_builder.py
rename to polystar_cv/research/common/datasets/roco/roco_dataset_builder.py
diff --git a/common/research/common/datasets/roco/roco_dataset_descriptor.py b/polystar_cv/research/common/datasets/roco/roco_dataset_descriptor.py
similarity index 100%
rename from common/research/common/datasets/roco/roco_dataset_descriptor.py
rename to polystar_cv/research/common/datasets/roco/roco_dataset_descriptor.py
diff --git a/common/research/common/datasets/roco/roco_datasets.py b/polystar_cv/research/common/datasets/roco/roco_datasets.py
similarity index 95%
rename from common/research/common/datasets/roco/roco_datasets.py
rename to polystar_cv/research/common/datasets/roco/roco_datasets.py
index d824c69..dbecdcd 100644
--- a/common/research/common/datasets/roco/roco_datasets.py
+++ b/polystar_cv/research/common/datasets/roco/roco_datasets.py
@@ -19,6 +19,9 @@ class ROCODatasetsMeta(type):
     def __iter__(cls) -> Iterator[ROCODatasetBuilder]:
         return (cls._make_builder_from_name(name) for name in dir(cls) if _is_builder_name(cls, name))
 
+    def __len__(cls):
+        return sum(_is_builder_name(cls, name) for name in dir(cls))
+
     def union(cls) -> UnionLazyDataset[Path, ROCOAnnotation]:
         return UnionLazyDataset(cls, cls.name)
 
diff --git a/common/tests/common/unittests/__init__.py b/polystar_cv/research/common/datasets/roco/zoo/__init__.py
similarity index 100%
rename from common/tests/common/unittests/__init__.py
rename to polystar_cv/research/common/datasets/roco/zoo/__init__.py
diff --git a/common/research/common/datasets/roco/zoo/dji.py b/polystar_cv/research/common/datasets/roco/zoo/dji.py
similarity index 100%
rename from common/research/common/datasets/roco/zoo/dji.py
rename to polystar_cv/research/common/datasets/roco/zoo/dji.py
diff --git a/common/research/common/datasets/roco/zoo/dji_zoomed.py b/polystar_cv/research/common/datasets/roco/zoo/dji_zoomed.py
similarity index 100%
rename from common/research/common/datasets/roco/zoo/dji_zoomed.py
rename to polystar_cv/research/common/datasets/roco/zoo/dji_zoomed.py
diff --git a/common/research/common/datasets/roco/zoo/roco_dataset_zoo.py b/polystar_cv/research/common/datasets/roco/zoo/roco_dataset_zoo.py
similarity index 100%
rename from common/research/common/datasets/roco/zoo/roco_dataset_zoo.py
rename to polystar_cv/research/common/datasets/roco/zoo/roco_dataset_zoo.py
diff --git a/common/research/common/datasets/roco/zoo/twitch.py b/polystar_cv/research/common/datasets/roco/zoo/twitch.py
similarity index 100%
rename from common/research/common/datasets/roco/zoo/twitch.py
rename to polystar_cv/research/common/datasets/roco/zoo/twitch.py
diff --git a/common/research/common/datasets/slice_dataset.py b/polystar_cv/research/common/datasets/slice_dataset.py
similarity index 100%
rename from common/research/common/datasets/slice_dataset.py
rename to polystar_cv/research/common/datasets/slice_dataset.py
diff --git a/common/research/common/datasets/transform_dataset.py b/polystar_cv/research/common/datasets/transform_dataset.py
similarity index 100%
rename from common/research/common/datasets/transform_dataset.py
rename to polystar_cv/research/common/datasets/transform_dataset.py
diff --git a/common/research/common/datasets/union_dataset.py b/polystar_cv/research/common/datasets/union_dataset.py
similarity index 100%
rename from common/research/common/datasets/union_dataset.py
rename to polystar_cv/research/common/datasets/union_dataset.py
diff --git a/common/research/common/gcloud/gcloud_storage.py b/polystar_cv/research/common/gcloud/gcloud_storage.py
similarity index 58%
rename from common/research/common/gcloud/gcloud_storage.py
rename to polystar_cv/research/common/gcloud/gcloud_storage.py
index d83de5b..4b47e20 100644
--- a/common/research/common/gcloud/gcloud_storage.py
+++ b/polystar_cv/research/common/gcloud/gcloud_storage.py
@@ -3,11 +3,12 @@ import shutil
 import tarfile
 from contextlib import contextmanager
 from enum import Enum
+from io import FileIO
 from pathlib import Path, PurePath
 from tempfile import TemporaryDirectory
 from typing import Iterable, Optional
 
-from google.cloud.storage import Blob, Bucket, Client
+from google.cloud.storage import Bucket, Client
 
 from polystar.common.constants import PROJECT_DIR
 
@@ -18,14 +19,19 @@ EXTENSIONS_TO_EXCLUDE = (".changes",)
 class GCStorage:
     def __init__(self, bucket_name: str):
         self.bucket_name = bucket_name
-        self.client: Optional[Client] = None
+        self._client: Optional[Client] = None
         self._bucket: Optional[Bucket] = None
-        self.url = f"https://console.cloud.google.com/storage/browser/{bucket_name}"
+        self.bucket_url = f"https://console.cloud.google.com/storage/browser/{bucket_name}"
+        self.storage_url = f"https://storage.cloud.google.com/{bucket_name}"
 
     def upload_file(self, local_path: Path, remote_path: Optional[PurePath] = None):
-        blob = self._make_remote_blob(local_path, remote_path)
+        remote_path = _make_remote_path(local_path, remote_path)
+        blob = self.bucket.blob(str(remote_path), chunk_size=10 * 1024 * 1024)
         blob.upload_from_filename(str(local_path), timeout=60 * 5)
-        logger.info(f"File file:///{local_path} uploaded")
+        logger.info(
+            f"File {local_path.name} uploaded to {self.bucket_url}/{remote_path.parent}. "
+            f"Download link: {self.storage_url}/{remote_path}"
+        )
 
     def upload_directory(self, local_path: Path, extensions_to_exclude: Iterable[str] = EXTENSIONS_TO_EXCLUDE):
         extensions_to_exclude = set(extensions_to_exclude)
@@ -35,14 +41,18 @@ class GCStorage:
                 tar.add(
                     str(local_path),
                     arcname="",
-                    exclude=lambda name: any(name.endswith(ext) for ext in extensions_to_exclude),
+                    filter=lambda f: None if any(f.name.endswith(ext) for ext in extensions_to_exclude) else f,
                 )
             return self.upload_file(tar_path, _make_remote_path(local_path.with_suffix(".tar.gz")))
 
     def download_file(self, local_path: Path, remote_path: Optional[PurePath] = None):
-        blob = self._make_remote_blob(local_path, remote_path)
+        local_path.parent.mkdir(exist_ok=True, parents=True)
+        remote_path = _make_remote_path(local_path, remote_path)
+        blob = self.bucket.get_blob(str(remote_path))
+        if blob is None:
+            raise FileNotFoundError(f"{remote_path} is not on {self.bucket_url}")
         blob.download_to_filename(str(local_path), timeout=60 * 5)
-        logger.info(f"File file:///{local_path} downloaded")
+        logger.info(f"File {local_path.name} downloaded to file:///{local_path}")
 
     def download_file_if_missing(self, local_path: Path, remote_path: Optional[PurePath] = None):
         if not local_path.exists():
@@ -69,7 +79,7 @@ class GCStorage:
         self.download_directory(local_path)
 
     @contextmanager
-    def open(self, local_path: Path, mode: str):
+    def open(self, local_path: Path, mode: str) -> FileIO:
         if "r" in mode:
             self.download_file_if_missing(local_path)
             with local_path.open(mode) as f:
@@ -82,23 +92,38 @@ class GCStorage:
         else:
             raise ValueError(f"mode {mode} is not supported")
 
-    def _make_remote_blob(self, local_path: Path, remote_path: Optional[PurePath]) -> Blob:
-        if remote_path is None:
-            remote_path = _make_remote_path(local_path)
+    @staticmethod
+    def open_from_str(remote_path: str, mode: str):
+        assert remote_path.startswith("gs://")
+        remote_path = remote_path[5:]
+        bucket_name, relative_path = remote_path.split("/", maxsplit=1)
+        return GCStorage(bucket_name).open(Path(PROJECT_DIR / relative_path), mode)
+
+    def glob(self, local_path: Path, remote_path: Optional[PurePath] = None, extension: str = None) -> Iterable[Path]:
+        remote_path = _make_remote_path(local_path, remote_path)
+        blobs = self.client.list_blobs(self.bucket, prefix=str(remote_path),)
+        if extension is None:
+            return (PROJECT_DIR / b.name for b in blobs)
+        for blob in blobs:
+            if blob.name.endswith(extension):
+                yield PROJECT_DIR / blob.name
 
-        return self.bucket.blob(str(remote_path), chunk_size=10 * 1024 * 1024)
+    @property
+    def client(self) -> Client:
+        if self._client is None:
+            self._client = Client()
+        return self._client
 
     @property
     def bucket(self) -> Bucket:
         if self._bucket is not None:
             return self._bucket
-        self.client = Client()
         self._bucket = self.client.bucket(self.bucket_name)
         return self._bucket
 
 
-def _make_remote_path(local_path: Path):
-    return local_path.relative_to(PROJECT_DIR)
+def _make_remote_path(local_path: Path, remote_path: Optional[PurePath] = None) -> PurePath:
+    return remote_path or local_path.relative_to(PROJECT_DIR)
 
 
 class GCStorages(GCStorage, Enum):
diff --git a/common/tests/common/unittests/object_validators/__init__.py b/polystar_cv/research/common/scripts/__init__.py
similarity index 100%
rename from common/tests/common/unittests/object_validators/__init__.py
rename to polystar_cv/research/common/scripts/__init__.py
diff --git a/common/research/common/scripts/construct_dataset_from_manual_annotation.py b/polystar_cv/research/common/scripts/construct_dataset_from_manual_annotation.py
similarity index 100%
rename from common/research/common/scripts/construct_dataset_from_manual_annotation.py
rename to polystar_cv/research/common/scripts/construct_dataset_from_manual_annotation.py
diff --git a/common/research/common/scripts/construct_twith_datasets_from_manual_annotation.py b/polystar_cv/research/common/scripts/construct_twith_datasets_from_manual_annotation.py
similarity index 100%
rename from common/research/common/scripts/construct_twith_datasets_from_manual_annotation.py
rename to polystar_cv/research/common/scripts/construct_twith_datasets_from_manual_annotation.py
diff --git a/common/research/common/scripts/correct_annotations.py b/polystar_cv/research/common/scripts/correct_annotations.py
similarity index 100%
rename from common/research/common/scripts/correct_annotations.py
rename to polystar_cv/research/common/scripts/correct_annotations.py
diff --git a/common/research/common/scripts/create_tensorflow_records.py b/polystar_cv/research/common/scripts/create_tensorflow_records.py
similarity index 100%
rename from common/research/common/scripts/create_tensorflow_records.py
rename to polystar_cv/research/common/scripts/create_tensorflow_records.py
diff --git a/common/research/common/scripts/extract_robots_views_from_video.py b/polystar_cv/research/common/scripts/extract_robots_views_from_video.py
similarity index 100%
rename from common/research/common/scripts/extract_robots_views_from_video.py
rename to polystar_cv/research/common/scripts/extract_robots_views_from_video.py
diff --git a/common/research/common/scripts/improve_roco_by_zooming.py b/polystar_cv/research/common/scripts/improve_roco_by_zooming.py
similarity index 100%
rename from common/research/common/scripts/improve_roco_by_zooming.py
rename to polystar_cv/research/common/scripts/improve_roco_by_zooming.py
diff --git a/common/research/common/scripts/make_twitch_chunks_to_annotate.py b/polystar_cv/research/common/scripts/make_twitch_chunks_to_annotate.py
similarity index 100%
rename from common/research/common/scripts/make_twitch_chunks_to_annotate.py
rename to polystar_cv/research/common/scripts/make_twitch_chunks_to_annotate.py
diff --git a/common/research/common/scripts/move_aerial_views.py b/polystar_cv/research/common/scripts/move_aerial_views.py
similarity index 100%
rename from common/research/common/scripts/move_aerial_views.py
rename to polystar_cv/research/common/scripts/move_aerial_views.py
diff --git a/common/research/common/scripts/visualize_dataset.py b/polystar_cv/research/common/scripts/visualize_dataset.py
similarity index 100%
rename from common/research/common/scripts/visualize_dataset.py
rename to polystar_cv/research/common/scripts/visualize_dataset.py
diff --git a/polystar_cv/research/common/utils/experiment_dir.py b/polystar_cv/research/common/utils/experiment_dir.py
new file mode 100644
index 0000000..d0a1c57
--- /dev/null
+++ b/polystar_cv/research/common/utils/experiment_dir.py
@@ -0,0 +1,15 @@
+from pathlib import Path
+
+from polystar.common.utils.time import create_time_id
+from research.common.constants import EVALUATION_DIR
+
+
+def prompt_experiment_dir(project_name: str) -> Path:
+    experiment_name: str = input(f"Experiment name for {project_name}: ")
+    return make_experiment_dir(project_name, experiment_name)
+
+
+def make_experiment_dir(project_name: str, experiment_name: str) -> Path:
+    experiment_dir = EVALUATION_DIR / project_name / f"{create_time_id()}_{experiment_name}"
+    experiment_dir.mkdir(exist_ok=True, parents=True)
+    return experiment_dir
diff --git a/robots-at-robots/polystar/robots_at_robots/__init__.py b/polystar_cv/research/robots/__init__.py
similarity index 100%
rename from robots-at-robots/polystar/robots_at_robots/__init__.py
rename to polystar_cv/research/robots/__init__.py
diff --git a/robots-at-robots/research/robots_at_robots/__init__.py b/polystar_cv/research/robots/armor_color/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/__init__.py
rename to polystar_cv/research/robots/armor_color/__init__.py
diff --git a/polystar_cv/research/robots/armor_color/benchmarker.py b/polystar_cv/research/robots/armor_color/benchmarker.py
new file mode 100644
index 0000000..79f0a91
--- /dev/null
+++ b/polystar_cv/research/robots/armor_color/benchmarker.py
@@ -0,0 +1,16 @@
+from pathlib import Path
+
+from research.robots.armor_color.datasets import make_armor_color_datasets
+from research.robots.armor_color.pipeline import ArmorColorPipeline
+from research.robots.evaluation.benchmarker import Benchmarker
+
+
+def make_armor_color_benchmarker(report_dir: Path, include_dji: bool = True) -> Benchmarker:
+    train_datasets, validation_datasets, test_datasets = make_armor_color_datasets()
+    return Benchmarker(
+        report_dir=report_dir,
+        classes=ArmorColorPipeline.classes,
+        train_datasets=train_datasets,
+        validation_datasets=validation_datasets,
+        test_datasets=test_datasets,
+    )
diff --git a/polystar_cv/research/robots/armor_color/datasets.py b/polystar_cv/research/robots/armor_color/datasets.py
new file mode 100644
index 0000000..5c53b9e
--- /dev/null
+++ b/polystar_cv/research/robots/armor_color/datasets.py
@@ -0,0 +1,49 @@
+from typing import List, Tuple
+
+from polystar.common.models.object import Armor, ArmorColor
+from research.common.datasets.image_dataset import FileImageDataset
+from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
+from research.robots.dataset.armor_value_dataset_generator import ArmorValueDatasetGenerator
+from research.robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
+
+
+class ArmorColorTargetFactory(ArmorValueTargetFactory[ArmorColor]):
+    def from_str(self, label: str) -> ArmorColor:
+        return ArmorColor(label)
+
+    def from_armor(self, armor: Armor) -> ArmorColor:
+        return armor.color
+
+
+def make_armor_color_dataset_generator() -> ArmorValueDatasetGenerator[ArmorColor]:
+    return ArmorValueDatasetGenerator("colors", ArmorColorTargetFactory())
+
+
+def make_armor_color_datasets(
+    include_dji: bool = True,
+) -> Tuple[List[FileImageDataset], List[FileImageDataset], List[FileImageDataset]]:
+    color_dataset_generator = make_armor_color_dataset_generator()
+
+    train_roco_datasets = [
+        ROCODatasetsZoo.TWITCH.T470150052,
+        ROCODatasetsZoo.TWITCH.T470152730,
+        ROCODatasetsZoo.TWITCH.T470153081,
+        ROCODatasetsZoo.TWITCH.T470158483,
+    ]
+    if include_dji:
+        train_roco_datasets.extend(
+            [
+                ROCODatasetsZoo.DJI.FINAL,
+                ROCODatasetsZoo.DJI.CENTRAL_CHINA,
+                ROCODatasetsZoo.DJI.NORTH_CHINA,
+                ROCODatasetsZoo.DJI.SOUTH_CHINA,
+            ]
+        )
+
+    train_datasets, validation_datasets, test_datasets = color_dataset_generator.from_roco_datasets(
+        train_roco_datasets,
+        [ROCODatasetsZoo.TWITCH.T470149568, ROCODatasetsZoo.TWITCH.T470152289],
+        [ROCODatasetsZoo.TWITCH.T470152838, ROCODatasetsZoo.TWITCH.T470151286],
+    )
+
+    return train_datasets, validation_datasets, test_datasets
diff --git a/polystar_cv/research/robots/armor_color/pipeline.py b/polystar_cv/research/robots/armor_color/pipeline.py
new file mode 100644
index 0000000..50cd441
--- /dev/null
+++ b/polystar_cv/research/robots/armor_color/pipeline.py
@@ -0,0 +1,11 @@
+from polystar.common.models.object import ArmorColor
+from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
+from polystar.common.pipeline.classification.keras_classification_pipeline import KerasClassificationPipeline
+
+
+class ArmorColorPipeline(ClassificationPipeline):
+    enum = ArmorColor
+
+
+class ArmorColorKerasPipeline(ArmorColorPipeline, KerasClassificationPipeline):
+    pass
diff --git a/robots-at-robots/research/robots_at_robots/armor_color/__init__.py b/polystar_cv/research/robots/armor_color/scripts/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/armor_color/__init__.py
rename to polystar_cv/research/robots/armor_color/scripts/__init__.py
diff --git a/polystar_cv/research/robots/armor_color/scripts/benchmark.py b/polystar_cv/research/robots/armor_color/scripts/benchmark.py
new file mode 100644
index 0000000..b76be32
--- /dev/null
+++ b/polystar_cv/research/robots/armor_color/scripts/benchmark.py
@@ -0,0 +1,64 @@
+import logging
+from dataclasses import dataclass
+from pathlib import Path
+
+from nptyping import Array
+from sklearn.linear_model import LogisticRegression
+
+from polystar.common.models.image import Image
+from polystar.common.models.object import ArmorColor
+from polystar.common.pipeline.classification.random_model import RandomClassifier
+from polystar.common.pipeline.classification.rule_based_classifier import RuleBasedClassifierABC
+from polystar.common.pipeline.featurizers.histogram_2d import Histogram2D
+from polystar.common.pipeline.featurizers.histogram_blocs_2d import HistogramBlocs2D
+from polystar.common.pipeline.pipe_abc import PipeABC
+from polystar.common.pipeline.preprocessors.rgb_to_hsv import RGB2HSV
+from research.common.utils.experiment_dir import prompt_experiment_dir
+from research.robots.armor_color.benchmarker import make_armor_color_benchmarker
+from research.robots.armor_color.pipeline import ArmorColorPipeline
+
+
+@dataclass
+class MeanChannels(PipeABC):
+    def transform_single(self, image: Image) -> Array[float, float, float]:
+        return image.mean(axis=(0, 1))
+
+
+class RedBlueComparisonClassifier(RuleBasedClassifierABC):
+    """A very simple model that compares the blue and red values obtained by the MeanChannels"""
+
+    def predict_single(self, features: Array[float, float, float]) -> ArmorColor:
+        return ArmorColor.RED if features[0] >= features[2] else ArmorColor.BLUE
+
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.info("Benchmarking")
+
+    _report_dir: Path = prompt_experiment_dir("armor-color")
+    _benchmarker = make_armor_color_benchmarker(_report_dir, include_dji=False)
+
+    _pipelines = [
+        ArmorColorPipeline.from_pipes([MeanChannels(), RedBlueComparisonClassifier()], name="rb-comparison"),
+        ArmorColorPipeline.from_pipes([RandomClassifier()], name="random"),
+        ArmorColorPipeline.from_pipes(
+            [RGB2HSV(), Histogram2D(), LogisticRegression(max_iter=200)], name="hsv-hist-lr",
+        ),
+        ArmorColorPipeline.from_pipes(
+            [RGB2HSV(), HistogramBlocs2D(rows=1, cols=3), LogisticRegression(max_iter=200)], name="hsv-hist-blocs-lr",
+        ),
+        ArmorColorPipeline.from_pipes([Histogram2D(), LogisticRegression(max_iter=200)], name="rgb-hist-lr"),
+        # ArmorColorKerasPipeline.from_custom_cnn(
+        #     logs_dir=str(_report_dir),
+        #     input_size=16,
+        #     conv_blocks=((32, 32), (64, 64)),
+        #     dropout=0.5,
+        #     dense_size=64,
+        #     lr=7.2e-4,
+        #     name="cnn",
+        #     batch_size=128,
+        #     steps_per_epoch="auto",
+        # ),
+    ]
+
+    _benchmarker.benchmark(_pipelines)
diff --git a/polystar_cv/research/robots/armor_color/scripts/hyper_tune_cnn.py b/polystar_cv/research/robots/armor_color/scripts/hyper_tune_cnn.py
new file mode 100644
index 0000000..656459f
--- /dev/null
+++ b/polystar_cv/research/robots/armor_color/scripts/hyper_tune_cnn.py
@@ -0,0 +1,35 @@
+import logging
+import warnings
+from pathlib import Path
+
+from optuna import Trial
+
+from research.common.utils.experiment_dir import make_experiment_dir
+from research.robots.armor_color.benchmarker import make_armor_color_benchmarker
+from research.robots.armor_color.pipeline import ArmorColorKerasPipeline
+from research.robots.evaluation.hyper_tuner import HyperTuner
+
+
+def cnn_pipeline_factory(report_dir: Path, trial: Trial) -> ArmorColorKerasPipeline:
+    return ArmorColorKerasPipeline.from_custom_cnn(
+        input_size=32,
+        conv_blocks=((32, 32), (64, 64)),
+        logs_dir=str(report_dir),
+        dropout=trial.suggest_uniform("dropout", 0, 0.99),
+        lr=trial.suggest_loguniform("lr", 1e-5, 1e-1),
+        dense_size=2 ** round(trial.suggest_discrete_uniform("dense_size_log2", 3, 10, 1)),
+        batch_size=64,
+        steps_per_epoch="auto",
+        verbose=0,
+    )
+
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    logging.info("Hyperparameter tuning for CNN pipeline on color task")
+    HyperTuner(make_armor_color_benchmarker(make_experiment_dir("armor-color", "cnn_tuning"), include_dji=False)).tune(
+        cnn_pipeline_factory, n_trials=50
+    )
diff --git a/robots-at-robots/research/robots_at_robots/armor_digit/__init__.py b/polystar_cv/research/robots/armor_digit/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/armor_digit/__init__.py
rename to polystar_cv/research/robots/armor_digit/__init__.py
diff --git a/polystar_cv/research/robots/armor_digit/armor_digit_dataset.py b/polystar_cv/research/robots/armor_digit/armor_digit_dataset.py
new file mode 100644
index 0000000..f7f7437
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/armor_digit_dataset.py
@@ -0,0 +1,62 @@
+from itertools import islice
+from typing import List, Set, Tuple
+
+from polystar.common.filters.exclude_filter import ExcludeFilter
+from polystar.common.models.object import Armor, ArmorDigit
+from research.common.datasets.image_dataset import FileImageDataset
+from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
+from research.robots.dataset.armor_value_dataset_generator import ArmorValueDatasetGenerator
+from research.robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
+
+VALID_NUMBERS_2021: Set[int] = {1, 3, 4}  # University League
+
+
+def make_armor_digit_dataset_generator() -> ArmorValueDatasetGenerator[ArmorDigit]:
+    return ArmorValueDatasetGenerator("digits", ArmorDigitTargetFactory(), ExcludeFilter({ArmorDigit.OUTDATED}))
+
+
+def default_armor_digit_datasets() -> Tuple[List[FileImageDataset], List[FileImageDataset], List[FileImageDataset]]:
+    digit_dataset_generator = make_armor_digit_dataset_generator()
+    train_datasets, validation_datasets, test_datasets = digit_dataset_generator.from_roco_datasets(
+        [
+            ROCODatasetsZoo.TWITCH.T470150052,
+            ROCODatasetsZoo.TWITCH.T470152730,
+            ROCODatasetsZoo.TWITCH.T470153081,
+            ROCODatasetsZoo.TWITCH.T470158483,
+            ROCODatasetsZoo.DJI.FINAL,
+            ROCODatasetsZoo.DJI.CENTRAL_CHINA,
+            ROCODatasetsZoo.DJI.NORTH_CHINA,
+            ROCODatasetsZoo.DJI.SOUTH_CHINA,
+        ],
+        [ROCODatasetsZoo.TWITCH.T470149568, ROCODatasetsZoo.TWITCH.T470152289],
+        [ROCODatasetsZoo.TWITCH.T470152838, ROCODatasetsZoo.TWITCH.T470151286],
+    )
+    # train_datasets.append(
+    #     digit_dataset_generator.from_roco_dataset(ROCODatasetsZoo.DJI.FINAL).to_file_images()
+    #     # .cap(2133 + 1764 + 1436)
+    #     # .skip(2133 + 176 + 1436)
+    #     # .cap(5_000)
+    #     .build()
+    # )
+    return train_datasets, validation_datasets, test_datasets
+
+
+class ArmorDigitTargetFactory(ArmorValueTargetFactory[ArmorDigit]):
+    def from_str(self, label: str) -> ArmorDigit:
+        n = int(label)
+
+        if n in VALID_NUMBERS_2021:  # CHANGING
+            return ArmorDigit(n - (n >= 3))  # hacky, but digit 2 is absent
+
+        return ArmorDigit.OUTDATED
+
+    def from_armor(self, armor: Armor) -> ArmorDigit:
+        return ArmorDigit(armor.number) if armor.number else ArmorDigit.UNKNOWN
+
+
+if __name__ == "__main__":
+    _roco_dataset_builder = ROCODatasetsZoo.DJI.CENTRAL_CHINA
+    _armor_digit_dataset = make_armor_digit_dataset_generator().from_roco_dataset(_roco_dataset_builder)
+
+    for p, c, _name in islice(_armor_digit_dataset, 20, 30):
+        print(p, c, _name)
diff --git a/polystar_cv/research/robots/armor_digit/digit_benchmarker.py b/polystar_cv/research/robots/armor_digit/digit_benchmarker.py
new file mode 100644
index 0000000..96473be
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/digit_benchmarker.py
@@ -0,0 +1,16 @@
+from pathlib import Path
+
+from research.robots.armor_digit.armor_digit_dataset import default_armor_digit_datasets
+from research.robots.armor_digit.pipeline import ArmorDigitPipeline
+from research.robots.evaluation.benchmarker import Benchmarker
+
+
+def make_default_digit_benchmarker(report_dir: Path) -> Benchmarker:
+    train_datasets, validation_datasets, test_datasets = default_armor_digit_datasets()
+    return Benchmarker(
+        report_dir=report_dir,
+        train_datasets=train_datasets,
+        validation_datasets=validation_datasets,
+        test_datasets=test_datasets,
+        classes=ArmorDigitPipeline.classes,
+    )
diff --git a/robots-at-robots/research/robots_at_robots/dataset/__init__.py b/polystar_cv/research/robots/armor_digit/gcloud/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/dataset/__init__.py
rename to polystar_cv/research/robots/armor_digit/gcloud/__init__.py
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/gather_performances.py b/polystar_cv/research/robots/armor_digit/gcloud/gather_performances.py
new file mode 100644
index 0000000..67e0b93
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/gather_performances.py
@@ -0,0 +1,41 @@
+import logging
+import pickle
+from pathlib import Path
+from typing import List
+
+from polystar.common.models.object import ArmorDigit
+from polystar.common.utils.iterable_utils import flatten
+from research.common.constants import EVALUATION_DIR
+from research.common.gcloud.gcloud_storage import GCStorages
+from research.robots.evaluation.metrics.f1 import F1Metric
+from research.robots.evaluation.performance import ClassificationPerformances
+from research.robots.evaluation.reporter import ImagePipelineEvaluationReporter
+
+
+def load_performances(performances_paths: List[Path]) -> ClassificationPerformances:
+    return ClassificationPerformances(flatten(pickle.loads(perf_path.read_bytes()) for perf_path in performances_paths))
+
+
+def gather_performances(task_name: str, job_id: str):
+    logging.info(f"gathering performances for {job_id} on task {task_name}")
+    experiment_dir = EVALUATION_DIR / task_name / job_id
+    performances_paths = download_performances(experiment_dir)
+    performances = load_performances(performances_paths)
+    ImagePipelineEvaluationReporter(
+        report_dir=EVALUATION_DIR / task_name / job_id, classes=list(ArmorDigit), other_metrics=[F1Metric()]
+    ).report(performances)
+
+
+def download_performances(experiment_dir: Path) -> List[Path]:
+    performances_paths = list(GCStorages.DEV.glob(experiment_dir, extension=".pkl"))
+    logging.info(f"Found {len(performances_paths)} performances")
+    for performance_path in performances_paths:
+        GCStorages.DEV.download_file_if_missing(performance_path)
+    return performances_paths
+
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+
+    gather_performances("armor-digit", "cnn_20201220_224525")
+    gather_performances("armor-digit", "vgg16_20201220_224417")
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/hptuning_config.yaml b/polystar_cv/research/robots/armor_digit/gcloud/hptuning_config.yaml
new file mode 100644
index 0000000..0a9fd93
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/hptuning_config.yaml
@@ -0,0 +1,33 @@
+trainingInput:
+  pythonVersion: "3.7"
+  runtimeVersion: "2.3"
+  scaleTier: BASIC_GPU
+  region: europe-west6
+
+  hyperparameters:
+    goal: MAXIMIZE
+    hyperparameterMetricTag: val_accuracy
+    maxTrials: 50
+    maxParallelTrials: 5
+    params:
+      - parameterName: lr
+        type: DOUBLE
+        minValue: 0.00001
+        maxValue: 0.1
+        scaleType: UNIT_LOG_SCALE
+      - parameterName: dropout
+        type: DOUBLE
+        minValue: 0
+        maxValue: .99
+      - parameterName: dense-size
+        type: DISCRETE
+        discreteValues:
+          - 16
+          - 32
+          - 64
+          - 128
+          - 256
+          - 512
+          - 1024
+          - 2048
+        scaleType: UNIT_LOG_SCALE
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/train.py b/polystar_cv/research/robots/armor_digit/gcloud/train.py
new file mode 100644
index 0000000..fecfaa7
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/train.py
@@ -0,0 +1,19 @@
+import pickle
+from os.path import join
+
+from research.common.gcloud.gcloud_storage import GCStorage
+from research.robots.armor_digit.armor_digit_dataset import default_armor_digit_datasets
+from research.robots.armor_digit.pipeline import ArmorDigitPipeline
+from research.robots.evaluation.evaluator import ImageClassificationPipelineEvaluator
+from research.robots.evaluation.trainer import ImageClassificationPipelineTrainer
+
+
+def train_evaluate_digit_pipeline(pipeline: ArmorDigitPipeline, job_dir: str):
+    train_datasets, val_datasets, test_datasets = default_armor_digit_datasets()
+    trainer = ImageClassificationPipelineTrainer(train_datasets, val_datasets)
+    evaluator = ImageClassificationPipelineEvaluator(train_datasets, val_datasets, test_datasets)
+
+    trainer.train_pipeline(pipeline)
+
+    with GCStorage.open_from_str(join(job_dir, pipeline.name, "perfs.pkl"), "wb") as f:
+        pickle.dump(evaluator.evaluate_pipeline(pipeline), f)
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/train_cnn.py b/polystar_cv/research/robots/armor_digit/gcloud/train_cnn.py
new file mode 100644
index 0000000..c9c7437
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/train_cnn.py
@@ -0,0 +1,29 @@
+import logging
+import warnings
+from argparse import ArgumentParser
+
+from research.robots.armor_digit.gcloud.train import train_evaluate_digit_pipeline
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    _parser = ArgumentParser()
+    _parser.add_argument("--job-dir", type=str, required=True)
+    _parser.add_argument("--lr", type=float, required=True)
+    _parser.add_argument("--dropout", type=float, required=True)
+    _parser.add_argument("--dense-size", type=int, required=True)
+    _args = _parser.parse_args()
+
+    _pipeline = ArmorDigitKerasPipeline.from_custom_cnn(
+        input_size=32,
+        conv_blocks=((32, 32), (64, 64)),
+        logs_dir=_args.job_dir,
+        lr=_args.lr,
+        dense_size=_args.dense_size,
+        dropout=_args.dropout,
+    )
+
+    train_evaluate_digit_pipeline(_pipeline, _args.job_dir)
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/train_vgg16.py b/polystar_cv/research/robots/armor_digit/gcloud/train_vgg16.py
new file mode 100644
index 0000000..b8cc89e
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/train_vgg16.py
@@ -0,0 +1,31 @@
+import logging
+import warnings
+from argparse import ArgumentParser
+
+from tensorflow.python.keras.applications.vgg16 import VGG16
+
+from research.robots.armor_digit.gcloud.train import train_evaluate_digit_pipeline
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    _parser = ArgumentParser()
+    _parser.add_argument("--job-dir", type=str, required=True)
+    _parser.add_argument("--lr", type=float, required=True)
+    _parser.add_argument("--dropout", type=float, required=True)
+    _parser.add_argument("--dense-size", type=int, required=True)
+    _args = _parser.parse_args()
+
+    _pipeline = ArmorDigitKerasPipeline.from_transfer_learning(
+        model_factory=VGG16,
+        logs_dir=_args.job_dir,
+        input_size=32,
+        lr=_args.lr,
+        dense_size=_args.dense_size,
+        dropout=_args.dropout,
+    )
+
+    train_evaluate_digit_pipeline(_pipeline, _args.job_dir)
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/train_xception.py b/polystar_cv/research/robots/armor_digit/gcloud/train_xception.py
new file mode 100644
index 0000000..c3ce07b
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/train_xception.py
@@ -0,0 +1,31 @@
+import logging
+import warnings
+from argparse import ArgumentParser
+
+from tensorflow.python.keras.applications.xception import Xception
+
+from research.robots.armor_digit.gcloud.train import train_evaluate_digit_pipeline
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    _parser = ArgumentParser()
+    _parser.add_argument("--job-dir", type=str, required=True)
+    _parser.add_argument("--lr", type=float, required=True)
+    _parser.add_argument("--dropout", type=float, required=True)
+    _parser.add_argument("--dense-size", type=int, required=True)
+    _args = _parser.parse_args()
+
+    _pipeline = ArmorDigitKerasPipeline.from_transfer_learning(
+        model_factory=Xception,
+        logs_dir=_args.job_dir,
+        input_size=72,
+        lr=_args.lr,
+        dense_size=_args.dense_size,
+        dropout=_args.dropout,
+    )
+
+    train_evaluate_digit_pipeline(_pipeline, _args.job_dir)
diff --git a/polystar_cv/research/robots/armor_digit/gcloud/trainer.sh b/polystar_cv/research/robots/armor_digit/gcloud/trainer.sh
new file mode 100644
index 0000000..d4fe0ec
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/gcloud/trainer.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -e
+
+cd ../../../../../
+
+# 1. Params
+task_name="armor-digit"
+
+read -rp "Experiment name:" experiment_name
+job_id=${experiment_name}_$(date +'%Y%m%d_%H%M%S')
+job_dir="gs://poly-cv-dev/experiments/$task_name/$job_id"
+author=$(git config user.name | tr " " - | tr '[:upper:]' '[:lower:]')
+echo Running job "$job_id" for task "$task_name" by "$author"
+
+# 2. build source
+poetry build -f wheel
+
+# 3. start job
+gcloud ai-platform jobs submit training "${job_id}" \
+    --config polystar_cv/research/robots/armor_digit/gcloud/hptuning_config.yaml \
+    --job-dir="${job_dir}" \
+    --packages ./dist/polystar_cv-0.2.0-py3-none-any.whl \
+    --module-name=research.robots.armor_digit.gcloud.train_cnn \
+    --labels task=${task_name},author="${author}"
+
+# 4. logs
+echo "logs:  https://console.cloud.google.com/logs/query;query=resource.labels.job_id%3D%22${job_id}%22?project=polystar-cv"
+echo "job:   https://console.cloud.google.com/ai-platform/jobs/${job_id}/charts/cpu?project=polystar-cv"
+
+tensorboard --logdir="${job_dir}"
diff --git a/polystar_cv/research/robots/armor_digit/pipeline.py b/polystar_cv/research/robots/armor_digit/pipeline.py
new file mode 100644
index 0000000..24467ba
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/pipeline.py
@@ -0,0 +1,13 @@
+from polystar.common.models.object import ArmorDigit
+from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
+from polystar.common.pipeline.classification.keras_classification_pipeline import KerasClassificationPipeline
+from polystar.common.utils.registry import registry
+
+
+class ArmorDigitPipeline(ClassificationPipeline):
+    enum = ArmorDigit
+
+
+@registry.register()
+class ArmorDigitKerasPipeline(ArmorDigitPipeline, KerasClassificationPipeline):
+    pass
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/__init__.py b/polystar_cv/research/robots/armor_digit/scripts/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/evaluation/__init__.py
rename to polystar_cv/research/robots/armor_digit/scripts/__init__.py
diff --git a/polystar_cv/research/robots/armor_digit/scripts/benchmark.py b/polystar_cv/research/robots/armor_digit/scripts/benchmark.py
new file mode 100644
index 0000000..2dde34d
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/scripts/benchmark.py
@@ -0,0 +1,56 @@
+import logging
+import warnings
+from pathlib import Path
+
+from polystar.common.constants import PROJECT_DIR
+from polystar.common.pipeline.classification.random_model import RandomClassifier
+from polystar.common.utils.serialization import pkl_load
+from research.common.utils.experiment_dir import prompt_experiment_dir
+from research.robots.armor_digit.digit_benchmarker import make_default_digit_benchmarker
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline, ArmorDigitPipeline
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    _report_dir: Path = prompt_experiment_dir("armor-digit")
+
+    logging.info(f"Running benchmarking {_report_dir.name}")
+
+    _benchmarker = make_default_digit_benchmarker(report_dir=_report_dir)
+
+    _random_pipeline = ArmorDigitPipeline.from_pipes([RandomClassifier()], name="random")
+    _cnn_pipeline = ArmorDigitKerasPipeline.from_custom_cnn(
+        input_size=32,
+        conv_blocks=((32, 32), (64, 64)),
+        logs_dir=str(_report_dir),
+        dropout=0.66,
+        lr=0.00078,
+        dense_size=1024,
+        name="cnn",
+    )
+    # _vgg16_pipeline = ArmorDigitKerasPipeline.from_transfer_learning(
+    #     input_size=32, logs_dir=_report_dir, dropout=0, lr=0.00021, dense_size=64, model_factory=VGG16
+    # )
+
+    _vgg16_pipeline = pkl_load(
+        PROJECT_DIR / "pipelines/armor-digit/20201225_131957_vgg16/VGG16 (32) - lr 2.1e-04 - drop 0.pkl"
+    )
+    _vgg16_pipeline.name = "vgg16_tl"
+
+    _distiled_vgg16_into_cnn_pipeline = ArmorDigitKerasPipeline.from_distillation(
+        teacher_pipeline=_vgg16_pipeline,
+        conv_blocks=((32, 32), (64, 64)),
+        logs_dir=_report_dir,
+        dropout=0.63,
+        lr=0.000776,
+        dense_size=1024,
+        temperature=41.2,
+        name="cnn_kd",
+    )
+
+    _benchmarker.benchmark(
+        pipelines=[_random_pipeline, _distiled_vgg16_into_cnn_pipeline, _cnn_pipeline],
+        trained_pipelines=[_vgg16_pipeline],
+    )
diff --git a/robots-at-robots/research/robots_at_robots/armor_digit/clean_datasets.py b/polystar_cv/research/robots/armor_digit/scripts/clean_datasets.py
similarity index 62%
rename from robots-at-robots/research/robots_at_robots/armor_digit/clean_datasets.py
rename to polystar_cv/research/robots/armor_digit/scripts/clean_datasets.py
index f78124e..39a1f9c 100644
--- a/robots-at-robots/research/robots_at_robots/armor_digit/clean_datasets.py
+++ b/polystar_cv/research/robots/armor_digit/scripts/clean_datasets.py
@@ -1,6 +1,6 @@
 from research.common.dataset.cleaning.dataset_cleaner_app import DatasetCleanerApp
 from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.armor_digit.armor_digit_dataset import make_armor_digit_dataset_generator
+from research.robots.armor_digit.armor_digit_dataset import make_armor_digit_dataset_generator
 
 if __name__ == "__main__":
     # _roco_dataset = ROCODatasetsZoo.TWITCH.T470149568
@@ -16,22 +16,7 @@ if __name__ == "__main__":
     _roco_dataset = ROCODatasetsZoo.DJI.FINAL
 
     _armor_digit_dataset = (
-        make_armor_digit_dataset_generator()
-        .from_roco_dataset(_roco_dataset)
-        .skip(
-            (1009 - 117)
-            + (1000 - 86)
-            + (1000 - 121)
-            + (1000 - 138)
-            + (1000 - 137)
-            + (1000 - 154)
-            + (1000 - 180)
-            + (1000 - 160)
-            + (1000 - 193)
-            + (1000 - 80)
-            + (1000 - 154)
-        )
-        .cap(1000)
+        make_armor_digit_dataset_generator().from_roco_dataset(_roco_dataset).skip(2133 + 1764 + 1436).cap(1000)
     )
 
     DatasetCleanerApp(_armor_digit_dataset, invalidate_key="u", validate_key="h").run()
diff --git a/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_cnn.py b/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_cnn.py
new file mode 100644
index 0000000..26ce661
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_cnn.py
@@ -0,0 +1,32 @@
+import logging
+import warnings
+from pathlib import Path
+
+from optuna import Trial
+
+from research.common.utils.experiment_dir import make_experiment_dir
+from research.robots.armor_digit.digit_benchmarker import make_default_digit_benchmarker
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline, ArmorDigitPipeline
+from research.robots.evaluation.hyper_tuner import HyperTuner
+
+
+def cnn_pipeline_factory(report_dir: Path, trial: Trial) -> ArmorDigitPipeline:
+    return ArmorDigitKerasPipeline.from_custom_cnn(
+        input_size=32,
+        conv_blocks=((32, 32), (64, 64)),
+        logs_dir=str(report_dir),
+        dropout=trial.suggest_uniform("dropout", 0, 0.99),
+        lr=trial.suggest_loguniform("lr", 1e-5, 1e-1),
+        dense_size=2 ** round(trial.suggest_discrete_uniform("dense_size_log2", 3, 10, 1)),
+    )
+
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    logging.info("Hyperparameter tuning for CNN pipeline on digit task")
+    HyperTuner(make_default_digit_benchmarker(make_experiment_dir("armor-digit", "cnn_tuning"))).tune(
+        cnn_pipeline_factory, n_trials=50
+    )
diff --git a/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_distiled_vgg16_into_cnn.py b/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_distiled_vgg16_into_cnn.py
new file mode 100644
index 0000000..3b035e7
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/scripts/hyper_tune_distiled_vgg16_into_cnn.py
@@ -0,0 +1,39 @@
+import logging
+import warnings
+from pathlib import Path
+
+from optuna import Trial
+
+from polystar.common.constants import PROJECT_DIR
+from polystar.common.utils.serialization import pkl_load
+from research.common.utils.experiment_dir import make_experiment_dir
+from research.robots.armor_digit.digit_benchmarker import make_default_digit_benchmarker
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline, ArmorDigitPipeline
+from research.robots.evaluation.hyper_tuner import HyperTuner
+
+
+class DistilledPipelineFactory:
+    def __init__(self, teacher_name: str):
+        self.teacher: ArmorDigitKerasPipeline = pkl_load(PROJECT_DIR / "pipelines/armor-digit" / teacher_name)
+
+    def __call__(self, report_dir: Path, trial: Trial) -> ArmorDigitPipeline:
+        return ArmorDigitKerasPipeline.from_distillation(
+            teacher_pipeline=self.teacher,
+            conv_blocks=((32, 32), (64, 64)),
+            logs_dir=str(report_dir),
+            dropout=trial.suggest_uniform("dropout", 0, 0.99),
+            lr=trial.suggest_loguniform("lr", 5e-4, 1e-3),
+            dense_size=1024,  # 2 ** round(trial.suggest_discrete_uniform("dense_size_log2", 3, 10, 1)),
+            temperature=trial.suggest_loguniform("temperature", 1, 100),
+        )
+
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+
+    logging.info("Hyperparameter tuning for VGG16 distilation into CNN pipeline on digit task")
+    HyperTuner(make_default_digit_benchmarker(make_experiment_dir("armor-digit", "distillation_tuning"))).tune(
+        DistilledPipelineFactory("20201225_131957_vgg16/VGG16 (32) - lr 2.1e-04 - drop 0.pkl"), n_trials=50
+    )
diff --git a/polystar_cv/research/robots/armor_digit/scripts/train_vgg16.py b/polystar_cv/research/robots/armor_digit/scripts/train_vgg16.py
new file mode 100644
index 0000000..650aabc
--- /dev/null
+++ b/polystar_cv/research/robots/armor_digit/scripts/train_vgg16.py
@@ -0,0 +1,37 @@
+import logging
+import warnings
+
+from tensorflow.python.keras.applications.vgg16 import VGG16
+
+from polystar.common.constants import PROJECT_DIR
+from polystar.common.utils.serialization import pkl_dump
+from polystar.common.utils.time import create_time_id
+from research.robots.armor_digit.digit_benchmarker import make_default_digit_benchmarker
+from research.robots.armor_digit.pipeline import ArmorDigitKerasPipeline
+
+PIPELINES_DIR = PROJECT_DIR / "pipelines"
+
+if __name__ == "__main__":
+    logging.getLogger().setLevel("INFO")
+    logging.getLogger("tensorflow").setLevel("ERROR")
+    warnings.filterwarnings("ignore")
+    logging.info("Training vgg16")
+
+    _training_dir = PIPELINES_DIR / "armor-digit" / f"{create_time_id()}_vgg16_full_dset"
+
+    _vgg16_pipeline = ArmorDigitKerasPipeline.from_transfer_learning(
+        input_size=32,
+        logs_dir=str(_training_dir),
+        dropout=0,
+        lr=0.00021,
+        dense_size=64,
+        model_factory=VGG16,
+        verbose=1,
+    )
+
+    logging.info(f"Run `tensorboard --logdir={_training_dir}` for realtime logs")
+
+    _benchmarker = make_default_digit_benchmarker(_training_dir)
+    _benchmarker.benchmark([_vgg16_pipeline])
+
+    pkl_dump(_vgg16_pipeline, _training_dir / _vgg16_pipeline.name)
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/metrics/__init__.py b/polystar_cv/research/robots/dataset/__init__.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/evaluation/metrics/__init__.py
rename to polystar_cv/research/robots/dataset/__init__.py
diff --git a/robots-at-robots/research/robots_at_robots/dataset/armor_dataset_factory.py b/polystar_cv/research/robots/dataset/armor_dataset_factory.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/dataset/armor_dataset_factory.py
rename to polystar_cv/research/robots/dataset/armor_dataset_factory.py
diff --git a/robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_cache.py b/polystar_cv/research/robots/dataset/armor_value_dataset_cache.py
similarity index 73%
rename from robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_cache.py
rename to polystar_cv/research/robots/dataset/armor_value_dataset_cache.py
index 68f1b15..b826aa2 100644
--- a/robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_cache.py
+++ b/polystar_cv/research/robots/dataset/armor_value_dataset_cache.py
@@ -3,6 +3,8 @@ from pathlib import Path
 from shutil import rmtree
 from typing import ClassVar, Generic, Optional
 
+from google.cloud.exceptions import Forbidden
+
 from polystar.common.models.image import Image, save_image
 from polystar.common.utils.misc import identity
 from polystar.common.utils.time import create_time_id
@@ -10,8 +12,9 @@ from polystar.common.utils.tqdm import smart_tqdm
 from research.common.datasets.lazy_dataset import LazyDataset, TargetT
 from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
 from research.common.datasets.transform_dataset import TransformDataset
-from research.robots_at_robots.dataset.armor_dataset_factory import ArmorDataset
-from research.robots_at_robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
+from research.common.gcloud.gcloud_storage import GCStorages
+from research.robots.dataset.armor_dataset_factory import ArmorDataset
+from research.robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
 
 
 class ArmorValueDatasetCache(Generic[TargetT]):
@@ -30,11 +33,23 @@ class ArmorValueDatasetCache(Generic[TargetT]):
         self.roco_dataset_builder = roco_dataset_builder
         self.lock_file = cache_dir / ".lock"
 
-    def generate_if_needed(self):
+        self.cache_dir.mkdir(parents=True, exist_ok=True)
+
+    def generate_or_download_if_needed(self):
         cause = self._get_generation_cause()
         if cause is None:
             return
         self._clean_cache_dir()
+        try:
+            GCStorages.DEV.download_directory(self.cache_dir)
+            cause = self._get_generation_cause()
+            if cause is None:
+                return
+            self._clean_cache_dir()
+        except FileNotFoundError:
+            cause += " and not on gcloud"
+        except Forbidden:
+            pass
         self.save(self._generate(), cause)
 
     def _clean_cache_dir(self):
@@ -44,7 +59,7 @@ class ArmorValueDatasetCache(Generic[TargetT]):
     def save(self, dataset: LazyDataset[Image, TargetT], cause: str):
         desc = f"Generating dataset {self.dataset_name} (cause: {cause})"
         for img, target, name in smart_tqdm(dataset, desc=desc, unit="img"):
-            save_image(img, self.cache_dir / f"{name}-{target}.jpg")
+            save_image(img, self.cache_dir / f"{name}-{str(target)}.jpg")
         self.lock_file.write_text(json.dumps({"version": self.VERSION, "date": create_time_id()}))
 
     def _generate(self) -> LazyDataset[Image, TargetT]:
diff --git a/robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_generator.py b/polystar_cv/research/robots/dataset/armor_value_dataset_generator.py
similarity index 70%
rename from robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_generator.py
rename to polystar_cv/research/robots/dataset/armor_value_dataset_generator.py
index 4aafd34..a296d9c 100644
--- a/robots-at-robots/research/robots_at_robots/dataset/armor_value_dataset_generator.py
+++ b/polystar_cv/research/robots/dataset/armor_value_dataset_generator.py
@@ -1,5 +1,5 @@
 from pathlib import Path
-from typing import Generic, List
+from typing import Generic, Iterable, List
 
 from polystar.common.filters.exclude_filter import ExcludeFilter
 from polystar.common.filters.filter_abc import FilterABC
@@ -9,8 +9,8 @@ from research.common.datasets.image_dataset import FileImageDataset
 from research.common.datasets.image_file_dataset_builder import DirectoryDatasetBuilder
 from research.common.datasets.lazy_dataset import TargetT
 from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
-from research.robots_at_robots.dataset.armor_value_dataset_cache import ArmorValueDatasetCache
-from research.robots_at_robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
+from research.robots.dataset.armor_value_dataset_cache import ArmorValueDatasetCache
+from research.robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
 
 
 class ExcludeFilesFilter(ExcludeFilter[Path]):
@@ -30,14 +30,21 @@ class ArmorValueDatasetGenerator(Generic[TargetT]):
         self.targets_filter = targets_filter or PassThroughFilter()
 
     # FIXME signature inconsistency across methods
-    def from_roco_datasets(self, roco_datasets: List[ROCODatasetBuilder]) -> List[FileImageDataset[TargetT]]:
-        return [self.from_roco_dataset(roco_dataset).to_file_images().build() for roco_dataset in roco_datasets]
+    def from_roco_datasets(
+        self, *roco_datasets_list: List[ROCODatasetBuilder]
+    ) -> Iterable[List[FileImageDataset[TargetT]]]:
+        return (
+            [self.from_roco_dataset(roco_dataset).to_file_images().build() for roco_dataset in roco_datasets]
+            for roco_datasets in roco_datasets_list
+        )
 
     def from_roco_dataset(self, roco_dataset_builder: ROCODatasetBuilder) -> DirectoryDatasetBuilder[TargetT]:
         cache_dir = roco_dataset_builder.main_dir / self.task_name
         dataset_name = roco_dataset_builder.name
 
-        ArmorValueDatasetCache(roco_dataset_builder, cache_dir, dataset_name, self.target_factory).generate_if_needed()
+        ArmorValueDatasetCache(
+            roco_dataset_builder, cache_dir, dataset_name, self.target_factory
+        ).generate_or_download_if_needed()
 
         return (
             DirectoryDatasetBuilder(cache_dir, self.target_factory.from_file, dataset_name)
diff --git a/robots-at-robots/research/robots_at_robots/dataset/armor_value_target_factory.py b/polystar_cv/research/robots/dataset/armor_value_target_factory.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/dataset/armor_value_target_factory.py
rename to polystar_cv/research/robots/dataset/armor_value_target_factory.py
diff --git a/robots-at-runes/polystar/robots_at_runes/__init__.py b/polystar_cv/research/robots/demos/__init__.py
similarity index 100%
rename from robots-at-runes/polystar/robots_at_runes/__init__.py
rename to polystar_cv/research/robots/demos/__init__.py
diff --git a/robots-at-robots/research/robots_at_robots/demos/demo_infer.py b/polystar_cv/research/robots/demos/demo_infer.py
similarity index 89%
rename from robots-at-robots/research/robots_at_robots/demos/demo_infer.py
rename to polystar_cv/research/robots/demos/demo_infer.py
index 40c567f..51c1cc2 100644
--- a/robots-at-robots/research/robots_at_robots/demos/demo_infer.py
+++ b/polystar_cv/research/robots/demos/demo_infer.py
@@ -1,12 +1,12 @@
+from polystar.common.dependency_injection import make_injector
 from polystar.common.models.label_map import LabelMap
 from polystar.common.target_pipeline.detected_objects.detected_objects_factory import DetectedObjectFactory
 from polystar.common.target_pipeline.objects_detectors.tf_model_objects_detector import TFModelObjectsDetector
 from polystar.common.target_pipeline.objects_validators.confidence_object_validator import ConfidenceObjectValidator
 from polystar.common.utils.tensorflow import patch_tf_v2
 from polystar.common.view.plt_results_viewer import PltResultViewer
-from polystar.robots_at_robots.dependency_injection import make_injector
 from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.demos.utils import load_tf_model
+from research.robots.demos.utils import load_tf_model
 
 if __name__ == "__main__":
     patch_tf_v2()
diff --git a/robots-at-robots/research/robots_at_robots/demos/demo_pipeline.py b/polystar_cv/research/robots/demos/demo_pipeline.py
similarity index 90%
rename from robots-at-robots/research/robots_at_robots/demos/demo_pipeline.py
rename to polystar_cv/research/robots/demos/demo_pipeline.py
index c3a4d34..ff5475b 100644
--- a/robots-at-robots/research/robots_at_robots/demos/demo_pipeline.py
+++ b/polystar_cv/research/robots/demos/demo_pipeline.py
@@ -1,6 +1,7 @@
 import cv2
 
 from polystar.common.communication.print_target_sender import PrintTargetSender
+from polystar.common.dependency_injection import make_injector
 from polystar.common.models.camera import Camera
 from polystar.common.models.label_map import LabelMap
 from polystar.common.target_pipeline.armors_descriptors.armors_color_descriptor import ArmorsColorDescriptor
@@ -14,14 +15,10 @@ from polystar.common.target_pipeline.target_factories.ratio_simple_target_factor
 from polystar.common.target_pipeline.target_pipeline import NoTargetFoundException
 from polystar.common.utils.tensorflow import patch_tf_v2
 from polystar.common.view.plt_results_viewer import PltResultViewer
-from polystar.robots_at_robots.dependency_injection import make_injector
 from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.armor_color.benchmark import (
-    ArmorColorPipeline,
-    MeanChannels,
-    RedBlueComparisonClassifier,
-)
-from research.robots_at_robots.demos.utils import load_tf_model
+from research.robots.armor_color.pipeline import ArmorColorPipeline
+from research.robots.armor_color.scripts.benchmark import MeanChannels, RedBlueComparisonClassifier
+from research.robots.demos.utils import load_tf_model
 
 if __name__ == "__main__":
     patch_tf_v2()
diff --git a/robots-at-robots/research/robots_at_robots/demos/demo_pipeline_camera.py b/polystar_cv/research/robots/demos/demo_pipeline_camera.py
similarity index 97%
rename from robots-at-robots/research/robots_at_robots/demos/demo_pipeline_camera.py
rename to polystar_cv/research/robots/demos/demo_pipeline_camera.py
index 0db0dd1..660e09e 100644
--- a/robots-at-robots/research/robots_at_robots/demos/demo_pipeline_camera.py
+++ b/polystar_cv/research/robots/demos/demo_pipeline_camera.py
@@ -5,6 +5,7 @@ import pycuda.autoinit  # This is needed for initializing CUDA driver
 
 from polystar.common.communication.file_descriptor_target_sender import FileDescriptorTargetSender
 from polystar.common.constants import MODELS_DIR
+from polystar.common.dependency_injection import make_injector
 from polystar.common.frame_generators.camera_frame_generator import CameraFrameGenerator
 from polystar.common.models.camera import Camera
 from polystar.common.models.label_map import LabelMap
@@ -18,7 +19,6 @@ from polystar.common.target_pipeline.objects_validators.type_object_validator im
 from polystar.common.target_pipeline.target_factories.ratio_simple_target_factory import RatioSimpleTargetFactory
 from polystar.common.utils.tensorflow import patch_tf_v2
 from polystar.common.view.cv2_results_viewer import CV2ResultViewer
-from polystar.robots_at_robots.dependency_injection import make_injector
 from polystar.robots_at_robots.globals import settings
 
 [pycuda.autoinit]  # So pycharm won't remove the import
diff --git a/robots-at-robots/research/robots_at_robots/demos/utils.py b/polystar_cv/research/robots/demos/utils.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/demos/utils.py
rename to polystar_cv/research/robots/demos/utils.py
diff --git a/robots-at-runes/research/robots_at_runes/__init__.py b/polystar_cv/research/robots/evaluation/__init__.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/__init__.py
rename to polystar_cv/research/robots/evaluation/__init__.py
diff --git a/polystar_cv/research/robots/evaluation/benchmarker.py b/polystar_cv/research/robots/evaluation/benchmarker.py
new file mode 100644
index 0000000..0c3123f
--- /dev/null
+++ b/polystar_cv/research/robots/evaluation/benchmarker.py
@@ -0,0 +1,50 @@
+import logging
+from dataclasses import dataclass
+from pathlib import Path
+from typing import List
+
+from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
+from research.common.datasets.image_dataset import FileImageDataset
+from research.robots.evaluation.evaluator import ImageClassificationPipelineEvaluator
+from research.robots.evaluation.metrics.f1 import F1Metric
+from research.robots.evaluation.performance import ClassificationPerformances
+from research.robots.evaluation.reporter import ImagePipelineEvaluationReporter
+from research.robots.evaluation.trainer import ImageClassificationPipelineTrainer
+
+logger = logging.getLogger(__name__)
+
+
+@dataclass
+class Benchmarker:
+    def __init__(
+        self,
+        train_datasets: List[FileImageDataset],
+        validation_datasets: List[FileImageDataset],
+        test_datasets: List[FileImageDataset],
+        classes: List,
+        report_dir: Path,
+    ):
+        report_dir.mkdir(exist_ok=True, parents=True)
+        self.trainer = ImageClassificationPipelineTrainer(train_datasets, validation_datasets)
+        self.evaluator = ImageClassificationPipelineEvaluator(train_datasets, validation_datasets, test_datasets)
+        self.reporter = ImagePipelineEvaluationReporter(
+            report_dir=report_dir, classes=classes, other_metrics=[F1Metric()]
+        )
+        self.performances = ClassificationPerformances()
+        logger.info(f"Run `tensorboard --logdir={report_dir}` for realtime logs when using keras")
+
+    def train_and_evaluate(self, pipeline: ClassificationPipeline) -> ClassificationPerformances:
+        self.trainer.train_pipeline(pipeline)
+        pipeline_performances = self.evaluator.evaluate_pipeline(pipeline)
+        self.performances += pipeline_performances
+        return pipeline_performances
+
+    def benchmark(
+        self, pipelines: List[ClassificationPipeline], trained_pipelines: List[ClassificationPipeline] = None
+    ):
+        self.trainer.train_pipelines(pipelines)
+        self.performances += self.evaluator.evaluate_pipelines(pipelines + (trained_pipelines or []))
+        self.make_report()
+
+    def make_report(self):
+        self.reporter.report(self.performances)
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluator.py b/polystar_cv/research/robots/evaluation/evaluator.py
similarity index 83%
rename from robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluator.py
rename to polystar_cv/research/robots/evaluation/evaluator.py
index 9f11ae3..d03c1c9 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluator.py
+++ b/polystar_cv/research/robots/evaluation/evaluator.py
@@ -9,12 +9,12 @@ from polystar.common.pipeline.classification.classification_pipeline import Clas
 from polystar.common.utils.iterable_utils import flatten
 from research.common.datasets.image_dataset import FileImageDataset
 from research.common.datasets.lazy_dataset import TargetT
-from research.robots_at_robots.evaluation.performance import (
+from research.robots.evaluation.performance import (
     ClassificationPerformance,
     ClassificationPerformances,
     ContextualizedClassificationPerformance,
 )
-from research.robots_at_robots.evaluation.set import Set
+from research.robots.evaluation.set import Set
 
 
 class ImageClassificationPipelineEvaluator(Generic[TargetT]):
@@ -27,11 +27,13 @@ class ImageClassificationPipelineEvaluator(Generic[TargetT]):
         self.set2datasets = {Set.TRAIN: train_datasets, Set.VALIDATION: validation_datasets, Set.TEST: test_datasets}
 
     def evaluate_pipelines(self, pipelines: Iterable[ClassificationPipeline]) -> ClassificationPerformances:
-        return ClassificationPerformances(flatten(self._evaluate_pipeline(pipeline) for pipeline in pipelines))
+        rv = ClassificationPerformances()
+        for pipeline in pipelines:
+            rv += self.evaluate_pipeline(pipeline)
+        return rv
 
-    def _evaluate_pipeline(self, pipeline: ClassificationPipeline) -> Iterable[ContextualizedClassificationPerformance]:
-        for set_ in Set:
-            yield from self._evaluate_pipeline_on_set(pipeline, set_)
+    def evaluate_pipeline(self, pipeline: ClassificationPipeline) -> ClassificationPerformances:
+        return ClassificationPerformances(flatten(self._evaluate_pipeline_on_set(pipeline, set_) for set_ in Set))
 
     def _evaluate_pipeline_on_set(
         self, pipeline: ClassificationPipeline, set_: Set
diff --git a/polystar_cv/research/robots/evaluation/hyper_tuner.py b/polystar_cv/research/robots/evaluation/hyper_tuner.py
new file mode 100644
index 0000000..4953894
--- /dev/null
+++ b/polystar_cv/research/robots/evaluation/hyper_tuner.py
@@ -0,0 +1,34 @@
+from pathlib import Path
+from typing import Callable, Optional
+
+from optuna import Trial, create_study
+
+from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
+from polystar.common.utils.serialization import pkl_dump
+from research.robots.evaluation.benchmarker import Benchmarker
+from research.robots.evaluation.metrics.accuracy import AccuracyMetric
+from research.robots.evaluation.metrics.metric_abc import MetricABC
+
+PipelineFactory = Callable[[Path, Trial], ClassificationPipeline]
+
+
+class HyperTuner:
+    def __init__(self, benchmarker: Benchmarker, metric: MetricABC = AccuracyMetric(), report_frequency: int = 5):
+        self.report_frequency = report_frequency
+        self.metric = metric
+        self.benchmarker = benchmarker
+        self._pipeline_factory: Optional[PipelineFactory] = None
+
+    def tune(self, pipeline_factory: PipelineFactory, n_trials: int, minimize: bool = False):
+        self._pipeline_factory = pipeline_factory
+        study = create_study(direction="minimize" if minimize else "maximize")
+        study.optimize(self._objective, n_trials=n_trials, show_progress_bar=True)
+        self.benchmarker.make_report()
+        pkl_dump(study, self.benchmarker.reporter.report_dir / "study")
+
+    def _objective(self, trial: Trial) -> float:
+        pipeline = self._pipeline_factory(self.benchmarker.reporter.report_dir, trial)
+        performances = self.benchmarker.train_and_evaluate(pipeline)
+        if not trial.number % self.report_frequency:
+            self.benchmarker.make_report()
+        return self.metric(performances.validation.collapse())
diff --git a/robots-at-runes/research/robots_at_runes/dataset/__init__.py b/polystar_cv/research/robots/evaluation/metrics/__init__.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/__init__.py
rename to polystar_cv/research/robots/evaluation/metrics/__init__.py
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/metrics/accuracy.py b/polystar_cv/research/robots/evaluation/metrics/accuracy.py
similarity index 59%
rename from robots-at-robots/research/robots_at_robots/evaluation/metrics/accuracy.py
rename to polystar_cv/research/robots/evaluation/metrics/accuracy.py
index ccfe9c7..60716f2 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/metrics/accuracy.py
+++ b/polystar_cv/research/robots/evaluation/metrics/accuracy.py
@@ -1,5 +1,5 @@
-from research.robots_at_robots.evaluation.metrics.metric_abc import MetricABC
-from research.robots_at_robots.evaluation.performance import ClassificationPerformance
+from research.robots.evaluation.metrics.metric_abc import MetricABC
+from research.robots.evaluation.performance import ClassificationPerformance
 
 
 class AccuracyMetric(MetricABC):
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/metrics/f1.py b/polystar_cv/research/robots/evaluation/metrics/f1.py
similarity index 79%
rename from robots-at-robots/research/robots_at_robots/evaluation/metrics/f1.py
rename to polystar_cv/research/robots/evaluation/metrics/f1.py
index dd5f48a..0730c42 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/metrics/f1.py
+++ b/polystar_cv/research/robots/evaluation/metrics/f1.py
@@ -2,8 +2,8 @@ from enum import Enum, auto
 
 from sklearn.metrics import f1_score
 
-from research.robots_at_robots.evaluation.metrics.metric_abc import MetricABC
-from research.robots_at_robots.evaluation.performance import ClassificationPerformance
+from research.robots.evaluation.metrics.metric_abc import MetricABC
+from research.robots.evaluation.performance import ClassificationPerformance
 
 
 class F1Strategy(Enum):
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/metrics/metric_abc.py b/polystar_cv/research/robots/evaluation/metrics/metric_abc.py
similarity index 77%
rename from robots-at-robots/research/robots_at_robots/evaluation/metrics/metric_abc.py
rename to polystar_cv/research/robots/evaluation/metrics/metric_abc.py
index f25a0c3..f939585 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/metrics/metric_abc.py
+++ b/polystar_cv/research/robots/evaluation/metrics/metric_abc.py
@@ -1,6 +1,6 @@
 from abc import ABC, abstractmethod
 
-from research.robots_at_robots.evaluation.performance import ClassificationPerformance
+from research.robots.evaluation.performance import ClassificationPerformance
 
 
 class MetricABC(ABC):
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/performance.py b/polystar_cv/research/robots/evaluation/performance.py
similarity index 85%
rename from robots-at-robots/research/robots_at_robots/evaluation/performance.py
rename to polystar_cv/research/robots/evaluation/performance.py
index 52c014c..1521fbd 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/performance.py
+++ b/polystar_cv/research/robots/evaluation/performance.py
@@ -1,4 +1,4 @@
-from dataclasses import dataclass
+from dataclasses import dataclass, field
 from typing import Dict, Iterable, List, Sequence
 
 import numpy as np
@@ -7,7 +7,7 @@ from memoized_property import memoized_property
 from polystar.common.filters.filter_abc import FilterABC
 from polystar.common.models.image import FileImage
 from polystar.common.utils.iterable_utils import flatten, group_by
-from research.robots_at_robots.evaluation.set import Set
+from research.robots.evaluation.set import Set
 
 
 @dataclass
@@ -39,7 +39,7 @@ class ContextualizedClassificationPerformance(ClassificationPerformance):
 
 @dataclass
 class ClassificationPerformances(Iterable[ContextualizedClassificationPerformance]):
-    performances: List[ContextualizedClassificationPerformance]
+    performances: List[ContextualizedClassificationPerformance] = field(default_factory=list)
 
     @property
     def train(self) -> "ClassificationPerformances":
@@ -62,7 +62,7 @@ class ClassificationPerformances(Iterable[ContextualizedClassificationPerformanc
             for name, performances in group_by(self, lambda p: p.pipeline_name).items()
         }
 
-    def merge(self) -> ClassificationPerformance:
+    def collapse(self) -> ClassificationPerformance:
         return ClassificationPerformance(
             examples=flatten(p.examples for p in self),
             labels=np.concatenate([p.labels for p in self]),
@@ -74,6 +74,13 @@ class ClassificationPerformances(Iterable[ContextualizedClassificationPerformanc
     def __iter__(self):
         return iter(self.performances)
 
+    def __len__(self):
+        return len(self.performances)
+
+    def __iadd__(self, other: "ClassificationPerformances"):
+        self.performances.extend(other.performances)
+        return self
+
 
 @dataclass
 class SetClassificationPerformanceFilter(FilterABC[ContextualizedClassificationPerformance]):
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluation_reporter.py b/polystar_cv/research/robots/evaluation/reporter.py
similarity index 66%
rename from robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluation_reporter.py
rename to polystar_cv/research/robots/evaluation/reporter.py
index 497ed62..e7f6d47 100644
--- a/robots-at-robots/research/robots_at_robots/evaluation/image_pipeline_evaluation_reporter.py
+++ b/polystar_cv/research/robots/evaluation/reporter.py
@@ -2,6 +2,7 @@ from collections import Counter
 from dataclasses import InitVar, dataclass, field
 from math import log
 from os.path import relpath
+from pathlib import Path
 from typing import Generic, List, Optional, Tuple
 
 import matplotlib.pyplot as plt
@@ -10,46 +11,49 @@ import seaborn as sns
 from matplotlib.axes import Axes, logging
 from matplotlib.figure import Figure
 from pandas import DataFrame
-from sklearn.metrics import classification_report, confusion_matrix
+from sklearn.metrics import ConfusionMatrixDisplay, classification_report, confusion_matrix
 
 from polystar.common.pipeline.classification.classification_pipeline import EnumT
 from polystar.common.utils.dataframe import Format, format_df_row, format_df_rows, make_formater
-from polystar.common.utils.markdown import MarkdownFile, markdown_to_pdf
-from polystar.common.utils.time import create_time_id
-from research.common.constants import DSET_DIR, EVALUATION_DIR
-from research.robots_at_robots.evaluation.metrics.accuracy import AccuracyMetric
-from research.robots_at_robots.evaluation.metrics.metric_abc import MetricABC
-from research.robots_at_robots.evaluation.performance import ClassificationPerformance, ClassificationPerformances
-from research.robots_at_robots.evaluation.set import Set
+from polystar.common.utils.markdown import MarkdownFile
+from research.common.constants import DSET_DIR
+from research.robots.evaluation.metrics.accuracy import AccuracyMetric
+from research.robots.evaluation.metrics.metric_abc import MetricABC
+from research.robots.evaluation.performance import ClassificationPerformance, ClassificationPerformances
+from research.robots.evaluation.set import Set
+
+logger = logging.getLogger(__name__)
 
 
 @dataclass
 class ImagePipelineEvaluationReporter(Generic[EnumT]):
-    evaluation_project: str
-    experiment_name: str
+    report_dir: Path
     classes: List[EnumT]
     main_metric: MetricABC = field(default_factory=AccuracyMetric)
     other_metrics: InitVar[List[MetricABC]] = None
     _mf: MarkdownFile = field(init=False)
     _performances: ClassificationPerformances = field(init=False)
+    _has_validation: bool = field(init=False)
+    _sorted_pipeline_names: List[str] = field(init=False)
 
     def __post_init__(self, other_metrics: List[MetricABC]):
-        self.report_dir = EVALUATION_DIR / self.evaluation_project / f"{create_time_id()}_{self.experiment_name}"
         self.all_metrics: List[MetricABC] = [self.main_metric] + (other_metrics or [])
 
     def report(self, performances: ClassificationPerformances):
         sns.set()
+
         self._performances = performances
-        report_path = self.report_dir / "report.md"
-        with MarkdownFile(report_path) as self._mf:
+        self._has_validation = bool(self._performances.validation)
+        self._sorted_pipeline_names = self._make_sorted_pipeline_names()
+
+        with MarkdownFile(self.report_dir / "report.md") as self._mf:
 
             self._mf.title(f"Evaluation report")
             self._report_datasets()
             self._report_aggregated_results()
             self._report_pipelines_results()
 
-            logging.info(f"Report generated at file:///{self.report_dir/'report.md'}")
-        markdown_to_pdf(report_path)
+            logger.info(f"Report generated at file:///{self.report_dir/'report.md'}")
 
     def _report_datasets(self):
         self._mf.title("Datasets", level=2)
@@ -57,8 +61,9 @@ class ImagePipelineEvaluationReporter(Generic[EnumT]):
         self._mf.title("Train-val", level=3)
         self._mf.paragraph("Train")
         self._report_dataset(self._performances.train)
-        self._mf.paragraph("Val")
-        self._report_dataset(self._performances.validation)
+        if self._has_validation:
+            self._mf.paragraph("Val")
+            self._report_dataset(self._performances.validation)
 
         self._mf.title("Testing", level=3)
         self._report_dataset(self._performances.test)
@@ -91,40 +96,48 @@ class ImagePipelineEvaluationReporter(Generic[EnumT]):
 
         self._mf.paragraph("On test set:")
         self._mf.table(self._make_aggregated_results_for_set(Set.TEST))
-        self._mf.paragraph("On validation set:")
-        self._mf.table(self._make_aggregated_results_for_set(Set.VALIDATION))
+        if self._has_validation:
+            self._mf.paragraph("On validation set:")
+            self._mf.table(self._make_aggregated_results_for_set(Set.VALIDATION))
         self._mf.paragraph("On train set:")
         self._mf.table(self._make_aggregated_results_for_set(Set.TRAIN))
 
+    def _make_sorted_pipeline_names(self) -> List[str]:
+        pipeline_name2score = {
+            pipeline_name: self.main_metric(
+                (performances.validation if self._has_validation else performances.test).collapse()
+            )
+            for pipeline_name, performances in self._performances.group_by_pipeline().items()
+        }
+        return sorted(pipeline_name2score, key=pipeline_name2score.get, reverse=True)
+
     def _report_pipelines_results(self):
-        for pipeline_name, performances in sorted(
-            self._performances.group_by_pipeline().items(),
-            key=lambda name_perfs: self.main_metric(name_perfs[1].test.merge()),
-            reverse=True,
-        ):
-            self._report_pipeline_results(pipeline_name, performances)
+        pipeline_name2perfs = self._performances.group_by_pipeline()
+        for pipeline_name in self._sorted_pipeline_names:
+            self._report_pipeline_results(pipeline_name, pipeline_name2perfs[pipeline_name])
 
     def _report_pipeline_results(self, pipeline_name: str, performances: ClassificationPerformances):
         self._mf.title(pipeline_name, level=2)
 
         self._mf.title("Test results", level=3)
-        self._report_pipeline_set_results(performances, Set.TEST)
+        self._report_pipeline_set_results(pipeline_name, performances, Set.TEST)
 
-        self._mf.title("Validation results", level=3)
-        self._report_pipeline_set_results(performances, Set.VALIDATION)
+        if self._has_validation:
+            self._mf.title("Validation results", level=3)
+            self._report_pipeline_set_results(pipeline_name, performances, Set.VALIDATION)
 
         self._mf.title("Train results", level=3)
-        self._report_pipeline_set_results(performances, Set.TRAIN)
+        self._report_pipeline_set_results(pipeline_name, performances, Set.TRAIN)
 
-    def _report_pipeline_set_results(self, performances: ClassificationPerformances, set_: Set):
+    def _report_pipeline_set_results(self, pipeline_name: str, performances: ClassificationPerformances, set_: Set):
         performances = performances.on_set(set_)
-        perf = performances.merge()
+        perf = performances.collapse()
 
         self._mf.title("Metrics", level=4)
         self._report_pipeline_set_metrics(performances, perf, set_)
 
         self._mf.title("Confusion Matrix:", level=4)
-        self._report_pipeline_set_confusion_matrix(perf)
+        self._report_pipeline_set_confusion_matrix(pipeline_name, perf, set_)
 
         self._mf.title("25 Mistakes examples", level=4)
         self._report_pipeline_set_mistakes(perf)
@@ -164,12 +177,15 @@ class ImagePipelineEvaluationReporter(Generic[EnumT]):
         format_df_row(df, "support", int)
         self._mf.table(df)
 
-    def _report_pipeline_set_confusion_matrix(self, perf: ClassificationPerformance):
-        self._mf.table(
-            DataFrame(
-                confusion_matrix(perf.labels, perf.predictions), index=perf.unique_labels, columns=perf.unique_labels
-            )
+    def _report_pipeline_set_confusion_matrix(self, pipeline_name: str, perf: ClassificationPerformance, set_: Set):
+        sns.reset_defaults()
+        cm = ConfusionMatrixDisplay(
+            confusion_matrix(perf.labels, perf.predictions, labels=perf.unique_labels),
+            display_labels=perf.unique_labels,
         )
+        cm.plot(cmap=plt.cm.Blues, values_format=".4g")
+        self._mf.figure(cm.figure_, f"{pipeline_name}_{set_}_cm.png")
+        sns.set()
 
     def _report_pipeline_set_mistakes(self, perf: ClassificationPerformance):
         mistakes = perf.mistakes
@@ -211,70 +227,70 @@ class ImagePipelineEvaluationReporter(Generic[EnumT]):
                 }
                 for perf in self._performances
             ]
-        ).sort_values(["set", self.main_metric.name], ascending=[True, False])
+        )
 
         df[f"{self.main_metric.name} "] = list(zip(df[self.main_metric.name], df.support))
         df["time "] = list(zip(df.time, df.support))
 
         return (
-            _cat_pipeline_results(df, f"{self.main_metric.name} ", "{:.1%}", limits=(0, 1)),
-            _cat_pipeline_results(df, "time ", "{:.2e}", log_scale=True),
+            self._cat_pipeline_results(df, f"{self.main_metric.name} ", "{:.1%}", limits=(0, 1)),
+            self._cat_pipeline_results(df, "time ", "{:.2e}", log_scale=True),
         )
 
     def _make_aggregated_results_for_set(self, set_: Set) -> DataFrame:
-        pipeline2performances = self._performances.on_set(set_).group_by_pipeline()
-        pipeline2performance = {
-            pipeline_name: performances.merge() for pipeline_name, performances in pipeline2performances.items()
+        pipeline_name2performances = self._performances.on_set(set_).group_by_pipeline()
+        pipeline_name2performance = {
+            pipeline_name: performances.collapse() for pipeline_name, performances in pipeline_name2performances.items()
         }
-        return (
-            DataFrame(
-                [
-                    {
-                        "pipeline": pipeline_name,
-                        self.main_metric.name: self.main_metric(performance),
-                        "inference time": performance.mean_inference_time,
-                    }
-                    for pipeline_name, performance in pipeline2performance.items()
-                ]
-            )
-            .set_index("pipeline")
-            .sort_values(self.main_metric.name, ascending=False)
+        return DataFrame(
+            [
+                {
+                    "pipeline": pipeline_name,
+                    self.main_metric.name: self.main_metric(pipeline_name2performance[pipeline_name]),
+                    "inference time": pipeline_name2performance[pipeline_name].mean_inference_time,
+                }
+                for pipeline_name in self._sorted_pipeline_names
+            ]
+        ).set_index("pipeline")
+
+    def _cat_pipeline_results(
+        self, df: DataFrame, y: str, fmt: str, limits: Optional[Tuple[float, float]] = None, log_scale: bool = False
+    ) -> Figure:
+        cols = ["test"]
+        if self._has_validation:
+            cols.append("validation")
+        cols.append("train")
+        grid: sns.FacetGrid = sns.catplot(
+            data=df,
+            x="pipeline",
+            y=y,
+            col="set",
+            kind="bar",
+            sharey=True,
+            legend=False,
+            col_order=cols,
+            height=8,
+            estimator=weighted_mean,
+            orient="v",
+            order=self._sorted_pipeline_names,
         )
 
+        fig: Figure = grid.fig
+
+        grid.set_xticklabels(rotation=30, ha="right")
+        _format_axes(fig.get_axes(), fmt, limits=limits, log_scale=log_scale)
+
+        fig.suptitle(y)
+        fig.tight_layout()
+
+        return fig
+
 
 def weighted_mean(x, **kws):
     val, weight = map(np.asarray, zip(*x))
     return (val * weight).sum() / weight.sum()
 
 
-def _cat_pipeline_results(
-    df: DataFrame, y: str, fmt: str, limits: Optional[Tuple[float, float]] = None, log_scale: bool = False
-) -> Figure:
-    grid: sns.FacetGrid = sns.catplot(
-        data=df,
-        x="pipeline",
-        y=y,
-        col="set",
-        kind="bar",
-        sharey=True,
-        legend=False,
-        col_order=["test", "validation", "train"],
-        height=8,
-        estimator=weighted_mean,
-        orient="v",
-    )
-
-    fig: Figure = grid.fig
-
-    grid.set_xticklabels(rotation=30, ha="right")
-    _format_axes(fig.get_axes(), fmt, limits=limits, log_scale=log_scale)
-
-    fig.suptitle(y)
-    fig.tight_layout()
-
-    return fig
-
-
 def bar_plot_with_secondary(
     df: DataFrame,
     title: str,
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/set.py b/polystar_cv/research/robots/evaluation/set.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/evaluation/set.py
rename to polystar_cv/research/robots/evaluation/set.py
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/trainer.py b/polystar_cv/research/robots/evaluation/trainer.py
similarity index 100%
rename from robots-at-robots/research/robots_at_robots/evaluation/trainer.py
rename to polystar_cv/research/robots/evaluation/trainer.py
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/__init__.py b/polystar_cv/research/runes/__init__.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/__init__.py
rename to polystar_cv/research/runes/__init__.py
diff --git a/robots-at-runes/research/robots_at_runes/constants.py b/polystar_cv/research/runes/constants.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/constants.py
rename to polystar_cv/research/runes/constants.py
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/__init__.py b/polystar_cv/research/runes/dataset/__init__.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/__init__.py
rename to polystar_cv/research/runes/dataset/__init__.py
diff --git a/polystar_cv/research/runes/dataset/blend/__init__.py b/polystar_cv/research/runes/dataset/blend/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/examples/.gitignore b/polystar_cv/research/runes/dataset/blend/examples/.gitignore
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/examples/.gitignore
rename to polystar_cv/research/runes/dataset/blend/examples/.gitignore
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/examples/back1.jpg b/polystar_cv/research/runes/dataset/blend/examples/back1.jpg
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/examples/back1.jpg
rename to polystar_cv/research/runes/dataset/blend/examples/back1.jpg
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/examples/logo.png b/polystar_cv/research/runes/dataset/blend/examples/logo.png
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/examples/logo.png
rename to polystar_cv/research/runes/dataset/blend/examples/logo.png
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/examples/logo.xml b/polystar_cv/research/runes/dataset/blend/examples/logo.xml
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/examples/logo.xml
rename to polystar_cv/research/runes/dataset/blend/examples/logo.xml
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/image_blender.py b/polystar_cv/research/runes/dataset/blend/image_blender.py
similarity index 88%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/image_blender.py
rename to polystar_cv/research/runes/dataset/blend/image_blender.py
index 976aba5..87eeb6d 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/blend/image_blender.py
+++ b/polystar_cv/research/runes/dataset/blend/image_blender.py
@@ -6,9 +6,8 @@ import cv2
 import numpy as np
 
 from polystar.common.models.image import Image
-from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import \
-    LabeledImageModifierABC
-from research.robots_at_runes.dataset.labeled_image import LabeledImage, PointOfInterest
+from research.runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import LabeledImageModifierABC
+from research.runes.dataset.labeled_image import LabeledImage, PointOfInterest
 
 
 @dataclass
@@ -70,8 +69,8 @@ if __name__ == "__main__":
 
     import matplotlib.pyplot as plt
 
-    from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_rotator import LabeledImageRotator
-    from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_scaler import LabeledImageScaler
+    from research.runes.dataset.blend import LabeledImageScaler
+    from research.runes.dataset.blend.labeled_image_modifiers.labeled_image_rotator import LabeledImageRotator
 
     EXAMPLES_DIR = Path(__file__).parent / "examples"
 
diff --git a/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/__init__.py b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py
similarity index 94%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py
rename to polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py
index cf6f90b..5072c90 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py
+++ b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_modifier_abc.py
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
 from random import random
 
 from polystar.common.models.image import Image
-from research.robots_at_runes.dataset.labeled_image import LabeledImage, PointOfInterest
+from research.runes.dataset.labeled_image import LabeledImage, PointOfInterest
 
 
 class LabeledImageModifierABC(ABC):
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py
similarity index 85%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py
rename to polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py
index 6e241c2..3dd178a 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py
+++ b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_rotator.py
@@ -1,12 +1,11 @@
 from dataclasses import dataclass
 
 import numpy as np
-
 from imutils import rotate_bound
+
 from polystar.common.models.image import Image
-from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import \
-    LabeledImageModifierABC
-from research.robots_at_runes.dataset.labeled_image import PointOfInterest
+from research.runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import LabeledImageModifierABC
+from research.runes.dataset.labeled_image import PointOfInterest
 
 
 @dataclass
diff --git a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py
similarity index 80%
rename from robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py
rename to polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py
index a7dacd8..a5ac388 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py
+++ b/polystar_cv/research/runes/dataset/blend/labeled_image_modifiers/labeled_image_scaler.py
@@ -3,9 +3,8 @@ from dataclasses import dataclass
 import cv2
 
 from polystar.common.models.image import Image
-from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import \
-    LabeledImageModifierABC
-from research.robots_at_runes.dataset.labeled_image import PointOfInterest
+from research.runes.dataset.blend.labeled_image_modifiers.labeled_image_modifier_abc import LabeledImageModifierABC
+from research.runes.dataset.labeled_image import PointOfInterest
 
 
 @dataclass
diff --git a/robots-at-runes/research/robots_at_runes/dataset/dataset_generator.py b/polystar_cv/research/runes/dataset/dataset_generator.py
similarity index 85%
rename from robots-at-runes/research/robots_at_runes/dataset/dataset_generator.py
rename to polystar_cv/research/runes/dataset/dataset_generator.py
index d5a871c..9dcf261 100644
--- a/robots-at-runes/research/robots_at_runes/dataset/dataset_generator.py
+++ b/polystar_cv/research/runes/dataset/dataset_generator.py
@@ -12,11 +12,11 @@ from research.common.dataset.perturbations.image_modifiers.gaussian_noise import
 from research.common.dataset.perturbations.image_modifiers.horizontal_blur import HorizontalBlurrer
 from research.common.dataset.perturbations.image_modifiers.saturation import SaturationModifier
 from research.common.dataset.perturbations.perturbator import ImagePerturbator
-from research.robots_at_runes.constants import RUNES_DATASET_DIR
-from research.robots_at_runes.dataset.blend.image_blender import ImageBlender
-from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_rotator import LabeledImageRotator
-from research.robots_at_runes.dataset.blend.labeled_image_modifiers.labeled_image_scaler import LabeledImageScaler
-from research.robots_at_runes.dataset.labeled_image import load_labeled_images_in_directory
+from research.runes.constants import RUNES_DATASET_DIR
+from research.runes.dataset.blend import LabeledImageScaler
+from research.runes.dataset.blend.image_blender import ImageBlender
+from research.runes.dataset.blend.labeled_image_modifiers.labeled_image_rotator import LabeledImageRotator
+from research.runes.dataset.labeled_image import load_labeled_images_in_directory
 
 
 class DatasetGenerator:
diff --git a/robots-at-runes/research/robots_at_runes/dataset/labeled_image.py b/polystar_cv/research/runes/dataset/labeled_image.py
similarity index 100%
rename from robots-at-runes/research/robots_at_runes/dataset/labeled_image.py
rename to polystar_cv/research/runes/dataset/labeled_image.py
diff --git a/polystar_cv/tests/common/integration_tests/__init__.py b/polystar_cv/tests/common/integration_tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/polystar_cv/tests/common/integration_tests/datasets/__init__.py b/polystar_cv/tests/common/integration_tests/datasets/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/common/tests/common/integration_tests/datasets/test_dji_dataset.py b/polystar_cv/tests/common/integration_tests/datasets/test_dji_dataset.py
similarity index 100%
rename from common/tests/common/integration_tests/datasets/test_dji_dataset.py
rename to polystar_cv/tests/common/integration_tests/datasets/test_dji_dataset.py
diff --git a/common/tests/common/integration_tests/datasets/test_dji_zoomed_dataset.py b/polystar_cv/tests/common/integration_tests/datasets/test_dji_zoomed_dataset.py
similarity index 100%
rename from common/tests/common/integration_tests/datasets/test_dji_zoomed_dataset.py
rename to polystar_cv/tests/common/integration_tests/datasets/test_dji_zoomed_dataset.py
diff --git a/common/tests/common/integration_tests/datasets/test_twitch_dataset_v1.py b/polystar_cv/tests/common/integration_tests/datasets/test_twitch_dataset_v1.py
similarity index 100%
rename from common/tests/common/integration_tests/datasets/test_twitch_dataset_v1.py
rename to polystar_cv/tests/common/integration_tests/datasets/test_twitch_dataset_v1.py
diff --git a/polystar_cv/tests/common/unittests/__init__.py b/polystar_cv/tests/common/unittests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py b/polystar_cv/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
similarity index 100%
rename from common/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
rename to polystar_cv/tests/common/unittests/datasets/roco/test_directory_dataset_zoo.py
diff --git a/common/tests/common/unittests/datasets/test_dataset.py b/polystar_cv/tests/common/unittests/datasets/test_dataset.py
similarity index 100%
rename from common/tests/common/unittests/datasets/test_dataset.py
rename to polystar_cv/tests/common/unittests/datasets/test_dataset.py
diff --git a/common/tests/common/unittests/datasets_v3/roco/test_directory_dataset_zoo.py b/polystar_cv/tests/common/unittests/datasets_v3/roco/test_directory_dataset_zoo.py
similarity index 100%
rename from common/tests/common/unittests/datasets_v3/roco/test_directory_dataset_zoo.py
rename to polystar_cv/tests/common/unittests/datasets_v3/roco/test_directory_dataset_zoo.py
diff --git a/common/tests/common/unittests/datasets_v3/test_dataset.py b/polystar_cv/tests/common/unittests/datasets_v3/test_dataset.py
similarity index 100%
rename from common/tests/common/unittests/datasets_v3/test_dataset.py
rename to polystar_cv/tests/common/unittests/datasets_v3/test_dataset.py
diff --git a/common/tests/common/unittests/filters/test_filters_abc.py b/polystar_cv/tests/common/unittests/filters/test_filters_abc.py
similarity index 100%
rename from common/tests/common/unittests/filters/test_filters_abc.py
rename to polystar_cv/tests/common/unittests/filters/test_filters_abc.py
diff --git a/common/tests/common/unittests/image_pipeline/test_image_classifier_pipeline.py b/polystar_cv/tests/common/unittests/image_pipeline/test_image_classifier_pipeline.py
similarity index 100%
rename from common/tests/common/unittests/image_pipeline/test_image_classifier_pipeline.py
rename to polystar_cv/tests/common/unittests/image_pipeline/test_image_classifier_pipeline.py
diff --git a/polystar_cv/tests/common/unittests/object_validators/__init__.py b/polystar_cv/tests/common/unittests/object_validators/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/common/tests/common/unittests/object_validators/test_in_box_validator.py b/polystar_cv/tests/common/unittests/object_validators/test_in_box_validator.py
similarity index 100%
rename from common/tests/common/unittests/object_validators/test_in_box_validator.py
rename to polystar_cv/tests/common/unittests/object_validators/test_in_box_validator.py
diff --git a/pyproject.toml b/pyproject.toml
index 5e88305..82b6310 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,8 +1,10 @@
 [tool.poetry]
-name = "polystar.cv"
+name = "polystar_cv"
 version = "0.2.0"
 description = "CV code for Polystar's RoboMaster team"
 authors = ["Polystar"]
+packages = [{ include = "polystar", from = "polystar_cv" }, { include = "research", from = "polystar_cv" }]
+include = ["**/.changes"]
 
 [tool.poetry.dependencies]
 python = "^3.6"
@@ -24,15 +26,25 @@ dataclasses = "^0.6.0"
 imutils = "^0.5.3"
 more-itertools = "^8.4.0"
 
-[tool.poetry.dev-dependencies]
-tensorflow = "2.1.x"
-tensorflow-estimator = "2.1.x"
 opencv-python = "4.1.x"
 matplotlib = "^3.1.3"
-kivy = "^1.11.1"
 markdown = "^3.3.3"
 xhtml2pdf = "^0.2.5"
 google-cloud-storage = "^1.35.0"
+pyyaml = "^5.3.1"
+six = "1.15.0"  # https://github.com/googleapis/python-bigquery/issues/70
+
+[tool.poetry.dev-dependencies]
+tensorflow = "2.3.x"
+tensorflow-estimator = "2.3.x"
+kivy = "^1.11.1"
+cloudml-hypertune = "^0.1.0-alpha.6"
+google-api-python-client = "^1.12.8"
+wheel = "^0.36.2"
+optuna = "^2.3.0"
+hyperopt = "^0.2.5"
+plotly = "^4.14.1"
+pydot = "^1.4.1"
 
 [tool.black]
 line-length = 120
@@ -43,3 +55,7 @@ profile='black'
 line_length = 120
 known_first_party = ['polystar','tests','research','tools','scripts']
 skip = ['.eggs','.git','.hg','.mypy_cache','.nox','.pants.d','.tox','.venv','_build','buck-out','build','dist','node_modules','venv','__init__.py']
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/robots-at-robots/Readme.md b/robots-at-robots/Readme.md
deleted file mode 100644
index fcca02b..0000000
--- a/robots-at-robots/Readme.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-# Robots@Robots
-
-## Goal
-
-The goal of this project is to detect the other robots, from our robots, to be able to assist the pilot into shooting them.
diff --git a/robots-at-robots/config/settings.toml b/robots-at-robots/config/settings.toml
deleted file mode 100644
index fe532b0..0000000
--- a/robots-at-robots/config/settings.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-[default]
-MODEL_NAME = 'robots/TRT_ssd_mobilenet_v2_roco.bin'
-
-[development]
-
-[production]
diff --git a/robots-at-robots/polystar/robots_at_robots/dependency_injection.py b/robots-at-robots/polystar/robots_at_robots/dependency_injection.py
deleted file mode 100644
index cc76fa1..0000000
--- a/robots-at-robots/polystar/robots_at_robots/dependency_injection.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from dataclasses import dataclass
-
-from dynaconf import LazySettings
-from injector import Injector, Module
-
-from polystar.common.dependency_injection import CommonModule
-from polystar.robots_at_robots.globals import settings
-
-
-def make_injector() -> Injector:
-    return Injector(modules=[CommonModule(settings), RobotsAtRobotsModule(settings)])
-
-
-@dataclass
-class RobotsAtRobotsModule(Module):
-    settings: LazySettings
diff --git a/robots-at-robots/polystar/robots_at_robots/globals.py b/robots-at-robots/polystar/robots_at_robots/globals.py
deleted file mode 100644
index bf1c91a..0000000
--- a/robots-at-robots/polystar/robots_at_robots/globals.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from polystar.common.settings import make_settings
-
-PROJECT_NAME = "robots-at-robots"
-
-settings = make_settings(PROJECT_NAME)
diff --git a/robots-at-robots/research/robots_at_robots/armor_color/armor_color_benchmarker.py b/robots-at-robots/research/robots_at_robots/armor_color/armor_color_benchmarker.py
deleted file mode 100644
index 37a9e35..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_color/armor_color_benchmarker.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from typing import List
-
-from polystar.common.models.object import ArmorColor
-from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
-from research.robots_at_robots.armor_color.armor_color_dataset import make_armor_color_dataset_generator
-from research.robots_at_robots.evaluation.benchmark import make_armor_value_benchmarker
-
-
-def make_armor_color_benchmarker(
-    train_roco_datasets: List[ROCODatasetBuilder],
-    validation_roco_datasets: List[ROCODatasetBuilder],
-    test_roco_datasets: List[ROCODatasetBuilder],
-    experiment_name: str,
-):
-    dataset_generator = make_armor_color_dataset_generator()
-    return make_armor_value_benchmarker(
-        train_roco_datasets=train_roco_datasets,
-        validation_roco_datasets=validation_roco_datasets,
-        test_roco_datasets=test_roco_datasets,
-        evaluation_project="armor-color",
-        experiment_name=experiment_name,
-        classes=list(ArmorColor),
-        dataset_generator=dataset_generator,
-    )
diff --git a/robots-at-robots/research/robots_at_robots/armor_color/armor_color_dataset.py b/robots-at-robots/research/robots_at_robots/armor_color/armor_color_dataset.py
deleted file mode 100644
index 02985e4..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_color/armor_color_dataset.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from itertools import islice
-
-from polystar.common.models.object import Armor, ArmorColor
-from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.dataset.armor_value_dataset_generator import ArmorValueDatasetGenerator
-from research.robots_at_robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
-
-
-class ArmorColorTargetFactory(ArmorValueTargetFactory[ArmorColor]):
-    def from_str(self, label: str) -> ArmorColor:
-        return ArmorColor(label)
-
-    def from_armor(self, armor: Armor) -> ArmorColor:
-        return armor.color
-
-
-def make_armor_color_dataset_generator() -> ArmorValueDatasetGenerator[ArmorColor]:
-    return ArmorValueDatasetGenerator("colors", ArmorColorTargetFactory())
-
-
-if __name__ == "__main__":
-    _roco_dataset_builder = ROCODatasetsZoo.DJI.CENTRAL_CHINA
-    _armor_color_dataset = make_armor_color_dataset_generator().from_roco_dataset(_roco_dataset_builder)
-
-    for p, c, _name in islice(_armor_color_dataset, 20, 25):
-        print(p, c, _name)
diff --git a/robots-at-robots/research/robots_at_robots/armor_color/benchmark.py b/robots-at-robots/research/robots_at_robots/armor_color/benchmark.py
deleted file mode 100644
index 441fb0d..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_color/benchmark.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import logging
-from dataclasses import dataclass
-
-from nptyping import Array
-from sklearn.linear_model import LogisticRegression
-
-from polystar.common.image_pipeline.featurizers.histogram_2d import Histogram2D
-from polystar.common.image_pipeline.preprocessors.rgb_to_hsv import RGB2HSV
-from polystar.common.models.image import Image
-from polystar.common.models.object import ArmorColor
-from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
-from polystar.common.pipeline.classification.random_model import RandomClassifier
-from polystar.common.pipeline.classification.rule_based_classifier import RuleBasedClassifierABC
-from polystar.common.pipeline.pipe_abc import PipeABC
-from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.armor_color.armor_color_benchmarker import make_armor_color_benchmarker
-
-
-class ArmorColorPipeline(ClassificationPipeline):
-    enum = ArmorColor
-
-
-@dataclass
-class MeanChannels(PipeABC):
-    def transform_single(self, image: Image) -> Array[float, float, float]:
-        return image.mean(axis=(0, 1))
-
-
-class RedBlueComparisonClassifier(RuleBasedClassifierABC):
-    """A very simple model that compares the blue and red values obtained by the MeanChannels"""
-
-    def predict_single(self, features: Array[float, float, float]) -> ArmorColor:
-        return ArmorColor.Red if features[0] >= features[2] else ArmorColor.Blue
-
-
-if __name__ == "__main__":
-    logging.getLogger().setLevel("INFO")
-
-    _benchmarker = make_armor_color_benchmarker(
-        train_roco_datasets=[
-            ROCODatasetsZoo.TWITCH.T470150052,
-            ROCODatasetsZoo.TWITCH.T470152289,
-            ROCODatasetsZoo.TWITCH.T470149568,
-            ROCODatasetsZoo.TWITCH.T470151286,
-        ],
-        validation_roco_datasets=[],
-        test_roco_datasets=[
-            ROCODatasetsZoo.TWITCH.T470152838,
-            ROCODatasetsZoo.TWITCH.T470153081,
-            ROCODatasetsZoo.TWITCH.T470158483,
-            ROCODatasetsZoo.TWITCH.T470152730,
-        ],
-        experiment_name="test",
-    )
-
-    red_blue_comparison_pipeline = ArmorColorPipeline.from_pipes(
-        [MeanChannels(), RedBlueComparisonClassifier()], name="rb-comparison",
-    )
-    random_pipeline = ArmorColorPipeline.from_pipes([RandomClassifier()], name="random")
-    hsv_hist_lr_pipeline = ArmorColorPipeline.from_pipes(
-        [RGB2HSV(), Histogram2D(), LogisticRegression()], name="hsv-hist-lr",
-    )
-
-    _benchmarker.benchmark([random_pipeline, red_blue_comparison_pipeline, hsv_hist_lr_pipeline])
diff --git a/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_benchmarker.py b/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_benchmarker.py
deleted file mode 100644
index 8e77cab..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_benchmarker.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from typing import List
-
-from polystar.common.models.object import ArmorDigit
-from research.common.datasets.image_dataset import FileImageDataset
-from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
-from research.robots_at_robots.armor_digit.armor_digit_dataset import make_armor_digit_dataset_generator
-from research.robots_at_robots.evaluation.benchmark import make_armor_value_benchmarker
-
-
-def make_armor_digit_benchmarker(
-    train_roco_datasets: List[ROCODatasetBuilder],
-    validation_roco_datasets: List[ROCODatasetBuilder],
-    test_roco_datasets: List[ROCODatasetBuilder],
-    experiment_name: str,
-    train_digit_datasets: List[FileImageDataset[ArmorDigit]] = None,
-    validation_digit_datasets: List[FileImageDataset[ArmorDigit]] = None,
-    test_digit_datasets: List[FileImageDataset[ArmorDigit]] = None,
-):
-    dataset_generator = make_armor_digit_dataset_generator()
-    return make_armor_value_benchmarker(
-        train_roco_datasets=train_roco_datasets,
-        validation_roco_datasets=validation_roco_datasets,
-        test_roco_datasets=test_roco_datasets,
-        evaluation_project="armor-digit",
-        experiment_name=experiment_name,
-        classes=list(ArmorDigit),
-        dataset_generator=dataset_generator,
-        train_datasets=train_digit_datasets,
-        validation_datasets=validation_digit_datasets,
-        test_datasets=test_digit_datasets,
-    )
diff --git a/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_dataset.py b/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_dataset.py
deleted file mode 100644
index bd35645..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_digit/armor_digit_dataset.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from itertools import islice
-
-from polystar.common.filters.exclude_filter import ExcludeFilter
-from polystar.common.models.object import Armor, ArmorDigit
-from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.dataset.armor_value_dataset_generator import ArmorValueDatasetGenerator
-from research.robots_at_robots.dataset.armor_value_target_factory import ArmorValueTargetFactory
-
-
-class ArmorDigitTargetFactory(ArmorValueTargetFactory[ArmorDigit]):
-    def from_str(self, label: str) -> ArmorDigit:
-        n = int(label)
-
-        if 1 <= n <= 5:  # CHANGING
-            return ArmorDigit(n)
-
-        return ArmorDigit.OUTDATED
-
-    def from_armor(self, armor: Armor) -> ArmorDigit:
-        return ArmorDigit(armor.number) if armor.number else ArmorDigit.UNKNOWN
-
-
-def make_armor_digit_dataset_generator() -> ArmorValueDatasetGenerator[ArmorDigit]:
-    return ArmorValueDatasetGenerator("digits", ArmorDigitTargetFactory(), ExcludeFilter({ArmorDigit.OUTDATED}))
-
-
-if __name__ == "__main__":
-    _roco_dataset_builder = ROCODatasetsZoo.DJI.CENTRAL_CHINA
-    _armor_digit_dataset = make_armor_digit_dataset_generator().from_roco_dataset(_roco_dataset_builder)
-
-    for p, c, _name in islice(_armor_digit_dataset, 20, 30):
-        print(p, c, _name)
diff --git a/robots-at-robots/research/robots_at_robots/armor_digit/benchmark.py b/robots-at-robots/research/robots_at_robots/armor_digit/benchmark.py
deleted file mode 100644
index 81e750a..0000000
--- a/robots-at-robots/research/robots_at_robots/armor_digit/benchmark.py
+++ /dev/null
@@ -1,253 +0,0 @@
-import logging
-import warnings
-from pathlib import Path
-from typing import Callable, List, Sequence, Tuple
-
-from keras_preprocessing.image import ImageDataGenerator
-from numpy import asarray
-from tensorflow_core.python.keras import Input, Model, Sequential
-from tensorflow_core.python.keras.applications.vgg16 import VGG16
-from tensorflow_core.python.keras.applications.xception import Xception
-from tensorflow_core.python.keras.callbacks import EarlyStopping, TensorBoard
-from tensorflow_core.python.keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D
-from tensorflow_core.python.keras.optimizer_v2.adam import Adam
-from tensorflow_core.python.keras.optimizer_v2.gradient_descent import SGD
-from tensorflow_core.python.keras.utils.np_utils import to_categorical
-
-from polystar.common.image_pipeline.preprocessors.normalise import Normalise
-from polystar.common.image_pipeline.preprocessors.resize import Resize
-from polystar.common.models.image import Image
-from polystar.common.models.object import ArmorDigit
-from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
-from polystar.common.pipeline.classification.classifier_abc import ClassifierABC
-from polystar.common.pipeline.classification.random_model import RandomClassifier
-from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
-from research.robots_at_robots.armor_digit.armor_digit_benchmarker import make_armor_digit_benchmarker
-from research.robots_at_robots.armor_digit.armor_digit_dataset import make_armor_digit_dataset_generator
-from research.robots_at_robots.evaluation.benchmark import Benchmarker
-
-
-class ArmorDigitPipeline(ClassificationPipeline):
-    enum = ArmorDigit
-
-
-class KerasClassifier(ClassifierABC):
-    def __init__(self, model: Model, optimizer, logs_dir: Path, with_data_augmentation: bool, batch_size: int = 32):
-        self.batch_size = batch_size
-        self.logs_dir = logs_dir
-        self.with_data_augmentation = with_data_augmentation
-        self.model = model
-        self.model.compile(optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"])
-
-    @property
-    def train_data_gen(self) -> ImageDataGenerator:
-        if not self.with_data_augmentation:
-            return ImageDataGenerator()
-        return ImageDataGenerator(rotation_range=45, zoom_range=[0.8, 1])  # brightness_range=[0.7, 1.4]
-
-    def fit(self, images: List[Image], labels: List[int], validation_size: int) -> "KerasClassifier":
-        images = asarray(images)
-        labels = to_categorical(asarray(labels), 5)  # FIXME
-        train_images, train_labels = images[:-validation_size], labels[:-validation_size]
-        val_images, val_labels = images[-validation_size:], labels[-validation_size:]
-
-        train_generator = self.train_data_gen.flow(train_images, train_labels, batch_size=self.batch_size, shuffle=True)
-
-        self.model.fit(
-            x=train_generator,
-            steps_per_epoch=len(train_images) / self.batch_size,
-            validation_data=(val_images, val_labels),
-            epochs=300,
-            callbacks=[
-                EarlyStopping(verbose=0, patience=15, restore_best_weights=True),
-                TensorBoard(log_dir=self.logs_dir, histogram_freq=4, write_graph=True, write_images=False),
-            ],
-            verbose=0,
-        )
-        return self
-
-    def predict_proba(self, examples: List[Image]) -> Sequence[float]:
-        return self.model.predict_proba(asarray(examples))
-
-
-class CNN(Sequential):
-    def __init__(
-        self, input_size: Tuple[int, int], conv_blocks: Sequence[Sequence[int]], dense_size: int, output_size: int,
-    ):
-        super().__init__()
-        self.add(Input((*input_size, 3)))
-
-        for conv_sizes in conv_blocks:
-            for size in conv_sizes:
-                self.add(Conv2D(size, (3, 3), activation="relu"))
-            self.add(MaxPooling2D())
-
-        self.add(Flatten())
-        self.add(Dense(dense_size))
-        self.add(Dropout(0.5))
-        self.add(Dense(output_size, activation="softmax"))
-
-
-def make_digits_cnn_pipeline(
-    input_size: int, conv_blocks: Sequence[Sequence[int]], report_dir: Path, with_data_augmentation: bool, lr: float
-) -> ArmorDigitPipeline:
-    name = (
-        f"cnn - ({input_size}) - lr {lr} - "
-        + " ".join("_".join(map(str, sizes)) for sizes in conv_blocks)
-        + (" - with_data_augm" * with_data_augmentation)
-    )
-    input_size = (input_size, input_size)
-    return ArmorDigitPipeline.from_pipes(
-        [
-            Resize(input_size),
-            Normalise(),
-            KerasClassifier(
-                CNN(input_size=input_size, conv_blocks=conv_blocks, dense_size=128, output_size=5),
-                logs_dir=report_dir / name,
-                with_data_augmentation=with_data_augmentation,
-                optimizer=SGD(lr, momentum=0.9),
-            ),
-        ],
-        name=name,
-    )
-
-
-class TransferLearning(Sequential):
-    def __init__(self, input_size: Tuple[int, int], n_classes: int, model_factory: Callable[..., Model]):
-        input_shape = (*input_size, 3)
-        base_model: Model = model_factory(weights="imagenet", input_shape=input_shape, include_top=False)
-
-        super().__init__(
-            [
-                Input(input_shape),
-                base_model,
-                Flatten(),
-                Dense(128, activation="relu"),
-                Dropout(0.5),
-                Dense(n_classes, activation="softmax"),
-            ]
-        )
-
-
-def make_tl_pipeline(
-    report_dir: Path, input_size: int, with_data_augmentation: bool, lr: float, model_factory: Callable[..., Model]
-):
-    name = f"{model_factory.__name__} ({input_size}) - lr {lr}" + (" - with_data_augm" * with_data_augmentation)
-    input_size = (input_size, input_size)
-    return ArmorDigitPipeline.from_pipes(
-        [
-            Resize(input_size),
-            Normalise(),
-            KerasClassifier(
-                model=TransferLearning(input_size=input_size, n_classes=5, model_factory=model_factory),  # FIXME
-                optimizer=Adam(lr),  # FIXME
-                logs_dir=report_dir,
-                with_data_augmentation=with_data_augmentation,
-            ),
-        ],
-        name=name,
-    )
-
-
-def make_vgg16_pipeline(
-    report_dir: Path, input_size: int, with_data_augmentation: bool, lr: float
-) -> ArmorDigitPipeline:
-    return make_tl_pipeline(
-        model_factory=VGG16,
-        input_size=input_size,
-        with_data_augmentation=with_data_augmentation,
-        lr=lr,
-        report_dir=report_dir,
-    )
-
-
-def make_xception_pipeline(
-    report_dir: Path, input_size: int, with_data_augmentation: bool, lr: float
-) -> ArmorDigitPipeline:
-    return make_tl_pipeline(
-        model_factory=Xception,
-        input_size=input_size,
-        with_data_augmentation=with_data_augmentation,
-        lr=lr,
-        report_dir=report_dir,
-    )
-
-
-def make_default_digit_benchmarker(exp_name: str) -> Benchmarker:
-    return make_armor_digit_benchmarker(
-        train_digit_datasets=[
-            # Only the start of the dataset is cleaned as of now
-            make_armor_digit_dataset_generator()
-            .from_roco_dataset(ROCODatasetsZoo.DJI.FINAL)
-            .to_file_images()
-            .cap(
-                (1009 - 117)
-                + (1000 - 86)
-                + (1000 - 121)
-                + (1000 - 138)
-                + (1000 - 137)
-                + (1000 - 154)
-                + (1000 - 180)
-                + (1000 - 160)
-                + (1000 - 193)
-                + (1000 - 80)
-            )
-            .build()
-        ],
-        train_roco_datasets=[
-            # ROCODatasetsZoo.DJI.CENTRAL_CHINA,
-            # ROCODatasetsZoo.DJI.FINAL,
-            # ROCODatasetsZoo.DJI.NORTH_CHINA,
-            # ROCODatasetsZoo.DJI.SOUTH_CHINA,
-            ROCODatasetsZoo.TWITCH.T470150052,
-            ROCODatasetsZoo.TWITCH.T470149568,
-            ROCODatasetsZoo.TWITCH.T470151286,
-        ],
-        validation_roco_datasets=[ROCODatasetsZoo.TWITCH.T470152289],
-        test_roco_datasets=[
-            ROCODatasetsZoo.TWITCH.T470152838,
-            ROCODatasetsZoo.TWITCH.T470153081,
-            ROCODatasetsZoo.TWITCH.T470158483,
-            ROCODatasetsZoo.TWITCH.T470152730,
-        ],
-        experiment_name=exp_name,
-    )
-
-
-if __name__ == "__main__":
-    logging.getLogger().setLevel("INFO")
-    logging.getLogger("tensorflow").setLevel("ERROR")
-    warnings.filterwarnings("ignore")
-
-    _benchmarker = make_default_digit_benchmarker("xception")
-
-    _report_dir = _benchmarker.reporter.report_dir
-
-    _random_pipeline = ArmorDigitPipeline.from_pipes([RandomClassifier()], name="random")
-    _cnn_pipelines = [
-        make_digits_cnn_pipeline(
-            32, ((32, 32), (64, 64)), _report_dir, with_data_augmentation=with_data_augmentation, lr=lr,
-        )
-        for with_data_augmentation in [False]
-        for lr in [2.5e-2, 1.6e-2, 1e-2, 6.3e-3, 4e-4]
-    ]
-    # cnn_pipelines = [
-    #     make_digits_cnn_pipeline(
-    #         64, ((32,), (64, 64), (64, 64)), reporter.report_dir, with_data_augmentation=True, lr=lr
-    #     )
-    #     for with_data_augmentation in [True, False]
-    #     for lr in (5.6e-2, 3.1e-2, 1.8e-2, 1e-2, 5.6e-3, 3.1e-3, 1.8e-3, 1e-3)
-    # ]
-
-    vgg16_pipelines = [
-        make_vgg16_pipeline(_report_dir, input_size=32, with_data_augmentation=False, lr=lr)
-        for lr in (1e-5, 5e-4, 2e-4, 1e-4, 5e-3)
-    ]
-
-    xception_pipelines = [
-        make_xception_pipeline(_report_dir, input_size=71, with_data_augmentation=False, lr=lr) for lr in (2e-4, 5e-5)
-    ]
-
-    logging.info(f"Run `tensorboard --logdir={_report_dir}` for realtime logs")
-
-    _benchmarker.benchmark([_random_pipeline] + xception_pipelines)
diff --git a/robots-at-robots/research/robots_at_robots/constants.py b/robots-at-robots/research/robots_at_robots/constants.py
deleted file mode 100644
index 8b13789..0000000
--- a/robots-at-robots/research/robots_at_robots/constants.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/robots-at-robots/research/robots_at_robots/evaluation/benchmark.py b/robots-at-robots/research/robots_at_robots/evaluation/benchmark.py
deleted file mode 100644
index 958a2bf..0000000
--- a/robots-at-robots/research/robots_at_robots/evaluation/benchmark.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from dataclasses import dataclass
-from typing import List
-
-from polystar.common.pipeline.classification.classification_pipeline import ClassificationPipeline
-from research.common.datasets.image_dataset import FileImageDataset
-from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
-from research.robots_at_robots.dataset.armor_value_dataset_generator import ArmorValueDatasetGenerator
-from research.robots_at_robots.evaluation.image_pipeline_evaluation_reporter import ImagePipelineEvaluationReporter
-from research.robots_at_robots.evaluation.image_pipeline_evaluator import ImageClassificationPipelineEvaluator
-from research.robots_at_robots.evaluation.metrics.f1 import F1Metric
-from research.robots_at_robots.evaluation.trainer import ImageClassificationPipelineTrainer
-
-
-@dataclass
-class Benchmarker:
-    def __init__(
-        self,
-        train_datasets: List[FileImageDataset],
-        validation_datasets: List[FileImageDataset],
-        test_datasets: List[FileImageDataset],
-        evaluation_project: str,
-        experiment_name: str,
-        classes: List,
-    ):
-        self.trainer = ImageClassificationPipelineTrainer(train_datasets, validation_datasets)
-        self.evaluator = ImageClassificationPipelineEvaluator(train_datasets, validation_datasets, test_datasets)
-        self.reporter = ImagePipelineEvaluationReporter(
-            evaluation_project, experiment_name, classes, other_metrics=[F1Metric()]
-        )
-
-    def benchmark(self, pipelines: List[ClassificationPipeline]):
-        self.trainer.train_pipelines(pipelines)
-        self.reporter.report(self.evaluator.evaluate_pipelines(pipelines))
-
-
-def make_armor_value_benchmarker(
-    train_roco_datasets: List[ROCODatasetBuilder],
-    validation_roco_datasets: List[ROCODatasetBuilder],
-    test_roco_datasets: List[ROCODatasetBuilder],
-    evaluation_project: str,
-    experiment_name: str,
-    dataset_generator: ArmorValueDatasetGenerator,
-    classes: List,
-    train_datasets: List[FileImageDataset] = None,
-    validation_datasets: List[FileImageDataset] = None,
-    test_datasets: List[FileImageDataset] = None,
-):
-    return Benchmarker(
-        train_datasets=dataset_generator.from_roco_datasets(train_roco_datasets) + (train_datasets or []),
-        validation_datasets=dataset_generator.from_roco_datasets(validation_roco_datasets)
-        + (validation_datasets or []),
-        test_datasets=dataset_generator.from_roco_datasets(test_roco_datasets) + (test_datasets or []),
-        evaluation_project=evaluation_project,
-        experiment_name=experiment_name,
-        classes=classes,
-    )
diff --git a/robots-at-runes/Readme.md b/robots-at-runes/Readme.md
deleted file mode 100644
index f2b9dad..0000000
--- a/robots-at-runes/Readme.md
+++ /dev/null
@@ -1,6 +0,0 @@
-
-# Robots@Runes
-
-## Goal
-
-The goal of this project is to detect the runes' rotation, from our robots, to be able to assist the pilot into shooting them.
diff --git a/robots-at-runes/config/settings.toml b/robots-at-runes/config/settings.toml
deleted file mode 100644
index bbefeb5..0000000
--- a/robots-at-runes/config/settings.toml
+++ /dev/null
@@ -1,5 +0,0 @@
-[default]
-
-[development]
-
-[production]
diff --git a/robots-at-runes/polystar/robots_at_runes/globals.py b/robots-at-runes/polystar/robots_at_runes/globals.py
deleted file mode 100644
index 30aaa4d..0000000
--- a/robots-at-runes/polystar/robots_at_runes/globals.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from polystar.common.settings import make_settings
-
-PROJECT_NAME = "robots-at-runes"
-
-settings = make_settings(PROJECT_NAME)
-- 
GitLab