From 9163adf86c66669473ebb36c76f816a3184c2a5b Mon Sep 17 00:00:00 2001 From: Diego Freitas Date: Wed, 23 Jul 2025 12:34:05 -0300 Subject: [PATCH] adicionado arquivos ao git --- .../bin/x64/Debug/OperacoesSalvas/CJ_Novo.opr | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 266 bytes .../__pycache__/_deeplab.cpython-311.pyc | Bin 0 -> 12911 bytes .../__pycache__/modeling.cpython-311.pyc | Bin 0 -> 9650 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 6410 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 356 bytes .../__pycache__/hrnetv2.cpython-311.pyc | Bin 0 -> 19240 bytes .../__pycache__/mobilenetv2.cpython-311.pyc | Bin 0 -> 9862 bytes .../__pycache__/resnet.cpython-311.pyc | Bin 0 -> 17496 bytes .../__pycache__/xception.cpython-311.pyc | Bin 0 -> 13343 bytes .../__pycache__/camera_oak.cpython-311.pyc | Bin 0 -> 18281 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 971 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 4004 bytes .../__pycache__/manager.cpython-311.pyc | Bin 0 -> 5224 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 216 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 711 bytes .../contexto_global.cpython-311.pyc | Bin 0 -> 7138 bytes .../__pycache__/health_worker.cpython-311.pyc | Bin 0 -> 221 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 5325 bytes .../padrao_dinamico.cpython-311.pyc | Bin 0 -> 3312 bytes .../__pycache__/atuador.cpython-311.pyc | Bin 0 -> 11927 bytes .../modulos/__pycache__/base.cpython-311.pyc | Bin 0 -> 1285 bytes .../__pycache__/camera.cpython-311.pyc | Bin 0 -> 6250 bytes .../camera_manager.cpython-311.pyc | Bin 0 -> 7223 bytes .../__pycache__/camera_oak.cpython-311.pyc | Bin 0 -> 13352 bytes .../__pycache__/direcional.cpython-311.pyc | Bin 0 -> 7671 bytes .../modulos/__pycache__/gps.cpython-311.pyc | Bin 0 -> 4571 bytes .../__pycache__/gps_modulo.cpython-311.pyc | Bin 0 -> 5654 bytes .../modulos/__pycache__/imu.cpython-311.pyc | Bin 0 -> 8702 bytes .../__pycache__/movimentacao.cpython-311.pyc | Bin 0 -> 11989 bytes .../__pycache__/sensoriamento.cpython-311.pyc | Bin 0 -> 9087 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 711 bytes .../__pycache__/filtros.cpython-311.pyc | Bin 0 -> 1308 bytes .../__pycache__/gerenciador.cpython-311.pyc | Bin 0 -> 3937 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 5332 bytes .../__pycache__/mensageria.cpython-311.pyc | Bin 0 -> 1770 bytes .../__pycache__/direcional.cpython-311.pyc | Bin 0 -> 11465 bytes .../__pycache__/movimentacao.cpython-311.pyc | Bin 0 -> 3906 bytes .../modulos/__pycache__/mpc.cpython-311.pyc | Bin 0 -> 89156 bytes .../_0_nao_iniciado.cpython-311.pyc | Bin 0 -> 1060 bytes .../_1_parametrizando.cpython-311.pyc | Bin 0 -> 1071 bytes .../__pycache__/_2_calibrando.cpython-311.pyc | Bin 0 -> 1055 bytes .../__pycache__/_3_aguardando.cpython-311.pyc | Bin 0 -> 1055 bytes .../_4_em_andamento.cpython-311.pyc | Bin 0 -> 2166 bytes .../__pycache__/_5_parado.cpython-311.pyc | Bin 0 -> 1039 bytes .../__pycache__/_6_concluido.cpython-311.pyc | Bin 0 -> 1051 bytes .../__pycache__/aguardando.cpython-311.pyc | Bin 0 -> 1052 bytes .../__pycache__/base.cpython-311.pyc | Bin 0 -> 864 bytes .../__pycache__/concluido.cpython-311.pyc | Bin 0 -> 1048 bytes .../__pycache__/em_andamento.cpython-311.pyc | Bin 0 -> 1910 bytes .../__pycache__/mpc.cpython-311.pyc | Bin 0 -> 12196 bytes .../__pycache__/nao_iniciado.cpython-311.pyc | Bin 0 -> 1057 bytes .../__pycache__/padroes.cpython-311.pyc | Bin 0 -> 2374 bytes .../__pycache__/parado.cpython-311.pyc | Bin 0 -> 1036 bytes .../parametrizando.cpython-311.pyc | Bin 0 -> 1068 bytes .../Python/Scripts/workers/models/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 209 bytes .../models/__pycache__/common.cpython-311.pyc | Bin 0 -> 149273 bytes .../__pycache__/experimental.cpython-311.pyc | Bin 0 -> 18921 bytes .../models/__pycache__/yolo.cpython-311.pyc | Bin 0 -> 69492 bytes .../Python/Scripts/workers/models/common.py | 2019 +++++++++++++++++ .../Scripts/workers/models/experimental.py | 272 +++ .../Python/Scripts/workers/models/yolo.py | 843 +++++++ .../contexto_global.cpython-311.pyc | Bin 0 -> 23065 bytes .../contexto_global_redis.cpython-311.pyc | Bin 0 -> 23327 bytes .../shared/__pycache__/enums.cpython-311.pyc | Bin 0 -> 6882 bytes .../__pycache__/gps_handler.cpython-311.pyc | Bin 0 -> 5323 bytes .../mensagem_worker.cpython-311.pyc | Bin 0 -> 3455 bytes .../__pycache__/mqtt_handler.cpython-311.pyc | Bin 0 -> 6349 bytes .../shared/__pycache__/utils.cpython-311.pyc | Bin 0 -> 3519 bytes .../visualizador_trajetoria.cpython-311.pyc | Bin 0 -> 2875 bytes .../Python/Scripts/workers/utils/__init__.py | 1 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 208 bytes .../__pycache__/autoanchor.cpython-311.pyc | Bin 0 -> 12994 bytes .../__pycache__/datasets.cpython-311.pyc | Bin 0 -> 89503 bytes .../utils/__pycache__/general.cpython-311.pyc | Bin 0 -> 56591 bytes .../__pycache__/google_utils.cpython-311.pyc | Bin 0 -> 6838 bytes .../utils/__pycache__/loss.cpython-311.pyc | Bin 0 -> 94976 bytes .../utils/__pycache__/metrics.cpython-311.pyc | Bin 0 -> 15503 bytes .../utils/__pycache__/plots.cpython-311.pyc | Bin 0 -> 38656 bytes .../__pycache__/torch_utils.cpython-311.pyc | Bin 0 -> 26826 bytes .../Scripts/workers/utils/activations.py | 72 + .../Python/Scripts/workers/utils/add_nms.py | 155 ++ .../Scripts/workers/utils/autoanchor.py | 160 ++ .../Scripts/workers/utils/aws/__init__.py | 1 + .../Python/Scripts/workers/utils/aws/mime.sh | 26 + .../Scripts/workers/utils/aws/resume.py | 37 + .../Scripts/workers/utils/aws/userdata.sh | 27 + .../Python/Scripts/workers/utils/datasets.py | 1320 +++++++++++ .../Python/Scripts/workers/utils/general.py | 892 ++++++++ .../utils/google_app_engine/Dockerfile | 25 + .../additional_requirements.txt | 4 + .../workers/utils/google_app_engine/app.yaml | 14 + .../Scripts/workers/utils/google_utils.py | 123 + .../Python/Scripts/workers/utils/loss.py | 1697 ++++++++++++++ .../Python/Scripts/workers/utils/metrics.py | 227 ++ .../Python/Scripts/workers/utils/plots.py | 489 ++++ .../Scripts/workers/utils/torch_utils.py | 374 +++ .../workers/utils/wandb_logging/__init__.py | 1 + .../utils/wandb_logging/log_dataset.py | 24 + .../utils/wandb_logging/wandb_utils.py | 306 +++ .../camera_manager.cpython-311.pyc | Bin 0 -> 63984 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 1886 bytes .../__pycache__/enums.cpython-311.pyc | Bin 0 -> 2292 bytes .../__pycache__/filtros.cpython-311.pyc | Bin 0 -> 5919 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 6006 bytes .../__pycache__/mqtt_handler.cpython-311.pyc | Bin 0 -> 3115 bytes .../__pycache__/utils.cpython-311.pyc | Bin 0 -> 8171 bytes .../analise_anomalias.cpython-311.pyc | Bin 0 -> 19136 bytes .../__pycache__/analise_solo.cpython-311.pyc | Bin 0 -> 6292 bytes .../__pycache__/corredor.cpython-311.pyc | Bin 0 -> 2950 bytes .../gestor_deteccoes.cpython-311.pyc | Bin 0 -> 2800 bytes .../__pycache__/imu.cpython-311.pyc | Bin 0 -> 3331 bytes .../__pycache__/obstaculos.cpython-311.pyc | Bin 0 -> 10809 bytes .../__pycache__/percepcao3d.cpython-311.pyc | Bin 0 -> 4958 bytes .../radar_top_down.cpython-311.pyc | Bin 0 -> 18762 bytes .../segmentacao_semantica.cpython-311.pyc | Bin 0 -> 9009 bytes .../__pycache__/visao3d.cpython-311.pyc | Bin 0 -> 2225 bytes .../camera_manager.cpython-311.pyc | Bin 0 -> 11276 bytes .../__pycache__/config.cpython-311.pyc | Bin 0 -> 4714 bytes .../__pycache__/main.cpython-311.pyc | Bin 0 -> 5730 bytes .../__pycache__/weed_detector.cpython-311.pyc | Bin 0 -> 17243 bytes .../workers/weed_worker/camera_manager.py | 187 ++ .../Scripts/workers/weed_worker/config.json | 12 + .../Scripts/workers/weed_worker/config.py | 75 + .../Scripts/workers/weed_worker/main.py | 80 + .../workers/weed_worker/weed_detector.py | 268 +++ 127 files changed, 9733 insertions(+) create mode 100644 AgroBase/AgroBase/bin/x64/Debug/OperacoesSalvas/CJ_Novo.opr create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/__init__.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/_deeplab.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/modeling.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/utils.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/__init__.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/hrnetv2.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/mobilenetv2.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/resnet.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/xception.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/camera_oak.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/config.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/main.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/manager.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/__init__.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/config.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/contexto_global.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/health_worker.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/main.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/aprendizado/__pycache__/padrao_dinamico.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/atuador.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/base.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_manager.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_oak.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/direcional.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/gps.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/gps_modulo.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/imu.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/movimentacao.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/sensoriamento.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/config.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/filtros.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/gerenciador.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/main.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/mensageria.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/direcional.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/movimentacao.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/mpc.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_0_nao_iniciado.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_1_parametrizando.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_2_calibrando.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_3_aguardando.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_4_em_andamento.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_5_parado.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_6_concluido.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/aguardando.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/base.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/concluido.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/em_andamento.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/mpc.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/nao_iniciado.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/padroes.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/parado.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/parametrizando.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__init__.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/__init__.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/common.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/experimental.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/yolo.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/common.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/experimental.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/yolo.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global_redis.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/enums.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/gps_handler.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mensagem_worker.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mqtt_handler.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/utils.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/visualizador_trajetoria.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__init__.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/__init__.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/autoanchor.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/datasets.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/general.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/google_utils.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/loss.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/metrics.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/plots.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/torch_utils.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/activations.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/add_nms.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/autoanchor.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/__init__.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/mime.sh create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/resume.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/userdata.sh create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/datasets.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/general.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/Dockerfile create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/additional_requirements.txt create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/app.yaml create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_utils.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/loss.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/metrics.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/plots.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/torch_utils.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/__init__.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/log_dataset.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/wandb_utils.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/enums.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/filtros.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/main.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/mqtt_handler.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/utils.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/analise_anomalias.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/analise_solo.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/corredor.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/gestor_deteccoes.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/imu.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/obstaculos.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/percepcao3d.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/radar_top_down.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/visao3d.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/config.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/main.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/camera_manager.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.json create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py create mode 100644 AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py diff --git a/AgroBase/AgroBase/bin/x64/Debug/OperacoesSalvas/CJ_Novo.opr b/AgroBase/AgroBase/bin/x64/Debug/OperacoesSalvas/CJ_Novo.opr new file mode 100644 index 000000000..0c7882212 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/OperacoesSalvas/CJ_Novo.opr @@ -0,0 +1 @@ +{"Modo":2,"Descricao":"CJ_Novo","ModulosMandatorios":[{"Dispositivo":100,"Mandatorio":false,"Utilizar":true,"Conectado":false,"Liberado":true},{"Dispositivo":101,"Mandatorio":false,"Utilizar":true,"Conectado":false,"Liberado":true},{"Dispositivo":102,"Mandatorio":false,"Utilizar":true,"Conectado":false,"Liberado":true},{"Dispositivo":103,"Mandatorio":false,"Utilizar":false,"Conectado":false,"Liberado":true},{"Dispositivo":104,"Mandatorio":false,"Utilizar":true,"Conectado":false,"Liberado":true}],"ParametrosMandatorios":[{"Parametro":1,"Mandatorio":false,"Utilizar":true},{"Parametro":4,"Mandatorio":false,"Utilizar":true},{"Parametro":2,"Mandatorio":false,"Utilizar":true},{"Parametro":5,"Mandatorio":false,"Utilizar":true}],"MovVelocidadeSErvasPercent":50,"MovVelocidadeCErvasPercent":20,"DirAnguloMaximo":30,"DirVelocidadeMovimento":50,"DirTipoMovimento":2,"AtuPercentualDeteccaoVertical":30,"AtuCapacidadeReservatorio":30.0,"AtuPressaoLinha":85.0,"AtuDuracaoAtuacao":200,"Mapa":{"browser":null,"pnlMapa":null,"lblMapa":null,"mapaService":{"opfMapa":null,"pythonProcess":null,"processLock":{},"NomeArquivoMapa":null,"NomeArquivos":null,"CaminhoMapa":null,"ArquivoSaida":null,"MapaUrl":null,"CaminhoDadosMapa":null,"CaminhoArquivo":null,"DadosMapa":{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"Id":"1","Name":"CidadeJardimTerreno2","Length":14.558011415731592,"Dist1":14.558011415731592,"Dist2":0.0},"geometry":{"id":"0","type":"LineString","coordinates":[[-47.395205344,-22.172559531333334],[-47.395212610166666,-22.172614638833334],[-47.395219157,-22.172656417833334],[-47.395223544833335,-22.1726892105],[-47.395224140984432,-22.172693691611649],[-47.395225326538004,-22.172702654611555]]}},{"type":"Feature","properties":{"Id":"2","Name":"CidadeJardimTerreno2","Length":14.771318274761821,"Dist1":14.558011415731592,"Dist2":0.0},"geometry":{"id":"1","type":"LineString","coordinates":[[-47.395210902333332,-22.172708800833334],[-47.395206943666665,-22.172679811],[-47.395202420666664,-22.1726491015],[-47.395198865666664,-22.172615782833333],[-47.395193255833334,-22.172577132833332],[-47.395192610024395,-22.172572657695412],[-47.395191325698136,-22.172563706509337]]}},{"type":"Feature","properties":{"Id":"3","Name":"CidadeJardimTerreno2","Length":14.827915524386164,"Dist1":14.558011415731592,"Dist2":0.0},"geometry":{"id":"2","type":"LineString","coordinates":[[-47.3951762925,-22.172561603],[-47.395180824166665,-22.172591686166665],[-47.395185745333336,-22.172623080166666],[-47.395190433,-22.172656367166667],[-47.395195199,-22.172693641833334],[-47.395195769044363,-22.172698125891035],[-47.395196902671593,-22.172707094716973]]}},{"type":"Feature","properties":{"Id":"4","Name":"CidadeJardimTerreno2","Length":14.785159385903514,"Dist1":14.558011415731592,"Dist2":0.0},"geometry":{"id":"3","type":"LineString","coordinates":[[-47.395182932166669,-22.1727127985],[-47.39517819266667,-22.172680514833335],[-47.3951732595,-22.172646752833334],[-47.395168805166669,-22.1726149155],[-47.395163812333337,-22.172581167166665],[-47.395163154299318,-22.172576693573966],[-47.395161845655842,-22.172567745443878]]}},{"type":"Feature","properties":{"Id":"5","Name":"CidadeJardimTerreno2","Length":14.801176921912329,"Dist1":14.558011415731592,"Dist2":0.0},"geometry":{"id":"4","type":"LineString","coordinates":[[-47.395147317833334,-22.172566031],[-47.395151503166666,-22.172595452333333],[-47.3951561855,-22.172628011166665],[-47.395161598666668,-22.172663757333332],[-47.395166727166668,-22.172697770833334],[-47.395167397572706,-22.172702242832194],[-47.39516873082615,-22.172711187810091]]}}]},"Carregado":false},"TrajetoriaMapa":[[{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172559531333334,"Longitude":-47.395205344,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172614638833334,"Longitude":-47.395212610166666,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172656417833334,"Longitude":-47.395219157,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.1726892105,"Longitude":-47.395223544833335,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172693691611649,"Longitude":-47.395224140984432,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172702654611555,"Longitude":-47.395225326538004,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0}],[{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172563706509337,"Longitude":-47.395191325698136,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172572657695412,"Longitude":-47.395192610024395,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172577132833332,"Longitude":-47.395193255833334,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172615782833333,"Longitude":-47.395198865666664,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.1726491015,"Longitude":-47.395202420666664,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172679811,"Longitude":-47.395206943666665,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172708800833334,"Longitude":-47.395210902333332,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0}],[{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172561603,"Longitude":-47.3951762925,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172591686166665,"Longitude":-47.395180824166665,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172623080166666,"Longitude":-47.395185745333336,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172656367166667,"Longitude":-47.395190433,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172693641833334,"Longitude":-47.395195199,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172698125891035,"Longitude":-47.395195769044363,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172707094716973,"Longitude":-47.395196902671593,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0}],[{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172567745443878,"Longitude":-47.395161845655842,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172576693573966,"Longitude":-47.395163154299318,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172581167166665,"Longitude":-47.395163812333337,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.1726149155,"Longitude":-47.395168805166669,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172646752833334,"Longitude":-47.3951732595,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172680514833335,"Longitude":-47.39517819266667,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.1727127985,"Longitude":-47.395182932166669,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0}],[{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172566031,"Longitude":-47.395147317833334,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172595452333333,"Longitude":-47.395151503166666,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172628011166665,"Longitude":-47.3951561855,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172663757333332,"Longitude":-47.395161598666668,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172697770833334,"Longitude":-47.395166727166668,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172702242832194,"Longitude":-47.395167397572706,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0},{"Momento":"0001-01-01T00:00:00","Inicializado":false,"Latitude":-22.172711187810091,"Longitude":-47.39516873082615,"DataHora":"0001-01-01T00:00:00","Altitude":0.0,"Velocidade":0.0,"Distancia":0.0,"OrientacaoMovimento":0.0,"NumeroSatelites":{"valor":0,"lidoEm":"0001-01-01T00:00:00","frequencia":0.0,"dt":0.0,"ValorMaximo":-1.7976931348623157E+308,"ValorMinimo":1.7976931348623157E+308},"PrecisaoHorizontal":0.0,"CursoVerdadeiro":0.0,"OrientacaoReal":0.0,"TipoOrientacao":null,"VariacaoMagnetica":0.0,"QualidadeFix":0,"PrecisaoCm":0.0,"SatelitesEmVista":[],"UltimoComandoRespondido":"0001-01-01T00:00:00","AnguloCarroDefinido":0.0}]],"RuasPercorrer":["0","1","2","3","4"],"TipoMapa":2},"ConfiguracaoUltrassom":{"Configuracao":0,"Sensores":[{"ID":1,"Posicao":0,"LimiteMinimo":false,"LimiteMinimoVal":0.0,"MargemMinimo":0.0,"LimiteMaximo":false,"LimiteMaximoVal":0.0,"MargemMaximo":0.0,"Desvio":false,"Parada":false,"DistanciaIdeal":0.0,"EmUso":false,"LeituraCm":0.0,"SensorAtuado":false},{"ID":2,"Posicao":0,"LimiteMinimo":false,"LimiteMinimoVal":0.0,"MargemMinimo":0.0,"LimiteMaximo":false,"LimiteMaximoVal":0.0,"MargemMaximo":0.0,"Desvio":false,"Parada":false,"DistanciaIdeal":0.0,"EmUso":false,"LeituraCm":0.0,"SensorAtuado":false},{"ID":3,"Posicao":0,"LimiteMinimo":false,"LimiteMinimoVal":0.0,"MargemMinimo":0.0,"LimiteMaximo":false,"LimiteMaximoVal":0.0,"MargemMaximo":0.0,"Desvio":false,"Parada":false,"DistanciaIdeal":0.0,"EmUso":false,"LeituraCm":0.0,"SensorAtuado":false},{"ID":4,"Posicao":0,"LimiteMinimo":false,"LimiteMinimoVal":0.0,"MargemMinimo":0.0,"LimiteMaximo":false,"LimiteMaximoVal":0.0,"MargemMaximo":0.0,"Desvio":false,"Parada":false,"DistanciaIdeal":0.0,"EmUso":false,"LeituraCm":0.0,"SensorAtuado":false}],"_paradaFrontal":false,"_paradaTraseira":false,"_paradaEsquerda":false,"_paradaDireita":false,"DesvioNecessario":false,"ParadaNecessaria":false,"DirecoesObstaculos":[],"DirecaoDesvio":1},"SonarAtivado":false,"QtdCamerasSolo":1} \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/__init__.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cc08c8179a6b38fc0ad2004e6ac9818fb1b148b GIT binary patch literal 266 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/utils.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68ce1f3ffad59377e594de8b3c840e9b2d2a33fa GIT binary patch literal 6410 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 XJ;jWx0qZ;i(H8<1d=cg|uFd^FpX9{b literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/resnet.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Models/deeplabv3plus/backbone/__pycache__/resnet.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2525ccc755eadc9f560bf78bec06394a4c4769d GIT binary patch literal 17496 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/config.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d534b3e6bd409a79097eebda264424cb5d0a10e9 GIT binary patch literal 971 zcmZuv&1(}u6rb6T>~22%s4XbHq^+{jOl82C(KT96Fda; z;8m!!AU#@@{xd1`P!E#_2iq~Y$WLH%x`{gX5PH{y|>>hl_HX@IoypgLcdaD z4(fcKIY15&K?L`aEoY^V1I1P_BC4$tjjCS}(T`MHoAUy2ofvk3m>-dCD2OW5IEMRA zU}ViUi3NKW!EID4{+5fa8cr%ZZa}&F%njWhO2PH$r9=Mp%dS`>ojE)=lM|Vve0RJ+#iU@dB^x$$|EPsT_-ag zkA<)O-u7UalmZqB?($Bb^>`Vg%U>XB0*ujxQJx~KKe#X}Usm_;d|uyQpIo~S{aY=Y zf5h2*YBtWy#w71k6`WGf8Qg_5wE}@UIIu~dQ^1g zBIf)2CTz%SB3W;EZfRxQN?mG2Zc3-MCDq%xpN!8fdB75E;Crki;sxa~m&HzV!LLgu z!*Chr?{luqhgtm|@io|xqM{D~pd90KR2rw>C8|%B`(=4`Y@L@k53NagGwc7c&GJ~E jY6^xNF2F>k+n=@$OQ*$+v*O0sm}(f`f*k(?=_CID=eFuc literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/main.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39f376b3b85e37e6694f1d6a150a2a9bf5adb63f GIT binary patch literal 4004 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 AcK`qY literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/camera_worker/__pycache__/manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95b2186227a61b23587feff4f5e4be2af207bd0c GIT binary patch literal 5224 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/health_worker.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/__pycache__/health_worker.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d44944d7bdb387738eb0acc12bedd6fb55fc8152 GIT binary patch literal 221 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 O^2^eQ*$QMb@P7fxBrZk( literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/aprendizado/__pycache__/padrao_dinamico.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/aprendizado/__pycache__/padrao_dinamico.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee25cd0d3e3661c7103aca439117b1ca2dd16332 GIT binary patch literal 3312 zcmb7GO>7&-6`uX0Xps~p(=us=QZ|m2U;|l@{8PY2<{!jShy#j`ev3~ladl3 z!{ys~^WMC9Gw*wE=O2m0Fk&rt`^LYA5c&^ix@ijoe1#aRRU=>QiutRj{Bjo92gpE60zpe6879hI zVM!v9y9mq-0aZv8XoMUAIz(hJ9P>v$#e`xzSF&)qRJ5Onl-b)OKqzbk?MXoL#R$+| zVR1<1Q@5xY21WxF&H3hhygUDqDyR9buQ+Nb=PhaaIy?0WmCc`|Xr%Ra4 z&;x?NaVZa{5h>$areR(0>1jj0Y}sYQkFVg;Ev%QZRmG-1vW88Y>V{J>C?+;#zWYOb z&~W?_vs5m*hE5D;tz?aKC+4he4X0MwyQ5=$0%F|=7WLGa>**(`o+0I&RDKq zFMN92y!+lL62AB+6233*MVBcS6) zaHRs926l|G!kWX@RaJV7u}MrIJhp|Y&I7zYwQuM9@nLx8DaA^F`tLblps@i1bLd!I7ocas`zr*z?LsLF43eQq)OQ! zCG1DK8Ocrn@+pY#coCb$stMucO5LnoKeaE)hiWmrIDZ7PiFRWnb@yJa70Y_D?DpzT zOl`*0b~5E9Pd1aY?erU7`n|`Wd#5k`Yt=h_`FYOEU2XpE_g?Oc*6A;rg=?*uYt8hv z=IFI{YI5u1?`L)-E<@z&_HH#%KccE?ipkMn`CX>V-09%(0!ZLQQdn$c_ko-o|t zObDJ|!LhMS*%XPe2qA8O@L+ocLr=ru0$AD*H>7=4SL(02HStcQCK2%_UzNTy0G@tO z2}SXic#{Le9v#r?gIFN9#G)ubZuWzP!GHm95awX#HSDBy=m!=ypu+zMxPP`E8%j;K zg&M$Jlk-Yhol1MDQ;pBO)Vq)8o2eW;JDfg~y}8TJ z-}2_Vd?0Jab3aT>ZsW%E!_vdqZ)P9oyqWi(ztfueaA)Gfda|9#dKvBUTV7`7nb6F9 z1kVnq&p!9gUwOXjo$vC2te$8mr<%#R#zHeX_Y?4D!0o~tYQ@imH;1s)g|NsjMQ&Ib z*@xp$=t-;+Zpdy3K+qh39g`Dr@XEzNRQ)SBJIp`gKz?huHnp%?Evx*SqOwRE>YQq* z^J)pczeQ{jTea2I>WjbI>WWRV3Os`W<00-Z{x%z0n^PCm04se;p>!Uk+yFLTJSj7c z;Cd1&&ifqanjApbZ@tOMY zZu02X#fI#uXIrVW&lX$B3tsXZBjK^ zKd$TGs2ND;IGe>1E;usw)u-&v`~-2k{2(Yh7fx7Rb_kHo@YfL|%2Wppl2K84yMsa! z9a1JcC?rP{%6tcfWNJjY*g*$L-~d}&Q~X%bHcg}G!ZpSTE~|=FEmv++736_~XIPKj zL%7qgqQ4Bbe2JciF;>Byt3W!EAP8+V*&KY@=xDR|bmX%Fcyy5bmyP}J5CLEI|JdaC AivR!s literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/atuador.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/atuador.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b08d214a57c7876d06040c06aa67110d70e65c3 GIT binary patch literal 11927 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 IN}Zkm0_m6BkpKVy literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/base.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/base.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7d96fdcc6b80cb0c0a35511ad837ab1414caeac GIT binary patch literal 1285 zcmZuwJ#W)M7(Ra_G^L@flrj_wrV&VyP!$kjsGw3mBm@!>(9NLcdco zfWlzz--pF6qKIlf3N%{_bX(Vuj-DW@cMvt0wr3!80RL>*1~pevt?) z(u|}at_+xLaq14V>2z|kUkAF27_v2rY@KQyWE;$+IxDQ{)aaOdU_Z!D+QrOIGWDF( z*yk%ASr2(6J(n+$h`Cy>L=Y`P%1wU(0Fu!@BAlBk0+y>i#gslBR0nnvAQS--Ga5-T z^C*<8W+rCH1C|seb~$B9IrgO&a2!b@<0zRJP}pOz6}0!Y#Eh7~7Acq3F<+i{Y!*_F zhfhP-A?t!Sxx`IK!C3^NayeZzz0lcs_`q3V&3N5;u?fWtomE$OZ5cW5xp>1wz0~l4%4R(N|Y1@ld%ZTRbM5VR=)@fReh+U9{&O|Y7NFKCbsum030v@X^t4*ljp|?dSzi^Pml$||iRjs;3 ztb6tPd11h%_l|=SUfnFtswLTmgt%e>$Tr%(=aTS@gHQPljeR7_kYd(mF%HXQ9Nq}x zLr4NgDu}Jy!#i+6rascB1h9>I6;vwkm|_xGqjpI(Gb91SI2pw_?X+5tVf+>xWfn03 zTA`Ymm{40OrWCIzP`9L!3DubtRG*?}0NeSmr<;Y@9?D@16)QcxR49L~^bky^aAbg~ z^Ex%2?q+V3hX#Bs25GuNc}YXc9G?&7c;r!*$%42BI`#HMF9CXnrfFSNJskG*%t;L* R`5(S4AF18HLriI4{{WRPJE8yp literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..343a67509e5aa206793d587f4cb35e270820bb97 GIT binary patch literal 6250 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 F{{ZBCme~LR literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97b979f93b8490297c7f78bf75a0816f9d946c9a GIT binary patch literal 7223 zcmb_BTWlNGm3KHC@imkvi84t&!cinAreaHR?6{8OIHE;MQ4-7XBXv#7U}(-rqRfYU zXK2|1Rrn*=(gqh%7A~x9(?TtR)NTwUDEi@#ek4e+_2-b7K!X9aFm_R(`C4_o3kdn@ zxicKTWH$!VtIIR@ea}7jyzbHO+-@g=bZ+Is+&6B7{+m?HWUUk)%|YQd;t@}!P}KNR zQ47>9DLQS9TGLFFp%72=)>KQ{7PUbc@l48|c0?U%XVgg{3z|TD%lnA8ePTiAQ}`Jv z>f-H!bH>6u-nT|so;r;}&TmQARLG*WoQaKPc|r62I4S1hsk2#pNx);-bUL2l&sF(-3EPDmL07|`7I(SE4kMgodbJc5Hz@1rQq zTL4;l8X&{ldFFj8+Cor{+ISm4J8uQ#9GctM?07sApA~SzT#{@+$~62Qy$IkonnigO zgOZW*6x0a9dg4i~I+sVVv}FpDh7hgU68d>W&6UoI5{~0oDm$yOv0O?@rn50I5zoLu zt)h^c!Ek3tB=b1#yK7^IrlUfJPi8ZdnZ$H_7H2PJrP#%|C`^Z;aa-T8=Y)7lnu{6L>2#LQrLy9*fnN;nfzV)h;hJWT z#gdt%6pJnGXrRxq*={Fz1_4}0sxMHm7Fo6Dxq?-(`_U!tNa6Nqj@Y6~e0p;hbqn3cfdFqnhhkJ zGkXw{2)31!r)H>omPUSGw&X2qb!(Yp5o%nOLNKnS)>|v_^gZh*aJEmY=Yz4-S{)9x zv5jkDe;%#X;a3~SGtDc`qGdYcg>@aZQMFRzx-fEj07>3j{~*FAmaRaw9ksT3)-10P zp52z3)?|q2<(WtmU0|=kw!PS8E5Y|1Xq{#2Ppuid@m-`A(N$<&q@LUZ^IGzB1g%gP z95-31uZC8EouNSR?Z8QGW@|GxJZidKTi%9;^0qaPIivpDctW_fmbLc!ezTmnHQz?@ z9k{s;v%mg(gKZou{#<&#*3a7;dnp8W;;Z$~SMRKOon^c9LTwz-pvpISd()X6dB+bt zucH~u;)88>ymQ(5MDBIu?Y~64GjIJR`qehsFz>$jzs+3HKq*%<4l7jACGDxBK}+KZ zp1q{oM{LWtfG)4{NF%>o*s<))JD-j|MPI{yb$s`Podmy`yQtl`;{?{+o z{^$M&+!>>Zi)T53*d-y3xx`n$CFWQh=B9+qTrSP=NpT@7CZ*(+EQbXti!*UPJKUh; z2`M|__vios!*l}aC{RF9-<}#p=|fA=cN*PAcL7Jh{P@*L{;ox{48N#Re8o0mB9Y%Y z52uo%l*pzRjx6nKv{P{jJdETL*xHKm9X_1OCgLga2o%go#D0k!fa^%!eZt81Pu@BC z*}31JyLfJ^cyIInR;x zMgVO675$Z*vhPjEYvKFtfAW2Cto+KP{L18N96&AWBlPnwxrH>B6SIf7aiE*96g+?I z)Tt8}NNp}oI0QU|M3OLw?%r@qmP6bUYtA`?bAsUjQCgpnxdH=YX(1zmhmnq5(H+2w z8xrc(tb`SDD5m(WuKBwV(y+4<8c+ zoYWbc&x#r)Xl#A2=FViV#7$I1%^_R`Ru?jopxJrh3b=GLSvW9^(P+3d10|j3hzaNF z(x}dTT6>Km6){>0eh(Mw4s0jz@YZ#-;c6?sa^uzFtE*lmFjR64-8ofujmWMMmF*~u zZ}f5>P5%2^AHAi!i<{Oq$H<1K>qFZuTOqvR@qH6~`ToiK5!n+idBTb(TnMYI`-6#_ z6E`M{lZ8oA8NWHcy00)^VuK1Bl-Z!#=J{ap=HiWO#cP|$>V8@Ec-K8#*~7{GLdi3u zcxGhJjJjt;_Jklm+%x>yyT5<;?%8{1p|-WP(dOSojANt@Sgh`mjo`B%Ev*Mf%fZo7 z@Td|zy27fuviM>*=!eqo6AIMy1-CXjdp8l~h80Yp?jAP!>fsioO&W%ff|)iQur*XR zTT!5=;C|ROyouT!Lw_TnaBRchYsjtv?|AQ#da3VCrSHuLk&^$U;y+oKd`M2v>K;-( zfpt%)>E({Z_gc}9F#{+DDuZ*BAp0)b%7dZ6#*PePM z^TspGgJ&2hH@sVk0U^If5dg$0)$CCOUGR}NOYJq4rwj{)ozSY=Dwu>Y4ZZ}t-ivdbhE*0rQ=qcj`N@}SqU&`4C3-iUWXsbal2U4P)0p`}@4Y}2L0{Se6u9-flz zUAI7lEDi&J$hUcli>o`!Z9%y$xEB6+5ukHm<%lZhNvLTCc z>BP&aziX|fVh;!bx%Z6g)?9U%ECrs3Q_$)2N5m^1pTAy+fcoUed9Ck|g*X7<&$S&e^4h=(ecSbK zt`FU}+|@?o1}XgpLzFJ4B8f6oZp+iW+0rvlyfPh`6J%q*zOkxrtec^>1EsbD3e@cf zj0>qM5g1A6sb9#ip+ID@KG;(yan<0*(?}dhvo5!|AA&0NyTKktYJ5*j!T zQs=1BIr_z5sdYkWohZ<%t951Y=BtHQ)j;ohU~f6Fw-nf?1oqu{vv6!>-@{J->VVRH z@cw|@`I^kW2IhX%yNXwifL+&#q2CTfe<9A90b0jmnll#D!=a?i#$uP@`DpZD64b%t z1Q2tEi9W?d2Ju+}h_o9^k|!x9k8T~1CyclV;Cl6M(?T=PY@#Y~?qg=a395thl$GfM z*VP0p!*qi?+5lQ9=11VPn4rbRfDf}B7&z(Z90n{JSi35f{|P-rP4Xy(mMI4W9&3)l z(@55`g+$r&mS(A4rXjdfl{DC-n(}mG@WTjMKzOJckU|K;jhr4Hdh80bsCfl5BxD3X zNXW1jDg@U>e}!&4|35>^V-l0Wy#Tf=_MykWUhAZ~8isg}EWiN(h0cu``SbR*_TL45 z8~9Dn$34|XLyj5E#={GDE}q0M!X&&GfJQImBs>fy5L9yCm{@!~3P9_seHUyWv`wV|AL?#h1O*;P-09a^iKsNKHg<(2WU-zx0b>G3V?_kMyNbwyiu&R#s z=PAIn#EmIXv-hcX#|O+!Ms6J_*#{N-pls%?4VRZNa7PVP#uhH#a1$r6SjL z{ZB<*vYAzMP<~uiQNP@nRWu|&E^k^-Q54AfKt E@7D03!~g&Q literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_oak.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/camera_oak.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20c48852c20d45903f3be31cca2ce57f801959e2 GIT binary patch literal 13352 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 o4MJSeiKh4|+*)>;M1& literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/gps.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/gps.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f16afd4b27517b42409570bbe78069a4bdaab87 GIT binary patch literal 4571 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 DLc-&D literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/imu.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/health_worker/modulos/__pycache__/imu.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ec31c3dc372f4d240bab1256139ef87c0eed0f3 GIT binary patch literal 8702 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%0ABAPSFbz|amiN6!;pJ~YgfHs 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 bZ_;FS(;6J@Lg`9q=zNiT{M`>wsk`%EXfUH$ literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/config.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6014f415bcd5c22515bc0e3e5fbb1a120d8fd108 GIT binary patch literal 711 zcmZuv&x_MQ6rRbCHnFLA(1Yxyr-dA}mqoBHg4&ChMX9Ga64K1r&DvzbWERunp$Go~ zVPyr`qpPz2Oa%{1U_tQY$wF^?@=bmeL?>^)_k9ENzBe=Z-0faLY7A%lzajlBMGbnL z*E>Lt5k(YFk%y?}VX8Cy5mDnr_q3Xuz;)X44BCE+Jd@%Pa;;yg#9q;kgCv+RF}Hcj zL_oRd&-K2{0Yv^R#I=!1ZJ5eQ6dXNMlmbwnA5{4^CRlw z!irgv!P(+WRqI_IJQ@&>B{bs6<0K@(MDQ_}{y4}Oc>vg zkWnb2RA%Hg7q6JeNPT&JrAf$>m(k>QIx9Lc&!h;1KjjnA0ecH(HUwCpi?*{u*t~aP zb>CkX6)TOms-O_z=4Wzo8>?r`0$M_s|7UlQDxv{X%o$U|y(%G)!KiYB@ O=E~48wDm8PmVW>=KC$Zn literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/filtros.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/filtros.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd75f4be7cad5dd3609d4c163e95d960f77f916f GIT binary patch literal 1308 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/mensageria.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/__pycache__/mensageria.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a87df144bbeca2cf997a651eb393cd8633f7323e GIT binary patch literal 1770 zcmd5+&uiR96dtX#yY@Ox+-#DirfuftEI4bD{y3N>rM26TL+lU-9~OZaYi7N2C5;VNod!^wgWd(CxENKfz0ym@-_ zzVFS`>FFAR@yBo1yI(m7{i%&&l!waEXP`Vm6j3Wgk=3>=t(U@5WVh|eX**!s?Q(!R z7Im0I%dFCIXysA4U8U7Ws69a^Ku*%hHPo#ArGqxD>@17710uYH2;wQ81U_$;vZ=d4 z!k2hNV#>41U2lm~cK<9-m>`skg)SqZ>Mn?0R;5g;Am&*$Av+-_)GrOQ)gL$svf(|V zAf6xvvKU*|NE_|gMO$OtqSn{;gOaKieh8_FvA$dS4t#$Y`NP>ua4tJt1D_g(afjVY z0~siSDI_g1i+ibpFJ(B5aJ2t3O+${EOqjp_V+YQSFR|VtzO}kOkC$)Wna69NuC!XM zX3Ip|v$Lu{EXWiMWWphIu+4?eZ>3ol`mEXM8%!hVakNAK{k_D*UO9N zsb13-6<9D4goa;gKgj$=ytL@HSxf^S-;8~iYzn@?mA648b3X>-lb4i@Aa;8nyyq^n zjdat!y`#E3cGr9nBuctpaPc`4(gi5VCKFz6yAg|}wgb{icCv;jY9%~VEH5(oiy25r z=O`CJJVeiH=-kDA{c^wl?x69`;|GKK>|xb8Q#nK+9^QcEa^u&=-1m)NoGX3j%0clQ z!EWShQH&8YdNA|f%uqUs;n>o9%^0&KfSv%hAT?F20b5Ru^uFfzHjO64J~wuSDLk~F1ti9+KTQe<#pkb z4VlOlJatUFP`KrD|1d{E%<=yQV#cNJ literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/direcional.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/direcional.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b37766ee94337a3f7461cc9fc01c3f3736671d94 GIT binary patch literal 11465 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/movimentacao.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/modulos/__pycache__/movimentacao.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..102d654b925d62a4ce4ba47151054172190fce70 GIT binary patch literal 3906 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%0DP*^<+UJ{*D~7&)?HrF+nopy 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 P!x#6jn*Db}s=NJvovjv% literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_1_parametrizando.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_1_parametrizando.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d15fe44e919153d3e4c15ec4bb2488acf9e78d6 GIT binary patch literal 1071 zcmZ`&K}#D!6n?X_Ta6@YO)2f65Q&$)2rZ>TP)l2-^ddq|ZnrF(ovDTQ-=~T;5jgJ7nsG#J_R_yo{D3#8)dMEPLUaG zl8h}&5pbn^Cgf9J@l(AF=SKrk_d_PU$d~8~(&!X*sFwCEggxNk5C)FH$SydP8Awl) zyO(&Ek)qCHS>4i%K{s4e==12@+A+c&WFWre-N(5D?5UKY2(i(%0H#3R+a__hBIsb` z9r*%ZjeX=e=Y-WpRCu4V7Kr-`vILY2@PR#z& zfbB3lca)uLUf0f0S`=e(RO871AvX@6MrP>ei3jwjHA9zdGa2^05TA(WB_p0!7i_(z z^{nT;UH5CLM~-6b4`?HT%xM_vf{BVMBJA|OmSI|$?NyNZZq$-(C8oX-sUKi6HCW+Z zVer4;YC7_n6kK(lK)UgS=IM~>WX^|Y)3oR1Z8Y`cQ?C&4)Ync|o Sgs;N4u|InD?=`N@_WuByPaF9F literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_2_calibrando.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_2_calibrando.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e73d78b5de9a4ea325131d52d6fddfdaf73bd45c GIT binary patch literal 1055 zcmZuw&rcIU6n?WmgpwAJs_|l6QZ9SpK#WM^k0=o11u;FbuGwsN254+|)|n**B1#N@ zfH#cBE5>j!{v{>hKsMpziCaxQ5Kg}7(iTErXTN=K-n?(Vd9(eI%MF3DxsBCF1mI6Q zI0y6m-72E93lu1+LX9|t0M)2o)oQw<6VTu}P-6$E$;h4#u#Zn_jzO=Lz#ce3X0S;z zvM6}Kr1U8l&wR;F)H0kO4tUKEDfc2@U@l0jQ&gqOS~n1Ofq_FPI2t88;OJCCdOf*$ zfp^xgR?0%L1)Wk=uFj%#wPJ)_$UuA{yN7f8_@q{X0>oM;0+;}CXN$z0ZeW6v_v9OV z*Y=QOzso~l3463fKA^zTxmPE3Sl5@JPHb&q)0!88TOqfiULUNU;G$*gLT!^|=GOxj z#T6bVW+W!fOe$%Xb@p+#=sGNxnND78 z-tBt;_TV^sy)pLS*DC}Z2icj! z>`bHII7MsfSc`8tiu|8)qwslXTK_sWrT;dk^#$8V20btIYs~YKAi+w*7M%2 z`PDQdhOzc1v=Bk$6by90Kt~o3w!6QkW|*k6p8=< literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_3_aguardando.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_3_aguardando.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..574c1de629a4778a2b6981df4696ce9a97e4a1d3 GIT binary patch literal 1055 zcmZuw&rcIU6n?X_E0nZ|RE-zol5*J#2Q?y%KY~Jx7sT|$x@NQ88B1fkyUuJ=Afm+Z z2YAD1ykZO&<6lw|4rCKfp19T21L5SGE^Q(3I{WQ=^X7f?&719yTy6xE&8;p!BmjT9 z!3h{A?@ke&EnvV%4eG=u1enHXO{-Iz642loFntr4!O1oS*uf_?TW6O_U=1B2Ggu_K zyeLASOX;ytJnfvqvJ2{vUK($nPH zDc;#i!xK!g`MpwAuFj%#bz+1q$UuB5yNz=@_@q{X0>oM`0+n-|;FUEQhgM#=bhU7^nshoN_gA zR_@((=6JbLaTZtO!5+w`|FO&2UZ8Ft;kt8>>)MqIZZvPMn$S=W8n z@M>vBjAHFi=pcf~DH!U3j*cuKZ1jI^O*c^Oc@X(t)q-Uty1ELXKDuP`Y-jt8!T$l5 zQ<29+$YuBH(~ZX!PnS%IIUatH%6=rSW2inj`5FOFPRKq?w9bxwc-%TW+PXnd@Ok+1 N;;)+hcR{MV{ePE#6sG_H literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_4_em_andamento.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/_4_em_andamento.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..383c2f01790382f71119507f19545b76f2a96e78 GIT binary patch literal 2166 zcmZ`)-ESL35a0XoS8B&e+>f*ZdTokYt5S;8s6dJkNt{#&l2)VwVi}z-ciZ&ZyIZq& z`DkTBL480xRY<6n5K^ClQ2M|lkEr|u=gJ6cB|q`hH&-cA)hA}ou^p#jy*Hkjot>T8 z`_0@hnaluz@qPL0>u<*h`3Hs0067}l9{k-W7O^CUxKdGa<)SR1EIW#;7FFOCOLbyy zycm!4F(=_Bi^+(OJ1KXdcwmQ51D}zIOfC^CahF(08sAh1*@CA%ii1{ak>m%SqL`OO zZiaaQ-3ZtvhbghqHIZbNl~$62vHG- zcbzlqPI;E$QZHa;XD{wgz=O$c{{Y(iBt?QSh(v-z9UDTx0`1gC$|gjy)s1=)z_v)nNN~7kdy4Sa ztBK%fPh+W-#82dBxj#;cEWz$-;;RJuXmjh*I@wT`$hwq|EeO>G4CPgkGQ5>lhv|M< z91ZL;(_OY^M{iWOY)(y^d4?l0Yt&(;Z5b9t%am`ss}7<}t0@1Fg0TO_N$a!|}d20zsr#XrQwlzzYQ=k(f8i#-%HZBDV+7 z81}15$V!YD7^8jL0PqJJU}pv#Z(C#vP&7W)92N;rqAoSNVbXE{>5EvI+g^}Q3e~61 zHIDG$gJ_eCQiJoNq{ML9;D%-B9*ydd+XhEi2#NaWQrj6XznPgV6{%-|?+-n*WUO$u z%mRHGincTb0#2>#)XMefiADEl&;(gt~0N+XmYz8_@#BmZ&L1;pp=aj%JsHf z+I6ecFXlkC|rd$w8ltX5d67nW*6Q}82yW!O@LT{}_$c+5441c5Z^0SCOk0@w{=jBH3$i0ad&FpB4NU4iG(o7GB zBk%rZ0D)OcPuA0u;ePW4jFtj>;ol#HyZCnc*z|Go`1rs~TzQ-ynURz~r5NxFPzy<2 z_u!UwT@2_@N!F@^d|KDP1mv|>_!#*3m23kg{3H_WN)bVn__%zh`)kQVF^KwA!q0Se zE#$M}HUGN7sWpy`9={v4YiS;-m#XSt}DwXGq%BGXPw!Sq*^J` zKfqf>@mi5w#J?nk9?C*bo_dSKL+Qyk*(7ag-_Cyf-n@NpzWHW;WHMu*ZMnHwAOL@Q zK_9ICx4VSSJ}_XU3N_*o0?c5vYSgGh2^jDKn7Iec;^cq=wD3*LG1;vm*dwRd8Eg_y ztVvPkO8HF4XTIX6`Zt^(FN>NVFyV#1#9TS4PEeO>>)uA#2M!Kl;24bTfkT;r?P)x9 zsqb3SVo;__^*%JGZiKK8DTpp*4{)x9Z(>ErLu3phfLV|ac1Sc(0uzjUAm8A-aezHq z!#)JoQJ)?15y=(%UY*pTLD!*9Y-4r%{)&_$7l>SVp+PPoyOgu3)Jw#vl}4F|QAGr? z70OBw*+y(CUVRf2t~9M&?)?4pCD-8r!=GLTW!K-3q9h`(gnGJ9G0?(ZIHgM9HWudH zWnS9aaMzmArU=|(SysYGxpg7ma;aPt-{0WUOJesDL#|w}&_9;Buol~lrFSy8fR!7 zinaJvli2@LW)eP+Ez+;k59x1fk*?ZiJnDIYU*nz^k9l5Au&t{0)1LQk%daLGIgYhI zp^FGIqhVwKCORsQuseJ@hG`+&>maj(s#V*HO??DXU$l7ks-i<-@ZNJZ7y3*Ju6jqF zEIh6I^qOfh=cA7j+K=TO4D}^buMu$PgdD?6`|3D`C+(}FV_F0WUxzOffA#FY8&bXH F{{!Z#4HW2O-2rMfJ3|!vkiK$1XkY}b_R>Y zLo0&&OiGV(@ye6zOudHlgFdf&0p)J!3C#IPb%Lr?S!Wbs9~jt#f~`@q2ewW%Y_G?o z*ZMB-z^^r`*d?u0)oLq>RwqK(hYUp5vIjVKh<9QoC_<#A5x_WzN82Py#lQq5AIUfP zt{q^H!(JZ(t0>Vn*+G7#ORq_qu%WL)lUUmF){`Y6cp>10@Jfb4kMC^3(uG~!m{agNs3u@;|l82f+94a4VwDgEonQ~kF&r7v4X-0!-9S7)vp54dif(?(7CS=as0 z@M=j$3}Wq1=pcf~Dd>>TDHvCPzNE@7Z;D;)U_uJ{sT-- zgdP=~$?m}?3y-Ki-DZl+MgNP0_Dk^qL-n=EcL=z1LQY_;b#t7+T z2YAD1ykbl*#=oQ_9LOe|j0bKt^*}iJhGkm_yv}}oZ{EB&-+Z(CJ(n8+w6Q-ctB(l4 zZyBVfpE!Zsx_sqS}FkrUI5j$f$EIxssL?#60yQdus~B6;;9gdNC0bS}G#b8UPQD?tGwC5-^4fZy39QK|+eDA^!i z;hVCHJ=*;~1eQ^wP4WQ=md?B;X+ld~h9)tU#dTwW^T6-}BOK^p3HNk3cW5R@_W*8?M&x*Fie2R-+`kw7pcu)=^r;NJV^B72h2Tr-_ z+pYWe?B}f9sMt$u(Q4q^C68CbNZ8GQzhhk3cnNNWaVLrGb1a#_4N_sJ!?ie*Ci(i^ zE7hOwFOVk^ZxFVitEidcVSaKiU)awVI@!YU;n?jikW6s^huQ0$i3dMlBjDJ}&h2OC zI#-Qjw62P^_%Ft>|Hs@od>WZmzf8=izw}vk(bVE$$MM}dbDVg@aq0nW)TEzvoc9g4 zmSp%S*8YGVBJiAqp%gT9L;+!||LZE6j%+Uj&!<(3rXFkZ9=Lqt;;DA_1Uxw*2Qb;WI1b=R=i=yUIzhsh;q$A% Ma`xX9snhNM1B#~^hHPfs26t!HNmkvS7U{un zupdBBN^gDzzd!{K67b{~SXoa!`DU`&+ENqpo8Oyx?>F!L$mdpT4a~DO-ur^fFB>et zTdeaC$rK!T1{@B6lTQx75q|ZP0C#pFaJ98L}(ak+!&aTlw+ z>hg|OnJ9{sEB!bvMAnoxGA@$&ik0DjB_uwCLsEH#=)WvXa(MFQ$pe}Q$#W&QWk%DU zR$W!HF3QnO42E@EYyLXpviSDRJcgE$Ol5{uI`LHbLTcuwKUC==))1J_*j_L5U zNl{QEyfGYC0b{w$OUCxEott8#IWaaBmk=iKBUu07&5^>H8O?ll#t_LA%75lZt1&uS z?z|_F6OZm-Uek}h9fk7iM-}FSVP6b{EYZhMs<25wS3}!wM5?Q?Dz}?F!Jlfav^LJx zHtGOrwc1qb{p;t4S|0hP=nBFFeztEO-+KP7-TB_`90wh3{--$djW;qK2*#>4#s-Ry z`i8GF_Ii}|>l3|-xw>V*2-;HHgf@OyFrydu5hlxb<`BO%gC(pG{{=DeMObSZxi})V m?AfbMf`@f;kF=S`-nWTl<`P0?j!%s0#rWIq|M}0Uz4eI}(xxp?MDcBEdz`9YsIynu2y^aSSoq&h)Ws;oPLuni1sLc!K3*#cXq8q(|W z$OYb69{BZU4O!AkRjsz7WOXBiZOA}$A-jWfyLcy7ff7Vo8Uc)fxVu53v>BM7kPoP@a^|&23s&_-Xc0@BU%x*m1TO}>7@oJG*wb4sTDnlH#F@EO zpM_D42eBE7S`b-UY)Dpr9TO&%vSRV%-E7&hSwQinmx1qi%Ys*VG)||$HTQaljg+w zz3W;(-pi}~$X5uP(9!hF)L~(CuTa`Al-k+S@!`^@4A84V6GbvxNd{fW?lJN*L~ac z>PbcnV(ky;B7(>%=u5#sN0tyadsjy@OjLUnL?Nx3x6Ig3ry$hl7LQ$SYOgW)128!r zdQ@;GPftBrIInn4$&{Fr{wIm-r{Wfd>Qj?T2)J`X4q&u>c^trW`|{`*CPBeh;q&xg MHT&W63`NsE!mC~BLP2RMeSHI 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 O-d{EQ@0QfX_WuDm^cGbB literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/padroes.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/padroes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7486a1034c323ecb52c6cc6eb85428724a681e49 GIT binary patch literal 2374 zcmbtVO=ufO6rNqJ){-SzZc^DwtpqjD29ZQ*Xl-2M2%Nb2YwU(N$w8rQqtp7@%61WyHKZe5Fp(wX1xw6}0=Gr51Nnd@N@v3wjWTbs3&M+&?etp+~`dL=Qhg`KaCxGNwo7P$v2>Uoazj z@@?f3(Xlst+o+HkvSe7eY?En&V$C3B)r2Y08KRSKDwwL8O1yzNTeYjKV`l^}Vi9Ij zl`1AFfqC>2DcksgO|F=vsG7I1ZZIzaziZ&Rs+!6YHi>5FDy%B235WoXc)Xxo*)|Gf-lB+7-?ub%aT)rxY(lz|wWFFpe5OwtaShkrMX@2r~GjU=o9E{3as3n5(HcUVo7oFAx za~ww2kS%mRAgDN9u*F?_v8z7?_+Ri8_{|5M@{gH-9}DF$dGT9BSJ||oLh=o&GWr3G z(hs>f!oLr6_EAwO-{mk24#2~bAnIr{GO|9kesAOCm+r}HPUN~9x!#bj`~UZBfG3BC zvi&!N-n#+28zMq`f``d3;AypA_sr>x@vT1vZ!fxF^jfRv5T3<-z66^WTDu*>Z=lz2 zueB;3!n3r`S3nOwLbbp&dJ0kRBDpHrJQwYz%Qdvfi_&jDBfYOCfDF~dp3!hkg3& zY^INLRA>l<7cF7C8=e;ci}J)#eg)AJATmLEjO!n9F#^I1&8w!(Nyr;06ALq?V>9lS zGh_=5@8|&t&{HsnlL6ZR!B6_|M)I_q9CwlvZgS#XGWRx_d$r^wFT2UhPUMOkxl*6r zjGuJl=bZR?H-7$IeDrO6^yQ)xpK{|<^{bn)k&Vf#^cPU?o6 zy5YoT+}KS0^8Xz)#_l-ryKek${c6)k{pD4_*-2b*6Bp{Un^L?nxUz0PS%w$%mkX`; zpP&A9x}`OY`)y@##gQJm(!)ll`)QiZ40uttpi-=7ak*+SzkGahY@yFl_7c6kp9NN* zH%dPEUaI{!XL^2tDkc90RluXcTFDE}n?$v}fB|;_FLIwoeT4V%?M`UYKQcH+&%!)j z;OsSst$-j1O*Gy(s5jAQ?(u)W=xm{T{nW=^*JKLEJiBgLc z|A5{sExlTbm(sr^f(KdfRC@51k{-lU-?(m6^zH1o@6Fry=9_QkdoDKwXbXQ<*Jla9 zZyls&T{yiGI=jGtkvcSpLkKW~(Yn!~4kci~OJL>>FpHBt3b2oFVvfn~l))Z2!Omcl zcw||MDp$&9LO$~qKheM8{BTt?{D28B^d;u1Np*s{R9o*Z!Y*)d2m{ApWCt9|3~W#1 zu`7L-lNM>2F4gzVKS41#P+lEn*u>n-3SI6oo((!b=Sb3E5J?rcy5vXBOL49!510 z#8xP4L1Y`Tsd)WOOt{jt3Wc-w^OEcEfZ&z&CDI=Cl2z(!+f!mEuJ2a-0uR(%w589cC0f#^HT$kgY4{KcD8fdI7RDH zti`Vy#r_|2qwr~Ho_-mhroXItx@4R2pyvgCgL_^)o4^!izlzk*%t;cJy%nq&!ph${JfKe z$913cW}3{|;Nyh$6L}9qeZ$l$0`8oUBbex1A4l-CbA5D8iy+~f@Ok8~p8a=A>U{bC Drzs5O literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/parametrizando.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/manager_worker/processadores/__pycache__/parametrizando.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93dcb84e76298e16f65098df278aa1791f0a7714 GIT binary patch literal 1068 zcmZ`&&r2IY6n?X_Ta6@YO)2f65Q#nPp&m-9AZqCkDZPl0liMxJW@oCwWM@0G5|vt{ z_y_bRv=n->6mLEC&xqhb7CiOjEhRmOr@nFB#Grkd{qo+-yl=jFZ})qlFa&51?(>y- z0`OZ0nOPT3XBwSdV8BQn8pI(4n89e>Xi$d|FyJLHa|f8k$sPsR$0s$%WH%~c51b$~ z*d!TQlp^3t`Ao=XzTzi(8_o|0qTz>3c#$u0El9Id)T!D!w-9!LgF_fN1|vJ*P-Y-K zP3~UeT||lok7aFBGX`0@rqK7%+Bz}9F61D-w7Q3L`}m|*filEK76D9vythT-tRlF; z$b0eyz8ZVTvESt(u!MEmA|Fs><-%){CalvXXcF64*qoS`Qj|hbih8vu^^2Y^*;MMI zlHB}yz@xY(!o-SXEsSj=F%_@BO$b+7Ua54p{e0SWc*tjrJW9|>=Ac8Dt7|6gxN0kw_yT7(!TB!Ca$YPeYU|Wf)`yut)mrV4Rw_6ze zD7c!8d?p1~=lxF)9@9MMWLnJG;7lrePTs~)zj^8n0^XdEBN%V>k0W@}>K|>>A}IJO Pd>i?rXa8QabbkCl3i%rW literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__init__.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__init__.py new file mode 100644 index 000000000..84952a816 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__init__.py @@ -0,0 +1 @@ +# init \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/__init__.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bba6d0d2aee4d31e91d38d96bb91ab24e049f0e5 GIT binary patch literal 209 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 z%0A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 UUx3QYW40|bj}5lEWcUsJe^xy(W&i*H literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/experimental.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/__pycache__/experimental.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3976f2ddc32213c96dfd59ea65457af9ddb3084a GIT binary patch literal 18921 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 o2?#$ZGO$QpV3zzK12hS29OPJ4BNj( y(b,4c,w/2,h/2) + return torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1) + + +class Concat(nn.Module): + def __init__(self, dimension=1): + super(Concat, self).__init__() + self.d = dimension + + def forward(self, x): + return torch.cat(x, self.d) + + +class Chuncat(nn.Module): + def __init__(self, dimension=1): + super(Chuncat, self).__init__() + self.d = dimension + + def forward(self, x): + x1 = [] + x2 = [] + for xi in x: + xi1, xi2 = xi.chunk(2, self.d) + x1.append(xi1) + x2.append(xi2) + return torch.cat(x1+x2, self.d) + + +class Shortcut(nn.Module): + def __init__(self, dimension=0): + super(Shortcut, self).__init__() + self.d = dimension + + def forward(self, x): + return x[0]+x[1] + + +class Foldcut(nn.Module): + def __init__(self, dimension=0): + super(Foldcut, self).__init__() + self.d = dimension + + def forward(self, x): + x1, x2 = x.chunk(2, self.d) + return x1+x2 + + +class Conv(nn.Module): + # Standard convolution + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super(Conv, self).__init__() + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) + + def forward(self, x): + return self.act(self.bn(self.conv(x))) + + def fuseforward(self, x): + return self.act(self.conv(x)) + + +class RobustConv(nn.Module): + # Robust convolution (use high kernel size 7-11 for: downsampling and other layers). Train for 300 - 450 epochs. + def __init__(self, c1, c2, k=7, s=1, p=None, g=1, act=True, layer_scale_init_value=1e-6): # ch_in, ch_out, kernel, stride, padding, groups + super(RobustConv, self).__init__() + self.conv_dw = Conv(c1, c1, k=k, s=s, p=p, g=c1, act=act) + self.conv1x1 = nn.Conv2d(c1, c2, 1, 1, 0, groups=1, bias=True) + self.gamma = nn.Parameter(layer_scale_init_value * torch.ones(c2)) if layer_scale_init_value > 0 else None + + def forward(self, x): + x = x.to(memory_format=torch.channels_last) + x = self.conv1x1(self.conv_dw(x)) + if self.gamma is not None: + x = x.mul(self.gamma.reshape(1, -1, 1, 1)) + return x + + +class RobustConv2(nn.Module): + # Robust convolution 2 (use [32, 5, 2] or [32, 7, 4] or [32, 11, 8] for one of the paths in CSP). + def __init__(self, c1, c2, k=7, s=4, p=None, g=1, act=True, layer_scale_init_value=1e-6): # ch_in, ch_out, kernel, stride, padding, groups + super(RobustConv2, self).__init__() + self.conv_strided = Conv(c1, c1, k=k, s=s, p=p, g=c1, act=act) + self.conv_deconv = nn.ConvTranspose2d(in_channels=c1, out_channels=c2, kernel_size=s, stride=s, + padding=0, bias=True, dilation=1, groups=1 + ) + self.gamma = nn.Parameter(layer_scale_init_value * torch.ones(c2)) if layer_scale_init_value > 0 else None + + def forward(self, x): + x = self.conv_deconv(self.conv_strided(x)) + if self.gamma is not None: + x = x.mul(self.gamma.reshape(1, -1, 1, 1)) + return x + + +def DWConv(c1, c2, k=1, s=1, act=True): + # Depthwise convolution + return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act) + + +class GhostConv(nn.Module): + # Ghost Convolution https://github.com/huawei-noah/ghostnet + def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups + super(GhostConv, self).__init__() + c_ = c2 // 2 # hidden channels + self.cv1 = Conv(c1, c_, k, s, None, g, act) + self.cv2 = Conv(c_, c_, 5, 1, None, c_, act) + + def forward(self, x): + y = self.cv1(x) + return torch.cat([y, self.cv2(y)], 1) + + +class Stem(nn.Module): + # Stem + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super(Stem, self).__init__() + c_ = int(c2/2) # hidden channels + self.cv1 = Conv(c1, c_, 3, 2) + self.cv2 = Conv(c_, c_, 1, 1) + self.cv3 = Conv(c_, c_, 3, 2) + self.pool = torch.nn.MaxPool2d(2, stride=2) + self.cv4 = Conv(2 * c_, c2, 1, 1) + + def forward(self, x): + x = self.cv1(x) + return self.cv4(torch.cat((self.cv3(self.cv2(x)), self.pool(x)), dim=1)) + + +class DownC(nn.Module): + # Spatial pyramid pooling layer used in YOLOv3-SPP + def __init__(self, c1, c2, n=1, k=2): + super(DownC, self).__init__() + c_ = int(c1) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c2//2, 3, k) + self.cv3 = Conv(c1, c2//2, 1, 1) + self.mp = nn.MaxPool2d(kernel_size=k, stride=k) + + def forward(self, x): + return torch.cat((self.cv2(self.cv1(x)), self.cv3(self.mp(x))), dim=1) + + +class SPP(nn.Module): + # Spatial pyramid pooling layer used in YOLOv3-SPP + def __init__(self, c1, c2, k=(5, 9, 13)): + super(SPP, self).__init__() + c_ = c1 // 2 # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1) + self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) + + def forward(self, x): + x = self.cv1(x) + return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1)) + + +class Bottleneck(nn.Module): + # Darknet bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super(Bottleneck, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c2, 3, 1, g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class Res(nn.Module): + # ResNet bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super(Res, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c_, 3, 1, g=g) + self.cv3 = Conv(c_, c2, 1, 1) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv3(self.cv2(self.cv1(x))) if self.add else self.cv3(self.cv2(self.cv1(x))) + + +class ResX(Res): + # ResNet bottleneck + def __init__(self, c1, c2, shortcut=True, g=32, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__(c1, c2, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + + +class Ghost(nn.Module): + # Ghost Bottleneck https://github.com/huawei-noah/ghostnet + def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride + super(Ghost, self).__init__() + c_ = c2 // 2 + self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw + DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw + GhostConv(c_, c2, 1, 1, act=False)) # pw-linear + self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False), + Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity() + + def forward(self, x): + return self.conv(x) + self.shortcut(x) + +##### end of basic ##### + + +##### cspnet ##### + +class SPPCSPC(nn.Module): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)): + super(SPPCSPC, self).__init__() + c_ = int(2 * c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(c_, c_, 3, 1) + self.cv4 = Conv(c_, c_, 1, 1) + self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) + self.cv5 = Conv(4 * c_, c_, 1, 1) + self.cv6 = Conv(c_, c_, 3, 1) + self.cv7 = Conv(2 * c_, c2, 1, 1) + + def forward(self, x): + x1 = self.cv4(self.cv3(self.cv1(x))) + y1 = self.cv6(self.cv5(torch.cat([x1] + [m(x1) for m in self.m], 1))) + y2 = self.cv2(x) + return self.cv7(torch.cat((y1, y2), dim=1)) + +class GhostSPPCSPC(SPPCSPC): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)): + super().__init__(c1, c2, n, shortcut, g, e, k) + c_ = int(2 * c2 * e) # hidden channels + self.cv1 = GhostConv(c1, c_, 1, 1) + self.cv2 = GhostConv(c1, c_, 1, 1) + self.cv3 = GhostConv(c_, c_, 3, 1) + self.cv4 = GhostConv(c_, c_, 1, 1) + self.cv5 = GhostConv(4 * c_, c_, 1, 1) + self.cv6 = GhostConv(c_, c_, 3, 1) + self.cv7 = GhostConv(2 * c_, c2, 1, 1) + + +class GhostStem(Stem): + # Stem + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super().__init__(c1, c2, k, s, p, g, act) + c_ = int(c2/2) # hidden channels + self.cv1 = GhostConv(c1, c_, 3, 2) + self.cv2 = GhostConv(c_, c_, 1, 1) + self.cv3 = GhostConv(c_, c_, 3, 2) + self.cv4 = GhostConv(2 * c_, c2, 1, 1) + + +class BottleneckCSPA(nn.Module): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(BottleneckCSPA, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.m(self.cv1(x)) + y2 = self.cv2(x) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class BottleneckCSPB(nn.Module): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(BottleneckCSPB, self).__init__() + c_ = int(c2) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + x1 = self.cv1(x) + y1 = self.m(x1) + y2 = self.cv2(x1) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class BottleneckCSPC(nn.Module): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(BottleneckCSPC, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(c_, c_, 1, 1) + self.cv4 = Conv(2 * c_, c2, 1, 1) + self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.cv3(self.m(self.cv1(x))) + y2 = self.cv2(x) + return self.cv4(torch.cat((y1, y2), dim=1)) + + +class ResCSPA(BottleneckCSPA): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class ResCSPB(BottleneckCSPB): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class ResCSPC(BottleneckCSPC): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class ResXCSPA(ResCSPA): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class ResXCSPB(ResCSPB): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class ResXCSPC(ResCSPC): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Res(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class GhostCSPA(BottleneckCSPA): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Ghost(c_, c_) for _ in range(n)]) + + +class GhostCSPB(BottleneckCSPB): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[Ghost(c_, c_) for _ in range(n)]) + + +class GhostCSPC(BottleneckCSPC): + # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[Ghost(c_, c_) for _ in range(n)]) + +##### end of cspnet ##### + + +##### yolor ##### + +class ImplicitA(nn.Module): + def __init__(self, channel, mean=0., std=.02): + super(ImplicitA, self).__init__() + self.channel = channel + self.mean = mean + self.std = std + self.implicit = nn.Parameter(torch.zeros(1, channel, 1, 1)) + nn.init.normal_(self.implicit, mean=self.mean, std=self.std) + + def forward(self, x): + return self.implicit + x + + +class ImplicitM(nn.Module): + def __init__(self, channel, mean=1., std=.02): + super(ImplicitM, self).__init__() + self.channel = channel + self.mean = mean + self.std = std + self.implicit = nn.Parameter(torch.ones(1, channel, 1, 1)) + nn.init.normal_(self.implicit, mean=self.mean, std=self.std) + + def forward(self, x): + return self.implicit * x + +##### end of yolor ##### + + +##### repvgg ##### + +class RepConv(nn.Module): + # Represented convolution + # https://arxiv.org/abs/2101.03697 + + def __init__(self, c1, c2, k=3, s=1, p=None, g=1, act=True, deploy=False): + super(RepConv, self).__init__() + + self.deploy = deploy + self.groups = g + self.in_channels = c1 + self.out_channels = c2 + + assert k == 3 + assert autopad(k, p) == 1 + + padding_11 = autopad(k, p) - k // 2 + + self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) + + if deploy: + self.rbr_reparam = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=True) + + else: + self.rbr_identity = (nn.BatchNorm2d(num_features=c1) if c2 == c1 and s == 1 else None) + + self.rbr_dense = nn.Sequential( + nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False), + nn.BatchNorm2d(num_features=c2), + ) + + self.rbr_1x1 = nn.Sequential( + nn.Conv2d( c1, c2, 1, s, padding_11, groups=g, bias=False), + nn.BatchNorm2d(num_features=c2), + ) + + def forward(self, inputs): + if hasattr(self, "rbr_reparam"): + return self.act(self.rbr_reparam(inputs)) + + if self.rbr_identity is None: + id_out = 0 + else: + id_out = self.rbr_identity(inputs) + + return self.act(self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out) + + def get_equivalent_kernel_bias(self): + kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense) + kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1) + kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity) + return ( + kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, + bias3x3 + bias1x1 + biasid, + ) + + def _pad_1x1_to_3x3_tensor(self, kernel1x1): + if kernel1x1 is None: + return 0 + else: + return nn.functional.pad(kernel1x1, [1, 1, 1, 1]) + + def _fuse_bn_tensor(self, branch): + if branch is None: + return 0, 0 + if isinstance(branch, nn.Sequential): + kernel = branch[0].weight + running_mean = branch[1].running_mean + running_var = branch[1].running_var + gamma = branch[1].weight + beta = branch[1].bias + eps = branch[1].eps + else: + assert isinstance(branch, nn.BatchNorm2d) + if not hasattr(self, "id_tensor"): + input_dim = self.in_channels // self.groups + kernel_value = np.zeros( + (self.in_channels, input_dim, 3, 3), dtype=np.float32 + ) + for i in range(self.in_channels): + kernel_value[i, i % input_dim, 1, 1] = 1 + self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device) + kernel = self.id_tensor + running_mean = branch.running_mean + running_var = branch.running_var + gamma = branch.weight + beta = branch.bias + eps = branch.eps + std = (running_var + eps).sqrt() + t = (gamma / std).reshape(-1, 1, 1, 1) + return kernel * t, beta - running_mean * gamma / std + + def repvgg_convert(self): + kernel, bias = self.get_equivalent_kernel_bias() + return ( + kernel.detach().cpu().numpy(), + bias.detach().cpu().numpy(), + ) + + def fuse_conv_bn(self, conv, bn): + + std = (bn.running_var + bn.eps).sqrt() + bias = bn.bias - bn.running_mean * bn.weight / std + + t = (bn.weight / std).reshape(-1, 1, 1, 1) + weights = conv.weight * t + + bn = nn.Identity() + conv = nn.Conv2d(in_channels = conv.in_channels, + out_channels = conv.out_channels, + kernel_size = conv.kernel_size, + stride=conv.stride, + padding = conv.padding, + dilation = conv.dilation, + groups = conv.groups, + bias = True, + padding_mode = conv.padding_mode) + + conv.weight = torch.nn.Parameter(weights) + conv.bias = torch.nn.Parameter(bias) + return conv + + def fuse_repvgg_block(self): + if self.deploy: + return + print(f"RepConv.fuse_repvgg_block") + + self.rbr_dense = self.fuse_conv_bn(self.rbr_dense[0], self.rbr_dense[1]) + + self.rbr_1x1 = self.fuse_conv_bn(self.rbr_1x1[0], self.rbr_1x1[1]) + rbr_1x1_bias = self.rbr_1x1.bias + weight_1x1_expanded = torch.nn.functional.pad(self.rbr_1x1.weight, [1, 1, 1, 1]) + + # Fuse self.rbr_identity + if (isinstance(self.rbr_identity, nn.BatchNorm2d) or isinstance(self.rbr_identity, nn.modules.batchnorm.SyncBatchNorm)): + # print(f"fuse: rbr_identity == BatchNorm2d or SyncBatchNorm") + identity_conv_1x1 = nn.Conv2d( + in_channels=self.in_channels, + out_channels=self.out_channels, + kernel_size=1, + stride=1, + padding=0, + groups=self.groups, + bias=False) + identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.to(self.rbr_1x1.weight.data.device) + identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.squeeze().squeeze() + # print(f" identity_conv_1x1.weight = {identity_conv_1x1.weight.shape}") + identity_conv_1x1.weight.data.fill_(0.0) + identity_conv_1x1.weight.data.fill_diagonal_(1.0) + identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.unsqueeze(2).unsqueeze(3) + # print(f" identity_conv_1x1.weight = {identity_conv_1x1.weight.shape}") + + identity_conv_1x1 = self.fuse_conv_bn(identity_conv_1x1, self.rbr_identity) + bias_identity_expanded = identity_conv_1x1.bias + weight_identity_expanded = torch.nn.functional.pad(identity_conv_1x1.weight, [1, 1, 1, 1]) + else: + # print(f"fuse: rbr_identity != BatchNorm2d, rbr_identity = {self.rbr_identity}") + bias_identity_expanded = torch.nn.Parameter( torch.zeros_like(rbr_1x1_bias) ) + weight_identity_expanded = torch.nn.Parameter( torch.zeros_like(weight_1x1_expanded) ) + + + #print(f"self.rbr_1x1.weight = {self.rbr_1x1.weight.shape}, ") + #print(f"weight_1x1_expanded = {weight_1x1_expanded.shape}, ") + #print(f"self.rbr_dense.weight = {self.rbr_dense.weight.shape}, ") + + self.rbr_dense.weight = torch.nn.Parameter(self.rbr_dense.weight + weight_1x1_expanded + weight_identity_expanded) + self.rbr_dense.bias = torch.nn.Parameter(self.rbr_dense.bias + rbr_1x1_bias + bias_identity_expanded) + + self.rbr_reparam = self.rbr_dense + self.deploy = True + + if self.rbr_identity is not None: + del self.rbr_identity + self.rbr_identity = None + + if self.rbr_1x1 is not None: + del self.rbr_1x1 + self.rbr_1x1 = None + + if self.rbr_dense is not None: + del self.rbr_dense + self.rbr_dense = None + + +class RepBottleneck(Bottleneck): + # Standard bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__(c1, c2, shortcut=True, g=1, e=0.5) + c_ = int(c2 * e) # hidden channels + self.cv2 = RepConv(c_, c2, 3, 1, g=g) + + +class RepBottleneckCSPA(BottleneckCSPA): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepBottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class RepBottleneckCSPB(BottleneckCSPB): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[RepBottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class RepBottleneckCSPC(BottleneckCSPC): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepBottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + +class RepRes(Res): + # Standard bottleneck + def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__(c1, c2, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.cv2 = RepConv(c_, c_, 3, 1, g=g) + + +class RepResCSPA(ResCSPA): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepRes(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class RepResCSPB(ResCSPB): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[RepRes(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class RepResCSPC(ResCSPC): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepRes(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class RepResX(ResX): + # Standard bottleneck + def __init__(self, c1, c2, shortcut=True, g=32, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + super().__init__(c1, c2, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.cv2 = RepConv(c_, c_, 3, 1, g=g) + + +class RepResXCSPA(ResXCSPA): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepResX(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class RepResXCSPB(ResXCSPB): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2) # hidden channels + self.m = nn.Sequential(*[RepResX(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + + +class RepResXCSPC(ResXCSPC): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=32, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super().__init__(c1, c2, n, shortcut, g, e) + c_ = int(c2 * e) # hidden channels + self.m = nn.Sequential(*[RepResX(c_, c_, shortcut, g, e=0.5) for _ in range(n)]) + +##### end of repvgg ##### + + +##### transformer ##### + +class TransformerLayer(nn.Module): + # Transformer layer https://arxiv.org/abs/2010.11929 (LayerNorm layers removed for better performance) + def __init__(self, c, num_heads): + super().__init__() + self.q = nn.Linear(c, c, bias=False) + self.k = nn.Linear(c, c, bias=False) + self.v = nn.Linear(c, c, bias=False) + self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads) + self.fc1 = nn.Linear(c, c, bias=False) + self.fc2 = nn.Linear(c, c, bias=False) + + def forward(self, x): + x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x + x = self.fc2(self.fc1(x)) + x + return x + + +class TransformerBlock(nn.Module): + # Vision Transformer https://arxiv.org/abs/2010.11929 + def __init__(self, c1, c2, num_heads, num_layers): + super().__init__() + self.conv = None + if c1 != c2: + self.conv = Conv(c1, c2) + self.linear = nn.Linear(c2, c2) # learnable position embedding + self.tr = nn.Sequential(*[TransformerLayer(c2, num_heads) for _ in range(num_layers)]) + self.c2 = c2 + + def forward(self, x): + if self.conv is not None: + x = self.conv(x) + b, _, w, h = x.shape + p = x.flatten(2) + p = p.unsqueeze(0) + p = p.transpose(0, 3) + p = p.squeeze(3) + e = self.linear(p) + x = p + e + + x = self.tr(x) + x = x.unsqueeze(3) + x = x.transpose(0, 3) + x = x.reshape(b, self.c2, w, h) + return x + +##### end of transformer ##### + + +##### yolov5 ##### + +class Focus(nn.Module): + # Focus wh information into c-space + def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups + super(Focus, self).__init__() + self.conv = Conv(c1 * 4, c2, k, s, p, g, act) + # self.contract = Contract(gain=2) + + def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2) + return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1)) + # return self.conv(self.contract(x)) + + +class SPPF(nn.Module): + # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher + def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13)) + super().__init__() + c_ = c1 // 2 # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_ * 4, c2, 1, 1) + self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2) + + def forward(self, x): + x = self.cv1(x) + y1 = self.m(x) + y2 = self.m(y1) + return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1)) + + +class Contract(nn.Module): + # Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40) + def __init__(self, gain=2): + super().__init__() + self.gain = gain + + def forward(self, x): + N, C, H, W = x.size() # assert (H / s == 0) and (W / s == 0), 'Indivisible gain' + s = self.gain + x = x.view(N, C, H // s, s, W // s, s) # x(1,64,40,2,40,2) + x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40) + return x.view(N, C * s * s, H // s, W // s) # x(1,256,40,40) + + +class Expand(nn.Module): + # Expand channels into width-height, i.e. x(1,64,80,80) to x(1,16,160,160) + def __init__(self, gain=2): + super().__init__() + self.gain = gain + + def forward(self, x): + N, C, H, W = x.size() # assert C / s ** 2 == 0, 'Indivisible gain' + s = self.gain + x = x.view(N, s, s, C // s ** 2, H, W) # x(1,2,2,16,80,80) + x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2) + return x.view(N, C // s ** 2, H * s, W * s) # x(1,16,160,160) + + +class NMS(nn.Module): + # Non-Maximum Suppression (NMS) module + conf = 0.25 # confidence threshold + iou = 0.45 # IoU threshold + classes = None # (optional list) filter by class + + def __init__(self): + super(NMS, self).__init__() + + def forward(self, x): + return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) + + +class autoShape(nn.Module): + # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS + conf = 0.25 # NMS confidence threshold + iou = 0.45 # NMS IoU threshold + classes = None # (optional list) filter by class + + def __init__(self, model): + super(autoShape, self).__init__() + self.model = model.eval() + + def autoshape(self): + print('autoShape already enabled, skipping... ') # model already converted to model.autoshape() + return self + + @torch.no_grad() + def forward(self, imgs, size=640, augment=False, profile=False): + # Inference from various sources. For height=640, width=1280, RGB images example inputs are: + # filename: imgs = 'data/samples/zidane.jpg' + # URI: = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg' + # OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(640,1280,3) + # PIL: = Image.open('image.jpg') # HWC x(640,1280,3) + # numpy: = np.zeros((640,1280,3)) # HWC + # torch: = torch.zeros(16,3,320,640) # BCHW (scaled to size=640, 0-1 values) + # multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images + + t = [time_synchronized()] + p = next(self.model.parameters()) # for device and type + if isinstance(imgs, torch.Tensor): # torch + with amp.autocast(enabled=p.device.type != 'cpu'): + return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference + + # Pre-process + n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images + shape0, shape1, files = [], [], [] # image and inference shapes, filenames + for i, im in enumerate(imgs): + f = f'image{i}' # filename + if isinstance(im, str): # filename or uri + im, f = np.asarray(Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im)), im + elif isinstance(im, Image.Image): # PIL Image + im, f = np.asarray(im), getattr(im, 'filename', f) or f + files.append(Path(f).with_suffix('.jpg').name) + if im.shape[0] < 5: # image in CHW + im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1) + im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input + s = im.shape[:2] # HWC + shape0.append(s) # image shape + g = (size / max(s)) # gain + shape1.append([y * g for y in s]) + imgs[i] = im # update + shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape + x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad + x = np.stack(x, 0) if n > 1 else x[0][None] # stack + x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW + x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32 + t.append(time_synchronized()) + + with amp.autocast(enabled=p.device.type != 'cpu'): + # Inference + y = self.model(x, augment, profile)[0] # forward + t.append(time_synchronized()) + + # Post-process + y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS + for i in range(n): + scale_coords(shape1, y[i][:, :4], shape0[i]) + + t.append(time_synchronized()) + return Detections(imgs, y, files, t, self.names, x.shape) + + +class Detections: + # detections class for YOLOv5 inference results + def __init__(self, imgs, pred, files, times=None, names=None, shape=None): + super(Detections, self).__init__() + d = pred[0].device # device + gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations + self.imgs = imgs # list of images as numpy arrays + self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls) + self.names = names # class names + self.files = files # image filenames + self.xyxy = pred # xyxy pixels + self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels + self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized + self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized + self.n = len(self.pred) # number of images (batch size) + self.t = tuple((times[i + 1] - times[i]) * 1000 / self.n for i in range(3)) # timestamps (ms) + self.s = shape # inference BCHW shape + + def display(self, pprint=False, show=False, save=False, render=False, save_dir=''): + colors = color_list() + for i, (img, pred) in enumerate(zip(self.imgs, self.pred)): + str = f'image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} ' + if pred is not None: + for c in pred[:, -1].unique(): + n = (pred[:, -1] == c).sum() # detections per class + str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string + if show or save or render: + for *box, conf, cls in pred: # xyxy, confidence, class + label = f'{self.names[int(cls)]} {conf:.2f}' + plot_one_box(box, img, label=label, color=colors[int(cls) % 10]) + img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np + if pprint: + print(str.rstrip(', ')) + if show: + img.show(self.files[i]) # show + if save: + f = self.files[i] + img.save(Path(save_dir) / f) # save + print(f"{'Saved' * (i == 0)} {f}", end=',' if i < self.n - 1 else f' to {save_dir}\n') + if render: + self.imgs[i] = np.asarray(img) + + def print(self): + self.display(pprint=True) # print results + print(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {tuple(self.s)}' % self.t) + + def show(self): + self.display(show=True) # show results + + def save(self, save_dir='runs/hub/exp'): + save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/hub/exp') # increment save_dir + Path(save_dir).mkdir(parents=True, exist_ok=True) + self.display(save=True, save_dir=save_dir) # save results + + def render(self): + self.display(render=True) # render results + return self.imgs + + def pandas(self): + # return detections as pandas DataFrames, i.e. print(results.pandas().xyxy[0]) + new = copy(self) # return copy + ca = 'xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class', 'name' # xyxy columns + cb = 'xcenter', 'ycenter', 'width', 'height', 'confidence', 'class', 'name' # xywh columns + for k, c in zip(['xyxy', 'xyxyn', 'xywh', 'xywhn'], [ca, ca, cb, cb]): + a = [[x[:5] + [int(x[5]), self.names[int(x[5])]] for x in x.tolist()] for x in getattr(self, k)] # update + setattr(new, k, [pd.DataFrame(x, columns=c) for x in a]) + return new + + def tolist(self): + # return a list of Detections objects, i.e. 'for result in results.tolist():' + x = [Detections([self.imgs[i]], [self.pred[i]], self.names, self.s) for i in range(self.n)] + for d in x: + for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']: + setattr(d, k, getattr(d, k)[0]) # pop out of list + return x + + def __len__(self): + return self.n + + +class Classify(nn.Module): + # Classification head, i.e. x(b,c1,20,20) to x(b,c2) + def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups + super(Classify, self).__init__() + self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1) + self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1) + self.flat = nn.Flatten() + + def forward(self, x): + z = torch.cat([self.aap(y) for y in (x if isinstance(x, list) else [x])], 1) # cat if list + return self.flat(self.conv(z)) # flatten to x(b,c2) + +##### end of yolov5 ###### + + +##### orepa ##### + +def transI_fusebn(kernel, bn): + gamma = bn.weight + std = (bn.running_var + bn.eps).sqrt() + return kernel * ((gamma / std).reshape(-1, 1, 1, 1)), bn.bias - bn.running_mean * gamma / std + + +class ConvBN(nn.Module): + def __init__(self, in_channels, out_channels, kernel_size, + stride=1, padding=0, dilation=1, groups=1, deploy=False, nonlinear=None): + super().__init__() + if nonlinear is None: + self.nonlinear = nn.Identity() + else: + self.nonlinear = nonlinear + if deploy: + self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, + stride=stride, padding=padding, dilation=dilation, groups=groups, bias=True) + else: + self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, + stride=stride, padding=padding, dilation=dilation, groups=groups, bias=False) + self.bn = nn.BatchNorm2d(num_features=out_channels) + + def forward(self, x): + if hasattr(self, 'bn'): + return self.nonlinear(self.bn(self.conv(x))) + else: + return self.nonlinear(self.conv(x)) + + def switch_to_deploy(self): + kernel, bias = transI_fusebn(self.conv.weight, self.bn) + conv = nn.Conv2d(in_channels=self.conv.in_channels, out_channels=self.conv.out_channels, kernel_size=self.conv.kernel_size, + stride=self.conv.stride, padding=self.conv.padding, dilation=self.conv.dilation, groups=self.conv.groups, bias=True) + conv.weight.data = kernel + conv.bias.data = bias + for para in self.parameters(): + para.detach_() + self.__delattr__('conv') + self.__delattr__('bn') + self.conv = conv + +class OREPA_3x3_RepConv(nn.Module): + + def __init__(self, in_channels, out_channels, kernel_size, + stride=1, padding=0, dilation=1, groups=1, + internal_channels_1x1_3x3=None, + deploy=False, nonlinear=None, single_init=False): + super(OREPA_3x3_RepConv, self).__init__() + self.deploy = deploy + + if nonlinear is None: + self.nonlinear = nn.Identity() + else: + self.nonlinear = nonlinear + + self.kernel_size = kernel_size + self.in_channels = in_channels + self.out_channels = out_channels + self.groups = groups + assert padding == kernel_size // 2 + + self.stride = stride + self.padding = padding + self.dilation = dilation + + self.branch_counter = 0 + + self.weight_rbr_origin = nn.Parameter(torch.Tensor(out_channels, int(in_channels/self.groups), kernel_size, kernel_size)) + nn.init.kaiming_uniform_(self.weight_rbr_origin, a=math.sqrt(1.0)) + self.branch_counter += 1 + + + if groups < out_channels: + self.weight_rbr_avg_conv = nn.Parameter(torch.Tensor(out_channels, int(in_channels/self.groups), 1, 1)) + self.weight_rbr_pfir_conv = nn.Parameter(torch.Tensor(out_channels, int(in_channels/self.groups), 1, 1)) + nn.init.kaiming_uniform_(self.weight_rbr_avg_conv, a=1.0) + nn.init.kaiming_uniform_(self.weight_rbr_pfir_conv, a=1.0) + self.weight_rbr_avg_conv.data + self.weight_rbr_pfir_conv.data + self.register_buffer('weight_rbr_avg_avg', torch.ones(kernel_size, kernel_size).mul(1.0/kernel_size/kernel_size)) + self.branch_counter += 1 + + else: + raise NotImplementedError + self.branch_counter += 1 + + if internal_channels_1x1_3x3 is None: + internal_channels_1x1_3x3 = in_channels if groups < out_channels else 2 * in_channels # For mobilenet, it is better to have 2X internal channels + + if internal_channels_1x1_3x3 == in_channels: + self.weight_rbr_1x1_kxk_idconv1 = nn.Parameter(torch.zeros(in_channels, int(in_channels/self.groups), 1, 1)) + id_value = np.zeros((in_channels, int(in_channels/self.groups), 1, 1)) + for i in range(in_channels): + id_value[i, i % int(in_channels/self.groups), 0, 0] = 1 + id_tensor = torch.from_numpy(id_value).type_as(self.weight_rbr_1x1_kxk_idconv1) + self.register_buffer('id_tensor', id_tensor) + + else: + self.weight_rbr_1x1_kxk_conv1 = nn.Parameter(torch.Tensor(internal_channels_1x1_3x3, int(in_channels/self.groups), 1, 1)) + nn.init.kaiming_uniform_(self.weight_rbr_1x1_kxk_conv1, a=math.sqrt(1.0)) + self.weight_rbr_1x1_kxk_conv2 = nn.Parameter(torch.Tensor(out_channels, int(internal_channels_1x1_3x3/self.groups), kernel_size, kernel_size)) + nn.init.kaiming_uniform_(self.weight_rbr_1x1_kxk_conv2, a=math.sqrt(1.0)) + self.branch_counter += 1 + + expand_ratio = 8 + self.weight_rbr_gconv_dw = nn.Parameter(torch.Tensor(in_channels*expand_ratio, 1, kernel_size, kernel_size)) + self.weight_rbr_gconv_pw = nn.Parameter(torch.Tensor(out_channels, in_channels*expand_ratio, 1, 1)) + nn.init.kaiming_uniform_(self.weight_rbr_gconv_dw, a=math.sqrt(1.0)) + nn.init.kaiming_uniform_(self.weight_rbr_gconv_pw, a=math.sqrt(1.0)) + self.branch_counter += 1 + + if out_channels == in_channels and stride == 1: + self.branch_counter += 1 + + self.vector = nn.Parameter(torch.Tensor(self.branch_counter, self.out_channels)) + self.bn = nn.BatchNorm2d(out_channels) + + self.fre_init() + + nn.init.constant_(self.vector[0, :], 0.25) #origin + nn.init.constant_(self.vector[1, :], 0.25) #avg + nn.init.constant_(self.vector[2, :], 0.0) #prior + nn.init.constant_(self.vector[3, :], 0.5) #1x1_kxk + nn.init.constant_(self.vector[4, :], 0.5) #dws_conv + + + def fre_init(self): + prior_tensor = torch.Tensor(self.out_channels, self.kernel_size, self.kernel_size) + half_fg = self.out_channels/2 + for i in range(self.out_channels): + for h in range(3): + for w in range(3): + if i < half_fg: + prior_tensor[i, h, w] = math.cos(math.pi*(h+0.5)*(i+1)/3) + else: + prior_tensor[i, h, w] = math.cos(math.pi*(w+0.5)*(i+1-half_fg)/3) + + self.register_buffer('weight_rbr_prior', prior_tensor) + + def weight_gen(self): + + weight_rbr_origin = torch.einsum('oihw,o->oihw', self.weight_rbr_origin, self.vector[0, :]) + + weight_rbr_avg = torch.einsum('oihw,o->oihw', torch.einsum('oihw,hw->oihw', self.weight_rbr_avg_conv, self.weight_rbr_avg_avg), self.vector[1, :]) + + weight_rbr_pfir = torch.einsum('oihw,o->oihw', torch.einsum('oihw,ohw->oihw', self.weight_rbr_pfir_conv, self.weight_rbr_prior), self.vector[2, :]) + + weight_rbr_1x1_kxk_conv1 = None + if hasattr(self, 'weight_rbr_1x1_kxk_idconv1'): + weight_rbr_1x1_kxk_conv1 = (self.weight_rbr_1x1_kxk_idconv1 + self.id_tensor).squeeze() + elif hasattr(self, 'weight_rbr_1x1_kxk_conv1'): + weight_rbr_1x1_kxk_conv1 = self.weight_rbr_1x1_kxk_conv1.squeeze() + else: + raise NotImplementedError + weight_rbr_1x1_kxk_conv2 = self.weight_rbr_1x1_kxk_conv2 + + if self.groups > 1: + g = self.groups + t, ig = weight_rbr_1x1_kxk_conv1.size() + o, tg, h, w = weight_rbr_1x1_kxk_conv2.size() + weight_rbr_1x1_kxk_conv1 = weight_rbr_1x1_kxk_conv1.view(g, int(t/g), ig) + weight_rbr_1x1_kxk_conv2 = weight_rbr_1x1_kxk_conv2.view(g, int(o/g), tg, h, w) + weight_rbr_1x1_kxk = torch.einsum('gti,gothw->goihw', weight_rbr_1x1_kxk_conv1, weight_rbr_1x1_kxk_conv2).view(o, ig, h, w) + else: + weight_rbr_1x1_kxk = torch.einsum('ti,othw->oihw', weight_rbr_1x1_kxk_conv1, weight_rbr_1x1_kxk_conv2) + + weight_rbr_1x1_kxk = torch.einsum('oihw,o->oihw', weight_rbr_1x1_kxk, self.vector[3, :]) + + weight_rbr_gconv = self.dwsc2full(self.weight_rbr_gconv_dw, self.weight_rbr_gconv_pw, self.in_channels) + weight_rbr_gconv = torch.einsum('oihw,o->oihw', weight_rbr_gconv, self.vector[4, :]) + + weight = weight_rbr_origin + weight_rbr_avg + weight_rbr_1x1_kxk + weight_rbr_pfir + weight_rbr_gconv + + return weight + + def dwsc2full(self, weight_dw, weight_pw, groups): + + t, ig, h, w = weight_dw.size() + o, _, _, _ = weight_pw.size() + tg = int(t/groups) + i = int(ig*groups) + weight_dw = weight_dw.view(groups, tg, ig, h, w) + weight_pw = weight_pw.squeeze().view(o, groups, tg) + + weight_dsc = torch.einsum('gtihw,ogt->ogihw', weight_dw, weight_pw) + return weight_dsc.view(o, i, h, w) + + def forward(self, inputs): + weight = self.weight_gen() + out = F.conv2d(inputs, weight, bias=None, stride=self.stride, padding=self.padding, dilation=self.dilation, groups=self.groups) + + return self.nonlinear(self.bn(out)) + +class RepConv_OREPA(nn.Module): + + def __init__(self, c1, c2, k=3, s=1, padding=1, dilation=1, groups=1, padding_mode='zeros', deploy=False, use_se=False, nonlinear=nn.SiLU()): + super(RepConv_OREPA, self).__init__() + self.deploy = deploy + self.groups = groups + self.in_channels = c1 + self.out_channels = c2 + + self.padding = padding + self.dilation = dilation + self.groups = groups + + assert k == 3 + assert padding == 1 + + padding_11 = padding - k // 2 + + if nonlinear is None: + self.nonlinearity = nn.Identity() + else: + self.nonlinearity = nonlinear + + if use_se: + self.se = SEBlock(self.out_channels, internal_neurons=self.out_channels // 16) + else: + self.se = nn.Identity() + + if deploy: + self.rbr_reparam = nn.Conv2d(in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=k, stride=s, + padding=padding, dilation=dilation, groups=groups, bias=True, padding_mode=padding_mode) + + else: + self.rbr_identity = nn.BatchNorm2d(num_features=self.in_channels) if self.out_channels == self.in_channels and s == 1 else None + self.rbr_dense = OREPA_3x3_RepConv(in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=k, stride=s, padding=padding, groups=groups, dilation=1) + self.rbr_1x1 = ConvBN(in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=1, stride=s, padding=padding_11, groups=groups, dilation=1) + print('RepVGG Block, identity = ', self.rbr_identity) + + + def forward(self, inputs): + if hasattr(self, 'rbr_reparam'): + return self.nonlinearity(self.se(self.rbr_reparam(inputs))) + + if self.rbr_identity is None: + id_out = 0 + else: + id_out = self.rbr_identity(inputs) + + out1 = self.rbr_dense(inputs) + out2 = self.rbr_1x1(inputs) + out3 = id_out + out = out1 + out2 + out3 + + return self.nonlinearity(self.se(out)) + + + # Optional. This improves the accuracy and facilitates quantization. + # 1. Cancel the original weight decay on rbr_dense.conv.weight and rbr_1x1.conv.weight. + # 2. Use like this. + # loss = criterion(....) + # for every RepVGGBlock blk: + # loss += weight_decay_coefficient * 0.5 * blk.get_cust_L2() + # optimizer.zero_grad() + # loss.backward() + + # Not used for OREPA + def get_custom_L2(self): + K3 = self.rbr_dense.weight_gen() + K1 = self.rbr_1x1.conv.weight + t3 = (self.rbr_dense.bn.weight / ((self.rbr_dense.bn.running_var + self.rbr_dense.bn.eps).sqrt())).reshape(-1, 1, 1, 1).detach() + t1 = (self.rbr_1x1.bn.weight / ((self.rbr_1x1.bn.running_var + self.rbr_1x1.bn.eps).sqrt())).reshape(-1, 1, 1, 1).detach() + + l2_loss_circle = (K3 ** 2).sum() - (K3[:, :, 1:2, 1:2] ** 2).sum() # The L2 loss of the "circle" of weights in 3x3 kernel. Use regular L2 on them. + eq_kernel = K3[:, :, 1:2, 1:2] * t3 + K1 * t1 # The equivalent resultant central point of 3x3 kernel. + l2_loss_eq_kernel = (eq_kernel ** 2 / (t3 ** 2 + t1 ** 2)).sum() # Normalize for an L2 coefficient comparable to regular L2. + return l2_loss_eq_kernel + l2_loss_circle + + def get_equivalent_kernel_bias(self): + kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense) + kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1) + kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity) + return kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, bias3x3 + bias1x1 + biasid + + def _pad_1x1_to_3x3_tensor(self, kernel1x1): + if kernel1x1 is None: + return 0 + else: + return torch.nn.functional.pad(kernel1x1, [1,1,1,1]) + + def _fuse_bn_tensor(self, branch): + if branch is None: + return 0, 0 + if not isinstance(branch, nn.BatchNorm2d): + if isinstance(branch, OREPA_3x3_RepConv): + kernel = branch.weight_gen() + elif isinstance(branch, ConvBN): + kernel = branch.conv.weight + else: + raise NotImplementedError + running_mean = branch.bn.running_mean + running_var = branch.bn.running_var + gamma = branch.bn.weight + beta = branch.bn.bias + eps = branch.bn.eps + else: + if not hasattr(self, 'id_tensor'): + input_dim = self.in_channels // self.groups + kernel_value = np.zeros((self.in_channels, input_dim, 3, 3), dtype=np.float32) + for i in range(self.in_channels): + kernel_value[i, i % input_dim, 1, 1] = 1 + self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device) + kernel = self.id_tensor + running_mean = branch.running_mean + running_var = branch.running_var + gamma = branch.weight + beta = branch.bias + eps = branch.eps + std = (running_var + eps).sqrt() + t = (gamma / std).reshape(-1, 1, 1, 1) + return kernel * t, beta - running_mean * gamma / std + + def switch_to_deploy(self): + if hasattr(self, 'rbr_reparam'): + return + print(f"RepConv_OREPA.switch_to_deploy") + kernel, bias = self.get_equivalent_kernel_bias() + self.rbr_reparam = nn.Conv2d(in_channels=self.rbr_dense.in_channels, out_channels=self.rbr_dense.out_channels, + kernel_size=self.rbr_dense.kernel_size, stride=self.rbr_dense.stride, + padding=self.rbr_dense.padding, dilation=self.rbr_dense.dilation, groups=self.rbr_dense.groups, bias=True) + self.rbr_reparam.weight.data = kernel + self.rbr_reparam.bias.data = bias + for para in self.parameters(): + para.detach_() + self.__delattr__('rbr_dense') + self.__delattr__('rbr_1x1') + if hasattr(self, 'rbr_identity'): + self.__delattr__('rbr_identity') + +##### end of orepa ##### + + +##### swin transformer ##### + +class WindowAttention(nn.Module): + + def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.): + + super().__init__() + self.dim = dim + self.window_size = window_size # Wh, Ww + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim ** -0.5 + + # define a parameter table of relative position bias + self.relative_position_bias_table = nn.Parameter( + torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(self.window_size[0]) + coords_w = torch.arange(self.window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += self.window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 + relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + self.register_buffer("relative_position_index", relative_position_index) + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + + nn.init.normal_(self.relative_position_bias_table, std=.02) + self.softmax = nn.Softmax(dim=-1) + + def forward(self, x, mask=None): + + B_, N, C = x.shape + qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + q = q * self.scale + attn = (q @ k.transpose(-2, -1)) + + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view( + self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + attn = attn + relative_position_bias.unsqueeze(0) + + if mask is not None: + nW = mask.shape[0] + attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) + attn = attn.view(-1, self.num_heads, N, N) + attn = self.softmax(attn) + else: + attn = self.softmax(attn) + + attn = self.attn_drop(attn) + + # print(attn.dtype, v.dtype) + try: + x = (attn @ v).transpose(1, 2).reshape(B_, N, C) + except: + #print(attn.dtype, v.dtype) + x = (attn.half() @ v).transpose(1, 2).reshape(B_, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + +class Mlp(nn.Module): + + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.SiLU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + +def window_partition(x, window_size): + + B, H, W, C = x.shape + assert H % window_size == 0, 'feature map h and w can not divide by window size' + x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) + windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) + return windows + +def window_reverse(windows, window_size, H, W): + + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) + return x + + +class SwinTransformerLayer(nn.Module): + + def __init__(self, dim, num_heads, window_size=8, shift_size=0, + mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., + act_layer=nn.SiLU, norm_layer=nn.LayerNorm): + super().__init__() + self.dim = dim + self.num_heads = num_heads + self.window_size = window_size + self.shift_size = shift_size + self.mlp_ratio = mlp_ratio + # if min(self.input_resolution) <= self.window_size: + # # if window size is larger than input resolution, we don't partition windows + # self.shift_size = 0 + # self.window_size = min(self.input_resolution) + assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" + + self.norm1 = norm_layer(dim) + self.attn = WindowAttention( + dim, window_size=(self.window_size, self.window_size), num_heads=num_heads, + qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + def create_mask(self, H, W): + # calculate attention mask for SW-MSA + img_mask = torch.zeros((1, H, W, 1)) # 1 H W 1 + h_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + w_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + cnt = 0 + for h in h_slices: + for w in w_slices: + img_mask[:, h, w, :] = cnt + cnt += 1 + + mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1 + mask_windows = mask_windows.view(-1, self.window_size * self.window_size) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) + + return attn_mask + + def forward(self, x): + # reshape x[b c h w] to x[b l c] + _, _, H_, W_ = x.shape + + Padding = False + if min(H_, W_) < self.window_size or H_ % self.window_size!=0 or W_ % self.window_size!=0: + Padding = True + # print(f'img_size {min(H_, W_)} is less than (or not divided by) window_size {self.window_size}, Padding.') + pad_r = (self.window_size - W_ % self.window_size) % self.window_size + pad_b = (self.window_size - H_ % self.window_size) % self.window_size + x = F.pad(x, (0, pad_r, 0, pad_b)) + + # print('2', x.shape) + B, C, H, W = x.shape + L = H * W + x = x.permute(0, 2, 3, 1).contiguous().view(B, L, C) # b, L, c + + # create mask from init to forward + if self.shift_size > 0: + attn_mask = self.create_mask(H, W).to(x.device) + else: + attn_mask = None + + shortcut = x + x = self.norm1(x) + x = x.view(B, H, W, C) + + # cyclic shift + if self.shift_size > 0: + shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) + else: + shifted_x = x + + # partition windows + x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C + x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C + + # W-MSA/SW-MSA + attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C + + # merge windows + attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) + shifted_x = window_reverse(attn_windows, self.window_size, H, W) # B H' W' C + + # reverse cyclic shift + if self.shift_size > 0: + x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) + else: + x = shifted_x + x = x.view(B, H * W, C) + + # FFN + x = shortcut + self.drop_path(x) + x = x + self.drop_path(self.mlp(self.norm2(x))) + + x = x.permute(0, 2, 1).contiguous().view(-1, C, H, W) # b c h w + + if Padding: + x = x[:, :, :H_, :W_] # reverse padding + + return x + + +class SwinTransformerBlock(nn.Module): + def __init__(self, c1, c2, num_heads, num_layers, window_size=8): + super().__init__() + self.conv = None + if c1 != c2: + self.conv = Conv(c1, c2) + + # remove input_resolution + self.blocks = nn.Sequential(*[SwinTransformerLayer(dim=c2, num_heads=num_heads, window_size=window_size, + shift_size=0 if (i % 2 == 0) else window_size // 2) for i in range(num_layers)]) + + def forward(self, x): + if self.conv is not None: + x = self.conv(x) + x = self.blocks(x) + return x + + +class STCSPA(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(STCSPA, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformerBlock(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.m(self.cv1(x)) + y2 = self.cv2(x) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class STCSPB(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(STCSPB, self).__init__() + c_ = int(c2) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformerBlock(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + x1 = self.cv1(x) + y1 = self.m(x1) + y2 = self.cv2(x1) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class STCSPC(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(STCSPC, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(c_, c_, 1, 1) + self.cv4 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformerBlock(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.cv3(self.m(self.cv1(x))) + y2 = self.cv2(x) + return self.cv4(torch.cat((y1, y2), dim=1)) + +##### end of swin transformer ##### + + +##### swin transformer v2 ##### + +class WindowAttention_v2(nn.Module): + + def __init__(self, dim, window_size, num_heads, qkv_bias=True, attn_drop=0., proj_drop=0., + pretrained_window_size=[0, 0]): + + super().__init__() + self.dim = dim + self.window_size = window_size # Wh, Ww + self.pretrained_window_size = pretrained_window_size + self.num_heads = num_heads + + self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1))), requires_grad=True) + + # mlp to generate continuous relative position bias + self.cpb_mlp = nn.Sequential(nn.Linear(2, 512, bias=True), + nn.ReLU(inplace=True), + nn.Linear(512, num_heads, bias=False)) + + # get relative_coords_table + relative_coords_h = torch.arange(-(self.window_size[0] - 1), self.window_size[0], dtype=torch.float32) + relative_coords_w = torch.arange(-(self.window_size[1] - 1), self.window_size[1], dtype=torch.float32) + relative_coords_table = torch.stack( + torch.meshgrid([relative_coords_h, + relative_coords_w])).permute(1, 2, 0).contiguous().unsqueeze(0) # 1, 2*Wh-1, 2*Ww-1, 2 + if pretrained_window_size[0] > 0: + relative_coords_table[:, :, :, 0] /= (pretrained_window_size[0] - 1) + relative_coords_table[:, :, :, 1] /= (pretrained_window_size[1] - 1) + else: + relative_coords_table[:, :, :, 0] /= (self.window_size[0] - 1) + relative_coords_table[:, :, :, 1] /= (self.window_size[1] - 1) + relative_coords_table *= 8 # normalize to -8, 8 + relative_coords_table = torch.sign(relative_coords_table) * torch.log2( + torch.abs(relative_coords_table) + 1.0) / np.log2(8) + + self.register_buffer("relative_coords_table", relative_coords_table) + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(self.window_size[0]) + coords_w = torch.arange(self.window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += self.window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 + relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + self.register_buffer("relative_position_index", relative_position_index) + + self.qkv = nn.Linear(dim, dim * 3, bias=False) + if qkv_bias: + self.q_bias = nn.Parameter(torch.zeros(dim)) + self.v_bias = nn.Parameter(torch.zeros(dim)) + else: + self.q_bias = None + self.v_bias = None + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.softmax = nn.Softmax(dim=-1) + + def forward(self, x, mask=None): + + B_, N, C = x.shape + qkv_bias = None + if self.q_bias is not None: + qkv_bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias, requires_grad=False), self.v_bias)) + qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias) + qkv = qkv.reshape(B_, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + # cosine attention + attn = (F.normalize(q, dim=-1) @ F.normalize(k, dim=-1).transpose(-2, -1)) + logit_scale = torch.clamp(self.logit_scale, max=torch.log(torch.tensor(1. / 0.01))).exp() + attn = attn * logit_scale + + relative_position_bias_table = self.cpb_mlp(self.relative_coords_table).view(-1, self.num_heads) + relative_position_bias = relative_position_bias_table[self.relative_position_index.view(-1)].view( + self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + relative_position_bias = 16 * torch.sigmoid(relative_position_bias) + attn = attn + relative_position_bias.unsqueeze(0) + + if mask is not None: + nW = mask.shape[0] + attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) + attn = attn.view(-1, self.num_heads, N, N) + attn = self.softmax(attn) + else: + attn = self.softmax(attn) + + attn = self.attn_drop(attn) + + try: + x = (attn @ v).transpose(1, 2).reshape(B_, N, C) + except: + x = (attn.half() @ v).transpose(1, 2).reshape(B_, N, C) + + x = self.proj(x) + x = self.proj_drop(x) + return x + + def extra_repr(self) -> str: + return f'dim={self.dim}, window_size={self.window_size}, ' \ + f'pretrained_window_size={self.pretrained_window_size}, num_heads={self.num_heads}' + + def flops(self, N): + # calculate flops for 1 window with token length of N + flops = 0 + # qkv = self.qkv(x) + flops += N * self.dim * 3 * self.dim + # attn = (q @ k.transpose(-2, -1)) + flops += self.num_heads * N * (self.dim // self.num_heads) * N + # x = (attn @ v) + flops += self.num_heads * N * N * (self.dim // self.num_heads) + # x = self.proj(x) + flops += N * self.dim * self.dim + return flops + +class Mlp_v2(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.SiLU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +def window_partition_v2(x, window_size): + + B, H, W, C = x.shape + x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) + windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) + return windows + + +def window_reverse_v2(windows, window_size, H, W): + + B = int(windows.shape[0] / (H * W / window_size / window_size)) + x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) + x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) + return x + + +class SwinTransformerLayer_v2(nn.Module): + + def __init__(self, dim, num_heads, window_size=7, shift_size=0, + mlp_ratio=4., qkv_bias=True, drop=0., attn_drop=0., drop_path=0., + act_layer=nn.SiLU, norm_layer=nn.LayerNorm, pretrained_window_size=0): + super().__init__() + self.dim = dim + #self.input_resolution = input_resolution + self.num_heads = num_heads + self.window_size = window_size + self.shift_size = shift_size + self.mlp_ratio = mlp_ratio + #if min(self.input_resolution) <= self.window_size: + # # if window size is larger than input resolution, we don't partition windows + # self.shift_size = 0 + # self.window_size = min(self.input_resolution) + assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" + + self.norm1 = norm_layer(dim) + self.attn = WindowAttention_v2( + dim, window_size=(self.window_size, self.window_size), num_heads=num_heads, + qkv_bias=qkv_bias, attn_drop=attn_drop, proj_drop=drop, + pretrained_window_size=(pretrained_window_size, pretrained_window_size)) + + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp_v2(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + def create_mask(self, H, W): + # calculate attention mask for SW-MSA + img_mask = torch.zeros((1, H, W, 1)) # 1 H W 1 + h_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + w_slices = (slice(0, -self.window_size), + slice(-self.window_size, -self.shift_size), + slice(-self.shift_size, None)) + cnt = 0 + for h in h_slices: + for w in w_slices: + img_mask[:, h, w, :] = cnt + cnt += 1 + + mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1 + mask_windows = mask_windows.view(-1, self.window_size * self.window_size) + attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) + attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) + + return attn_mask + + def forward(self, x): + # reshape x[b c h w] to x[b l c] + _, _, H_, W_ = x.shape + + Padding = False + if min(H_, W_) < self.window_size or H_ % self.window_size!=0 or W_ % self.window_size!=0: + Padding = True + # print(f'img_size {min(H_, W_)} is less than (or not divided by) window_size {self.window_size}, Padding.') + pad_r = (self.window_size - W_ % self.window_size) % self.window_size + pad_b = (self.window_size - H_ % self.window_size) % self.window_size + x = F.pad(x, (0, pad_r, 0, pad_b)) + + # print('2', x.shape) + B, C, H, W = x.shape + L = H * W + x = x.permute(0, 2, 3, 1).contiguous().view(B, L, C) # b, L, c + + # create mask from init to forward + if self.shift_size > 0: + attn_mask = self.create_mask(H, W).to(x.device) + else: + attn_mask = None + + shortcut = x + x = x.view(B, H, W, C) + + # cyclic shift + if self.shift_size > 0: + shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) + else: + shifted_x = x + + # partition windows + x_windows = window_partition_v2(shifted_x, self.window_size) # nW*B, window_size, window_size, C + x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C + + # W-MSA/SW-MSA + attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C + + # merge windows + attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) + shifted_x = window_reverse_v2(attn_windows, self.window_size, H, W) # B H' W' C + + # reverse cyclic shift + if self.shift_size > 0: + x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) + else: + x = shifted_x + x = x.view(B, H * W, C) + x = shortcut + self.drop_path(self.norm1(x)) + + # FFN + x = x + self.drop_path(self.norm2(self.mlp(x))) + x = x.permute(0, 2, 1).contiguous().view(-1, C, H, W) # b c h w + + if Padding: + x = x[:, :, :H_, :W_] # reverse padding + + return x + + def extra_repr(self) -> str: + return f"dim={self.dim}, input_resolution={self.input_resolution}, num_heads={self.num_heads}, " \ + f"window_size={self.window_size}, shift_size={self.shift_size}, mlp_ratio={self.mlp_ratio}" + + def flops(self): + flops = 0 + H, W = self.input_resolution + # norm1 + flops += self.dim * H * W + # W-MSA/SW-MSA + nW = H * W / self.window_size / self.window_size + flops += nW * self.attn.flops(self.window_size * self.window_size) + # mlp + flops += 2 * H * W * self.dim * self.dim * self.mlp_ratio + # norm2 + flops += self.dim * H * W + return flops + + +class SwinTransformer2Block(nn.Module): + def __init__(self, c1, c2, num_heads, num_layers, window_size=7): + super().__init__() + self.conv = None + if c1 != c2: + self.conv = Conv(c1, c2) + + # remove input_resolution + self.blocks = nn.Sequential(*[SwinTransformerLayer_v2(dim=c2, num_heads=num_heads, window_size=window_size, + shift_size=0 if (i % 2 == 0) else window_size // 2) for i in range(num_layers)]) + + def forward(self, x): + if self.conv is not None: + x = self.conv(x) + x = self.blocks(x) + return x + + +class ST2CSPA(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(ST2CSPA, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformer2Block(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.m(self.cv1(x)) + y2 = self.cv2(x) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class ST2CSPB(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(ST2CSPB, self).__init__() + c_ = int(c2) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c_, c_, 1, 1) + self.cv3 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformer2Block(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + x1 = self.cv1(x) + y1 = self.m(x1) + y2 = self.cv2(x1) + return self.cv3(torch.cat((y1, y2), dim=1)) + + +class ST2CSPC(nn.Module): + # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks + def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion + super(ST2CSPC, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, 1, 1) + self.cv2 = Conv(c1, c_, 1, 1) + self.cv3 = Conv(c_, c_, 1, 1) + self.cv4 = Conv(2 * c_, c2, 1, 1) + num_heads = c_ // 32 + self.m = SwinTransformer2Block(c_, c_, num_heads, n) + #self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + + def forward(self, x): + y1 = self.cv3(self.m(self.cv1(x))) + y2 = self.cv2(x) + return self.cv4(torch.cat((y1, y2), dim=1)) + +##### end of swin transformer v2 ##### diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/experimental.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/experimental.py new file mode 100644 index 000000000..735d7aa0e --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/experimental.py @@ -0,0 +1,272 @@ +import numpy as np +import random +import torch +import torch.nn as nn + +from models.common import Conv, DWConv +from utils.google_utils import attempt_download + + +class CrossConv(nn.Module): + # Cross Convolution Downsample + def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): + # ch_in, ch_out, kernel, stride, groups, expansion, shortcut + super(CrossConv, self).__init__() + c_ = int(c2 * e) # hidden channels + self.cv1 = Conv(c1, c_, (1, k), (1, s)) + self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) + self.add = shortcut and c1 == c2 + + def forward(self, x): + return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) + + +class Sum(nn.Module): + # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070 + def __init__(self, n, weight=False): # n: number of inputs + super(Sum, self).__init__() + self.weight = weight # apply weights boolean + self.iter = range(n - 1) # iter object + if weight: + self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights + + def forward(self, x): + y = x[0] # no weight + if self.weight: + w = torch.sigmoid(self.w) * 2 + for i in self.iter: + y = y + x[i + 1] * w[i] + else: + for i in self.iter: + y = y + x[i + 1] + return y + + +class MixConv2d(nn.Module): + # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 + def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): + super(MixConv2d, self).__init__() + groups = len(k) + if equal_ch: # equal c_ per group + i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices + c_ = [(i == g).sum() for g in range(groups)] # intermediate channels + else: # equal weight.numel() per group + b = [c2] + [0] * groups + a = np.eye(groups + 1, groups, k=-1) + a -= np.roll(a, 1, axis=1) + a *= np.array(k) ** 2 + a[0] = 1 + c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b + + self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)]) + self.bn = nn.BatchNorm2d(c2) + self.act = nn.LeakyReLU(0.1, inplace=True) + + def forward(self, x): + return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) + + +class Ensemble(nn.ModuleList): + # Ensemble of models + def __init__(self): + super(Ensemble, self).__init__() + + def forward(self, x, augment=False): + y = [] + for module in self: + y.append(module(x, augment)[0]) + # y = torch.stack(y).max(0)[0] # max ensemble + # y = torch.stack(y).mean(0) # mean ensemble + y = torch.cat(y, 1) # nms ensemble + return y, None # inference, train output + + + + + +class ORT_NMS(torch.autograd.Function): + '''ONNX-Runtime NMS operation''' + @staticmethod + def forward(ctx, + boxes, + scores, + max_output_boxes_per_class=torch.tensor([100]), + iou_threshold=torch.tensor([0.45]), + score_threshold=torch.tensor([0.25])): + device = boxes.device + batch = scores.shape[0] + num_det = random.randint(0, 100) + batches = torch.randint(0, batch, (num_det,)).sort()[0].to(device) + idxs = torch.arange(100, 100 + num_det).to(device) + zeros = torch.zeros((num_det,), dtype=torch.int64).to(device) + selected_indices = torch.cat([batches[None], zeros[None], idxs[None]], 0).T.contiguous() + selected_indices = selected_indices.to(torch.int64) + return selected_indices + + @staticmethod + def symbolic(g, boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold): + return g.op("NonMaxSuppression", boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold) + + +class TRT_NMS(torch.autograd.Function): + '''TensorRT NMS operation''' + @staticmethod + def forward( + ctx, + boxes, + scores, + background_class=-1, + box_coding=1, + iou_threshold=0.45, + max_output_boxes=100, + plugin_version="1", + score_activation=0, + score_threshold=0.25, + ): + batch_size, num_boxes, num_classes = scores.shape + num_det = torch.randint(0, max_output_boxes, (batch_size, 1), dtype=torch.int32) + det_boxes = torch.randn(batch_size, max_output_boxes, 4) + det_scores = torch.randn(batch_size, max_output_boxes) + det_classes = torch.randint(0, num_classes, (batch_size, max_output_boxes), dtype=torch.int32) + return num_det, det_boxes, det_scores, det_classes + + @staticmethod + def symbolic(g, + boxes, + scores, + background_class=-1, + box_coding=1, + iou_threshold=0.45, + max_output_boxes=100, + plugin_version="1", + score_activation=0, + score_threshold=0.25): + out = g.op("TRT::EfficientNMS_TRT", + boxes, + scores, + background_class_i=background_class, + box_coding_i=box_coding, + iou_threshold_f=iou_threshold, + max_output_boxes_i=max_output_boxes, + plugin_version_s=plugin_version, + score_activation_i=score_activation, + score_threshold_f=score_threshold, + outputs=4) + nums, boxes, scores, classes = out + return nums, boxes, scores, classes + + +class ONNX_ORT(nn.Module): + '''onnx module with ONNX-Runtime NMS operation.''' + def __init__(self, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=640, device=None, n_classes=80): + super().__init__() + self.device = device if device else torch.device("cpu") + self.max_obj = torch.tensor([max_obj]).to(device) + self.iou_threshold = torch.tensor([iou_thres]).to(device) + self.score_threshold = torch.tensor([score_thres]).to(device) + self.max_wh = max_wh # if max_wh != 0 : non-agnostic else : agnostic + self.convert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]], + dtype=torch.float32, + device=self.device) + self.n_classes=n_classes + + def forward(self, x): + boxes = x[:, :, :4] + conf = x[:, :, 4:5] + scores = x[:, :, 5:] + if self.n_classes == 1: + scores = conf # for models with one class, cls_loss is 0 and cls_conf is always 0.5, + # so there is no need to multiplicate. + else: + scores *= conf # conf = obj_conf * cls_conf + boxes @= self.convert_matrix + max_score, category_id = scores.max(2, keepdim=True) + dis = category_id.float() * self.max_wh + nmsbox = boxes + dis + max_score_tp = max_score.transpose(1, 2).contiguous() + selected_indices = ORT_NMS.apply(nmsbox, max_score_tp, self.max_obj, self.iou_threshold, self.score_threshold) + X, Y = selected_indices[:, 0], selected_indices[:, 2] + selected_boxes = boxes[X, Y, :] + selected_categories = category_id[X, Y, :].float() + selected_scores = max_score[X, Y, :] + X = X.unsqueeze(1).float() + return torch.cat([X, selected_boxes, selected_categories, selected_scores], 1) + +class ONNX_TRT(nn.Module): + '''onnx module with TensorRT NMS operation.''' + def __init__(self, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=None ,device=None, n_classes=80): + super().__init__() + assert max_wh is None + self.device = device if device else torch.device('cpu') + self.background_class = -1, + self.box_coding = 1, + self.iou_threshold = iou_thres + self.max_obj = max_obj + self.plugin_version = '1' + self.score_activation = 0 + self.score_threshold = score_thres + self.n_classes=n_classes + + def forward(self, x): + boxes = x[:, :, :4] + conf = x[:, :, 4:5] + scores = x[:, :, 5:] + if self.n_classes == 1: + scores = conf # for models with one class, cls_loss is 0 and cls_conf is always 0.5, + # so there is no need to multiplicate. + else: + scores *= conf # conf = obj_conf * cls_conf + num_det, det_boxes, det_scores, det_classes = TRT_NMS.apply(boxes, scores, self.background_class, self.box_coding, + self.iou_threshold, self.max_obj, + self.plugin_version, self.score_activation, + self.score_threshold) + return num_det, det_boxes, det_scores, det_classes + + +class End2End(nn.Module): + '''export onnx or tensorrt model with NMS operation.''' + def __init__(self, model, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=None, device=None, n_classes=80): + super().__init__() + device = device if device else torch.device('cpu') + assert isinstance(max_wh,(int)) or max_wh is None + self.model = model.to(device) + self.model.model[-1].end2end = True + self.patch_model = ONNX_TRT if max_wh is None else ONNX_ORT + self.end2end = self.patch_model(max_obj, iou_thres, score_thres, max_wh, device, n_classes) + self.end2end.eval() + + def forward(self, x): + x = self.model(x) + x = self.end2end(x) + return x + + + + + +def attempt_load(weights, map_location=None): + # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a + model = Ensemble() + for w in weights if isinstance(weights, list) else [weights]: + attempt_download(w) + ckpt = torch.load(w, map_location=map_location) # load + model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model + + # Compatibility updates + for m in model.modules(): + if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]: + m.inplace = True # pytorch 1.7.0 compatibility + elif type(m) is nn.Upsample: + m.recompute_scale_factor = None # torch 1.11.0 compatibility + elif type(m) is Conv: + m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility + + if len(model) == 1: + return model[-1] # return model + else: + print('Ensemble created with %s\n' % weights) + for k in ['names', 'stride']: + setattr(model, k, getattr(model[-1], k)) + return model # return ensemble + + diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/yolo.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/yolo.py new file mode 100644 index 000000000..95a019c6a --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/models/yolo.py @@ -0,0 +1,843 @@ +import argparse +import logging +import sys +from copy import deepcopy + +sys.path.append('./') # to run '$ python *.py' files in subdirectories +logger = logging.getLogger(__name__) +import torch +from models.common import * +from models.experimental import * +from utils.autoanchor import check_anchor_order +from utils.general import make_divisible, check_file, set_logging +from utils.torch_utils import time_synchronized, fuse_conv_and_bn, model_info, scale_img, initialize_weights, \ + select_device, copy_attr +from utils.loss import SigmoidBin + +try: + import thop # for FLOPS computation +except ImportError: + thop = None + + +class Detect(nn.Module): + stride = None # strides computed during build + export = False # onnx export + end2end = False + include_nms = False + concat = False + + def __init__(self, nc=80, anchors=(), ch=()): # detection layer + super(Detect, self).__init__() + self.nc = nc # number of classes + self.no = nc + 5 # number of outputs per anchor + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer('anchors', a) # shape(nl,na,2) + self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv + + def forward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](x[i]) # conv + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + y = x[i].sigmoid() + if not torch.onnx.is_in_onnx_export(): + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + else: + xy, wh, conf = y.split((2, 2, self.nc + 1), 4) # y.tensor_split((2, 4, 5), 4) # torch 1.8.0 + xy = xy * (2. * self.stride[i]) + (self.stride[i] * (self.grid[i] - 0.5)) # new xy + wh = wh ** 2 * (4 * self.anchor_grid[i].data) # new wh + y = torch.cat((xy, wh, conf), 4) + z.append(y.view(bs, -1, self.no)) + + if self.training: + out = x + elif self.end2end: + out = torch.cat(z, 1) + elif self.include_nms: + z = self.convert(z) + out = (z, ) + elif self.concat: + out = torch.cat(z, 1) + else: + out = (torch.cat(z, 1), x) + + return out + + @staticmethod + def _make_grid(nx=20, ny=20): + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + def convert(self, z): + z = torch.cat(z, 1) + box = z[:, :, :4] + conf = z[:, :, 4:5] + score = z[:, :, 5:] + score *= conf + convert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]], + dtype=torch.float32, + device=z.device) + box @= convert_matrix + return (box, score) + + +class IDetect(nn.Module): + stride = None # strides computed during build + export = False # onnx export + end2end = False + include_nms = False + concat = False + + def __init__(self, nc=80, anchors=(), ch=()): # detection layer + super(IDetect, self).__init__() + self.nc = nc # number of classes + self.no = nc + 5 # number of outputs per anchor + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer('anchors', a) # shape(nl,na,2) + self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv + + self.ia = nn.ModuleList(ImplicitA(x) for x in ch) + self.im = nn.ModuleList(ImplicitM(self.no * self.na) for _ in ch) + + def forward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](self.ia[i](x[i])) # conv + x[i] = self.im[i](x[i]) + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = x[i].sigmoid() + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + z.append(y.view(bs, -1, self.no)) + + return x if self.training else (torch.cat(z, 1), x) + + def fuseforward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](x[i]) # conv + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = x[i].sigmoid() + if not torch.onnx.is_in_onnx_export(): + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + else: + xy, wh, conf = y.split((2, 2, self.nc + 1), 4) # y.tensor_split((2, 4, 5), 4) # torch 1.8.0 + xy = xy * (2. * self.stride[i]) + (self.stride[i] * (self.grid[i] - 0.5)) # new xy + wh = wh ** 2 * (4 * self.anchor_grid[i].data) # new wh + y = torch.cat((xy, wh, conf), 4) + z.append(y.view(bs, -1, self.no)) + + if self.training: + out = x + elif self.end2end: + out = torch.cat(z, 1) + elif self.include_nms: + z = self.convert(z) + out = (z, ) + elif self.concat: + out = torch.cat(z, 1) + else: + out = (torch.cat(z, 1), x) + + return out + + def fuse(self): + print("IDetect.fuse") + # fuse ImplicitA and Convolution + for i in range(len(self.m)): + c1,c2,_,_ = self.m[i].weight.shape + c1_,c2_, _,_ = self.ia[i].implicit.shape + self.m[i].bias += torch.matmul(self.m[i].weight.reshape(c1,c2),self.ia[i].implicit.reshape(c2_,c1_)).squeeze(1) + + # fuse ImplicitM and Convolution + for i in range(len(self.m)): + c1,c2, _,_ = self.im[i].implicit.shape + self.m[i].bias *= self.im[i].implicit.reshape(c2) + self.m[i].weight *= self.im[i].implicit.transpose(0,1) + + @staticmethod + def _make_grid(nx=20, ny=20): + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + def convert(self, z): + z = torch.cat(z, 1) + box = z[:, :, :4] + conf = z[:, :, 4:5] + score = z[:, :, 5:] + score *= conf + convert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]], + dtype=torch.float32, + device=z.device) + box @= convert_matrix + return (box, score) + + +class IKeypoint(nn.Module): + stride = None # strides computed during build + export = False # onnx export + + def __init__(self, nc=80, anchors=(), nkpt=17, ch=(), inplace=True, dw_conv_kpt=False): # detection layer + super(IKeypoint, self).__init__() + self.nc = nc # number of classes + self.nkpt = nkpt + self.dw_conv_kpt = dw_conv_kpt + self.no_det=(nc + 5) # number of outputs per anchor for box and class + self.no_kpt = 3*self.nkpt ## number of outputs per anchor for keypoints + self.no = self.no_det+self.no_kpt + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + self.flip_test = False + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer('anchors', a) # shape(nl,na,2) + self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no_det * self.na, 1) for x in ch) # output conv + + self.ia = nn.ModuleList(ImplicitA(x) for x in ch) + self.im = nn.ModuleList(ImplicitM(self.no_det * self.na) for _ in ch) + + if self.nkpt is not None: + if self.dw_conv_kpt: #keypoint head is slightly more complex + self.m_kpt = nn.ModuleList( + nn.Sequential(DWConv(x, x, k=3), Conv(x,x), + DWConv(x, x, k=3), Conv(x, x), + DWConv(x, x, k=3), Conv(x,x), + DWConv(x, x, k=3), Conv(x, x), + DWConv(x, x, k=3), Conv(x, x), + DWConv(x, x, k=3), nn.Conv2d(x, self.no_kpt * self.na, 1)) for x in ch) + else: #keypoint head is a single convolution + self.m_kpt = nn.ModuleList(nn.Conv2d(x, self.no_kpt * self.na, 1) for x in ch) + + self.inplace = inplace # use in-place ops (e.g. slice assignment) + + def forward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + if self.nkpt is None or self.nkpt==0: + x[i] = self.im[i](self.m[i](self.ia[i](x[i]))) # conv + else : + x[i] = torch.cat((self.im[i](self.m[i](self.ia[i](x[i]))), self.m_kpt[i](x[i])), axis=1) + + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + x_det = x[i][..., :6] + x_kpt = x[i][..., 6:] + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + kpt_grid_x = self.grid[i][..., 0:1] + kpt_grid_y = self.grid[i][..., 1:2] + + if self.nkpt == 0: + y = x[i].sigmoid() + else: + y = x_det.sigmoid() + + if self.inplace: + xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i].view(1, self.na, 1, 1, 2) # wh + if self.nkpt != 0: + x_kpt[..., 0::3] = (x_kpt[..., ::3] * 2. - 0.5 + kpt_grid_x.repeat(1,1,1,1,17)) * self.stride[i] # xy + x_kpt[..., 1::3] = (x_kpt[..., 1::3] * 2. - 0.5 + kpt_grid_y.repeat(1,1,1,1,17)) * self.stride[i] # xy + #x_kpt[..., 0::3] = (x_kpt[..., ::3] + kpt_grid_x.repeat(1,1,1,1,17)) * self.stride[i] # xy + #x_kpt[..., 1::3] = (x_kpt[..., 1::3] + kpt_grid_y.repeat(1,1,1,1,17)) * self.stride[i] # xy + #print('=============') + #print(self.anchor_grid[i].shape) + #print(self.anchor_grid[i][...,0].unsqueeze(4).shape) + #print(x_kpt[..., 0::3].shape) + #x_kpt[..., 0::3] = ((x_kpt[..., 0::3].tanh() * 2.) ** 3 * self.anchor_grid[i][...,0].unsqueeze(4).repeat(1,1,1,1,self.nkpt)) + kpt_grid_x.repeat(1,1,1,1,17) * self.stride[i] # xy + #x_kpt[..., 1::3] = ((x_kpt[..., 1::3].tanh() * 2.) ** 3 * self.anchor_grid[i][...,1].unsqueeze(4).repeat(1,1,1,1,self.nkpt)) + kpt_grid_y.repeat(1,1,1,1,17) * self.stride[i] # xy + #x_kpt[..., 0::3] = (((x_kpt[..., 0::3].sigmoid() * 4.) ** 2 - 8.) * self.anchor_grid[i][...,0].unsqueeze(4).repeat(1,1,1,1,self.nkpt)) + kpt_grid_x.repeat(1,1,1,1,17) * self.stride[i] # xy + #x_kpt[..., 1::3] = (((x_kpt[..., 1::3].sigmoid() * 4.) ** 2 - 8.) * self.anchor_grid[i][...,1].unsqueeze(4).repeat(1,1,1,1,self.nkpt)) + kpt_grid_y.repeat(1,1,1,1,17) * self.stride[i] # xy + x_kpt[..., 2::3] = x_kpt[..., 2::3].sigmoid() + + y = torch.cat((xy, wh, y[..., 4:], x_kpt), dim = -1) + + else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953 + xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + if self.nkpt != 0: + y[..., 6:] = (y[..., 6:] * 2. - 0.5 + self.grid[i].repeat((1,1,1,1,self.nkpt))) * self.stride[i] # xy + y = torch.cat((xy, wh, y[..., 4:]), -1) + + z.append(y.view(bs, -1, self.no)) + + return x if self.training else (torch.cat(z, 1), x) + + @staticmethod + def _make_grid(nx=20, ny=20): + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + +class IAuxDetect(nn.Module): + stride = None # strides computed during build + export = False # onnx export + end2end = False + include_nms = False + concat = False + + def __init__(self, nc=80, anchors=(), ch=()): # detection layer + super(IAuxDetect, self).__init__() + self.nc = nc # number of classes + self.no = nc + 5 # number of outputs per anchor + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer('anchors', a) # shape(nl,na,2) + self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch[:self.nl]) # output conv + self.m2 = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch[self.nl:]) # output conv + + self.ia = nn.ModuleList(ImplicitA(x) for x in ch[:self.nl]) + self.im = nn.ModuleList(ImplicitM(self.no * self.na) for _ in ch[:self.nl]) + + def forward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](self.ia[i](x[i])) # conv + x[i] = self.im[i](x[i]) + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + x[i+self.nl] = self.m2[i](x[i+self.nl]) + x[i+self.nl] = x[i+self.nl].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = x[i].sigmoid() + if not torch.onnx.is_in_onnx_export(): + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + else: + xy, wh, conf = y.split((2, 2, self.nc + 1), 4) # y.tensor_split((2, 4, 5), 4) # torch 1.8.0 + xy = xy * (2. * self.stride[i]) + (self.stride[i] * (self.grid[i] - 0.5)) # new xy + wh = wh ** 2 * (4 * self.anchor_grid[i].data) # new wh + y = torch.cat((xy, wh, conf), 4) + z.append(y.view(bs, -1, self.no)) + + return x if self.training else (torch.cat(z, 1), x[:self.nl]) + + def fuseforward(self, x): + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](x[i]) # conv + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = x[i].sigmoid() + if not torch.onnx.is_in_onnx_export(): + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + else: + xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i].data # wh + y = torch.cat((xy, wh, y[..., 4:]), -1) + z.append(y.view(bs, -1, self.no)) + + if self.training: + out = x + elif self.end2end: + out = torch.cat(z, 1) + elif self.include_nms: + z = self.convert(z) + out = (z, ) + elif self.concat: + out = torch.cat(z, 1) + else: + out = (torch.cat(z, 1), x) + + return out + + def fuse(self): + print("IAuxDetect.fuse") + # fuse ImplicitA and Convolution + for i in range(len(self.m)): + c1,c2,_,_ = self.m[i].weight.shape + c1_,c2_, _,_ = self.ia[i].implicit.shape + self.m[i].bias += torch.matmul(self.m[i].weight.reshape(c1,c2),self.ia[i].implicit.reshape(c2_,c1_)).squeeze(1) + + # fuse ImplicitM and Convolution + for i in range(len(self.m)): + c1,c2, _,_ = self.im[i].implicit.shape + self.m[i].bias *= self.im[i].implicit.reshape(c2) + self.m[i].weight *= self.im[i].implicit.transpose(0,1) + + @staticmethod + def _make_grid(nx=20, ny=20): + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + def convert(self, z): + z = torch.cat(z, 1) + box = z[:, :, :4] + conf = z[:, :, 4:5] + score = z[:, :, 5:] + score *= conf + convert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]], + dtype=torch.float32, + device=z.device) + box @= convert_matrix + return (box, score) + + +class IBin(nn.Module): + stride = None # strides computed during build + export = False # onnx export + + def __init__(self, nc=80, anchors=(), ch=(), bin_count=21): # detection layer + super(IBin, self).__init__() + self.nc = nc # number of classes + self.bin_count = bin_count + + self.w_bin_sigmoid = SigmoidBin(bin_count=self.bin_count, min=0.0, max=4.0) + self.h_bin_sigmoid = SigmoidBin(bin_count=self.bin_count, min=0.0, max=4.0) + # classes, x,y,obj + self.no = nc + 3 + \ + self.w_bin_sigmoid.get_length() + self.h_bin_sigmoid.get_length() # w-bce, h-bce + # + self.x_bin_sigmoid.get_length() + self.y_bin_sigmoid.get_length() + + self.nl = len(anchors) # number of detection layers + self.na = len(anchors[0]) // 2 # number of anchors + self.grid = [torch.zeros(1)] * self.nl # init grid + a = torch.tensor(anchors).float().view(self.nl, -1, 2) + self.register_buffer('anchors', a) # shape(nl,na,2) + self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2) + self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv + + self.ia = nn.ModuleList(ImplicitA(x) for x in ch) + self.im = nn.ModuleList(ImplicitM(self.no * self.na) for _ in ch) + + def forward(self, x): + + #self.x_bin_sigmoid.use_fw_regression = True + #self.y_bin_sigmoid.use_fw_regression = True + self.w_bin_sigmoid.use_fw_regression = True + self.h_bin_sigmoid.use_fw_regression = True + + # x = x.copy() # for profiling + z = [] # inference output + self.training |= self.export + for i in range(self.nl): + x[i] = self.m[i](self.ia[i](x[i])) # conv + x[i] = self.im[i](x[i]) + bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) + x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() + + if not self.training: # inference + if self.grid[i].shape[2:4] != x[i].shape[2:4]: + self.grid[i] = self._make_grid(nx, ny).to(x[i].device) + + y = x[i].sigmoid() + y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy + #y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh + + + #px = (self.x_bin_sigmoid.forward(y[..., 0:12]) + self.grid[i][..., 0]) * self.stride[i] + #py = (self.y_bin_sigmoid.forward(y[..., 12:24]) + self.grid[i][..., 1]) * self.stride[i] + + pw = self.w_bin_sigmoid.forward(y[..., 2:24]) * self.anchor_grid[i][..., 0] + ph = self.h_bin_sigmoid.forward(y[..., 24:46]) * self.anchor_grid[i][..., 1] + + #y[..., 0] = px + #y[..., 1] = py + y[..., 2] = pw + y[..., 3] = ph + + y = torch.cat((y[..., 0:4], y[..., 46:]), dim=-1) + + z.append(y.view(bs, -1, y.shape[-1])) + + return x if self.training else (torch.cat(z, 1), x) + + @staticmethod + def _make_grid(nx=20, ny=20): + yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)]) + return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float() + + +class Model(nn.Module): + def __init__(self, cfg='yolor-csp-c.yaml', ch=3, nc=None, anchors=None): # model, input channels, number of classes + super(Model, self).__init__() + self.traced = False + if isinstance(cfg, dict): + self.yaml = cfg # model dict + else: # is *.yaml + import yaml # for torch hub + self.yaml_file = Path(cfg).name + with open(cfg) as f: + self.yaml = yaml.load(f, Loader=yaml.SafeLoader) # model dict + + # Define model + ch = self.yaml['ch'] = self.yaml.get('ch', ch) # input channels + if nc and nc != self.yaml['nc']: + logger.info(f"Overriding model.yaml nc={self.yaml['nc']} with nc={nc}") + self.yaml['nc'] = nc # override yaml value + if anchors: + logger.info(f'Overriding model.yaml anchors with anchors={anchors}') + self.yaml['anchors'] = round(anchors) # override yaml value + self.model, self.save = parse_model(deepcopy(self.yaml), ch=[ch]) # model, savelist + self.names = [str(i) for i in range(self.yaml['nc'])] # default names + # print([x.shape for x in self.forward(torch.zeros(1, ch, 64, 64))]) + + # Build strides, anchors + m = self.model[-1] # Detect() + if isinstance(m, Detect): + s = 256 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + check_anchor_order(m) + m.anchors /= m.stride.view(-1, 1, 1) + self.stride = m.stride + self._initialize_biases() # only run once + # print('Strides: %s' % m.stride.tolist()) + if isinstance(m, IDetect): + s = 256 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + check_anchor_order(m) + m.anchors /= m.stride.view(-1, 1, 1) + self.stride = m.stride + self._initialize_biases() # only run once + # print('Strides: %s' % m.stride.tolist()) + if isinstance(m, IAuxDetect): + s = 256 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))[:4]]) # forward + #print(m.stride) + check_anchor_order(m) + m.anchors /= m.stride.view(-1, 1, 1) + self.stride = m.stride + self._initialize_aux_biases() # only run once + # print('Strides: %s' % m.stride.tolist()) + if isinstance(m, IBin): + s = 256 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + check_anchor_order(m) + m.anchors /= m.stride.view(-1, 1, 1) + self.stride = m.stride + self._initialize_biases_bin() # only run once + # print('Strides: %s' % m.stride.tolist()) + if isinstance(m, IKeypoint): + s = 256 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + check_anchor_order(m) + m.anchors /= m.stride.view(-1, 1, 1) + self.stride = m.stride + self._initialize_biases_kpt() # only run once + # print('Strides: %s' % m.stride.tolist()) + + # Init weights, biases + initialize_weights(self) + self.info() + logger.info('') + + def forward(self, x, augment=False, profile=False): + if augment: + img_size = x.shape[-2:] # height, width + s = [1, 0.83, 0.67] # scales + f = [None, 3, None] # flips (2-ud, 3-lr) + y = [] # outputs + for si, fi in zip(s, f): + xi = scale_img(x.flip(fi) if fi else x, si, gs=int(self.stride.max())) + yi = self.forward_once(xi)[0] # forward + # cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save + yi[..., :4] /= si # de-scale + if fi == 2: + yi[..., 1] = img_size[0] - yi[..., 1] # de-flip ud + elif fi == 3: + yi[..., 0] = img_size[1] - yi[..., 0] # de-flip lr + y.append(yi) + return torch.cat(y, 1), None # augmented inference, train + else: + return self.forward_once(x, profile) # single-scale inference, train + + def forward_once(self, x, profile=False): + y, dt = [], [] # outputs + for m in self.model: + if m.f != -1: # if not from previous layer + x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers + + if not hasattr(self, 'traced'): + self.traced=False + + if self.traced: + if isinstance(m, Detect) or isinstance(m, IDetect) or isinstance(m, IAuxDetect) or isinstance(m, IKeypoint): + break + + if profile: + c = isinstance(m, (Detect, IDetect, IAuxDetect, IBin)) + o = thop.profile(m, inputs=(x.copy() if c else x,), verbose=False)[0] / 1E9 * 2 if thop else 0 # FLOPS + for _ in range(10): + m(x.copy() if c else x) + t = time_synchronized() + for _ in range(10): + m(x.copy() if c else x) + dt.append((time_synchronized() - t) * 100) + print('%10.1f%10.0f%10.1fms %-40s' % (o, m.np, dt[-1], m.type)) + + x = m(x) # run + + y.append(x if m.i in self.save else None) # save output + + if profile: + print('%.1fms total' % sum(dt)) + return x + + def _initialize_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1. + m = self.model[-1] # Detect() module + for mi, s in zip(m.m, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + + def _initialize_aux_biases(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1. + m = self.model[-1] # Detect() module + for mi, mi2, s in zip(m.m, m.m2, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + b2 = mi2.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b2.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b2.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi2.bias = torch.nn.Parameter(b2.view(-1), requires_grad=True) + + def _initialize_biases_bin(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1. + m = self.model[-1] # Bin() module + bc = m.bin_count + for mi, s in zip(m.m, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + old = b[:, (0,1,2,bc+3)].data + obj_idx = 2*bc+4 + b[:, :obj_idx].data += math.log(0.6 / (bc + 1 - 0.99)) + b[:, obj_idx].data += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b[:, (obj_idx+1):].data += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + b[:, (0,1,2,bc+3)].data = old + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + + def _initialize_biases_kpt(self, cf=None): # initialize biases into Detect(), cf is class frequency + # https://arxiv.org/abs/1708.02002 section 3.3 + # cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1. + m = self.model[-1] # Detect() module + for mi, s in zip(m.m, m.stride): # from + b = mi.bias.view(m.na, -1) # conv.bias(255) to (3,85) + b.data[:, 4] += math.log(8 / (640 / s) ** 2) # obj (8 objects per 640 image) + b.data[:, 5:] += math.log(0.6 / (m.nc - 0.99)) if cf is None else torch.log(cf / cf.sum()) # cls + mi.bias = torch.nn.Parameter(b.view(-1), requires_grad=True) + + def _print_biases(self): + m = self.model[-1] # Detect() module + for mi in m.m: # from + b = mi.bias.detach().view(m.na, -1).T # conv.bias(255) to (3,85) + print(('%6g Conv2d.bias:' + '%10.3g' * 6) % (mi.weight.shape[1], *b[:5].mean(1).tolist(), b[5:].mean())) + + # def _print_weights(self): + # for m in self.model.modules(): + # if type(m) is Bottleneck: + # print('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights + + def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers + print('Fusing layers... ') + for m in self.model.modules(): + if isinstance(m, RepConv): + #print(f" fuse_repvgg_block") + m.fuse_repvgg_block() + elif isinstance(m, RepConv_OREPA): + #print(f" switch_to_deploy") + m.switch_to_deploy() + elif type(m) is Conv and hasattr(m, 'bn'): + m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv + delattr(m, 'bn') # remove batchnorm + m.forward = m.fuseforward # update forward + elif isinstance(m, (IDetect, IAuxDetect)): + m.fuse() + m.forward = m.fuseforward + self.info() + return self + + def nms(self, mode=True): # add or remove NMS module + present = type(self.model[-1]) is NMS # last layer is NMS + if mode and not present: + print('Adding NMS... ') + m = NMS() # module + m.f = -1 # from + m.i = self.model[-1].i + 1 # index + self.model.add_module(name='%s' % m.i, module=m) # add + self.eval() + elif not mode and present: + print('Removing NMS... ') + self.model = self.model[:-1] # remove + return self + + def autoshape(self): # add autoShape module + print('Adding autoShape... ') + m = autoShape(self) # wrap model + copy_attr(m, self, include=('yaml', 'nc', 'hyp', 'names', 'stride'), exclude=()) # copy attributes + return m + + def info(self, verbose=False, img_size=640): # print model information + model_info(self, verbose, img_size) + + +def parse_model(d, ch): # model_dict, input_channels(3) + logger.info('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments')) + anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple'] + na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors + no = na * (nc + 5) # number of outputs = anchors * (classes + 5) + + layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out + for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']): # from, number, module, args + m = eval(m) if isinstance(m, str) else m # eval strings + for j, a in enumerate(args): + try: + args[j] = eval(a) if isinstance(a, str) else a # eval strings + except: + pass + + n = max(round(n * gd), 1) if n > 1 else n # depth gain + if m in [nn.Conv2d, Conv, RobustConv, RobustConv2, DWConv, GhostConv, RepConv, RepConv_OREPA, DownC, + SPP, SPPF, SPPCSPC, GhostSPPCSPC, MixConv2d, Focus, Stem, GhostStem, CrossConv, + Bottleneck, BottleneckCSPA, BottleneckCSPB, BottleneckCSPC, + RepBottleneck, RepBottleneckCSPA, RepBottleneckCSPB, RepBottleneckCSPC, + Res, ResCSPA, ResCSPB, ResCSPC, + RepRes, RepResCSPA, RepResCSPB, RepResCSPC, + ResX, ResXCSPA, ResXCSPB, ResXCSPC, + RepResX, RepResXCSPA, RepResXCSPB, RepResXCSPC, + Ghost, GhostCSPA, GhostCSPB, GhostCSPC, + SwinTransformerBlock, STCSPA, STCSPB, STCSPC, + SwinTransformer2Block, ST2CSPA, ST2CSPB, ST2CSPC]: + c1, c2 = ch[f], args[0] + if c2 != no: # if not output + c2 = make_divisible(c2 * gw, 8) + + args = [c1, c2, *args[1:]] + if m in [DownC, SPPCSPC, GhostSPPCSPC, + BottleneckCSPA, BottleneckCSPB, BottleneckCSPC, + RepBottleneckCSPA, RepBottleneckCSPB, RepBottleneckCSPC, + ResCSPA, ResCSPB, ResCSPC, + RepResCSPA, RepResCSPB, RepResCSPC, + ResXCSPA, ResXCSPB, ResXCSPC, + RepResXCSPA, RepResXCSPB, RepResXCSPC, + GhostCSPA, GhostCSPB, GhostCSPC, + STCSPA, STCSPB, STCSPC, + ST2CSPA, ST2CSPB, ST2CSPC]: + args.insert(2, n) # number of repeats + n = 1 + elif m is nn.BatchNorm2d: + args = [ch[f]] + elif m is Concat: + c2 = sum([ch[x] for x in f]) + elif m is Chuncat: + c2 = sum([ch[x] for x in f]) + elif m is Shortcut: + c2 = ch[f[0]] + elif m is Foldcut: + c2 = ch[f] // 2 + elif m in [Detect, IDetect, IAuxDetect, IBin, IKeypoint]: + args.append([ch[x] for x in f]) + if isinstance(args[1], int): # number of anchors + args[1] = [list(range(args[1] * 2))] * len(f) + elif m is ReOrg: + c2 = ch[f] * 4 + elif m is Contract: + c2 = ch[f] * args[0] ** 2 + elif m is Expand: + c2 = ch[f] // args[0] ** 2 + else: + c2 = ch[f] + + m_ = nn.Sequential(*[m(*args) for _ in range(n)]) if n > 1 else m(*args) # module + t = str(m)[8:-2].replace('__main__.', '') # module type + np = sum([x.numel() for x in m_.parameters()]) # number params + m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params + logger.info('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print + save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist + layers.append(m_) + if i == 0: + ch = [] + ch.append(c2) + return nn.Sequential(*layers), sorted(save) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--cfg', type=str, default='yolor-csp-c.yaml', help='model.yaml') + parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') + parser.add_argument('--profile', action='store_true', help='profile model speed') + opt = parser.parse_args() + opt.cfg = check_file(opt.cfg) # check file + set_logging() + device = select_device(opt.device) + + # Create model + model = Model(opt.cfg).to(device) + model.train() + + if opt.profile: + img = torch.rand(1, 3, 640, 640).to(device) + y = model(img, profile=True) + + # Profile + # img = torch.rand(8 if torch.cuda.is_available() else 1, 3, 640, 640).to(device) + # y = model(img, profile=True) + + # Tensorboard + # from torch.utils.tensorboard import SummaryWriter + # tb_writer = SummaryWriter() + # print("Run 'tensorboard --logdir=models/runs' to view tensorboard at http://localhost:6006/") + # tb_writer.add_graph(model.model, img) # add model to tensorboard + # tb_writer.add_image('test', img[0], dataformats='CWH') # add model to tensorboard diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90521a36a1f13a1de55b194f979ed37b8ede68fa GIT binary patch literal 23065 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global_redis.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/contexto_global_redis.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc765fa3c19e0caf91805383df30cd439f5879af GIT binary patch literal 23327 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 lYAFUXE`-A&{~SggP2)_;0b`hT%rK`T`>iQp5 z|6ld{cs!!e@9@!2_Wu!8lz*ad_6rOiUi@2Clus3=F!ew=R5evqCX_XWP5ew@K12PT zPf`9zxAW5`Bz;*wh;ZrQ^nBzJ|ls~#>XxogB- z_i!o6-5~C!hntezE#hu_xM_B$p=9pbFlR;;p<=sJXm<~H2lkc!$RFhI#TV$`r;4Fy zDpRxxrfNPmq4}9l3oySHWC1P2f?Ak`sK>CF-)vc3{otO-e{Arad3dO|S@ZFcVWT^H z0suF%{Qx+h9VDn^CPjFAyR9D@+uI_ty?to1?g5@-+uI*ee*-4j*yXLGPEP0C*wdLQ z(mDO0wZnB@NURyu zAKI;Q`X^Ei{dmC13hNKb3mT#Fxt@D(OEc&bX1mzl-O~5Cxnp*=cXZ3x%2HwF!oyCh zz4hqbcee7zPIqss_PDcewzr(oTU!sMcC0OHU*`tfGD!8-*N+}^=tBZjWPOvMcYgKf z=6lut@|9=)g$p=4*Q@mB7oPdk&L5vU>twt&L65U0Snj9qZA?w~zTclo_ey#41-->LmVzh ztIjwz@hRf?Gyr1pGyv9eOMe%2fF-(LJ6(qp)ltQ$o1>HQO1MVMtGt9fBq(BKlbPo> z!Xi7Iu+lXunUG6T5v}NE-gwYzw`ds+l8e#WC_DgWxmt$?bG+xjz6Z0xT&!UVr$-*S`q)0S6dZ%l> zO6=i`7}z^9P7BVYAT1J@Q0%0nOHmG?$dCFv**LDZDXG#n1?51{`-O}!`+hnRcBG{0o z;vdi;hmP%XYq==cA!=g7-)+QC8}ZXdbg+@;*hq71q&YSMzMTRfKG=v4HsXVg_+TT| zvF89tb!;RCHj)<`vB5^JVKb9CnIkMzfVpa&uvw@Ou9of-Zj{inoNW-U6w#(u$8$N0cUQ_Qgr!2Buu;LgDZ#CA@rnI4<&Bi9-hgtNWTi;_590jqRde<2ug*=W3%??+>A0`iUjJM{9syfaH6%J^A=;P|>(LLM6uF=th#N!~p$ zIhN#ce(TNbXhkhFa@9&9*UaXt6o$?p1FPB6dR7Fpr9!=#72#^FP|sGH1-CJNiEjb0 zXjzERBgT-a8^TaO7Vj0DxSmY))^Rzqku!rUae>A#j1#sB*j6&Ezqy&yBzQl-441Ys7JBL>=!7vq198zC9F$=EHO zN&@2f&<5ytybazsJDhK}j!X)wEt$feCmjcuut6o`NfVQGlj&BzrMEjqi|dw1$ciRC zI^|T|?C3JB;T5ycdSr_D`Ny+-*A%JqVoC4NX6ispo!3ymdz26zh$x=grqyZfdUB&b zk#0EPr*vEI5R5prJR2C?Ljh;Q)xYTL=w`g0FSXrRf^N^YiIn^DSF6{%`QRBE zfbi9ddCF>N_c`I+V*Hs}aKO*$wy=thh$~$z5=A}(n2hvRa5)nhFO3%t2P!XHk46u& zu*En_0BN0yiWmfz=NgpgIyuE9r*b6|b1!AI31?^fu4v7)TC}gO(@~O0lv;b(WXq(Q z=_0mb(uU)#ZraSLaR?FFg6qbJU&=saQyzK2f~zJ-%E28cwpP}6`)_|)`?;)PFWpj3N0q#{ua*$Ze% zwHWAtUkag5FTk8s3pEY+X}=nU>fP|!JX9Zr>IYP8Qca#HUVubYdpe}%0#K6|pn9r> K>~3DpzyAVW3O5e` literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/gps_handler.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/gps_handler.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0747354d48bdcefb46905003b707a311af03fe8e GIT binary patch literal 5323 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mensagem_worker.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mensagem_worker.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91ca8d4694991ab456af5bad340589ea865db2fa GIT binary patch literal 3455 zcmb7HU2NOd6}}`zS+eMquANv$Y=^PiWJGE^#saT#i?vOyIF2!6LyT=vR1mbt%CaR= zy`<(J$bpCTAs7tQ1w3SF9n1rQEJFwEwU2oTut&)tV1a;vVZ$E!rXm{<6noe?q{N7c z19V51=l-92?m6dv_v%kwU7ZAJdVNXnAmpE3rO6`A{OP+;xJxvmF$Gd&(hP%Tw!o%Y zFXz%6OJ<11eM+>*Lza-Q;PWXR(byYA=D)$J%((o|%6eI!&o;ZgtTp)QMX25-I!QAc z0ntpF(^wDyV$-~q&^kV4(oro2wGJ%~Jf?MmXu<9Ig-KU*dNp5Af1(?fngiyNg=|YM zE&^L49DGkHu)Cx}mLd0vj@#JNB%5!Q2w4s!JcP!tf{ri25FjC#1$DNMwaGFwv}1+EN5k`C6(%0rnQpdw~v+9T|p_DmQ7Wv6wKU)bd0IT+DF7e zMQaY&7O5v*+n#C=CO*p?3EjV2*uDArt=(JI*JmK@YYz;+^z|?Oye3?8glpASYH&Eu zpZyfA`3(l+Rf;CaMzNtu=q}H(4Jd$P!7)m!kF+~B>_Qk7IJd$=EpTQf0<~7;lL`;D zASBT3O0c^9TCfnQaADU^laIzXqjvZ65zA;Ak05mXGY2(|{RuEjQuyn2NrpXI-AEMh@cCUZT3H<&B&hUGR!-!VemV z6SL&<7N}y&9aO-z*}UfVwymsW)k0xW&91m{L-8e9v=3EH;&4W>@VrVs(G%DfQ!_IANo>({^_{`ybfiEN)Z>h1r0W_JdP@%riUI}sOQrzUh6*Vbf3k@oAcstj=A%}k#N!x2KJ|&H!gj>P!+D!ge#74&;h^&IL}un?mY#%BE>|ksQ1)!x zQBv0nrm8hj!nax+T$BZzQ0d0tab^p;(eBm&FtA%p*p7eJ_1mtUTrD=_#D=P^ZYqe|K zif0QtRSC7PcmwEo``8NClmc+E83}0-pq7mh*Q5$u%3;)cF;FYycpQ5C;8L9Aam9sDI=q6m`oBVCwi23;{vr5*u zn`D*QVYVwWKfmBZ@%x{>@7+@6(;)L7_qU}KrKV(NGYAvgmY&h%`?(~YWdyIZEq=oVVP4Rh%r)Hlu?hZ}8BkK44i;Bah_ z274-=VjAqpt`t-6>2rE6G)T02tUffnm;XcYi{d?V&)k{utL7JGZD`yX8gCHx>P_xmzi;hNKAO09d+&Cwcg*P>tM-olUFFP{f=AQH=Q$};crpWq)1@5w_kH2rhq>Gvb7;;roI{9Wu_f7?H%m!S>O-C6<$R%Mtv>8plQ eoru*|s*}W)_iIE(8Pw}|{;k*k?^pDCn*0w+hWPsc literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mqtt_handler.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/mqtt_handler.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a0defa9013918550df6baeaf61aacdc1e834869 GIT binary patch literal 6349 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/visualizador_trajetoria.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/shared/__pycache__/visualizador_trajetoria.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed71963be717c8e4e24ee612bc528a3636c73d70 GIT binary patch literal 2875 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/datasets.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/datasets.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9ac63a1e163b6dc2e61c806b1c99d1a874e917a GIT binary patch literal 89503 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

      t9QX!XxJeOQndPX(d&l0TfygOMp#+ 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/loss.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/loss.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8dee6ab47a1d8e389658376672dec1c4909ba5e GIT binary patch literal 94976 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/metrics.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/metrics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a16c449e747efa38521694e1f7b7a16e76e4871 GIT binary patch literal 15503 zcmch8eQX;?mS;EF{HCZ6Q?z8+9_lL=9a**%$sdYsS+Xp}apYLGVke3+9E#ndC{rZW zY{im>oYM^w9BpV3I?$Zy9ebBE>&eKlW}(~5-f4Gpx8B{%tnUlx78;PyH~~H!f;j_s zfO6Tz1^H+1y>7C}rer6xxV`G;>#FLiSD*dfuU-{@SyEyp!2QG7+41g1g81(k$T=$O z`DzY4cLIxG(UuR$ zVH@qFt?;z#h<<{$eL&FmPjm$F2!4tiE}<)F2c&S&F1iAqrL^t};o1ERUwcT2y5!@= zFFqrckSV}~8P*r_V>suq&U*NLbs5MVA^>&%Ihi2iSYzTt9oF$f-I%UAm!61|a|VLQ zsNq5nt^WjgKT`QaU8sv5>bJQxtOCs8# zj6~juVN2caZ${5{jtnzlIv5F`5Bo=a0X8xk;k={17&FohfgX)#G#DP4Iodwb%ZyG3 zMlQ{AvRggH*c&4nYN3F!($30;CrXgRC1C0;Z- z2}|5Ef%TCcP3B8TST%LSk;$f^buh+|=aHH(KqTx5ni;aiXAvf>|`C)3)(> z`Dy(>K4qdb;YgHlIG$M-w`fc4B~s*j%n0;QqAXD!*Tu`P%e@6XuFv^1IA^!HPax)z z8k?wyo4-@KUGZHssgw~j#w&7umRvYT>$Iah2XZD;ID6F6Xj0pXgj1XIJLam4S3(Ua zlvQ&!=mD2z3~;W>E^f2IH(xl%Anc+Iv;m|8StuE3a@qI^%#}y#e2JIF%T59pD5)~; zR7=|ZJCwA0$C7m0N^&SAah2I>W2w)Xd)nUXCEgoNRB^it@?~2WuPSU^JX@2ZSZdj% z{T@c(%ZQi6%P<|bJyD(56F0Dj<5lWtd*b#CN|RM_f(5;J7VrkC$-!3Sx{}-n0d0N8I!Y@O`9y*(AYzuON>t zUh_g8R)q&`Teg1-AIA&%lw|ohzLihu3;C30`IK$v6R*jXCqN{Wb!cU8}l)tsE-bYLcYUqj!rOs?ocno!2?P;G>}!UGMjpLHyfCqWWrob z4kK$W+Ua%=vC~ksNG!-fonr2$aMZ`LzS(AXc&61IVexUqqf`a+_#@#l$fXp9`r7@z zF9aOM*vO>&$e~u1K|vm9IPLX^Vvz3=c!Pe9q1_R=x^92S7mF2?Tabpq5W|XV> z?Rlc12#3YMBj2BgvYjk$@$V&&=TuuzTAq*WEVP~`bcbVa) z*>Jx1hISfSw=V`=B#%Aq4u;*)*+XN#7{{=OSbsRIbR&y8PKH~-{{g}(@Y&6cJAblv z@8m^9j3H)>gQw;F4zB`^o?+@a`=!fXx%w^9aA3~f*P0JWy1rJ(VVn*5Mw!r@?NY%j z3-q@#=jLn$3?$=adD=;aV3>QQ&9h6=g`<)Q`a2e3ImtL34o*!ok|8$ki!zcq6b#3r zK0idvF>E9zS(q@)B-RJbLNz5B{nL|x8YLreW7w!nr zLjICDH=vRs?3-j_5*d^nN~2&p$rBD*?3Z-W8TidgI;bHDH!JDKPbG2!Y70T25GU!zT4Q^m7Wkb!&#|a@B)c!_jWW;+SQ)ktqEN!b z-h}>`C;omvVX-Y(Q@yDU!CWnxs{#2|*QC4eRc5Hn-~-cA)k^JhO>#U*&G-Dhk#JQl zzLTC4Di4a42j?$tSaypR0K=liEx&b?Exem9Pj{y|p`<}9X~>Kwb@M%&rR57V>5BB_ zdv9la_ul2JJVNO~vGm}tdX`^X8C`8y85Ww)h|OnyUH5s@lOF!k(34J~=c?FqHQAaZ zA$$3r^l72|pjduzi>NZ3_!7xYLsFLnjP6X6w+52rMrn0=@YX+Dchs*r>IFxm=x9vp zHyouOF}IkTfrUU)|J3G4)!sUuJf4cBeIL)HW+3mDk#JU~*mUjU^z9St6%A_@4Vgiq z;(%CjAbED9!kHReY`oL_adUd=_Tk0D$+M8&Rh>SYX}o(usBB91Z8}|x=hJN3pXpsH zf6$*fzI1h^TfU}NdY4BYlf1J>aQ2AKo@94&^yWFpU%fXS%UroTD^z)s=eOs7XJrbn z)!G$q+55Pacb*fR=S1f@B|r9mO}g#g@l5x(R>*^N}AgHLz$+#-@o_$rAcAmtE+E4 zd6hR`6U^5{^EL21C(Uu*JS3QhL^CiPdTO?QWV&TakvFXi)^&6Bnz>pq*NEmCnD#b% zauBZ+nH*X;mg-LV@Jg4b=*9l@fXWK(gK;;yQMZ2KT8eL7EKUI$ReZ$BI{O`f%Ng@<1+qel@4y zzn!=|hX<4xC$aQx4y3dk;zq3vCSeNyK$BZtv&HpLzU9M$ zRWHO?XnkSKb$MoJTT2`BW0x>%c;{DT`nZ`kA-d0}g}N4432^-wr?#iUm4UXrgeh)< zl=?9};76w$2PdZ|b7gQ5_bB6TZ8_46Knd{CJ8!=6=KCGZ?tuZstq5D)4ChyHQoXwv za&>P<(nU3D&XpvCCAk^0(d)O$G~4QW_7C6!KXm?a|9_kNpo^`8_&gqa#z63*$I9-3 zr(^@L;RlcthMz=@g@OQ)vh9(qK0kn-=@4R|{g~LmvA*b>%uh1h7-v`o8+k138F*wzm0RP;Yj}!m*#UEq1Q&YFgXX^s9>H-7C(A`P^UaAx)-R(|*BTC)(?hX z?JqBNi}n_U(^Wa4?{4GbyTFZna10>O61~#8GWD-ImahxW6GBO+Skjr)J++l2xzted zG*4A4>HvCCbvYsW)e(rgLomcJNfX05+D7ZapbMs9FbIRd18Sh{v>D=zU>qjEY;1+k zwL~gC3y^dgmvsX^FlNqVXByx2TgANkU%UL2S{Ol~bd5sA{_bj%- z#R+e&+Grahys{L-Qox2rc-8ViA`#xaJSL#Rx{HdT*C@|%7L5on5#^e*?GRv_F~$;% zOnbR;mWg>NiHb3yvHVN+ZAcNrGH{kEc-HLo2E#$l>z%7A>W-G&n;7Og0c4)o1jss` z(PajIW&|WN_|V9I@37!HBD#(wsRbLLJb0KkoK^6!Y*g35!;ysR1*)6?MFOf^R+}^l zhB2+loS1-e6?a|koV?HmQ#+W^4zdkWQ-dNfy;!dhzny=Bzsg-f~s&e zv1%QFr$JjosEMW=Op)*T!Zv4nS7YWJ(-rf}pak9Kv}2V4BieSSO#_NpQ?H%d53om` zL+qb`+BRitXe_Mt&c;P;S(uMDeOLe!^F4ropwLJ-ff!bHYI%mj@@Vt2Mcc!L{i)@v za`^vUW6js~-D@0nYI>qH*Auk0XzoIvJ!ic1%6P%1f@x>dqIP6l!x1;L$2d@(OI4f@-t*f*`b2=&FE60GR;MVp)@D z1OVhAFa!b2YtT&Xk;d5bw$T3eNikucNjo{rK0Xwib1g*1TQ5!1M# zt0F8N3tK*n^lCjhwAVSvMv?t;AkKf?!9|`@*xHGWH$xTl2#q8I_KetO2w?D+a@I z^}QOUhYHK^9Tx_ie;h51VM#Z61b(d&>6HP!qzm}j9xOpe`(wDbfxTDg6bw}oWg~tt zsKd58u$tRf45T!#XMs&@4|F9g1e}D+ov|*p%9n4cU34E=Y5(-Zn){?!c`9k$s07=g zd`+c$7f04B8`mlu6;l9Ncp zUP~mPHC^Vddy`AUtE7Cjt#+@rts2Bbrv*os=;%u7zp$4B?ANF1d*QXJX2E_)v>yUv zAR2NO+7~)gzJ*g?5@ySRO;^og@E4U2YL*6J1yI{2*0!yj1)jB^QLDbkQ0DtCsrHa>RD|N>$=1}XHq6e z{ENB=o~8Z*yWzkSimw|G>ITHRfhRt(?vl7?P)_+z!S?Gf5FV%B7hEriu9uSiPwl%F z2GX^neJ`}f?uO2E)~q`l*PM-l^ML3)u;&9m*@@Lbq3pC066Cn2a{AtT{h7<@3w&7<++tbNQnyfcNC^pY+*6w! z3bV9rR@d?M?Lu{jSlzKj=t|mTa@&w1Q*E2O_oUmv-g)eHd@+vZ&o)>GEM9=cLhpnA zjE#3T!!0_SVa_n!g8TtSpe#Tg#8y; zG{6G|LJX5D>l-JQM6g(n#`cIHRIWi2&^v?$ z6cR0+~)8^!-Nb~@fEGZ6=e}j)MFg(#U?1O07U1P z2>>oAn!1h*2;5EQQ~(A`3l;douBj3B;i0aN)cS&jP@hp30WyOu(bUSU@Md+iB}~%R zWt$clz=Y|{qZ|U9NIe)=O<~h(++w<=Xnz{OXu6WUj7`htg6uoD;Mms0)?A;Zjp5Pktc^e4U zmN2~oZxW^-nee^hyFuO{X7uk6H%QOki+K}W;P?L5pJx8gZ&prcZFpRlNB0c()Sls7 z8+xe2gJgkNaE_7eTre=sc|*S0$Tas1ZENgnK(goGo^=60v5rN;oHqtr@XRwmGLdb0 z6lQ;hBPAJnU~@b6jlQLAY_4P!&l8`G7~Sz$pA|WuHXh&bNdcxyr`*oKr<- zUPS=mmhiK0Aj3mAy+=obu$^m)F}~3V8&;qW_Kai#V-9ZTDux_f&AyLyYEVLRhvSh``8M*$7U)g+ya$)BjcqtpZ zmUA=A5ME0(Q`g`uku^g_=-h6#)$nHctZ4jU=PPh39CIg4d4?(;r? zH?hw^Oalngm08L+>~21HKb6%+`-4}1+4b|Tl~$p?L#*#uuRpa`e+ux0^Yh(LTK}p> zs2>pP2Vi?ss2>uXZza!d);B&lnmm_kOgDafNU*!VsHj{#n7Ovv&R3ifD$a-%XJ8l7 z?)a$hR$pf5!L=p!=OdYsv_I`%i6#34`>Ue;Ro?#UhHLlz`ZRO*;Jt&q%LDhCOf#(V zRO0x`-e1L5Iv&m~&#ex9c5Ur&Z?X?rzkciW)HG_lrO}_iv3m9K;Od1Z0sh?|@WIKA zGktvh>~)!=g8iUqKgin;7QcB_upbufhk5&9c=OSPTNmzA>9_AX?m2i* zmr&L9_)7AEU_UR~&-1za>&?Pv^*ny)4s6sL zgc6Lo65<7iG_|5lTdFn$$HUH|VDsn|pB1uCHv^PhL{2lxkq% z-`H~}R#4XZRg)^b0zIcp&Al+Gv{SQiVgk;Cle^25_I##nA~C?8XaUFk{w*4Omy1lN z36eGzPE^<(h3WrD{RHxOh=tu(1*xzX!hnauuW?w{tc#QRNv-7oA55EEFB2Hdj60vI z#q+K-3Ao6VH!l5@fYXmUFfVEhi?rpZ)P4Q3Roia(AYKVqzkoX$NAav81#N?$;z;5} zIlkMeTe<{s8(=j+8L&;Dsq4-hw8p_X5CzL+#b+p?UOOV);uC^$3kq;{u4RCgDzu{@ zrz{w+f|N!;{~Dw%r`_>9E9}5;ORL#&$|K8Nv=b0&F<-;ZC{uIxw8*?kyOwts^O1#L zl_LBe>exAG%`=p#L+lTM0uBRI5sWG3A+&6=*y#YeUKs;CXMy8q%#9$;jY|dvaZmx4 znFT)o22x1|8JDo%M?WWi*KT$d4p8w&0erPOd-PQztx}WNt2numCBW=PpkNr*Wm^e%+%xBRqnIb zlrYd_zhJ*0+Ar|(jS2{O8R2Z4d|XJE>dDkD9Zi}9bDL-eV^!Nz%J?t(f7}nY58WNp z$EGxS+q!7Y_?F)1PhI1ONBH-?pS^$J6+GfgcZ~ngpswq-uDoCKvOvK}pHQi{Z#xzpnX~g31gc4-n)uu;YoBlIxnq&2cqA+8`ykDP z;Vh<6Io|8X?C#VtJ8!O5mCo_e; zrd9#=6*bEATWV^BGXnOwWoH%n32=Rs=UnnrigsZ8zKfE{D^&^;ihqTX8IOTN3@~&-sjo(4x6q$f_Qe3jaaVZLi zU4daxG>u|L_}36-{}jn8l0QR&>Vy3YAanLY?ZJMCQ52pTXFtUxpCS2oNKlfpe~F}6 z53qlYv8v!#r2gMv9EyGf_%o~rgQ^l>KgZy&k^BZpUhZ29uRH$g*yqO}kiCbl z^4H#8zvf-L<`u5_M7W1W)wd|;gCZ5=sbEg3za&uAB2^6sgm#YkrLvR&=LXoJKH-h}i!I0s~2+ zawe;JqN?zIN_>yk-Wx;(pSw2*`@DQTB`SICy=ByswP4cE5&KEKnfC{1yB-c z(4o`H1k|wGkeVJtj=X_AasrK+ZaPtBn5?tSWYZ@~j{9_#wzA^jG-uu2$J?#5GqcJ` zXV>jBJH6k33y&(0Qaru0g~DIAZrxYiy7#~T{U7&!*Wt+4;rOS0V;4T?)am|~Zjy&7 z;rQ(xy-xRzj@R+}eqEPx)pzOHy`js%u324KxElJo0j`VFQ<$uNcR83}PFF6wI=fu#n%9-buK8X0xN`mOfr74rfx@oBfugP=7T4Hc zJW$eAGEmx8%I;15Wdr41Z$EJc;AokR}Q3PzozS2!*AqsuIam~`AvKtt~GoE@5Z&3e}*r>wT>@DsI`0% z;5vRYUk1N=zL8&r>w11Qd^hlwfE)QHz6ySu_%$e%iLcP+#_M~t_-Z_F;G6kcT%X~$ z@O8Lu=C|@|ac$(c@#}DH;_H!GGryf*kGn0rzEfAf;}I3PyGQ^|?9wWDt|XlMhXw=nhPdhcXdvJf;+9@te}Mk*vtAv$a-r$!*wry|ULAw; z$^|58?)3!*y?%e3>qX=^=l6|!AK8#^JgcRzuScmg1!mzw|KDB!_$IX>{WaZnT}aof z=MC2^0VNcJ7gP0p(UA0xQru<7ci{6;%C}Xgy+-TE1!BoUY@ukS+I!iu;HjK5_!k*Uj?bt7XLv4V&Y7UtE8|PZd>JIr-&l z`*xn~@(%L8p}|9gJ!d_A!qEAlK=*l%-+Q(NH+waX^S;5eSGR3B+v+_(+IRNFvA~6) z!Lyw`f^RtBKYL|Jxa1Z5XGa6Re*f9w{-J=sVR-CI>+^lyLGRUJVb^#=&rtu6(B1Fz z2O6I5AL{Y+`*$^5@Lp{a`p&04QsFC6M*m)bFaBQl_qs`)=x)V1*YS2|#1(0bjNIB9 zbwxYgFNnF`FNy4yOE$z#NClhag3U@mHT*47!FLv@QiKA4NA!_VF~-2?aKE=+FO=fm zPZjr?PzD$`C(1Qm_LM@erbu-FUrg$ja^312=j(ANxm#tx+*UcaRkXAU6$q+nYDRdl zrbdapt$#6^=XC1s>V!22jF&0Kg_T>Vge&<;S@FNjex7Q% zXJ}w}*Z8WH-XQ&)Qu9{<;3Y)cVdac8h7N=dgj=Q#PbYlf&&prV#j^)I0s6lEzVmSt zOWYsNIoY!JXj{vv?tRCP9zW3;H(mDhk9z&6Syniz**!|lW~YlItb;dIioX>A`9J3L z#y0(9oeG+C_9OaKxjxL6D;wp?Jr64nJg7V%RUVQn58r z7+%?n7NG6WX*?3tMX7x(JGA%$q0}j-LT7|Bdf~K>*Lyxa|Tu;ZUF88EE1$!uIBZ+!&yr3R;;slrCkWFipJhJf76z z@Ba?~le*6fie_Gky1rZRLBab)cZyOCYw*FAK_xeKPFSh<{ z>rc1;eEa>Dzij(e+b<7%dO$q&lC=G_y!~`&|K$FqvWnZgVpXw@xx)EEaq}LjtVJ$s zk+|Z?)=*uzZpr4D-u`ye%yW^EXq}W_FXz|CTBQ6`6Ana9hR5Z+HIl7bwpEKlo3X6WImBaJ!2en zj_5LL$WfibialV{LF4Mi`|*RMAM z?~VqBdVAxBUVq#;B=DfcvIfUS;*P-~VZhVhtvvQqEwHAdG?W~LZ0kni!ydnX{2A?i zcaoY*kH*>nn~|J<1HhziF~=3&J9QzHwOG6=nt#h4axCQ*guRh-bF~XbF?YX|yI;=T zFIx61qlR8MNCnpmR=Swu9PbHug#EY?4iKQH!V!SD{(M~T@e}6IwDBTsNm4}>jw56P z{)|+hAtcLq07HlhX6x*wp=$crYXls&eO|q@&&DP15$&n>nez+w(5~-1Ft7Y42 zkz1`aEc(DbRC&pRW<9JqWdnk4`K6G(9O? zuVKQBu0~BEaa>HjA-X59FM5eFKutaQJT>0Tn!f}0N2z-~dYSO!KyET5-r0!yqY)@$ zT$-?Ga!jQj%%ZW1H_>+pT4pWUeyOg%32V@#z7JZ}cl6EhTvd;FuDWAoG7+Tr*OX=1 z+w<0y-#+u*HL0ZPla24%l4{C@Hpfble(ydVpZ$S8XjwT|)H!PFqg0L2T)C6|>uv3Q z|K;v)9vnOW+HQrqX5%7}*cw8YJ{LohJK#u=xkcKEkE;6siE4*;kiNx0kOr2;qklpg zGzS$tgZ$7y+)P)R;20Q59M^aIiH@tRWH?W#hI_n{=+*9_L2vi@p{r@^mZpLUdHZVs zKm-@;R?a!s+r2Y`(T-S_RJc(t+?aa6aD?z8Lf^%-H*mq%bBRrGi3aeV1imJ3n(TIB zj^h>1AmF2a!cm{~=&#`xI+LJ2J8l$)MhAJJ30`n}2K&515xKK^E;q%kg109CzkYAr zeDqLzTX#!~K)5b$>+=RqdanjLiIOl6kFuNkY+(zF>Ko{b8yT4$H}-qZd;8-#RI=`L zNezMixS^*J|E9Ph&>J@ex@lVOCp4EpySONWhFx8E!XQFCi@*O3(DakK&#XnF_4s>R zqXn_*+4{Mb*^Tr0pOh>#eo`*2Zho!P3a@mpl9;xj3V0Fs@XETV`seeAB-7@*^FQk=rM4mE0~79n~N9fDUPyt$E*f z$M?hhxsJJ!dnZ3GxmP0AJ{K}f?p@-{*N%m&!$PDfGCF7YN#`f8h}B=+@Yp z@Ab@Hk}NxA%TCd-@C0;=Nj{MYCwcAfoo z-$l7k5G{Vm;+HLcIF$3Tjov_c57rDdqZDC&HcG{xHqXhJSE3uH@Q+S+k>ax>bN-3( zh#PKHW1IjmX7UI)k}hCDeVX#uju4m|EFbMMxET{%&=^hOnslm27vpY#&`9D48nmg; zW@(8vHa7fPQ)0qQi9_xGloGErJ{z^@31b=c*~D?to^w1Grom z8t@2$XH1}hEY1-_lp$hNF>VP6qh3bEn}M|n&BnO^aJR65 zo|-*=mNJHdER0Qpm*Vvj=p%4}!f-q>U@bJFP;^!{j5}8Y-O7t+lkyHkg+Rv@$RIF#+sFO) z`X}2ZZlBEU6S;i}{CiHnlwbJv=zE>f{5!?B&fPu-wq3vg14O`6%TJvJt6af}tX|FQyPR4-y)LBHlI4m<9_<9X3s%Vu;`^ zymdBv847q(cmr}rUWjG;Z;EA4#B%(eSVZ}-gU{h}XPw&j0BTZ~D>dx#EN9J$QR8PXTAxM`Sm63go$uC z7m|)Weed3)<43z03a&RiqUOUW;*-F~kofd5JZBFOPU`OWi~%)&X=vCKH@yu0@a5*a zdWPP{Ep&UL^3|q9g8#UCJUijl*s$%gKXXK(AxOG+9+9@Wh>!H>-NdLpJzL!*GFEOs}ZxeOWE7y?Clr@Ku#YF zb%YJ!)~H3~>LspT=IY@{Kk(czN8Hgak=rbBn`Le@9O;KWH>{B@(V)m}k+>}~w*?ON zFuW$xB66!FZk5cffBDc{p@M;)-OhNaTu?`>S9(%3QI?6|+ch z9=>*X@-Tb!=Fw|MCyy?2=5HSP`VrAt8SR-oB5~_wZoQa16%38KOC8iV8HU~iUz(L_ zXQY`JvsHI)jFDvqF!!>e>XcGRYWv$)Vecc=V`0OqU=tGmG57>#KNupWK7*QMeG>7V zCcVKVM;X(CX2Wn-uSz>3ECf^fcJQ&SUi1>L;0b`U z-7`EYyo;Iv_i}icT0ciZDS@!3()ElAE+#lp>8Fva1T*@`1`*NfZ1t|bMnBFvm{a{Ijdx6)nlE(*ooN_!6`1J4>c~X zuKf1syQ8Fm$XgFe8xF}E4vDpg@%Vl#fMh=*+fRt>e4IsTeGYnINBDqbE0=BM zB3I7ZE{G(M9F2OZ+l{B`wF4$fpk@7 zw2_Hi&3mONn*fP=6GzaoQW-KrtdK9yLER-QkfN%wp0_9^ohS|#3sA8cw59T~ zsLM4`5-eOf1>s<@FsjDOsbXj`6rY{IB(=dp%px&@sQV7;YuqD<4`i-M); zx6{U%C=cca%QdYmR~up+JtZA`q9Vu%7RYGJ(|&{cU^&t$_S=Ic>YR9+(msWC_@YMM z9xSJ{2JO5fSgsA(YoI3+s{)OwPo@i&p@*yrR)7ta^Bv&Ak5c0rsGiZFLHJsrSryt4 zbfrtAeS?do$#7`vxoR~nru;bZXk813e61Gg!GC z180O|qu@&6Ao5h#Q;IE#8L}2fI|D7rxOqMTLt4=oGR`Du3 z5uDAdg}rcL>T2YPmB^;u!Z+w4A;!M?vv2+Eck3SS{yW^@5OZACf5}JOw>tyMbUB0>`6Et?*;?a0il~$7#`~P z1fV}yUmef(`Jn^@fmM%Jcn87brjy=5|BxWe(v7jVf5;PHGXh5bD-2W;d@%+pD1EQ5 zKh8n9>;h8^1A}(7zh9At8Ap-V4&ft8#~Q$d-9J3!_X@Aky-DygWuI2fy%rmeWNxi4uN5dG``v?O=dC6PVT?F&v(HDBK7e<3=Q^;xkGKfB$pn zXOlW`A}o2~y7y{tZx|%&zd-_qN`CUNWzf&Fd)Qy2ZSbcQ)MIaAVWVriXd85AtfIytQ)P+K`#@ z$-Z_u)En-Y_DSYq*<2iHd0?&(%@vE5oN)D&BV_u_noW!kd+0>CYU-tM$JDCnRggL6 z70+yZ^T_00n(~D_%JXu`x=OagNTw>7TGW5Rm*wR zk98Z2{rcZ9IAy{+G%wohVfzD2BW>l7r%ttfo>vg|L>i-pTib68NO?7KUQNinq z^XfzUKHJq2GKH>$uS8upUKexML=VIc%z5q}5o@;3yXYt8?vyOgE!lEHSEinyex4;J z*~%8ns}Zz?{YGM~caF_U zu-{0uRW7W5Sh)E?;pVvlsc^eoxIJ`Wv0#l{urB733pT_?9u#a2?O!ab67zS4w}iJu zCm=g^o=h zi=3A1HRPN+_T(o^rPa~4I|roFjdJP6$2yba6)+$O-Y|vr;pU~nlE~U<)$R2+hGvGs zq;eJUf7lr-eE*d@uSlym%f*dh<6=#{Sl9#&tbH>V!xv+I1j{c0tHbq*K5`OqPe%jq zz8ZOTas6ho{KZIXwE5QI$l*EPLi2s@e#fWgg$8Nci{Z80WV;*8kkOPr%uma1y*l!24tX!wl|;2#J8UpN|mXVF>hkh=FquEfmZ zs?BrT=Q}=rZoz!N>Zdlb>FE6-(RWd_Kn{ONwp>yipOGp6XWWRwgpKb(c7$$ey=?`P zD7sg2)N+pxxm$=5m%iV9r~8LJ^K0fuepI*6_@nh758WFQ zYxe*8rI+QGc+usRTwdAb4VgZ-IHq$WCdsl|wyb_=sd-?jiJp=y8)VA{(XwID=9)al zI6#j!>Vf&6puD&r6T3{7kCwDhYoTRM;U@GG#MI;h#6C(KnKE;b_0L1H|CKb6L7;F^ zRa(h3X>y$wKdA{=HDRhPH^}T4)GW-4Akmcr`jJ^6CXHHB3jz+1gvle#h|JW7%LoUu zRyp_#W@tt@u-260JGc;;p&S{ZQY;h@))|xTjF73aVp@;yn`XjhgiA3p0;%Z%YBNI@ zfHyO8*fML?62uv4JyA5o#5mdQjI4|s`UPPMZx=TlJt6!{au~hCLp>M9O-E0pWF*2L z<3W9{Kp$J^ra+uO;2jpeM@~B=DxQIEn1_rG`-N{&koO2g3A|6>0|Ng80pi07cL*>d z%-GWpx~<+6w+;;ld;=uRj$8db7rgu^6s6)ORuWKDM0<7fK0(+@QHgZ&Q~OW|6@?;k zF^Z&L=uSl!{xgF7l*0ZN0Q%2mtE1LiyKnFQUh7=R-Q%;zLt~SN7A@HjlQ{Ci+osM< zpCeXq69XarqQym`WXV!2TZ%v2R%Qe1f4|iVGav|=da%aAbh=1c`m#*={TBxqb& zh7HL{78&mWrPwm(r-(x}wCv#R%Q|feL13PjMk)?IXEs+`OX!s9u%M8Y1EqIDUi_5u z^D4#Ks49(Q3P217$jtLH^MmrLG0mNLLQK@b@|c>Err#=`cQ1b{hC~Y5$1MRb?6;tC zBFv*;OgfvMuR?oAB{!vr$>?uzQ9?KJDQpGB#|`k0n=!%UQ8EaiX{y=Wq{x~GLjk2E zr)AI6ZMNSV==S)Bpr)S|?Z(>qeMet;7 z^U&81h0a5~+8jL*^GrhXF(H$sXUD#NEWB3YN@cDT+A0jOgzZt7Qq)OYoy^t2kxss7 zJ|O0_N;$1^PHQUp$&qVq-#q;F!yzG(HF;R#%4LqkSV;T{PgcwX`QTiRsi?g(Ox3WA z)DtTcg$>w^u$)rx6FG!Y5lW9KR)!MBcB|(@4DaksrxUBe^va;(oajp z=T1uXCqvni<&!!wU(;V z_J=6)Tz+I?7;PNjo6n$IKF&K0za}Vs@3b zU9qsPWGU5GO_@@yf?v&7@>SYVWrY;;)F~z1{2IPmTT3kW{N!`>m9ODzR|xA?he^8m zx)n;GG73nB!*^f1LdcYyIvoxn*Ui>vhSgw&qGV{b1oCOU{$i@pYt!Y|GwI%j*^Qa+ z%c%2B?TTE8)!2A8p6w5e@?-01%&a%3iFhTE7ZLgqwG$TM z5q(w2k$!|r+&t>{!n_E=hd~}hJj^-uFUIvJ#!Uk)FYamBI-aducQ!n`b=)23_6_!W z1uuj`mFoul&%ijTemp18-F~byeecBhHWcvmr+gHd7ZLq^_~Uqa593;}!o+Q?;N3k# zJwx5X-{2{fFR`{noFm;)%rmTH29VH=hvGi1OF^s#N=iv?&s%%yO^+npKbj`sA+VD` zH34dp^`^eR_!P&{mOY6$tqI3uTf(t$AfX8R?++$@4(%Dwy40{q_%Tw5+uM43eLX(! zV4&3-fXZhc)qFlc-00~az5sPMgi2_>{t^;3p$yL+(Pw%@lN2VKW1r>Gv*bsg_0e{K z@w@{so~rB|_ImkLg^kmNG5BF*wPm8yxrp8`P0CiGz{$GAU?K}5!Q-r;^&!7~<+GRqnZz&?(6 zIv`@0qAYVUWmz^0ODN1Tx1u!5s2)b@5D_E@zd~lPkKjo7rp(a3OJyLshpagIn&MfW zt4(ozufi@eQ8oVuJY3NjB8N@elmeBdU*W zTi`@zo8)Yhoo#O(f-QXY=Z^fhTO;lphi4AIbNuG<=uyetD7zaaN0aOTk15-D0z7?! zBrD39J{%tT&~PVP+;S*!c0gOOQ?hr;be;m~R9hE)`49I((Bzrvi+E-(O7<$*UM1SA zez^DELGZYb+&h9$_I{C_A^qcQog*jYnQndS$n_&rBzkC(>=i_V*%%{>i6tWXa8G#K zObKR|9;~0z7|oCL-aZt|x^+Bua=zn!<6=?q%;k69xcNqOVD5xev_mf1@vvymgQ7hP zh4%}kq8H_&7awQotS&I&tta&0!UKX0pN1x#(ellMUq2Y?#Hs*0#PV&*DdlgM^S4Wm z?c%fNlylLMhnZ~4T<1a#QZzb{qVYva5s(CdfXTGN&65XH95Pb!g_Q|Xe>QRgwsakk z&dK?p_SVVVI+0sP&yRlnC??W-HF3xGxLKnw~7i&Q>j40+RayrTM&vMaRF*8z(t3OW)0QoqEzDqg$9k?e;*+?h-4YVB$*>uG51Cy>9SiMt&<&ae-&jZ}+r(w58JCEqk-#bed@w zINu5-j^3~4w+xnLlru#IrsIOz%vb;pts~VU(7Q^LVbK#-@vD(yUOE|Ylx(w=%Ubu7 z;#cu&5Wo8O#perDSg4w2Rz$r7h*C)$s^_G8s=`v*`r~Vu=2lX&kicmyHKKqLM_O-9 z(;|sHVh3OQ1nsRlEe_$=F4qv70LKk-jdf~0KFp_EU)3{x?v`X8sOhStJD7o8o}lez z=d*%l4Yti#IFK*XYC~dHk$-`Ypf%`7w}p!-Y!J*z`$p?lOnXyi+SB#Z4H~SL8Xo4~y(@Y(P zUM47F3XXrL*cCq7o#3lNxlftKS(A!|egVsK>K=8WoNR&%13-&_sUxW%3X|mfR|K{Y z*b0zfmMYc2IHinVyMwZ&S`oI8i%oV#GlFLct>nP~`6^PbPPlx3kKQPZz!lHJ(o3jc z;w35zfzkNP5`k}++luzx%|@rkG!obZ9oI#w;g`guobeGb27u@n?TSuEu| z9H~_SH%#cK3{m(jKQ#Is8N(u0HlSj%V^*haOjuTBBNR^ctVMVrkcSp$mZAc=i7J83 zyAlfxQiaC#mo>`_Dr>8(X5T&WeyMdG(@U%DLQ?)lME?S7t5kTEbD=8Y`F7vCeYB*< z`@MI1=REg%BYo1U7vxnhqyjO*dECBUNqarDAruu$4h{_X0`=>mjVVAaRQPkGtr+4r zkc)a_+%U|?t*uyNYCjquY|;FXJ6eW4K7k4ggL)t2evAWuhoU?pKv*%J1=fb}k8}&d zL1D)$GvSToN2)r)FDRTcb7eDIML!~!O>fCVDXp*pVdEB(Yf_d>F;%TyinVC`!p|vD z6M-=T9!eSdrv3{C~9|kMK4~_?B&zGTJdmd}S7<~cnfL~~)I5o%yrmRck z776?(N(7S7KCC3;kL!DcW8`5N>|rXVO2=URz)PWjK;R7m?F39zX#FMOFs^5vx7a@z$y9r>0I%pAL^qy&_plWGfl8rqn4RT(yTg-g)Kb zD>u%~oD(-5N}QI%lI5^$IXu~dxw_GE4YR<=me{s=(_gs7&Qtd*ep&Hp#jibH@zS8^ z8In9hvS&!-h9z!T=7!-=qz4wUtXXn4&*zIACW0@>+zW8fqlBsjC7pY%J-k)oN@T7? z2ip4UD5sXZqd3+vhI?tyXHq^Ys*9Hfd|$DlJ$^mJp@Hmm9TW=ZDHnhOu)~_ z*2G5c)=5Q;a#7>lKB;K?0+X+{${f^6S`$$oa_$G5JN!y&7P@G*{9$uwbZY1HPSIS7 z^8teqgK|~;02AVphnC_8mg4sc-t$Md-WtC>E|sj8OV+3L(G)q&LrcX2OGU&RJ3rq9 zfr@0=C0llh?EEYlg3^hUNS12ZQY~7lsbXld-203yx%f zzG!jm{G4mnJHKWDv*ZI3cR=P2z=3IgVaeovXy($-6V``%LfhVKCk@fwXb1gbdaRaX zh@QGVAh|a}&oCB{+|B72q4W4{ee)Pw#^Sv-x7S9W`AOa<#bW7RsdTSgx_6;dDutB& zkeq!8QuczfNL%D&q-_S;KYC^kN-$&%Wrcc{EUxLCFc#e@lPzTrEwv9Uwb7nA*ZlrJ zZ(i{KbjQzk+&}-*7v$$VL`$t?IU!q4i0n*$R$?*aOI(4>6^L9x(wAiBqbc9d9Hf|a%XZyXKj?JK3xX4#bCUgG>C;} zvHAM*q30tlq30LvjQWr87qrTz!r>t+JO;sMq9P6QRnBb7_Mf$th)l9`OBbGo~YZS zEa<4^P-LmCATw3^P;T|C3mhQjNa!MHQbMjOxra*9R;gW$lF2Ly)iXE910fMySxTkNB|SUH;M$}lY9!l79y#MwLVPRr ztXNY|sXC*)HFj&b=X14jnZ6aR&w&wEEnU;3NP`?Zf)>0@Uh+ zIe6-8@kLle6!wKQufyEC*r%T@$!uwOZwGr`dWp4C!L2D@iZ{}iVZVwzH>69+m!)&k zepSpE{-Y+v6!(KK=Sjww+1AvluFg!0%{{7UO>xsvC|h}j@oewqq5jKWrg%YQFqE|H zAO@21r%`w%cGDd<2vP}T^*MF} z)v;kFCW&X$`nYV7JO8-h(uPOWG$2AD@yfUrhEFdr{R|-+_b4q}Jc&_#L=BH;*EVkT z?_?oTFOXaxgeh%|mt?;PvaOO7QTa-bzcTTb2!t1;H>od*b3<6x0HP;gFsyBW>WF7| zdV0M|#i#@*)DsnI^ESB{p=l6$m^qX13JNIH(#x3>Dr#`_cxZWjwxF+~TT7HJRwp4H z5G)O>(49vq<{c`EHEyLs(4webp^?(c0*+N^J?eczHw7i4j?IJOhJjH(3G`BQokG9Ugm-qZZ=1t8Im{ci|9YBx$}&e(n2-1 z$KZB2d^N^vwPJIw9fKeMXXK@5N92g&23#tD;l#Jc-W|L3`t8@Hf(@`8A(2cS15?(J zerj8&B{YIzFXW*g$eNBD1)&Sm5VU*-*~-XUSFc~48lN7YKBuhpXJf0!VlHzF^Z+fv zB3deyb^frb?0fskiYAsPm2Z^GH_kn~uvhGSS+sOXmM+->t-vk{X@N|liZFvAP-tXoYq;h5&d^S> ztvDPOrrM|5L+wizN2nh%o;((yvIVC?jGsDHxax(h zpKvXD^G`WFU=N7=)R7vi|0nor$Lfn{Gs>Z?akX3*My)CGRaQ!w;2;YyLodXrmYu7$ z#WF|Ps4-av_4Vo6GmvCZ_f8yGvjOjsmKtCb} z#|~vog+{VX%L^qwylQk;>CsGNYcP14SB7=81gCl>2I$shEZ7J8=?tF<8@PzdxQ?|0 zRHHM=0aW@n+PkkpPY}O<0xJ?wOvbfU*JOqP26$?}X7%U*XVIaSgak9J`Y*9s6Ygrl zWGwv%t8qh6wfx{ ztEYd8TxrN&c4#RcP!RtsmOq`S)&+B zcuTd~j5%&P#1`rqckLIv-pWpJO<{&xx$eMTXtSJXIaYbfpS+Ko_p;%8Y~02Ny0LU4 zDQO@SZu|p7LxBs4d%^4J--K;b`uWQJFLsXGdWlu#XX-ctkFO={1@Qe1t${{F3$)r< z07c{FOML^Qo9=S38fGdkifwO7#{`P{z5x`_Uk_3&#gp9sW%+53FbJmc&PpP182cEM z#*k3ii~pB~=PJhweY`gS#dwea-NV8V5fv#_dqtyFr8=VU$^Hc8W6C3pAEgjBL`Mot z>GeLj7<>6`a&00&Tr{DM-f}I0_vqG0F5;UBFB9kjAnWddq088bsO@Txm$4`XM)&+{ ziol4UJZz-^9anJmu-U?}!U_9N^ptVAOq7U`-ocN>@_h<*1T7W+Xg^G}w^2BSwHZ*X zt{EGw58g-)^$^#4{WK{^QA#O?LNetgDyTbMXTmhHq!;jC#%Ck4Dajl01&mxUDVvrNZKwi&0msN-W%{EMP4c zHpE8gH@9Z4bFMBnCKo;nLt1Cij3eTaot2@MC1>7@HBvk07OS5BFh6=S*7$z;o$_e; ze52%iL3X|X21Q{7%w+M4x+46{0HV8!BQ6+8_C-CnFMfOQ-9c$pgS@Iiay1}uNvX1n z%u;bxbj>ZtZO3Ds%>num;06cF*2cwr_uKx+_8Y;O;Dh{XF~53g{iay&-3_xF#PTzd z{n4h|uu`3G{AAmo?fUVq`}N|f)6#QiPxaP7t^VXwnrFzH{*AK~nb#7*~LZPn*DC*l)^6Yekk4%tTcAY1;ycp4Csk3cCn# zviaPEh!rXkn)oS}k-9oF7djl&^I+9YzZ8vtHb!XxVPs-}t3HF^DmOVZ6Md z;TfE*Lstfg6XAbG{q$~jTBlbrWzkb%h&RHl>mCuE{)p;UQ5vS6!D8Gs2sR2t936#2 zL%bRg6z{@=MoAp@Jqbc-0)BY(H40157}YY)G0xB$@*F15KL&uns+v4Vc%|Z#r0E1^ z3&ZGyl$ne|Xr$=`MgVN21c(Y?R7AZ)xJDk|AV34ZFhzhEB0`8jYPioZoMGCcj5Xq? znn;cDBI)=ah+W=T@(t?Es=p+N0Jt8|XmXluS%%RAj z^K^+0pe_=QQs44FPC}=DD#1M~ArXNaE}~DdwG$ z@=nQgo(`F@8YPrFp$uA1s-&)PZOko}Y{6y;C!>wG%5RrP%CWYAATgKg1mp}iF4p}qWpy*z>?HCM~_ z)uMfMEbzq_q||D=4*ttKhi)E%oTyi+-MQwFO|op4E!#!Qb~qkap-NfhD&{|6#HNna z$oFr7_r7WPZ)IOF{B9yP_*Y_Hm@$5t$f#w?Ae`dO*I-_ksqisjrA4vT%#F;cFexn2 z$jN6DR8Mw71}K}@plq@%RV_wlOQB^yK_N3kkqKguXVi=b_4pSwYSUfWG%?eVS{nyr zo;me3Fj8or`qv-U@eYl-QfeH1!l;%}CVE%xDEoE2hFOx4Bcnj_*>}a_onY>)sBRRr z<4fi+W2p>*LwYGnEY|5r?_j{Irsoqmfojz|E0jpB%10l}U`l`z$?(Afo2oQ2)kQ%W z`~lbl4lLG|t6}fBf=)i~GAs}RrRn@sOaxcZ#pf&IE@R%PzfA`9#un*Q(GG*Ap#5Ts z@qpzt%^H@(icAJ94d{Y_YaZBx?&T&>87ZWM7U`0MYO|ZHmNF-mYD+q*D3MkZqajIJkulX z>LE|<$zEM}2l2<7+J`EWS-{ezk}*NR=D~hbmA=8sTK~q%L2#o;fiN({cF1ZFsD6cS z5uo0zXxN!D72Q~4W0E^2!=u@xVpT?aLJ5-u-UJx8C3Dw^*6PdBhDQof_X|p3H9*`j zL@G~XSZiiL_!IJ_mZp%24M-8YrJTnmo3H}`KdN5D_^V3JQV6u z*UaKW)a(~eH%VGlTVOvn=&h$2;~@zrTRt;`=VCJM{EYRM*m?93$=N77 z8$-rLM@gjZ!+kO1`-krwmdYC>N5fpc>YDj#wS8DH*+xw)VK8fp-*>eql%gOj#1e5%&+%$hn+;}{3o<1WMzAP2K zEYtZ)CSwbmQ^rn;6+5u;j5|7V>+J2bk+X9niQ=^^6_iGrZoEA6@?)LZ0eK)mCYhV> z7K_)5`E)K-uDN5Pg*+mSH+yd!`n;|_R`>p;JD0@b7b2#Ja63DajlIb(V^OwcwDH!3 z7V$(U5yvNG=Sk6dG8rWIW^RH}<*b#RwNTH&`8XE^`Qmr(|DU-^WaWf?6Ow$TXoci# znA=A?DD05A9b)oiT&169@2fnp!SIU>+2AMrVr#)cr}4hi2)72a5~qOB-fv$*8~!R= zD?qH4#V9Yfxk5rj1}vN*BEvEoiD%$bWjxdHs{Z?J&=95fSaxXXN-gaH3hZ0#U5spF z2h0&*%C3(RE?ahh2V=p;4z~0IwfIp@eXER|GK7-9XJZHR4ZS*ja6f=tmLJ+T`YLmp zuTnEX)zU=TTFNw62gP<9l$K_Zd(}H<=rx1BFtjuEk$S7LF02Vs3s27lw%Dw)%Ix>> zlFBeb)vF8_e+n03+$4`V0t{P!3>OsIv5!6NAj5mFDmt;>rvN_ySV>zVFTMN3CGXhq z5UC;)7xW&X?o|L_2rS!wf-}AWRz1PQ{u8;$HGdkzP58N|uVAaIl>UJM8VH6EKYaiQ zcU|LL)xTT!{dFI&zqkJ1HGIE8YT7F|?S0sE@Ili-sp+uXbXZz9}8{e?!EfXw6>&S(M+ z`J~x6=3d}W1A;pZh^2i1VIT+y?l2(c*nm(I0JJcOg)QL5{-%I72JH+w7{p8t{<#b~ z8FVp-<+9;z5=kEUj(e!lF9E&@zPIK}Y0LFh<7a+VZ7SH;IDc76YV*N=PD!2+ z|M!-}t!hcnp(Q=BPWa5WMeQz6WA>;j_p;EmVMU@P`c2r9A>AsMutdN5J7`<_T0B+1 zoAzmOM%;R(tz#1eZLEa9V8F;(5OhB^KlfAf)5cBbr;Vk~@0h07rInv_v`K)b9sKv} zJylrx1npS8T?5|yCX>f(YKQewCwlv%NdPu!O?uoSUz+JBJ!oWq)XO{`{qML4-v@3` zM-l#nJSa6pA@`$CDOkdTF2cvE5X|FGRUYi&oXUeeR42-!Fb@KWf+>kocec%+KJT<}O0fw|zpCg&_GawR=dzVJvrRKi}NS0i>Clkq6xA(DFvK6)8qOejjb zZ0fJ4TP8+&OhHm|BW3v(#zG*oTxFMmd?h3co(3$PG+-rL39uqP6+o)=#OEuenD$GT zW+m6Zr8i)K_mb-@flmNHMs6TCk(7!)3u8XKhFch^`Gg)Q;}T7ykf+%E40_B(;4TaZ zcTsJmwc;K2$Fqm9N=vu@f@hd^^2Uw;(EP<>DVMx(4GqR~X`_Q~>@0X5=9K+ILOh3g zfK*M~n*Xe?P^Rs`W!c)>rz-~YED=!k>ioW2lo z&@cK1{pP+#zs2HO=JmBGe)GBXTP$81$&KX3R?%;MHT@Qg>moUkoLDaXl3puBRJ>k~ z;x~7MehX#vOQiVNVJfcwjn0(2{vQZT9(e53<+;NrU_*n=x+CjgyOb9_7!&52Vi&M> z`H6YULgT{ug|&;Wyzt(z8EbWRz;X@y0YnS233*4XaIR|Z#9YyQr<7AJcnSOII&!cNKyF@m%hcHPSfowLt(rXed9FKrX6kFx*r(27>;|1r5KH*y zVc~W;uNIO$w#UID7C$z|vfj7JWgBDs+}8PKtcmXWc(>fJFI)r>qO&MceZvuTJ#elO zook|fb6M{X%#A##+acENSj=@t^w-Bj$qIBG0ZLIBi_xO*m&1n zo=`V_Z(r1W>*(#H->Z)G-Q765Q7qd&U-;+M3!b0W{k-lks_%P#S@&t(uTPv3UpkE? z-DRxm-tw~e$~npYs%(E%w7>er=g`W&Diy55a{iBX4&y;gkO)E_+ZYlS^Qxk?a^AY+ zdo4Nh!>!myq2qRKRDWwNjM-Mp?t00&UUshk-o80*?)<##9&Ib%H$VDuH})ppx?kRW zK-zdv-gr>VJrv^pKPRwgb1LfAi&zF09vp z*f;O{_jY$OcIVU!*bl>2A=)ay;_}=niZ#DqF<14Vs#&aRo(s&k+`BSA^8K;7v4z(A zuAd*c-}19V3x^OtsfK+AXyH` zmII38aT$Fj<%=m?=S%4EFu|w?#=yAp0-?TL1a>oz_IAZWI;*|Cz24X^{1CqF0z*rxZB;nQKO>L# zk*;u(Tr}k*%p17S?>nzBoL?jV8uE8xI~o%G<59!#7`fsm<*G2T-=&Z=3cwo%-5$Si z3<<-6!|OQ@i+SNVW%CXN8X*5H+I*X4Ws21=N!Vx~7~k+%1GbzUgq8@AQ39!o#~mcR z@Hdc1K)?f%eX{k8lMk>jtl;bM3&d&@hAE#D1lG~*^8}6)7$ooq1Y!j45%`S2e z3DBaTY#A)!HFA;F2h(9>>J5w=&G;G&sVh}lLD5J!`laRhfgxJEeU~638cv=6f5Df4 z3Y(smZ_?W*ZS-5zu?sn@lkB&sb4;?|qOL&Ho{PF-QG0%-%bsMvMV(7bo{PHGqV`fy1;s6T>!rGieAz6M-;4al?TD}nqqpwOldAm%byZqt{M zw;GhY^e}!&fpsQ*1!a){c}Mm7yvMp_@Kr;O-uYOk1#5EjYsoJG)@18}$Wx%SLJtiL zE!d;it*(k~iEWuP+}$y|WB%l0U4x!XMVG8}+@p6>URuylrH9^% W797-D=uK0gaJ`;T+0!6HasMy%h&KTM literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/torch_utils.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/__pycache__/torch_utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f10e4255e33343f96c25fa31a13cf4e4d61ad11b GIT binary patch literal 26826 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/analise_anomalias.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/analise_anomalias.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58071a15e8696769f3cb04de883fc1d3fa9a049e GIT binary patch literal 19136 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 K-#z6m7VLi%9s!I1 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/corredor.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/corredor.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39a243d4e476625952b72f77bb0bd694d5a5eec3 GIT binary patch literal 2950 zcmZ`5TWs6b^-`i9mMz(q49k|{l(XQ)0urzCmMvAXWJwmcMVlr|(!>u90Bam2hzGTOUIIq6Y_DJDVr_VRIK@gqb3mX6(0X+J#x{ew&$g zW9A}CdbTL>8(TUE$+0_(^yP)e4bPKNSbrD3i!^kV;%&%)iYi<6sFY#{G!`>U9)#{Q z2t9zO0afoF8rT1!@o7l&YQA?|*p1!qxbm(+haFY@D#&!;YEz%)$Lyz``(Vcd$6ZwI zXrpawO)y}qPbUBwd!#H@&)_XIobzZa4OOWwBTG^H?cByfP z8)DtHhceWVK|C~`1(bJR1RC7RAB1zDMP5i9+LZ@Zgm&-X6x=;6K}_1URlBvI7Fq&! z9HKebJa*=Lmcw}lcfajdU)YrlS@+_CE#5p%qp%~2D< zvC$VH0V(dEzx@LT;adNA{Uez=+lX1yNC-zG1G1#b;1wiiv~tk)n5R(!^Bc5e4oNUPP{?r_(?Jr%U49j@`?pXCIXzjSX>aXApRQ$#ugoiTmJ<}y0*GuPiW7J zUqDB1VR}VnYnTX15F4>GG~S zu3HB#qEArLWw}L3wLJNv%&SSaa{S&Z98HeR*4e61Y#`n+6dl&1ycP_ts z`A=`^iJz`rHxn-zLuqp;t@j;mQau4Kv_;2dvd8i$b39Zb&5Pxd%9ghzV3C(Bzd#5? zK@}`ER}{;ZJGU@yv9rZ_V)=+*Gg)3UEqr%mKE?-xjrut463k51X1O!9>*4^)vWG4 z-DP!^?S{!>VRpViXuT?Qp{>{szaV`yx&z;rUh)!L(_a*&8(IPKxy_;Iu-Sj$H|J^- z%P&8U^xs}sVeeFp$cPykslEQVCvtnDe(BCxqi5Lc8Lqv$?M1w_s{Fl7#=bhaPRfv#Ke`YU?Jh|w`(c8qKzm**7IST_gb z^||+lR{M;>Lwf48F>rc0_;?_8&$kj>y}mYP44gCvPA&(xJ-aLo#{bwtzHJfJD)#yP z)2C0LM>a6u1yFqdz0sAQKS~_gNF4dB_p?(*;)Izv@hCB|k(l_Be6eICE|`f6`lT5o zmN8?QM=`t+!$vG`#_~EFg_wlGwTZ{!zIR9UeLq@DZ0tL%r_bu=FK?tT8}Tbfc+w0{ z>QmEZc&7H+H@?8~QNtHCebM?4Hhgj27vJoT+`d#FUw%vX4}IMotEXr&FG}g_H9OASUF%sldHcn`p4FyM*or8Ik2cN z!!*0_%QqjU9v1W~Q^uKT^UO5hjmV4{nW?@0b!WVuT`lUJX`M|gVJM8hB!^E$-JeI< zQ*qzt`x#i1451S#$yy$1zEr+J=(r>FZ-HG+Bt&=pbfLBP%zvyTUFRHu|2$mi#~O;Rx~o literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/gestor_deteccoes.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/gestor_deteccoes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3898d0a493e586730dd4686c5cf2c7ecc18435c GIT binary patch literal 2800 zcmZ`*-A@}w5a0VU*g4k*AHjeL4k-jTd@7Ff{{$aBDb`7mkgBTU%@L^@<*Bo0eE34LoM(4-W_M@j zH#5s`e!mZqHoA28KMz8G*~~s+_xrsl3^ovrZXyNULkjhfM(8p8s%;!8^lcPl)2T+h~#NR6vmC9P#Y&dMoi3TqQuPMnZ*HFXu3Yp%gWI-6Qd#8ac{#Qaq1v!&c! zEt|S6N?yZo6}NLwl_t`%)z-Ot5Fo;ZaGn0 z4!_~-!J344N$&%)ipt0O3#{4LQSSVJ^zG$HAL-l5(QeXrmh5j2bdl^h7Z?JW4NMUR z1Q>;CgHg39^uc(A1{qVwGr+TTJezYMM2J8-?eNP-5PAsQV`s*t3xH2MHXYW@vy;pU zm*)U_Zp>$xiavlg36&uKBn;;=TKRSp1-c`#3u8Es

      F-Syv|B25Fsbul@?ST63mW;XcYU&v{#&h0K;NhS#CQ7NKHiINlyU*M4E(BOP|>k z+d6JG^%p#4p>@-53cUqyIncbGSo^lX{>?Y7_io0`jsc^2z~Bc>e(*U=#Wof5*pSgY zWbnf#KV0U6l?JxSSKul>6l^K*H~h1^L6o3A7`NB6pI<#CfxDvb&K?3 zU0f>kGQcAzp*GMIl=C|sD-_fYba03J z^fbt;ZN~QX?;&?3`iWkP+(x(ybYhfUwIRFd!C{LfR@h0cldy|MSM6+l?Mr5$9?074 z+~wtgH`7%jWh1>T;lX4^lckJ)83r}=LD)vW0OZvwsvtJ#J0Lqjp&SUUe_{r@igEKq zVmpv11rikw`J2{T*FN2`S(vdCG}d4-x|+l7u$9F~ih`jEBHMP0ol%!`%d;S5=2g8` zlz3u-Bu-3di>ht~vKl;82TuY%N>&q}S^jE)g12m5O6zu^`U)N`r%*Z9J6xz75kWa@1 zF9rMmUzmYYTY&f3rz6{eWGRrW-J&E6zp8j#(P78Q zPB;S2covRthvP;#VTK_*KJeo3F~~prlp%5yd5NkC*O;tYdl?VIoPGRR7*rUFqAK(V1=}7bFUZ`> Jw+QTw{|DfvZwdea literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/imu.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/imu.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8c5e574c0f60cace0ba4e69a6863096cb6d554a GIT binary patch literal 3331 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 LfkAyAI2-*h*M1BX literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/radar_top_down.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/radar_top_down.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5716da996e9ce9ce0989abc56694cbf467c3a45 GIT binary patch literal 18762 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 Hq}%@wo^DxH literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/processamento/__pycache__/segmentacao_semantica.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e22103993f69b13627c4e598ba91ba87a63f38d2 GIT binary patch literal 9009 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 G5c4k`wm)D1 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/camera_manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca5ecee2bc96b8c02b6582db0e731657a5372b77 GIT binary patch literal 11276 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 Ix0J=M0(s?%7Z59jQBB0%@I}G6?M3eq= za!O4oSi*27TKPy}NT8;j242xdQxHc_OdgszEFL>Cden5mp4mA`KV$L`m!z$Du1fLT z@YE60U!flz9z8OCuX=oH;`q49f&1on4T%$3b>-=s(`35^CjM0`Fl#SJ)d|4>w;rd@J0(7H)q(o)7mH!o4fw#XxHb zx&2*NmW}OCT<89>ZSBCMabR-2spZO^YwpjIM$=HfX{gXNR6?#zT{j+GZ|{6>;+pik zWA7cic)Zviy?DIDL5J0Afv+juHx%4!`yuSR@4oxt=5OmzV@nA&c!T$V?g`T?JaU+-yjTJ&;EBtzBSG9MAFLv~N5dVGr zv+nCJ=R1ZA9m8+>FP_cwJBnSspYHi+kFo!GW9ldQt``bjFTCkDns?>--Nl~%PvalO zukZV^FW+;b&~t)zMDu)CvAgfn!H)*7^EY&lz;C)4inA8(q4YQT`ca0v8SEGtU;@^tCTiwrzPkee#3MBkJ!U@H;Az3ax+kZi3>gDrD5$U#8V}SwGW@n0RrKK) z^ind7zKZGlqIq@9@I~{!Xu%gX*r+u!${dQe*^%iGkeOcn3|KvCxXFH6-&*tT+PmQa zLzy?*v@>8Q@!pD#ib?Ce3n~o{Uds-UdV6AZj#_Fx3v1XY$rAbsAT-Rd%?1sQt#=V= zqu2oeU}jV$V|uVcGGMt)UKD2(cz23qfRa2-0d-y`drVE7B~Q}2bvShU8&2qZe$alk z4v`;2FFkOrA0WK=7^aAPIqR!I8Vywbi^!X^zT4;t!}%A{pyB*)3&EVXDC~O2Ye1}O z{XbCWg&bFGdgS8NrBiR6zI56^!4m6Y;N@8bJo0T>>3PSOuWv8Zx92=1mSIBhGOd8R zdWHtaHE=6x3|?6H<;G&y9~dtL#&bfkF`V<;Ah2!a g{DoKYf$fFB_MA{+A7Q}kIKhwLXuyu$4=nNdA6!aYrT_o{ literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/main.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0d69a7f803ac4951961b8356952455a26c04331 GIT binary patch literal 5730 zcmdT|Z)_CD6`%cccYEhM`|LaW&S1)!IKV;b1RIkSY=doV114Nz69|r_%ldXPNA7Nq z-80zEU1CxspIzFT2t-Q~;YJCerBYFpKd#gdP$i)scde~ht%QV9^+Tmry@*6b`P4Ui z|7{$l%BRlmzL}kQZ+7O*o8No0A2=K~1nJxGYbQRjBlI`?l22r=@L(SlE+Yil;g16+=R_lZdCTfy|A3Ply27A>;AW|G>3}LWMRc;4x6}JP@IW z8DfTGQ-SRXKa^IqJ;k>k$+jpk1=0i0))q^v@S2Jo#nLLg zC0{Gytt^(W!FX>J#>^4dVAMAXBQI|)etc%6*|HbHZy zh&5`PvqmfC%u)NC#i%n!9MQ@-Q?zQ1Fy1W@XS8~biq_1LQP&(zpfr`H)7Ik@$DCv4 zm^6_lp~jRpr!7!x;jB<=%hfSyYua|)%-PN{DMX?qT49%H2>B}xO_9yb<8?5u%+-s( z@b$3KaO{NOlP0C+IGZ@JQ@0)EdG4qnzQT*xMvtc9bO8~ZT;mmySYZ0DKXV?Y1b#|!`Y;%4XgkDw}Z^{orO z-dG6i3(P_AVGHqP4XznXa}BV3`ObM?uP6$>nBa>DLfn^Q+vx*Cu|$O^A4>?j?YPLF z8s}qRc{&qe6Gmz5%CB+y<8Hr2rxW2QuTxQWLazqv85LNO+Yd%2isSKwZta~2@$p1h zi0L(KVw{bHr?3SlCW2fzl+dg8vYa3l%~v-`5uT6x8J$c#rJL9>0d_rnX?lA4TfS%K$S@z{;A{J1p%HdW6h?(aaFmt!kuG@YE-Q?NV_VQmH3MEJBY+SO!1+KrknzKm zB`@gXHdWizPTgK>#k8ri>uKut(G)U0}DCH@{BVBzLxo%l;sEa$#B+XeKJ3|PutkC`J}9mSVQoaD^t>%v?Q4oK@(UT z^ia)JB#Dz4SB>;2&xo}Tz8AZWTmo;}n6eqt_Ht>?k+eY{kMzhX+iN!57x-5w75%Hf z@{mr2qMQ*B>E<{Kaf<}5$aEYBFuE1{87aX=!cO1R8p9*9f-f!# zAzqSL(HF{bPLbdlD|HG&Gu^#E8;fNltY>o(n!79S;fC08j?Xsgbw$4?;p$xcrdRg! zi7bl|q3P7%*r;wg%|^y~-HGEriI4E1L@+yjXMB7#5)NVKnH`UAFS^ac6S{MVJ)Qfa z(Ve6_@Z5nvs|ipMjt1SJ0e_8d;;`SuVQR5`8g=)Q0 z#1SZHBoY~AL$6FVl)KaN){P(`rC^tWOU;mLt^I`N+AO;^U$6Z`{qO2;wkS4Vp9{VJF2c|!GcEmd{NRbAk-u3E2H7d9*2t*UqHyh(F;Wml8t-XgoVTyIm{ z+f?^9Ie%v;|Hr;jZT5|3xc;Kuv)pGnD zOVu56b%*BJIRE;>_y?2kPTuq`?pAjlQgq2?>??$7kCDIu6k3*32LgS#{FzEr zQ)f(k_fqw4xq7$eX}CQ3=H!C+dW+&|Rz1ykJuOR~mYX{k;k{G!bizdP3clX0c%M|g zPu}&mE_qvT?q7Ue@di|H093vTOaJ1a;@zWq_W(n=ozfwnFxqwRc~WWJ0Mn1+Kq#6iV_J%MJ4g@QpE_i~Y3AycXvCzhBe zt{+i%63-W&MHl#YSXC9#TELLN}rPH zQ}^j%dG%YPd*nym8vUHS`kkTcOmGtWPV!WP34_xQpnC|~R12Qqe;{^ba!wDNefHe5 z)5axs!=hkQ!>bew`b}aFRET(Ui1=Tjv2eH@n`AKfTVx0rEBh_e9u>Ip2;V6V0Vk|0 zl8yp_QzSuX$Tn?U8nR9smxd~)jVnX<6GTl0mC!*F(f0Jz5>+o#^%~7g?>*ahu5a47 Nz!TGJpxXHGe*qm2Nkaet literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/__pycache__/weed_detector.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6eabebe1c29953be6e68b25d5d1bf1478ee4f6fa GIT binary patch literal 17243 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 1: + self.camera.atualizar_saude() + ultima_atualizacao_saude = time.time() + try: + status = StatusModulo((self.camera.ultima_saude or {}).get("status", StatusModulo.DESCONECTADO.value)) + if status == StatusModulo.DESCONECTADO: + self.camera = None + continue + elif self.operante and status == StatusModulo.OPERANTE and self.camera is not None: + self._realizar_analises() + #if self._ultimo_rgb_frame is not None and self._ultimo_rgb_frame.size > 0 and self._ultima_analise is not None: + # Aqui pode chamar o frame de debug com as divisoes na imagem para ver onde a erva ta e pintar caso tenha erva detectada para debug + except Exception as e: + self.mostrar_log(f"Erro no loop de analise continua: {e}") + finally: + latencia = time.time() - t0 + time.sleep(max(0, (1.0 / freq) - latencia)) + + threading.Thread(target=loop, daemon=True).start() + + def _realizar_analises(self): + _operacao = ContextoGlobalRedis.get_operacao() + status_operacao = StatusOperacao(_operacao.get("status", StatusOperacao.NaoIniciado.value)) + pulverizador_automatico = _operacao.get("Atu", {}).get("pulverizador_automatico", False) + + frame_rgb, ts, res = self.get_rgb_frame() + if frame_rgb is not None: + analise = self.detectar_ervas(frame_rgb) + + ContextoGlobalRedis.atualizar_ctx_dict( + CtxKey.DadosWeedWorker, + ts_analise=time.time(), + analise=analise + ) + + if status_operacao == StatusOperacao.EmAndamento: + atuacao_bicos = analise.get("controle") + else: + atuacao_bicos = {i: False for i in analise.get("controle", {}).keys()} + analise["controle"] = atuacao_bicos + + if pulverizador_automatico: + ContextoGlobalRedis.atualizar_ctx_dict( + CtxKey.DadosControle, + controle_bicos=atuacao_bicos + ) + if self._ultima_analise.get("controle") != atuacao_bicos: + ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerTx, { "cmd": WeedWorkerCommandType.EnviarDadosControle.value, "params": atuacao_bicos }) + + self._ultima_analise = analise.copy() + + def detectar_ervas(self, frame_rgb): + if self.weed_detector is None: + self.mostrar_log("WeedDetector não inicializado!") + return [] + try: + return self.weed_detector.detectar(frame_rgb) + except Exception as e: + self.mostrar_log(f"Erro na detecção de ervas: {e}") + return [] + + def salvar_frames(self, tipos: list, nome: str, pasta="frames_salvos"): + if not self.operante: + return [] + + try: + os.makedirs(pasta, exist_ok=True) + frames_salvos = [] + + frame_name = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + if nome is not None and nome != "": + frame_name = nome + + if CameraFrameType.Rgb.value in tipos: + rgb_frame, rgb_ts, rgb_res = self.get_rgb_frame() + if rgb_frame is not None and rgb_res["frame_valido"]: + nome_rgb = f"{frame_name}_rgb.jpeg" + caminho_rgb = os.path.join(pasta, nome_rgb) + cv2.imwrite(caminho_rgb, rgb_frame) + frames_salvos.append(caminho_rgb) + + return frames_salvos + except Exception as e: + self.mostrar_log(f"❌ Erro ao salvar frames: {e}") + return [] diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.json b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.json new file mode 100644 index 000000000..3619b4d18 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.json @@ -0,0 +1,12 @@ +{ + "dose_ml_cm2": 0.5, + "vazao_bico_ml_s": 0.2, + "offset_antecipacao_y": 0.05, + "debug_visual": true, + "weights_path": "C:/AgroBaseModels/Ervas/model-2_1.pt", + "frames_consecutivos": 3, + "frames_histerese": 2, + "min_area_px": 400, + "max_area_frac": 0.2, + "faixa_atuacao_y": 0.1 +} \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py new file mode 100644 index 000000000..6e489452e --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/config.py @@ -0,0 +1,75 @@ +import time +import json +import threading +import os + +from weed_worker.camera_manager import CameraManager +from shared.contexto_global_redis import ContextoGlobalRedis + +module_id = "weed" +topico_tx = f"operador/{module_id}/tx" +topico_rx = f"operador/{module_id}/rx" +debug = True +ultima_atualizacao = time.time() + +def mostrar_log(mensagem): + if debug: + print(f"{time.time()} - [{module_id}] {mensagem}") + +manager = CameraManager(mostrar_log) + +def get_camera_manager(): + return manager + +def iniciar_camera_manager(mx_id): + global manager, ultima_atualizacao + if mx_id is None: + return + agora = time.time() + if (not manager.iniciando and (agora - ultima_atualizacao) > 5 and (manager.camera is None or manager.mx_id != mx_id)): + ultima_atualizacao = agora + mostrar_log("Iniciando Camera Manager...") + manager.inicializar(mx_id=mx_id) + if manager.camera is not None and manager.operante: + mostrar_log(f"✅ Camera manager iniciado, com MX_ID: {mx_id}") + + +_CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json") +_CONFIG_CACHE = None +_CONFIG_MTIME = None +_CONFIG_LOCK = threading.Lock() + +def load_config(force_reload=False): + global _CONFIG_CACHE, _CONFIG_MTIME + with _CONFIG_LOCK: + try: + mtime = os.path.getmtime(_CONFIG_PATH) + if force_reload or _CONFIG_CACHE is None or mtime != _CONFIG_MTIME: + with open(_CONFIG_PATH, "r", encoding="utf-8") as f: + _CONFIG_CACHE = json.load(f) + _CONFIG_MTIME = mtime + except Exception as e: + mostrar_log(f"Erro ao ler config: {e}") + if _CONFIG_CACHE is None: + # Valores default se der ruim no primeiro load + _CONFIG_CACHE = { + "dose_ml_cm2": 0.5, + "vazao_bico_ml_s": 0.2, + "offset_antecipacao_y": 0.05, + "debug_visual": True, + "frames_consecutivos": 3, + "frames_histerese": 2, + "min_area_px": 400, + "max_area_frac": 0.2, + "faixa_atuacao_y": 0.1 + } + dadosAtu = ContextoGlobalRedis.get_operacao().get("Atu", {}) + contexto = ContextoGlobalRedis.get_contexto() + _CONFIG_CACHE["qtd_bicos"] = dadosAtu.get("qtd_bicos", 4) + _CONFIG_CACHE["zona_atuacao_y"] = dadosAtu.get("percent_vertical_deteccao", 0.3) + _CONFIG_CACHE["tempo_max_atuacao"] = dadosAtu.get("tempo_atuacao", 200) + _CONFIG_CACHE["velocidade_robo"] = contexto.get("Gerais", {}).get("velocidade_ms", 0.0) + return _CONFIG_CACHE + +def reload_config(): + return load_config(force_reload=True) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py new file mode 100644 index 000000000..4d1647bba --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/main.py @@ -0,0 +1,80 @@ +import sys +import os +import time + +def main(): + + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "WeedWorker") + + from weed_worker.config import mostrar_log, get_camera_manager, iniciar_camera_manager + from shared.enums import WeedWorkerCommandType, CameraFrameType + from shared.contexto_global_redis import ContextoGlobalRedis, CmdKey, CtxKey + + def loop_ativo(): + t0 = time.time() + while True: + agora = time.time() + frequencia = (1.0 / max(agora - t0, 1e-6)) + t0 = agora + + try: + pass + except KeyboardInterrupt: + mostrar_log("🛑 Encerrando Weed Worker...") + break + except Exception as e: + mostrar_log(f"❌ Erro no loop do Weed: {e}") + + latencia = time.time() - t0 + ContextoGlobalRedis.atualizar_ctx_dict( + CtxKey.DadosWeedWorker, + momento=agora, + frequencia=frequencia, + latencia=latencia + ) + time.sleep(0.1) + + def redis_callback(dados): + try: + acao = WeedWorkerCommandType(dados.get("cmd", 0)) + + if acao == WeedWorkerCommandType.IniciarCameraManager: + mx_id = dados.get("params") + if mx_id is not None: + iniciar_camera_manager(mx_id) + elif acao == WeedWorkerCommandType.AtualizarSaudeCamera: + get_camera_manager().atualizar_saude_camera() + elif acao == WeedWorkerCommandType.GetCameraFrame: + tipo = CameraFrameType(dados.get("params", CameraFrameType.Rgb.value)) + base64_img, ts = get_camera_manager().get_select_frame(tipo) + if base64_img is not None: + resposta = { + "frame": base64_img, + "timestamp": ts, + "tipo": tipo.value + } + ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerTx, { "cmd": WeedWorkerCommandType.GetCameraFrame.value, "params": resposta }) + elif acao == WeedWorkerCommandType.SaveCameraFrames: + nome = dados.get("params", {}).get("nome", "") + pasta = dados.get("params", {}).get("caminho", "frames_salvos") + tipos = dados.get("params", {}).get("tipos", []) + get_camera_manager().salvar_frames(tipos, nome, pasta) + else: + mostrar_log(f"⚠️ Comando desconhecido: {acao.name}") + except Exception as e: + mostrar_log(f"Erro ao processar comando {acao.name}: {e}") + + + + def inicializar(): + mostrar_log("🚀 Iniciando Weed Worker...") + ContextoGlobalRedis.ouvir_comandos(CmdKey.WeedWorkerRx, redis_callback) + ContextoGlobalRedis.publicar_comando(CmdKey.WeedWorkerTx, { "cmd": WeedWorkerCommandType.ScriptCarregado.value }) + loop_ativo() + + inicializar() + +if __name__ == "__main__": + main() + diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py new file mode 100644 index 000000000..e94a9c138 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/weed_worker/weed_detector.py @@ -0,0 +1,268 @@ +import time +import cv2 +import numpy as np +import torch +from models.experimental import attempt_load +from utils.general import non_max_suppression, scale_coords, check_img_size +from utils.torch_utils import select_device +from utils.datasets import letterbox +from shared.contexto_global_redis import ContextoGlobalRedis + +class WeedDetector: + def __init__(self, weights_path, img_size=512, conf_thres=0.25, iou_thres=0.45, device=''): + self.device = select_device(device) + self.model = attempt_load(weights_path, map_location=self.device) + self.model.eval() + self.half = self.device.type != 'cpu' + if self.half: + self.model.half() + self.stride = int(self.model.stride.max()) + self.imgsz = check_img_size(img_size, s=self.stride) + self.conf_thres = conf_thres + self.iou_thres = iou_thres + self.ervas_ativas = [] + self.next_erva_id = 1 + self.frame_idx = 0 + self.ervas_identificadas = [dict() for _ in range(4)] + self.ultimo_status_bicos = {i: False for i in range(4)} + self.ervas_registradas_bico = [set() for _ in range(4)] + + def detectar(self, frame_bgr): + try: + # Pré-processamento + img = letterbox(frame_bgr, self.imgsz, stride=self.stride)[0] + img = img[:, :, ::-1].transpose(2, 0, 1) # BGR → RGB, CHW + img = np.ascontiguousarray(img) + img = torch.from_numpy(img).to(self.device) + img = img.half() if self.half else img.float() + img /= 255.0 + if img.ndimension() == 3: + img = img.unsqueeze(0) + + # Inferência + with torch.no_grad(): + pred = self.model(img, augment=False)[0] + pred = non_max_suppression(pred, self.conf_thres, self.iou_thres, classes=None, agnostic=False) + + deteccoes = [] + for i, det in enumerate(pred): + if len(det): + det[:, :4] = scale_coords(img.shape[2:], det[:, :4], frame_bgr.shape).round() + for *xyxy, conf, cls in reversed(det): + x1, y1, x2, y2 = [int(x.item()) for x in xyxy] + class_id = int(cls.item()) + conf_val = float(conf.item()) + deteccoes.append({ + 'id': class_id, + 'x': x1, + 'y': y1, + 'largura': int(x2-x1), + 'altura': int(y2-y1), + 'confianca': conf_val, + 'descricao': self.model.names[class_id] if hasattr(self.model, 'names') else 'classe' + }) + controle_bicos, deteccoes_filtro = self._calcular_atuacao_bicos(deteccoes, frame_bgr.shape) + + #self._mostrar_debug_bicos(frame_bgr, deteccoes_filtro, controle_bicos) + + self.frame_idx += 1 + + return { + "timestamp": time.time(), + "frame_width": frame_bgr.shape[1], + "frame_height": frame_bgr.shape[0], + "deteccoes": deteccoes_filtro, + "controle": controle_bicos, + "ervas_identificadas": self.ervas_identificadas + } + except Exception as e: + print(f"Erro ao detectar ervas no frame: {e}") + + def _track_ervas(self, detections, histerese, velocidade_robo): + try: + max_dist_base = 40 # px (parado) + fator_velocidade = 80 # px por m/s (ajuste conforme calibragem real!) + max_dist = max_dist_base + fator_velocidade * velocidade_robo + + cur_ervas = [] + + for det in detections: + cx = det["x"] + det["largura"] // 2 + cy = det["y"] + det["altura"] // 2 + class_id = det["id"] + + found = False + for erva in self.ervas_ativas: + if erva["class_id"] == class_id: + ecx, ecy = erva["centro"] + dist = np.hypot(cx - ecx, cy - ecy) + if dist < max_dist: + # Atualiza erva + erva["centro"] = (cx, cy) + erva["bbox"] = (det["x"], det["y"], det["largura"], det["altura"]) + erva["ultimo_frame"] = self.frame_idx + erva["frames_detectada"] = erva.get("frames_detectada", 0) + 1 + cur_ervas.append(erva) + found = True + break + if not found: + erva = { + "id": self.next_erva_id, + "centro": (cx, cy), + "bbox": (det["x"], det["y"], det["largura"], det["altura"]), + "class_id": class_id, + "ultimo_frame": self.frame_idx, + "frames_detectada": 1, # Primeira vez detectada + "status": "ativa", + "descricao": det["descricao"], + "confianca": det["confianca"] + } + self.next_erva_id += 1 + cur_ervas.append(erva) + + # Remove ervas sumidas há mais de N frames + self.ervas_ativas = [ + erva for erva in cur_ervas + if self.frame_idx - erva["ultimo_frame"] < histerese + ] + return self.ervas_ativas + except Exception as e: + print(f"Erro ao trackear ervas: {e}") + + def _calcular_atuacao_bicos(self, detections, frame_shape): + try: + from weed_worker.config import load_config + config = load_config() + qtd_bicos = config.get("qtd_bicos") + zona_inicio = config.get("zona_atuacao_y") + zona_altura = config.get("faixa_atuacao_y") + frames_consecutivos = config.get("frames_consecutivos") + histerese = config.get("frames_histerese") + velocidade_robo = config.get("velocidade_robo") + min_area = config.get("min_area_px") + max_area = config.get("max_area_frac") + + H, W = frame_shape[:2] + largura_bico = W / qtd_bicos + + y_inicio = int((1.0 - zona_inicio) * H) + y_fim = int((1.0 - (zona_inicio + zona_altura)) * H) + + atuacao_bicos = {i: False for i in range(qtd_bicos)} + ervas = self._track_ervas(detections, histerese, velocidade_robo) + + deteccoes = [] + + for det in ervas: + if not self._filtro_bbox(det, frame_shape, min_area, max_area): + continue + if det.get("frames_detectada", 0) < frames_consecutivos: + continue + deteccoes.append(det) + x, y, w, h = det["bbox"][0], det["bbox"][1], det["bbox"][2], det["bbox"][3] + y_faixa_top = min(y_inicio, y_fim) + y_faixa_bot = max(y_inicio, y_fim) + y_base = y + h + y_top = y + + # Só atua se base da erva está dentro da faixa fina de atuação! + if y_base >= y_faixa_top and y_top <= y_faixa_bot: + x1 = x + x2 = x + w - 1 + idx_ini = int(x1 / largura_bico) + idx_fim = int(x2 / largura_bico) + idx_ini = max(0, min(idx_ini, qtd_bicos - 1)) + idx_fim = max(0, min(idx_fim, qtd_bicos - 1)) + for i in range(idx_ini, idx_fim + 1): + atuacao_bicos[i] = True + self._registrar_erva_identificada(i, det["descricao"], det["id"]) + + self.ultimo_status_bicos = atuacao_bicos.copy() + + return atuacao_bicos, deteccoes + except Exception as e: + print(f"Erro ao calcular atuacaoi dos bicos: {e}") + + def _filtro_bbox(self, det, frame_shape, min_area, max_area_frac): + """Filtra bbox por área mínima e máxima (anti ruído/falsos positivos)""" + try: + H, W = frame_shape[:2] + max_area = max_area_frac * (W * H) + w, h = det["bbox"][2], det["bbox"][3] + area = w * h + if area < min_area or area > max_area: + return False + return True + except Exception as e: + print(f"Erro ao filtrar as bboxes: {e}") + + def _mostrar_debug_bicos(self, frame_rgb, detections, atuacao_bicos): + try: + from weed_worker.config import load_config + config = load_config() + qtd_bicos = config.get("qtd_bicos") + zona_inicio = config.get("zona_atuacao_y") + faixa_atuacao = config.get("faixa_atuacao_y") + H, W = frame_rgb.shape[:2] + largura_bico = W / qtd_bicos + + # Limites ajustados da faixa de atuação (do topo para baixo!) + y_inicio = int((1.0 - zona_inicio) * H) + y_fim = int((1.0 - (zona_inicio + faixa_atuacao)) * H) + + debug_img = frame_rgb.copy() + + # Paleta de cores pros bicos + CORES = [(0,255,0), (255,0,0), (0,255,255), (255,128,0), (255,0,255), (0,128,255), (128,255,0), (0,0,255)] + + # Desenha faixa de atuação (transparente) + overlay = debug_img.copy() + cv2.rectangle(overlay, (0, y_fim), (W, y_inicio), (220,220,100), -1) + cv2.addWeighted(overlay, 0.18, debug_img, 0.82, 0, debug_img) + + # Desenha a borda da zona de atuação + cv2.rectangle(debug_img, (0, y_fim), (W, y_inicio), (180,180,80), 2) + cv2.putText(debug_img, "Zona de Atuacao", (10, y_inicio-10), + cv2.FONT_HERSHEY_SIMPLEX, 0.8, (180,180,80), 2) + + controle_bicos = ContextoGlobalRedis.get_controle().get("controle_bicos", {}) + + # Desenha as zonas dos bicos e destaca ON + for i in range(qtd_bicos): + x0 = int(i * largura_bico) + x1 = int((i+1) * largura_bico) + cor = CORES[i % len(CORES)] + # Transparência para zona ON + if atuacao_bicos.get(i, False): + overlay2 = debug_img.copy() + cv2.rectangle(overlay2, (x0, y_fim), (x1, y_inicio), cor, -1) + cv2.addWeighted(overlay2, 0.15, debug_img, 0.85, 0, debug_img) + # Desenha borda da zona do bico + cv2.rectangle(debug_img, (x0, y_fim), (x1, y_inicio), cor, 1) + # Legenda ON/OFF acima de cada bico + status = "ON" if controle_bicos.get(i, False) else "OFF" + cv2.putText(debug_img, f"Bico {i} {status}", (x0+5, y_inicio+25), + cv2.FONT_HERSHEY_SIMPLEX, 0.6, cor, 2) + + # Desenha as bboxes das detecções + for det in detections: + x, y, w, h = det["bbox"][0], det["bbox"][1], det["bbox"][2], det["bbox"][3] + id = det.get("id") + class_name = det.get("descricao", "erva") + conf = det.get("confianca", 0) + bbox_cor = (0,0,255) + cv2.rectangle(debug_img, (x, y), (x+w, y+h), bbox_cor, 2) + cv2.putText(debug_img, f"ID: {id} | {class_name} {conf:.2f}", (x, y-5), + cv2.FONT_HERSHEY_SIMPLEX, 0.5, bbox_cor, 2) + + cv2.imshow("Debug Weed Worker", debug_img) + cv2.waitKey(1) + except Exception as e: + print(f"Erro ao mostrar debug: {e}") + + def _registrar_erva_identificada(self, idx_bico, class_erva, id_erva): + if id_erva in self.ervas_registradas_bico[idx_bico]: + return + d = self.ervas_identificadas[idx_bico] + d[class_erva] = d.get(class_erva, 0) + 1 + self.ervas_registradas_bico[idx_bico].add(id_erva)

      QEx@f_8$f}d% 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 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/activations.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/activations.py new file mode 100644 index 000000000..aa3ddf071 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/activations.py @@ -0,0 +1,72 @@ +# Activation functions + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +# SiLU https://arxiv.org/pdf/1606.08415.pdf ---------------------------------------------------------------------------- +class SiLU(nn.Module): # export-friendly version of nn.SiLU() + @staticmethod + def forward(x): + return x * torch.sigmoid(x) + + +class Hardswish(nn.Module): # export-friendly version of nn.Hardswish() + @staticmethod + def forward(x): + # return x * F.hardsigmoid(x) # for torchscript and CoreML + return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX + + +class MemoryEfficientSwish(nn.Module): + class F(torch.autograd.Function): + @staticmethod + def forward(ctx, x): + ctx.save_for_backward(x) + return x * torch.sigmoid(x) + + @staticmethod + def backward(ctx, grad_output): + x = ctx.saved_tensors[0] + sx = torch.sigmoid(x) + return grad_output * (sx * (1 + x * (1 - sx))) + + def forward(self, x): + return self.F.apply(x) + + +# Mish https://github.com/digantamisra98/Mish -------------------------------------------------------------------------- +class Mish(nn.Module): + @staticmethod + def forward(x): + return x * F.softplus(x).tanh() + + +class MemoryEfficientMish(nn.Module): + class F(torch.autograd.Function): + @staticmethod + def forward(ctx, x): + ctx.save_for_backward(x) + return x.mul(torch.tanh(F.softplus(x))) # x * tanh(ln(1 + exp(x))) + + @staticmethod + def backward(ctx, grad_output): + x = ctx.saved_tensors[0] + sx = torch.sigmoid(x) + fx = F.softplus(x).tanh() + return grad_output * (fx + x * sx * (1 - fx * fx)) + + def forward(self, x): + return self.F.apply(x) + + +# FReLU https://arxiv.org/abs/2007.11824 ------------------------------------------------------------------------------- +class FReLU(nn.Module): + def __init__(self, c1, k=3): # ch_in, kernel + super().__init__() + self.conv = nn.Conv2d(c1, c1, k, 1, 1, groups=c1, bias=False) + self.bn = nn.BatchNorm2d(c1) + + def forward(self, x): + return torch.max(x, self.bn(self.conv(x))) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/add_nms.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/add_nms.py new file mode 100644 index 000000000..0a1f7976a --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/add_nms.py @@ -0,0 +1,155 @@ +import numpy as np +import onnx +from onnx import shape_inference +try: + import onnx_graphsurgeon as gs +except Exception as e: + print('Import onnx_graphsurgeon failure: %s' % e) + +import logging + +LOGGER = logging.getLogger(__name__) + +class RegisterNMS(object): + def __init__( + self, + onnx_model_path: str, + precision: str = "fp32", + ): + + self.graph = gs.import_onnx(onnx.load(onnx_model_path)) + assert self.graph + LOGGER.info("ONNX graph created successfully") + # Fold constants via ONNX-GS that PyTorch2ONNX may have missed + self.graph.fold_constants() + self.precision = precision + self.batch_size = 1 + def infer(self): + """ + Sanitize the graph by cleaning any unconnected nodes, do a topological resort, + and fold constant inputs values. When possible, run shape inference on the + ONNX graph to determine tensor shapes. + """ + for _ in range(3): + count_before = len(self.graph.nodes) + + self.graph.cleanup().toposort() + try: + for node in self.graph.nodes: + for o in node.outputs: + o.shape = None + model = gs.export_onnx(self.graph) + model = shape_inference.infer_shapes(model) + self.graph = gs.import_onnx(model) + except Exception as e: + LOGGER.info(f"Shape inference could not be performed at this time:\n{e}") + try: + self.graph.fold_constants(fold_shapes=True) + except TypeError as e: + LOGGER.error( + "This version of ONNX GraphSurgeon does not support folding shapes, " + f"please upgrade your onnx_graphsurgeon module. Error:\n{e}" + ) + raise + + count_after = len(self.graph.nodes) + if count_before == count_after: + # No new folding occurred in this iteration, so we can stop for now. + break + + def save(self, output_path): + """ + Save the ONNX model to the given location. + Args: + output_path: Path pointing to the location where to write + out the updated ONNX model. + """ + self.graph.cleanup().toposort() + model = gs.export_onnx(self.graph) + onnx.save(model, output_path) + LOGGER.info(f"Saved ONNX model to {output_path}") + + def register_nms( + self, + *, + score_thresh: float = 0.25, + nms_thresh: float = 0.45, + detections_per_img: int = 100, + ): + """ + Register the ``EfficientNMS_TRT`` plugin node. + NMS expects these shapes for its input tensors: + - box_net: [batch_size, number_boxes, 4] + - class_net: [batch_size, number_boxes, number_labels] + Args: + score_thresh (float): The scalar threshold for score (low scoring boxes are removed). + nms_thresh (float): The scalar threshold for IOU (new boxes that have high IOU + overlap with previously selected boxes are removed). + detections_per_img (int): Number of best detections to keep after NMS. + """ + + self.infer() + # Find the concat node at the end of the network + op_inputs = self.graph.outputs + op = "EfficientNMS_TRT" + attrs = { + "plugin_version": "1", + "background_class": -1, # no background class + "max_output_boxes": detections_per_img, + "score_threshold": score_thresh, + "iou_threshold": nms_thresh, + "score_activation": False, + "box_coding": 0, + } + + if self.precision == "fp32": + dtype_output = np.float32 + elif self.precision == "fp16": + dtype_output = np.float16 + else: + raise NotImplementedError(f"Currently not supports precision: {self.precision}") + + # NMS Outputs + output_num_detections = gs.Variable( + name="num_dets", + dtype=np.int32, + shape=[self.batch_size, 1], + ) # A scalar indicating the number of valid detections per batch image. + output_boxes = gs.Variable( + name="det_boxes", + dtype=dtype_output, + shape=[self.batch_size, detections_per_img, 4], + ) + output_scores = gs.Variable( + name="det_scores", + dtype=dtype_output, + shape=[self.batch_size, detections_per_img], + ) + output_labels = gs.Variable( + name="det_classes", + dtype=np.int32, + shape=[self.batch_size, detections_per_img], + ) + + op_outputs = [output_num_detections, output_boxes, output_scores, output_labels] + + # Create the NMS Plugin node with the selected inputs. The outputs of the node will also + # become the final outputs of the graph. + self.graph.layer(op=op, name="batched_nms", inputs=op_inputs, outputs=op_outputs, attrs=attrs) + LOGGER.info(f"Created NMS plugin '{op}' with attributes: {attrs}") + + self.graph.outputs = op_outputs + + self.infer() + + def save(self, output_path): + """ + Save the ONNX model to the given location. + Args: + output_path: Path pointing to the location where to write + out the updated ONNX model. + """ + self.graph.cleanup().toposort() + model = gs.export_onnx(self.graph) + onnx.save(model, output_path) + LOGGER.info(f"Saved ONNX model to {output_path}") diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/autoanchor.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/autoanchor.py new file mode 100644 index 000000000..f491032e5 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/autoanchor.py @@ -0,0 +1,160 @@ +# Auto-anchor utils + +import numpy as np +import torch +import yaml +from scipy.cluster.vq import kmeans +from tqdm import tqdm + +from utils.general import colorstr + + +def check_anchor_order(m): + # Check anchor order against stride order for YOLO Detect() module m, and correct if necessary + a = m.anchor_grid.prod(-1).view(-1) # anchor area + da = a[-1] - a[0] # delta a + ds = m.stride[-1] - m.stride[0] # delta s + if da.sign() != ds.sign(): # same order + print('Reversing anchor order') + m.anchors[:] = m.anchors.flip(0) + m.anchor_grid[:] = m.anchor_grid.flip(0) + + +def check_anchors(dataset, model, thr=4.0, imgsz=640): + # Check anchor fit to data, recompute if necessary + prefix = colorstr('autoanchor: ') + print(f'\n{prefix}Analyzing anchors... ', end='') + m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1] # Detect() + shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True) + scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1)) # augment scale + wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float() # wh + + def metric(k): # compute metric + r = wh[:, None] / k[None] + x = torch.min(r, 1. / r).min(2)[0] # ratio metric + best = x.max(1)[0] # best_x + aat = (x > 1. / thr).float().sum(1).mean() # anchors above threshold + bpr = (best > 1. / thr).float().mean() # best possible recall + return bpr, aat + + anchors = m.anchor_grid.clone().cpu().view(-1, 2) # current anchors + bpr, aat = metric(anchors) + print(f'anchors/target = {aat:.2f}, Best Possible Recall (BPR) = {bpr:.4f}', end='') + if bpr < 0.98: # threshold to recompute + print('. Attempting to improve anchors, please wait...') + na = m.anchor_grid.numel() // 2 # number of anchors + try: + anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False) + except Exception as e: + print(f'{prefix}ERROR: {e}') + new_bpr = metric(anchors)[0] + if new_bpr > bpr: # replace anchors + anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors) + m.anchor_grid[:] = anchors.clone().view_as(m.anchor_grid) # for inference + check_anchor_order(m) + m.anchors[:] = anchors.clone().view_as(m.anchors) / m.stride.to(m.anchors.device).view(-1, 1, 1) # loss + print(f'{prefix}New anchors saved to model. Update model *.yaml to use these anchors in the future.') + else: + print(f'{prefix}Original anchors better than new anchors. Proceeding with original anchors.') + print('') # newline + + +def kmean_anchors(path='./data/coco.yaml', n=9, img_size=640, thr=4.0, gen=1000, verbose=True): + """ Creates kmeans-evolved anchors from training dataset + + Arguments: + path: path to dataset *.yaml, or a loaded dataset + n: number of anchors + img_size: image size used for training + thr: anchor-label wh ratio threshold hyperparameter hyp['anchor_t'] used for training, default=4.0 + gen: generations to evolve anchors using genetic algorithm + verbose: print all results + + Return: + k: kmeans evolved anchors + + Usage: + from utils.autoanchor import *; _ = kmean_anchors() + """ + thr = 1. / thr + prefix = colorstr('autoanchor: ') + + def metric(k, wh): # compute metrics + r = wh[:, None] / k[None] + x = torch.min(r, 1. / r).min(2)[0] # ratio metric + # x = wh_iou(wh, torch.tensor(k)) # iou metric + return x, x.max(1)[0] # x, best_x + + def anchor_fitness(k): # mutation fitness + _, best = metric(torch.tensor(k, dtype=torch.float32), wh) + return (best * (best > thr).float()).mean() # fitness + + def print_results(k): + k = k[np.argsort(k.prod(1))] # sort small to large + x, best = metric(k, wh0) + bpr, aat = (best > thr).float().mean(), (x > thr).float().mean() * n # best possible recall, anch > thr + print(f'{prefix}thr={thr:.2f}: {bpr:.4f} best possible recall, {aat:.2f} anchors past thr') + print(f'{prefix}n={n}, img_size={img_size}, metric_all={x.mean():.3f}/{best.mean():.3f}-mean/best, ' + f'past_thr={x[x > thr].mean():.3f}-mean: ', end='') + for i, x in enumerate(k): + print('%i,%i' % (round(x[0]), round(x[1])), end=', ' if i < len(k) - 1 else '\n') # use in *.cfg + return k + + if isinstance(path, str): # *.yaml file + with open(path) as f: + data_dict = yaml.load(f, Loader=yaml.SafeLoader) # model dict + from utils.datasets import LoadImagesAndLabels + dataset = LoadImagesAndLabels(data_dict['train'], augment=True, rect=True) + else: + dataset = path # dataset + + # Get label wh + shapes = img_size * dataset.shapes / dataset.shapes.max(1, keepdims=True) + wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh + + # Filter + i = (wh0 < 3.0).any(1).sum() + if i: + print(f'{prefix}WARNING: Extremely small objects found. {i} of {len(wh0)} labels are < 3 pixels in size.') + wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels + # wh = wh * (np.random.rand(wh.shape[0], 1) * 0.9 + 0.1) # multiply by random scale 0-1 + + # Kmeans calculation + print(f'{prefix}Running kmeans for {n} anchors on {len(wh)} points...') + s = wh.std(0) # sigmas for whitening + k, dist = kmeans(wh / s, n, iter=30) # points, mean distance + assert len(k) == n, print(f'{prefix}ERROR: scipy.cluster.vq.kmeans requested {n} points but returned only {len(k)}') + k *= s + wh = torch.tensor(wh, dtype=torch.float32) # filtered + wh0 = torch.tensor(wh0, dtype=torch.float32) # unfiltered + k = print_results(k) + + # Plot + # k, d = [None] * 20, [None] * 20 + # for i in tqdm(range(1, 21)): + # k[i-1], d[i-1] = kmeans(wh / s, i) # points, mean distance + # fig, ax = plt.subplots(1, 2, figsize=(14, 7), tight_layout=True) + # ax = ax.ravel() + # ax[0].plot(np.arange(1, 21), np.array(d) ** 2, marker='.') + # fig, ax = plt.subplots(1, 2, figsize=(14, 7)) # plot wh + # ax[0].hist(wh[wh[:, 0]<100, 0],400) + # ax[1].hist(wh[wh[:, 1]<100, 1],400) + # fig.savefig('wh.png', dpi=200) + + # Evolve + npr = np.random + f, sh, mp, s = anchor_fitness(k), k.shape, 0.9, 0.1 # fitness, generations, mutation prob, sigma + pbar = tqdm(range(gen), desc=f'{prefix}Evolving anchors with Genetic Algorithm:') # progress bar + for _ in pbar: + v = np.ones(sh) + while (v == 1).all(): # mutate until a change occurs (prevent duplicates) + v = ((npr.random(sh) < mp) * npr.random() * npr.randn(*sh) * s + 1).clip(0.3, 3.0) + kg = (k.copy() * v).clip(min=2.0) + fg = anchor_fitness(kg) + if fg > f: + f, k = fg, kg.copy() + pbar.desc = f'{prefix}Evolving anchors with Genetic Algorithm: fitness = {f:.4f}' + if verbose: + print_results(k) + + return print_results(k) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/__init__.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/__init__.py new file mode 100644 index 000000000..e9691f241 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/__init__.py @@ -0,0 +1 @@ +#init \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/mime.sh b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/mime.sh new file mode 100644 index 000000000..c319a83cf --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/mime.sh @@ -0,0 +1,26 @@ +# AWS EC2 instance startup 'MIME' script https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/ +# This script will run on every instance restart, not only on first start +# --- DO NOT COPY ABOVE COMMENTS WHEN PASTING INTO USERDATA --- + +Content-Type: multipart/mixed; boundary="//" +MIME-Version: 1.0 + +--// +Content-Type: text/cloud-config; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="cloud-config.txt" + +#cloud-config +cloud_final_modules: +- [scripts-user, always] + +--// +Content-Type: text/x-shellscript; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="userdata.txt" + +#!/bin/bash +# --- paste contents of userdata.sh here --- +--// diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/resume.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/resume.py new file mode 100644 index 000000000..338685b19 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/resume.py @@ -0,0 +1,37 @@ +# Resume all interrupted trainings in yolor/ dir including DDP trainings +# Usage: $ python utils/aws/resume.py + +import os +import sys +from pathlib import Path + +import torch +import yaml + +sys.path.append('./') # to run '$ python *.py' files in subdirectories + +port = 0 # --master_port +path = Path('').resolve() +for last in path.rglob('*/**/last.pt'): + ckpt = torch.load(last) + if ckpt['optimizer'] is None: + continue + + # Load opt.yaml + with open(last.parent.parent / 'opt.yaml') as f: + opt = yaml.load(f, Loader=yaml.SafeLoader) + + # Get device count + d = opt['device'].split(',') # devices + nd = len(d) # number of devices + ddp = nd > 1 or (nd == 0 and torch.cuda.device_count() > 1) # distributed data parallel + + if ddp: # multi-GPU + port += 1 + cmd = f'python -m torch.distributed.launch --nproc_per_node {nd} --master_port {port} train.py --resume {last}' + else: # single-GPU + cmd = f'python train.py --resume {last}' + + cmd += ' > /dev/null 2>&1 &' # redirect output to dev/null and run in daemon thread + print(cmd) + os.system(cmd) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/userdata.sh b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/userdata.sh new file mode 100644 index 000000000..5a99d4bec --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/aws/userdata.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# AWS EC2 instance startup script https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html +# This script will run only once on first instance start (for a re-start script see mime.sh) +# /home/ubuntu (ubuntu) or /home/ec2-user (amazon-linux) is working dir +# Use >300 GB SSD + +cd home/ubuntu +if [ ! -d yolor ]; then + echo "Running first-time script." # install dependencies, download COCO, pull Docker + git clone -b main https://github.com/WongKinYiu/yolov7 && sudo chmod -R 777 yolov7 + cd yolov7 + bash data/scripts/get_coco.sh && echo "Data done." & + sudo docker pull nvcr.io/nvidia/pytorch:21.08-py3 && echo "Docker done." & + python -m pip install --upgrade pip && pip install -r requirements.txt && python detect.py && echo "Requirements done." & + wait && echo "All tasks done." # finish background tasks +else + echo "Running re-start script." # resume interrupted runs + i=0 + list=$(sudo docker ps -qa) # container list i.e. $'one\ntwo\nthree\nfour' + while IFS= read -r id; do + ((i++)) + echo "restarting container $i: $id" + sudo docker start $id + # sudo docker exec -it $id python train.py --resume # single-GPU + sudo docker exec -d $id python utils/aws/resume.py # multi-scenario + done <<<"$list" +fi diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/datasets.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/datasets.py new file mode 100644 index 000000000..5fe4f7bcc --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/datasets.py @@ -0,0 +1,1320 @@ +# Dataset utils and dataloaders + +import glob +import logging +import math +import os +import random +import shutil +import time +from itertools import repeat +from multiprocessing.pool import ThreadPool +from pathlib import Path +from threading import Thread + +import cv2 +import numpy as np +import torch +import torch.nn.functional as F +from PIL import Image, ExifTags +from torch.utils.data import Dataset +from tqdm import tqdm + +import pickle +from copy import deepcopy +#from pycocotools import mask as maskUtils +from torchvision.utils import save_image +from torchvision.ops import roi_pool, roi_align, ps_roi_pool, ps_roi_align + +from utils.general import check_requirements, xyxy2xywh, xywh2xyxy, xywhn2xyxy, xyn2xy, segment2box, segments2boxes, \ + resample_segments, clean_str +from utils.torch_utils import torch_distributed_zero_first + +# Parameters +help_url = 'https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data' +img_formats = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'dng', 'webp', 'mpo'] # acceptable image suffixes +vid_formats = ['mov', 'avi', 'mp4', 'mpg', 'mpeg', 'm4v', 'wmv', 'mkv'] # acceptable video suffixes +logger = logging.getLogger(__name__) + +# Get orientation exif tag +for orientation in ExifTags.TAGS.keys(): + if ExifTags.TAGS[orientation] == 'Orientation': + break + + +def get_hash(files): + # Returns a single hash value of a list of files + return sum(os.path.getsize(f) for f in files if os.path.isfile(f)) + + +def exif_size(img): + # Returns exif-corrected PIL size + s = img.size # (width, height) + try: + rotation = dict(img._getexif().items())[orientation] + if rotation == 6: # rotation 270 + s = (s[1], s[0]) + elif rotation == 8: # rotation 90 + s = (s[1], s[0]) + except: + pass + + return s + + +def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=False, cache=False, pad=0.0, rect=False, + rank=-1, world_size=1, workers=8, image_weights=False, quad=False, prefix=''): + # Make sure only the first process in DDP process the dataset first, and the following others can use the cache + with torch_distributed_zero_first(rank): + dataset = LoadImagesAndLabels(path, imgsz, batch_size, + augment=augment, # augment images + hyp=hyp, # augmentation hyperparameters + rect=rect, # rectangular training + cache_images=cache, + single_cls=opt.single_cls, + stride=int(stride), + pad=pad, + image_weights=image_weights, + prefix=prefix) + + batch_size = min(batch_size, len(dataset)) + nw = min([os.cpu_count() // world_size, batch_size if batch_size > 1 else 0, workers]) # number of workers + sampler = torch.utils.data.distributed.DistributedSampler(dataset) if rank != -1 else None + loader = torch.utils.data.DataLoader if image_weights else InfiniteDataLoader + # Use torch.utils.data.DataLoader() if dataset.properties will update during training else InfiniteDataLoader() + dataloader = loader(dataset, + batch_size=batch_size, + num_workers=nw, + sampler=sampler, + pin_memory=True, + collate_fn=LoadImagesAndLabels.collate_fn4 if quad else LoadImagesAndLabels.collate_fn) + return dataloader, dataset + + +class InfiniteDataLoader(torch.utils.data.dataloader.DataLoader): + """ Dataloader that reuses workers + + Uses same syntax as vanilla DataLoader + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + object.__setattr__(self, 'batch_sampler', _RepeatSampler(self.batch_sampler)) + self.iterator = super().__iter__() + + def __len__(self): + return len(self.batch_sampler.sampler) + + def __iter__(self): + for i in range(len(self)): + yield next(self.iterator) + + +class _RepeatSampler(object): + """ Sampler that repeats forever + + Args: + sampler (Sampler) + """ + + def __init__(self, sampler): + self.sampler = sampler + + def __iter__(self): + while True: + yield from iter(self.sampler) + + +class LoadImages: # for inference + def __init__(self, path, img_size=640, stride=32): + p = str(Path(path).absolute()) # os-agnostic absolute path + if '*' in p: + files = sorted(glob.glob(p, recursive=True)) # glob + elif os.path.isdir(p): + files = sorted(glob.glob(os.path.join(p, '*.*'))) # dir + elif os.path.isfile(p): + files = [p] # files + else: + raise Exception(f'ERROR: {p} does not exist') + + images = [x for x in files if x.split('.')[-1].lower() in img_formats] + videos = [x for x in files if x.split('.')[-1].lower() in vid_formats] + ni, nv = len(images), len(videos) + + self.img_size = img_size + self.stride = stride + self.files = images + videos + self.nf = ni + nv # number of files + self.video_flag = [False] * ni + [True] * nv + self.mode = 'image' + if any(videos): + self.new_video(videos[0]) # new video + else: + self.cap = None + assert self.nf > 0, f'No images or videos found in {p}. ' \ + f'Supported formats are:\nimages: {img_formats}\nvideos: {vid_formats}' + + def __iter__(self): + self.count = 0 + return self + + def __next__(self): + if self.count == self.nf: + raise StopIteration + path = self.files[self.count] + + if self.video_flag[self.count]: + # Read video + self.mode = 'video' + ret_val, img0 = self.cap.read() + if not ret_val: + self.count += 1 + self.cap.release() + if self.count == self.nf: # last video + raise StopIteration + else: + path = self.files[self.count] + self.new_video(path) + ret_val, img0 = self.cap.read() + + self.frame += 1 + print(f'video {self.count + 1}/{self.nf} ({self.frame}/{self.nframes}) {path}: ', end='') + + else: + # Read image + self.count += 1 + img0 = cv2.imread(path) # BGR + assert img0 is not None, 'Image Not Found ' + path + #print(f'image {self.count}/{self.nf} {path}: ', end='') + + # Padded resize + img = letterbox(img0, self.img_size, stride=self.stride)[0] + + # Convert + img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 + img = np.ascontiguousarray(img) + + return path, img, img0, self.cap + + def new_video(self, path): + self.frame = 0 + self.cap = cv2.VideoCapture(path) + self.nframes = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) + + def __len__(self): + return self.nf # number of files + + +class LoadWebcam: # for inference + def __init__(self, pipe='0', img_size=640, stride=32): + self.img_size = img_size + self.stride = stride + + if pipe.isnumeric(): + pipe = eval(pipe) # local camera + # pipe = 'rtsp://192.168.1.64/1' # IP camera + # pipe = 'rtsp://username:password@192.168.1.64/1' # IP camera with login + # pipe = 'http://wmccpinetop.axiscam.net/mjpg/video.mjpg' # IP golf camera + + self.pipe = pipe + self.cap = cv2.VideoCapture(pipe) # video capture object + self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 3) # set buffer size + + def __iter__(self): + self.count = -1 + return self + + def __next__(self): + self.count += 1 + if cv2.waitKey(1) == ord('q'): # q to quit + self.cap.release() + cv2.destroyAllWindows() + raise StopIteration + + # Read frame + if self.pipe == 0: # local camera + ret_val, img0 = self.cap.read() + img0 = cv2.flip(img0, 1) # flip left-right + else: # IP camera + n = 0 + while True: + n += 1 + self.cap.grab() + if n % 30 == 0: # skip frames + ret_val, img0 = self.cap.retrieve() + if ret_val: + break + + # Print + assert ret_val, f'Camera Error {self.pipe}' + img_path = 'webcam.jpg' + print(f'webcam {self.count}: ', end='') + + # Padded resize + img = letterbox(img0, self.img_size, stride=self.stride)[0] + + # Convert + img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 + img = np.ascontiguousarray(img) + + return img_path, img, img0, None + + def __len__(self): + return 0 + + +class LoadStreams: # multiple IP or RTSP cameras + def __init__(self, sources='streams.txt', img_size=640, stride=32): + self.mode = 'stream' + self.img_size = img_size + self.stride = stride + + if os.path.isfile(sources): + with open(sources, 'r') as f: + sources = [x.strip() for x in f.read().strip().splitlines() if len(x.strip())] + else: + sources = [sources] + + n = len(sources) + self.imgs = [None] * n + self.sources = [clean_str(x) for x in sources] # clean source names for later + for i, s in enumerate(sources): + # Start the thread to read frames from the video stream + print(f'{i + 1}/{n}: {s}... ', end='') + url = eval(s) if s.isnumeric() else s + if 'youtube.com/' in str(url) or 'youtu.be/' in str(url): # if source is YouTube video + check_requirements(('pafy', 'youtube_dl')) + import pafy + url = pafy.new(url).getbest(preftype="mp4").url + cap = cv2.VideoCapture(url) + assert cap.isOpened(), f'Failed to open {s}' + w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + self.fps = cap.get(cv2.CAP_PROP_FPS) % 100 + + _, self.imgs[i] = cap.read() # guarantee first frame + thread = Thread(target=self.update, args=([i, cap]), daemon=True) + print(f' success ({w}x{h} at {self.fps:.2f} FPS).') + thread.start() + print('') # newline + + # check for common shapes + s = np.stack([letterbox(x, self.img_size, stride=self.stride)[0].shape for x in self.imgs], 0) # shapes + self.rect = np.unique(s, axis=0).shape[0] == 1 # rect inference if all shapes equal + if not self.rect: + print('WARNING: Different stream shapes detected. For optimal performance supply similarly-shaped streams.') + + def update(self, index, cap): + # Read next stream frame in a daemon thread + n = 0 + while cap.isOpened(): + n += 1 + # _, self.imgs[index] = cap.read() + cap.grab() + if n == 4: # read every 4th frame + success, im = cap.retrieve() + self.imgs[index] = im if success else self.imgs[index] * 0 + n = 0 + time.sleep(1 / self.fps) # wait time + + def __iter__(self): + self.count = -1 + return self + + def __next__(self): + self.count += 1 + img0 = self.imgs.copy() + if cv2.waitKey(1) == ord('q'): # q to quit + cv2.destroyAllWindows() + raise StopIteration + + # Letterbox + img = [letterbox(x, self.img_size, auto=self.rect, stride=self.stride)[0] for x in img0] + + # Stack + img = np.stack(img, 0) + + # Convert + img = img[:, :, :, ::-1].transpose(0, 3, 1, 2) # BGR to RGB, to bsx3x416x416 + img = np.ascontiguousarray(img) + + return self.sources, img, img0, None + + def __len__(self): + return 0 # 1E12 frames = 32 streams at 30 FPS for 30 years + + +def img2label_paths(img_paths): + # Define label paths as a function of image paths + sa, sb = os.sep + 'images' + os.sep, os.sep + 'labels' + os.sep # /images/, /labels/ substrings + return ['txt'.join(x.replace(sa, sb, 1).rsplit(x.split('.')[-1], 1)) for x in img_paths] + + +class LoadImagesAndLabels(Dataset): # for training/testing + def __init__(self, path, img_size=640, batch_size=16, augment=False, hyp=None, rect=False, image_weights=False, + cache_images=False, single_cls=False, stride=32, pad=0.0, prefix=''): + self.img_size = img_size + self.augment = augment + self.hyp = hyp + self.image_weights = image_weights + self.rect = False if image_weights else rect + self.mosaic = self.augment and not self.rect # load 4 images at a time into a mosaic (only during training) + self.mosaic_border = [-img_size // 2, -img_size // 2] + self.stride = stride + self.path = path + #self.albumentations = Albumentations() if augment else None + + try: + f = [] # image files + for p in path if isinstance(path, list) else [path]: + p = Path(p) # os-agnostic + if p.is_dir(): # dir + f += glob.glob(str(p / '**' / '*.*'), recursive=True) + # f = list(p.rglob('**/*.*')) # pathlib + elif p.is_file(): # file + with open(p, 'r') as t: + t = t.read().strip().splitlines() + parent = str(p.parent) + os.sep + f += [x.replace('./', parent) if x.startswith('./') else x for x in t] # local to global path + # f += [p.parent / x.lstrip(os.sep) for x in t] # local to global path (pathlib) + else: + raise Exception(f'{prefix}{p} does not exist') + self.img_files = sorted([x.replace('/', os.sep) for x in f if x.split('.')[-1].lower() in img_formats]) + # self.img_files = sorted([x for x in f if x.suffix[1:].lower() in img_formats]) # pathlib + assert self.img_files, f'{prefix}No images found' + except Exception as e: + raise Exception(f'{prefix}Error loading data from {path}: {e}\nSee {help_url}') + + # Check cache + self.label_files = img2label_paths(self.img_files) # labels + cache_path = (p if p.is_file() else Path(self.label_files[0]).parent).with_suffix('.cache') # cached labels + if cache_path.is_file(): + cache, exists = torch.load(cache_path), True # load + #if cache['hash'] != get_hash(self.label_files + self.img_files) or 'version' not in cache: # changed + # cache, exists = self.cache_labels(cache_path, prefix), False # re-cache + else: + cache, exists = self.cache_labels(cache_path, prefix), False # cache + + # Display cache + nf, nm, ne, nc, n = cache.pop('results') # found, missing, empty, corrupted, total + if exists: + d = f"Scanning '{cache_path}' images and labels... {nf} found, {nm} missing, {ne} empty, {nc} corrupted" + tqdm(None, desc=prefix + d, total=n, initial=n) # display cache results + assert nf > 0 or not augment, f'{prefix}No labels in {cache_path}. Can not train without labels. See {help_url}' + + # Read cache + cache.pop('hash') # remove hash + cache.pop('version') # remove version + labels, shapes, self.segments = zip(*cache.values()) + self.labels = list(labels) + self.shapes = np.array(shapes, dtype=np.float64) + self.img_files = list(cache.keys()) # update + self.label_files = img2label_paths(cache.keys()) # update + if single_cls: + for x in self.labels: + x[:, 0] = 0 + + n = len(shapes) # number of images + bi = np.floor(np.arange(n) / batch_size).astype(int) # batch index + nb = bi[-1] + 1 # number of batches + self.batch = bi # batch index of image + self.n = n + self.indices = range(n) + + # Rectangular Training + if self.rect: + # Sort by aspect ratio + s = self.shapes # wh + ar = s[:, 1] / s[:, 0] # aspect ratio + irect = ar.argsort() + self.img_files = [self.img_files[i] for i in irect] + self.label_files = [self.label_files[i] for i in irect] + self.labels = [self.labels[i] for i in irect] + self.shapes = s[irect] # wh + ar = ar[irect] + + # Set training image shapes + shapes = [[1, 1]] * nb + for i in range(nb): + ari = ar[bi == i] + mini, maxi = ari.min(), ari.max() + if maxi < 1: + shapes[i] = [maxi, 1] + elif mini > 1: + shapes[i] = [1, 1 / mini] + + self.batch_shapes = np.ceil(np.array(shapes) * img_size / stride + pad).astype(int) * stride + + # Cache images into memory for faster training (WARNING: large datasets may exceed system RAM) + self.imgs = [None] * n + if cache_images: + if cache_images == 'disk': + self.im_cache_dir = Path(Path(self.img_files[0]).parent.as_posix() + '_npy') + self.img_npy = [self.im_cache_dir / Path(f).with_suffix('.npy').name for f in self.img_files] + self.im_cache_dir.mkdir(parents=True, exist_ok=True) + gb = 0 # Gigabytes of cached images + self.img_hw0, self.img_hw = [None] * n, [None] * n + results = ThreadPool(8).imap(lambda x: load_image(*x), zip(repeat(self), range(n))) + pbar = tqdm(enumerate(results), total=n) + for i, x in pbar: + if cache_images == 'disk': + if not self.img_npy[i].exists(): + np.save(self.img_npy[i].as_posix(), x[0]) + gb += self.img_npy[i].stat().st_size + else: + self.imgs[i], self.img_hw0[i], self.img_hw[i] = x + gb += self.imgs[i].nbytes + pbar.desc = f'{prefix}Caching images ({gb / 1E9:.1f}GB)' + pbar.close() + + def cache_labels(self, path=Path('./labels.cache'), prefix=''): + # Cache dataset labels, check images and read shapes + x = {} # dict + nm, nf, ne, nc = 0, 0, 0, 0 # number missing, found, empty, duplicate + pbar = tqdm(zip(self.img_files, self.label_files), desc='Scanning images', total=len(self.img_files)) + for i, (im_file, lb_file) in enumerate(pbar): + try: + # verify images + im = Image.open(im_file) + im.verify() # PIL verify + shape = exif_size(im) # image size + segments = [] # instance segments + assert (shape[0] > 9) & (shape[1] > 9), f'image size {shape} <10 pixels' + assert im.format.lower() in img_formats, f'invalid image format {im.format}' + + # verify labels + if os.path.isfile(lb_file): + nf += 1 # label found + with open(lb_file, 'r') as f: + l = [x.split() for x in f.read().strip().splitlines()] + if any([len(x) > 8 for x in l]): # is segment + classes = np.array([x[0] for x in l], dtype=np.float32) + segments = [np.array(x[1:], dtype=np.float32).reshape(-1, 2) for x in l] # (cls, xy1...) + l = np.concatenate((classes.reshape(-1, 1), segments2boxes(segments)), 1) # (cls, xywh) + l = np.array(l, dtype=np.float32) + if len(l): + assert l.shape[1] == 5, 'labels require 5 columns each' + assert (l >= 0).all(), 'negative labels' + assert (l[:, 1:] <= 1).all(), 'non-normalized or out of bounds coordinate labels' + assert np.unique(l, axis=0).shape[0] == l.shape[0], 'duplicate labels' + else: + ne += 1 # label empty + l = np.zeros((0, 5), dtype=np.float32) + else: + nm += 1 # label missing + l = np.zeros((0, 5), dtype=np.float32) + x[im_file] = [l, shape, segments] + except Exception as e: + nc += 1 + print(f'{prefix}WARNING: Ignoring corrupted image and/or label {im_file}: {e}') + + pbar.desc = f"{prefix}Scanning '{path.parent / path.stem}' images and labels... " \ + f"{nf} found, {nm} missing, {ne} empty, {nc} corrupted" + pbar.close() + + if nf == 0: + print(f'{prefix}WARNING: No labels found in {path}. See {help_url}') + + x['hash'] = get_hash(self.label_files + self.img_files) + x['results'] = nf, nm, ne, nc, i + 1 + x['version'] = 0.1 # cache version + torch.save(x, path) # save for next time + logging.info(f'{prefix}New cache created: {path}') + return x + + def __len__(self): + return len(self.img_files) + + # def __iter__(self): + # self.count = -1 + # print('ran dataset iter') + # #self.shuffled_vector = np.random.permutation(self.nF) if self.augment else np.arange(self.nF) + # return self + + def __getitem__(self, index): + index = self.indices[index] # linear, shuffled, or image_weights + + hyp = self.hyp + mosaic = self.mosaic and random.random() < hyp['mosaic'] + if mosaic: + # Load mosaic + if random.random() < 0.8: + img, labels = load_mosaic(self, index) + else: + img, labels = load_mosaic9(self, index) + shapes = None + + # MixUp https://arxiv.org/pdf/1710.09412.pdf + if random.random() < hyp['mixup']: + if random.random() < 0.8: + img2, labels2 = load_mosaic(self, random.randint(0, len(self.labels) - 1)) + else: + img2, labels2 = load_mosaic9(self, random.randint(0, len(self.labels) - 1)) + r = np.random.beta(8.0, 8.0) # mixup ratio, alpha=beta=8.0 + img = (img * r + img2 * (1 - r)).astype(np.uint8) + labels = np.concatenate((labels, labels2), 0) + + else: + # Load image + img, (h0, w0), (h, w) = load_image(self, index) + + # Letterbox + shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size # final letterboxed shape + img, ratio, pad = letterbox(img, shape, auto=False, scaleup=self.augment) + shapes = (h0, w0), ((h / h0, w / w0), pad) # for COCO mAP rescaling + + labels = self.labels[index].copy() + if labels.size: # normalized xywh to pixel xyxy format + labels[:, 1:] = xywhn2xyxy(labels[:, 1:], ratio[0] * w, ratio[1] * h, padw=pad[0], padh=pad[1]) + + if self.augment: + # Augment imagespace + if not mosaic: + img, labels = random_perspective(img, labels, + degrees=hyp['degrees'], + translate=hyp['translate'], + scale=hyp['scale'], + shear=hyp['shear'], + perspective=hyp['perspective']) + + + #img, labels = self.albumentations(img, labels) + + # Augment colorspace + augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v']) + + # Apply cutouts + # if random.random() < 0.9: + # labels = cutout(img, labels) + + if random.random() < hyp['paste_in']: + sample_labels, sample_images, sample_masks = [], [], [] + while len(sample_labels) < 30: + sample_labels_, sample_images_, sample_masks_ = load_samples(self, random.randint(0, len(self.labels) - 1)) + sample_labels += sample_labels_ + sample_images += sample_images_ + sample_masks += sample_masks_ + #print(len(sample_labels)) + if len(sample_labels) == 0: + break + labels = pastein(img, labels, sample_labels, sample_images, sample_masks) + + nL = len(labels) # number of labels + if nL: + labels[:, 1:5] = xyxy2xywh(labels[:, 1:5]) # convert xyxy to xywh + labels[:, [2, 4]] /= img.shape[0] # normalized height 0-1 + labels[:, [1, 3]] /= img.shape[1] # normalized width 0-1 + + if self.augment: + # flip up-down + if random.random() < hyp['flipud']: + img = np.flipud(img) + if nL: + labels[:, 2] = 1 - labels[:, 2] + + # flip left-right + if random.random() < hyp['fliplr']: + img = np.fliplr(img) + if nL: + labels[:, 1] = 1 - labels[:, 1] + + labels_out = torch.zeros((nL, 6)) + if nL: + labels_out[:, 1:] = torch.from_numpy(labels) + + # Convert + img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 + img = np.ascontiguousarray(img) + + return torch.from_numpy(img), labels_out, self.img_files[index], shapes + + @staticmethod + def collate_fn(batch): + img, label, path, shapes = zip(*batch) # transposed + for i, l in enumerate(label): + l[:, 0] = i # add target image index for build_targets() + return torch.stack(img, 0), torch.cat(label, 0), path, shapes + + @staticmethod + def collate_fn4(batch): + img, label, path, shapes = zip(*batch) # transposed + n = len(shapes) // 4 + img4, label4, path4, shapes4 = [], [], path[:n], shapes[:n] + + ho = torch.tensor([[0., 0, 0, 1, 0, 0]]) + wo = torch.tensor([[0., 0, 1, 0, 0, 0]]) + s = torch.tensor([[1, 1, .5, .5, .5, .5]]) # scale + for i in range(n): # zidane torch.zeros(16,3,720,1280) # BCHW + i *= 4 + if random.random() < 0.5: + im = F.interpolate(img[i].unsqueeze(0).float(), scale_factor=2., mode='bilinear', align_corners=False)[ + 0].type(img[i].type()) + l = label[i] + else: + im = torch.cat((torch.cat((img[i], img[i + 1]), 1), torch.cat((img[i + 2], img[i + 3]), 1)), 2) + l = torch.cat((label[i], label[i + 1] + ho, label[i + 2] + wo, label[i + 3] + ho + wo), 0) * s + img4.append(im) + label4.append(l) + + for i, l in enumerate(label4): + l[:, 0] = i # add target image index for build_targets() + + return torch.stack(img4, 0), torch.cat(label4, 0), path4, shapes4 + + +# Ancillary functions -------------------------------------------------------------------------------------------------- +def load_image(self, index): + # loads 1 image from dataset, returns img, original hw, resized hw + img = self.imgs[index] + if img is None: # not cached + path = self.img_files[index] + img = cv2.imread(path) # BGR + assert img is not None, 'Image Not Found ' + path + h0, w0 = img.shape[:2] # orig hw + r = self.img_size / max(h0, w0) # resize image to img_size + if r != 1: # always resize down, only resize up if training with augmentation + interp = cv2.INTER_AREA if r < 1 and not self.augment else cv2.INTER_LINEAR + img = cv2.resize(img, (int(w0 * r), int(h0 * r)), interpolation=interp) + return img, (h0, w0), img.shape[:2] # img, hw_original, hw_resized + else: + return self.imgs[index], self.img_hw0[index], self.img_hw[index] # img, hw_original, hw_resized + + +def augment_hsv(img, hgain=0.5, sgain=0.5, vgain=0.5): + r = np.random.uniform(-1, 1, 3) * [hgain, sgain, vgain] + 1 # random gains + hue, sat, val = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HSV)) + dtype = img.dtype # uint8 + + x = np.arange(0, 256, dtype=np.int16) + lut_hue = ((x * r[0]) % 180).astype(dtype) + lut_sat = np.clip(x * r[1], 0, 255).astype(dtype) + lut_val = np.clip(x * r[2], 0, 255).astype(dtype) + + img_hsv = cv2.merge((cv2.LUT(hue, lut_hue), cv2.LUT(sat, lut_sat), cv2.LUT(val, lut_val))).astype(dtype) + cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR, dst=img) # no return needed + + +def hist_equalize(img, clahe=True, bgr=False): + # Equalize histogram on BGR image 'img' with img.shape(n,m,3) and range 0-255 + yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV if bgr else cv2.COLOR_RGB2YUV) + if clahe: + c = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) + yuv[:, :, 0] = c.apply(yuv[:, :, 0]) + else: + yuv[:, :, 0] = cv2.equalizeHist(yuv[:, :, 0]) # equalize Y channel histogram + return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR if bgr else cv2.COLOR_YUV2RGB) # convert YUV image to RGB + + +def load_mosaic(self, index): + # loads images in a 4-mosaic + + labels4, segments4 = [], [] + s = self.img_size + yc, xc = [int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border] # mosaic center x, y + indices = [index] + random.choices(self.indices, k=3) # 3 additional image indices + for i, index in enumerate(indices): + # Load image + img, _, (h, w) = load_image(self, index) + + # place img in img4 + if i == 0: # top left + img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles + x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image) + x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image) + elif i == 1: # top right + x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc + x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h + elif i == 2: # bottom left + x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h) + x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h) + elif i == 3: # bottom right + x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h) + x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h) + + img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax] + padw = x1a - x1b + padh = y1a - y1b + + # Labels + labels, segments = self.labels[index].copy(), self.segments[index].copy() + if labels.size: + labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh) # normalized xywh to pixel xyxy format + segments = [xyn2xy(x, w, h, padw, padh) for x in segments] + labels4.append(labels) + segments4.extend(segments) + + # Concat/clip labels + labels4 = np.concatenate(labels4, 0) + for x in (labels4[:, 1:], *segments4): + np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective() + # img4, labels4 = replicate(img4, labels4) # replicate + + # Augment + #img4, labels4, segments4 = remove_background(img4, labels4, segments4) + #sample_segments(img4, labels4, segments4, probability=self.hyp['copy_paste']) + img4, labels4, segments4 = copy_paste(img4, labels4, segments4, probability=self.hyp['copy_paste']) + img4, labels4 = random_perspective(img4, labels4, segments4, + degrees=self.hyp['degrees'], + translate=self.hyp['translate'], + scale=self.hyp['scale'], + shear=self.hyp['shear'], + perspective=self.hyp['perspective'], + border=self.mosaic_border) # border to remove + + return img4, labels4 + + +def load_mosaic9(self, index): + # loads images in a 9-mosaic + + labels9, segments9 = [], [] + s = self.img_size + indices = [index] + random.choices(self.indices, k=8) # 8 additional image indices + for i, index in enumerate(indices): + # Load image + img, _, (h, w) = load_image(self, index) + + # place img in img9 + if i == 0: # center + img9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles + h0, w0 = h, w + c = s, s, s + w, s + h # xmin, ymin, xmax, ymax (base) coordinates + elif i == 1: # top + c = s, s - h, s + w, s + elif i == 2: # top right + c = s + wp, s - h, s + wp + w, s + elif i == 3: # right + c = s + w0, s, s + w0 + w, s + h + elif i == 4: # bottom right + c = s + w0, s + hp, s + w0 + w, s + hp + h + elif i == 5: # bottom + c = s + w0 - w, s + h0, s + w0, s + h0 + h + elif i == 6: # bottom left + c = s + w0 - wp - w, s + h0, s + w0 - wp, s + h0 + h + elif i == 7: # left + c = s - w, s + h0 - h, s, s + h0 + elif i == 8: # top left + c = s - w, s + h0 - hp - h, s, s + h0 - hp + + padx, pady = c[:2] + x1, y1, x2, y2 = [max(x, 0) for x in c] # allocate coords + + # Labels + labels, segments = self.labels[index].copy(), self.segments[index].copy() + if labels.size: + labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padx, pady) # normalized xywh to pixel xyxy format + segments = [xyn2xy(x, w, h, padx, pady) for x in segments] + labels9.append(labels) + segments9.extend(segments) + + # Image + img9[y1:y2, x1:x2] = img[y1 - pady:, x1 - padx:] # img9[ymin:ymax, xmin:xmax] + hp, wp = h, w # height, width previous + + # Offset + yc, xc = [int(random.uniform(0, s)) for _ in self.mosaic_border] # mosaic center x, y + img9 = img9[yc:yc + 2 * s, xc:xc + 2 * s] + + # Concat/clip labels + labels9 = np.concatenate(labels9, 0) + labels9[:, [1, 3]] -= xc + labels9[:, [2, 4]] -= yc + c = np.array([xc, yc]) # centers + segments9 = [x - c for x in segments9] + + for x in (labels9[:, 1:], *segments9): + np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective() + # img9, labels9 = replicate(img9, labels9) # replicate + + # Augment + #img9, labels9, segments9 = remove_background(img9, labels9, segments9) + img9, labels9, segments9 = copy_paste(img9, labels9, segments9, probability=self.hyp['copy_paste']) + img9, labels9 = random_perspective(img9, labels9, segments9, + degrees=self.hyp['degrees'], + translate=self.hyp['translate'], + scale=self.hyp['scale'], + shear=self.hyp['shear'], + perspective=self.hyp['perspective'], + border=self.mosaic_border) # border to remove + + return img9, labels9 + + +def load_samples(self, index): + # loads images in a 4-mosaic + + labels4, segments4 = [], [] + s = self.img_size + yc, xc = [int(random.uniform(-x, 2 * s + x)) for x in self.mosaic_border] # mosaic center x, y + indices = [index] + random.choices(self.indices, k=3) # 3 additional image indices + for i, index in enumerate(indices): + # Load image + img, _, (h, w) = load_image(self, index) + + # place img in img4 + if i == 0: # top left + img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles + x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image) + x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image) + elif i == 1: # top right + x1a, y1a, x2a, y2a = xc, max(yc - h, 0), min(xc + w, s * 2), yc + x1b, y1b, x2b, y2b = 0, h - (y2a - y1a), min(w, x2a - x1a), h + elif i == 2: # bottom left + x1a, y1a, x2a, y2a = max(xc - w, 0), yc, xc, min(s * 2, yc + h) + x1b, y1b, x2b, y2b = w - (x2a - x1a), 0, w, min(y2a - y1a, h) + elif i == 3: # bottom right + x1a, y1a, x2a, y2a = xc, yc, min(xc + w, s * 2), min(s * 2, yc + h) + x1b, y1b, x2b, y2b = 0, 0, min(w, x2a - x1a), min(y2a - y1a, h) + + img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax] + padw = x1a - x1b + padh = y1a - y1b + + # Labels + labels, segments = self.labels[index].copy(), self.segments[index].copy() + if labels.size: + labels[:, 1:] = xywhn2xyxy(labels[:, 1:], w, h, padw, padh) # normalized xywh to pixel xyxy format + segments = [xyn2xy(x, w, h, padw, padh) for x in segments] + labels4.append(labels) + segments4.extend(segments) + + # Concat/clip labels + labels4 = np.concatenate(labels4, 0) + for x in (labels4[:, 1:], *segments4): + np.clip(x, 0, 2 * s, out=x) # clip when using random_perspective() + # img4, labels4 = replicate(img4, labels4) # replicate + + # Augment + #img4, labels4, segments4 = remove_background(img4, labels4, segments4) + sample_labels, sample_images, sample_masks = sample_segments(img4, labels4, segments4, probability=0.5) + + return sample_labels, sample_images, sample_masks + + +def copy_paste(img, labels, segments, probability=0.5): + # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy) + n = len(segments) + if probability and n: + h, w, c = img.shape # height, width, channels + im_new = np.zeros(img.shape, np.uint8) + for j in random.sample(range(n), k=round(probability * n)): + l, s = labels[j], segments[j] + box = w - l[3], l[2], w - l[1], l[4] + ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area + if (ioa < 0.30).all(): # allow 30% obscuration of existing labels + labels = np.concatenate((labels, [[l[0], *box]]), 0) + segments.append(np.concatenate((w - s[:, 0:1], s[:, 1:2]), 1)) + cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED) + + result = cv2.bitwise_and(src1=img, src2=im_new) + result = cv2.flip(result, 1) # augment segments (flip left-right) + i = result > 0 # pixels to replace + # i[:, :] = result.max(2).reshape(h, w, 1) # act over ch + img[i] = result[i] # cv2.imwrite('debug.jpg', img) # debug + + return img, labels, segments + + +def remove_background(img, labels, segments): + # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy) + n = len(segments) + h, w, c = img.shape # height, width, channels + im_new = np.zeros(img.shape, np.uint8) + img_new = np.ones(img.shape, np.uint8) * 114 + for j in range(n): + cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED) + + result = cv2.bitwise_and(src1=img, src2=im_new) + + i = result > 0 # pixels to replace + img_new[i] = result[i] # cv2.imwrite('debug.jpg', img) # debug + + return img_new, labels, segments + + +def sample_segments(img, labels, segments, probability=0.5): + # Implement Copy-Paste augmentation https://arxiv.org/abs/2012.07177, labels as nx5 np.array(cls, xyxy) + n = len(segments) + sample_labels = [] + sample_images = [] + sample_masks = [] + if probability and n: + h, w, c = img.shape # height, width, channels + for j in random.sample(range(n), k=round(probability * n)): + l, s = labels[j], segments[j] + box = l[1].astype(int).clip(0,w-1), l[2].astype(int).clip(0,h-1), l[3].astype(int).clip(0,w-1), l[4].astype(int).clip(0,h-1) + + #print(box) + if (box[2] <= box[0]) or (box[3] <= box[1]): + continue + + sample_labels.append(l[0]) + + mask = np.zeros(img.shape, np.uint8) + + cv2.drawContours(mask, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED) + sample_masks.append(mask[box[1]:box[3],box[0]:box[2],:]) + + result = cv2.bitwise_and(src1=img, src2=mask) + i = result > 0 # pixels to replace + mask[i] = result[i] # cv2.imwrite('debug.jpg', img) # debug + #print(box) + sample_images.append(mask[box[1]:box[3],box[0]:box[2],:]) + + return sample_labels, sample_images, sample_masks + + +def replicate(img, labels): + # Replicate labels + h, w = img.shape[:2] + boxes = labels[:, 1:].astype(int) + x1, y1, x2, y2 = boxes.T + s = ((x2 - x1) + (y2 - y1)) / 2 # side length (pixels) + for i in s.argsort()[:round(s.size * 0.5)]: # smallest indices + x1b, y1b, x2b, y2b = boxes[i] + bh, bw = y2b - y1b, x2b - x1b + yc, xc = int(random.uniform(0, h - bh)), int(random.uniform(0, w - bw)) # offset x, y + x1a, y1a, x2a, y2a = [xc, yc, xc + bw, yc + bh] + img[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b] # img4[ymin:ymax, xmin:xmax] + labels = np.append(labels, [[labels[i, 0], x1a, y1a, x2a, y2a]], axis=0) + + return img, labels + + +def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32): + # Resize and pad image while meeting stride-multiple constraints + shape = img.shape[:2] # current shape [height, width] + if isinstance(new_shape, int): + new_shape = (new_shape, new_shape) + + # Scale ratio (new / old) + r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) + if not scaleup: # only scale down, do not scale up (for better test mAP) + r = min(r, 1.0) + + # Compute padding + ratio = r, r # width, height ratios + new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) + dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding + if auto: # minimum rectangle + dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding + elif scaleFill: # stretch + dw, dh = 0.0, 0.0 + new_unpad = (new_shape[1], new_shape[0]) + ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios + + dw /= 2 # divide padding into 2 sides + dh /= 2 + + if shape[::-1] != new_unpad: # resize + img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) + top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) + left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) + img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border + return img, ratio, (dw, dh) + + +def random_perspective(img, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0, + border=(0, 0)): + # torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10)) + # targets = [cls, xyxy] + + height = img.shape[0] + border[0] * 2 # shape(h,w,c) + width = img.shape[1] + border[1] * 2 + + # Center + C = np.eye(3) + C[0, 2] = -img.shape[1] / 2 # x translation (pixels) + C[1, 2] = -img.shape[0] / 2 # y translation (pixels) + + # Perspective + P = np.eye(3) + P[2, 0] = random.uniform(-perspective, perspective) # x perspective (about y) + P[2, 1] = random.uniform(-perspective, perspective) # y perspective (about x) + + # Rotation and Scale + R = np.eye(3) + a = random.uniform(-degrees, degrees) + # a += random.choice([-180, -90, 0, 90]) # add 90deg rotations to small rotations + s = random.uniform(1 - scale, 1.1 + scale) + # s = 2 ** random.uniform(-scale, scale) + R[:2] = cv2.getRotationMatrix2D(angle=a, center=(0, 0), scale=s) + + # Shear + S = np.eye(3) + S[0, 1] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # x shear (deg) + S[1, 0] = math.tan(random.uniform(-shear, shear) * math.pi / 180) # y shear (deg) + + # Translation + T = np.eye(3) + T[0, 2] = random.uniform(0.5 - translate, 0.5 + translate) * width # x translation (pixels) + T[1, 2] = random.uniform(0.5 - translate, 0.5 + translate) * height # y translation (pixels) + + # Combined rotation matrix + M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT + if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed + if perspective: + img = cv2.warpPerspective(img, M, dsize=(width, height), borderValue=(114, 114, 114)) + else: # affine + img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114)) + + # Visualize + # import matplotlib.pyplot as plt + # ax = plt.subplots(1, 2, figsize=(12, 6))[1].ravel() + # ax[0].imshow(img[:, :, ::-1]) # base + # ax[1].imshow(img2[:, :, ::-1]) # warped + + # Transform label coordinates + n = len(targets) + if n: + use_segments = any(x.any() for x in segments) + new = np.zeros((n, 4)) + if use_segments: # warp segments + segments = resample_segments(segments) # upsample + for i, segment in enumerate(segments): + xy = np.ones((len(segment), 3)) + xy[:, :2] = segment + xy = xy @ M.T # transform + xy = xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2] # perspective rescale or affine + + # clip + new[i] = segment2box(xy, width, height) + + else: # warp boxes + xy = np.ones((n * 4, 3)) + xy[:, :2] = targets[:, [1, 2, 3, 4, 1, 4, 3, 2]].reshape(n * 4, 2) # x1y1, x2y2, x1y2, x2y1 + xy = xy @ M.T # transform + xy = (xy[:, :2] / xy[:, 2:3] if perspective else xy[:, :2]).reshape(n, 8) # perspective rescale or affine + + # create new boxes + x = xy[:, [0, 2, 4, 6]] + y = xy[:, [1, 3, 5, 7]] + new = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T + + # clip + new[:, [0, 2]] = new[:, [0, 2]].clip(0, width) + new[:, [1, 3]] = new[:, [1, 3]].clip(0, height) + + # filter candidates + i = box_candidates(box1=targets[:, 1:5].T * s, box2=new.T, area_thr=0.01 if use_segments else 0.10) + targets = targets[i] + targets[:, 1:5] = new[i] + + return img, targets + + +def box_candidates(box1, box2, wh_thr=2, ar_thr=20, area_thr=0.1, eps=1e-16): # box1(4,n), box2(4,n) + # Compute candidate boxes: box1 before augment, box2 after augment, wh_thr (pixels), aspect_ratio_thr, area_ratio + w1, h1 = box1[2] - box1[0], box1[3] - box1[1] + w2, h2 = box2[2] - box2[0], box2[3] - box2[1] + ar = np.maximum(w2 / (h2 + eps), h2 / (w2 + eps)) # aspect ratio + return (w2 > wh_thr) & (h2 > wh_thr) & (w2 * h2 / (w1 * h1 + eps) > area_thr) & (ar < ar_thr) # candidates + + +def bbox_ioa(box1, box2): + # Returns the intersection over box2 area given box1, box2. box1 is 4, box2 is nx4. boxes are x1y1x2y2 + box2 = box2.transpose() + + # Get the coordinates of bounding boxes + b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] + b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] + + # Intersection area + inter_area = (np.minimum(b1_x2, b2_x2) - np.maximum(b1_x1, b2_x1)).clip(0) * \ + (np.minimum(b1_y2, b2_y2) - np.maximum(b1_y1, b2_y1)).clip(0) + + # box2 area + box2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) + 1e-16 + + # Intersection over box2 area + return inter_area / box2_area + + +def cutout(image, labels): + # Applies image cutout augmentation https://arxiv.org/abs/1708.04552 + h, w = image.shape[:2] + + # create random masks + scales = [0.5] * 1 + [0.25] * 2 + [0.125] * 4 + [0.0625] * 8 + [0.03125] * 16 # image size fraction + for s in scales: + mask_h = random.randint(1, int(h * s)) + mask_w = random.randint(1, int(w * s)) + + # box + xmin = max(0, random.randint(0, w) - mask_w // 2) + ymin = max(0, random.randint(0, h) - mask_h // 2) + xmax = min(w, xmin + mask_w) + ymax = min(h, ymin + mask_h) + + # apply random color mask + image[ymin:ymax, xmin:xmax] = [random.randint(64, 191) for _ in range(3)] + + # return unobscured labels + if len(labels) and s > 0.03: + box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32) + ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area + labels = labels[ioa < 0.60] # remove >60% obscured labels + + return labels + + +def pastein(image, labels, sample_labels, sample_images, sample_masks): + # Applies image cutout augmentation https://arxiv.org/abs/1708.04552 + h, w = image.shape[:2] + + # create random masks + scales = [0.75] * 2 + [0.5] * 4 + [0.25] * 4 + [0.125] * 4 + [0.0625] * 6 # image size fraction + for s in scales: + if random.random() < 0.2: + continue + mask_h = random.randint(1, int(h * s)) + mask_w = random.randint(1, int(w * s)) + + # box + xmin = max(0, random.randint(0, w) - mask_w // 2) + ymin = max(0, random.randint(0, h) - mask_h // 2) + xmax = min(w, xmin + mask_w) + ymax = min(h, ymin + mask_h) + + box = np.array([xmin, ymin, xmax, ymax], dtype=np.float32) + if len(labels): + ioa = bbox_ioa(box, labels[:, 1:5]) # intersection over area + else: + ioa = np.zeros(1) + + if (ioa < 0.30).all() and len(sample_labels) and (xmax > xmin+20) and (ymax > ymin+20): # allow 30% obscuration of existing labels + sel_ind = random.randint(0, len(sample_labels)-1) + #print(len(sample_labels)) + #print(sel_ind) + #print((xmax-xmin, ymax-ymin)) + #print(image[ymin:ymax, xmin:xmax].shape) + #print([[sample_labels[sel_ind], *box]]) + #print(labels.shape) + hs, ws, cs = sample_images[sel_ind].shape + r_scale = min((ymax-ymin)/hs, (xmax-xmin)/ws) + r_w = int(ws*r_scale) + r_h = int(hs*r_scale) + + if (r_w > 10) and (r_h > 10): + r_mask = cv2.resize(sample_masks[sel_ind], (r_w, r_h)) + r_image = cv2.resize(sample_images[sel_ind], (r_w, r_h)) + temp_crop = image[ymin:ymin+r_h, xmin:xmin+r_w] + m_ind = r_mask > 0 + if m_ind.astype(np.int32).sum() > 60: + temp_crop[m_ind] = r_image[m_ind] + #print(sample_labels[sel_ind]) + #print(sample_images[sel_ind].shape) + #print(temp_crop.shape) + box = np.array([xmin, ymin, xmin+r_w, ymin+r_h], dtype=np.float32) + if len(labels): + labels = np.concatenate((labels, [[sample_labels[sel_ind], *box]]), 0) + else: + labels = np.array([[sample_labels[sel_ind], *box]]) + + image[ymin:ymin+r_h, xmin:xmin+r_w] = temp_crop + + return labels + +class Albumentations: + # YOLOv5 Albumentations class (optional, only used if package is installed) + def __init__(self): + self.transform = None + import albumentations as A + + self.transform = A.Compose([ + A.CLAHE(p=0.01), + A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.01), + A.RandomGamma(gamma_limit=[80, 120], p=0.01), + A.Blur(p=0.01), + A.MedianBlur(p=0.01), + A.ToGray(p=0.01), + A.ImageCompression(quality_lower=75, p=0.01),], + bbox_params=A.BboxParams(format='pascal_voc', label_fields=['class_labels'])) + + #logging.info(colorstr('albumentations: ') + ', '.join(f'{x}' for x in self.transform.transforms if x.p)) + + def __call__(self, im, labels, p=1.0): + if self.transform and random.random() < p: + new = self.transform(image=im, bboxes=labels[:, 1:], class_labels=labels[:, 0]) # transformed + im, labels = new['image'], np.array([[c, *b] for c, b in zip(new['class_labels'], new['bboxes'])]) + return im, labels + + +def create_folder(path='./new'): + # Create folder + if os.path.exists(path): + shutil.rmtree(path) # delete output folder + os.makedirs(path) # make new output folder + + +def flatten_recursive(path='../coco'): + # Flatten a recursive directory by bringing all files to top level + new_path = Path(path + '_flat') + create_folder(new_path) + for file in tqdm(glob.glob(str(Path(path)) + '/**/*.*', recursive=True)): + shutil.copyfile(file, new_path / Path(file).name) + + +def extract_boxes(path='../coco/'): # from utils.datasets import *; extract_boxes('../coco128') + # Convert detection dataset into classification dataset, with one directory per class + + path = Path(path) # images dir + shutil.rmtree(path / 'classifier') if (path / 'classifier').is_dir() else None # remove existing + files = list(path.rglob('*.*')) + n = len(files) # number of files + for im_file in tqdm(files, total=n): + if im_file.suffix[1:] in img_formats: + # image + im = cv2.imread(str(im_file))[..., ::-1] # BGR to RGB + h, w = im.shape[:2] + + # labels + lb_file = Path(img2label_paths([str(im_file)])[0]) + if Path(lb_file).exists(): + with open(lb_file, 'r') as f: + lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32) # labels + + for j, x in enumerate(lb): + c = int(x[0]) # class + f = (path / 'classifier') / f'{c}' / f'{path.stem}_{im_file.stem}_{j}.jpg' # new filename + if not f.parent.is_dir(): + f.parent.mkdir(parents=True) + + b = x[1:] * [w, h, w, h] # box + # b[2:] = b[2:].max() # rectangle to square + b[2:] = b[2:] * 1.2 + 3 # pad + b = xywh2xyxy(b.reshape(-1, 4)).ravel().astype(np.int) + + b[[0, 2]] = np.clip(b[[0, 2]], 0, w) # clip boxes outside of image + b[[1, 3]] = np.clip(b[[1, 3]], 0, h) + assert cv2.imwrite(str(f), im[b[1]:b[3], b[0]:b[2]]), f'box failure in {f}' + + +def autosplit(path='../coco', weights=(0.9, 0.1, 0.0), annotated_only=False): + """ Autosplit a dataset into train/val/test splits and save path/autosplit_*.txt files + Usage: from utils.datasets import *; autosplit('../coco') + Arguments + path: Path to images directory + weights: Train, val, test weights (list) + annotated_only: Only use images with an annotated txt file + """ + path = Path(path) # images dir + files = sum([list(path.rglob(f"*.{img_ext}")) for img_ext in img_formats], []) # image files only + n = len(files) # number of files + indices = random.choices([0, 1, 2], weights=weights, k=n) # assign each image to a split + + txt = ['autosplit_train.txt', 'autosplit_val.txt', 'autosplit_test.txt'] # 3 txt files + [(path / x).unlink() for x in txt if (path / x).exists()] # remove existing + + print(f'Autosplitting images from {path}' + ', using *.txt labeled images only' * annotated_only) + for i, img in tqdm(zip(indices, files), total=n): + if not annotated_only or Path(img2label_paths([str(img)])[0]).exists(): # check label + with open(path / txt[i], 'a') as f: + f.write(str(img) + '\n') # add image to txt file + + +def load_segmentations(self, index): + key = '/work/handsomejw66/coco17/' + self.img_files[index] + #print(key) + # /work/handsomejw66/coco17/ + return self.segs[key] diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/general.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/general.py new file mode 100644 index 000000000..decdcc64e --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/general.py @@ -0,0 +1,892 @@ +# YOLOR general utils + +import glob +import logging +import math +import os +import platform +import random +import re +import subprocess +import time +from pathlib import Path + +import cv2 +import numpy as np +import pandas as pd +import torch +import torchvision +import yaml + +from utils.google_utils import gsutil_getsize +from utils.metrics import fitness +from utils.torch_utils import init_torch_seeds + +# Settings +torch.set_printoptions(linewidth=320, precision=5, profile='long') +np.set_printoptions(linewidth=320, formatter={'float_kind': '{:11.5g}'.format}) # format short g, %precision=5 +pd.options.display.max_columns = 10 +cv2.setNumThreads(0) # prevent OpenCV from multithreading (incompatible with PyTorch DataLoader) +os.environ['NUMEXPR_MAX_THREADS'] = str(min(os.cpu_count(), 8)) # NumExpr max threads + + +def set_logging(rank=-1): + logging.basicConfig( + format="%(message)s", + level=logging.INFO if rank in [-1, 0] else logging.WARN) + + +def init_seeds(seed=0): + # Initialize random number generator (RNG) seeds + random.seed(seed) + np.random.seed(seed) + init_torch_seeds(seed) + + +def get_latest_run(search_dir='.'): + # Return path to most recent 'last.pt' in /runs (i.e. to --resume from) + last_list = glob.glob(f'{search_dir}/**/last*.pt', recursive=True) + return max(last_list, key=os.path.getctime) if last_list else '' + + +def isdocker(): + # Is environment a Docker container + return Path('/workspace').exists() # or Path('/.dockerenv').exists() + + +def emojis(str=''): + # Return platform-dependent emoji-safe version of string + return str.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else str + + +def check_online(): + # Check internet connectivity + import socket + try: + socket.create_connection(("1.1.1.1", 443), 5) # check host accesability + return True + except OSError: + return False + + +def check_git_status(): + # Recommend 'git pull' if code is out of date + print(colorstr('github: '), end='') + try: + assert Path('.git').exists(), 'skipping check (not a git repository)' + assert not isdocker(), 'skipping check (Docker image)' + assert check_online(), 'skipping check (offline)' + + cmd = 'git fetch && git config --get remote.origin.url' + url = subprocess.check_output(cmd, shell=True).decode().strip().rstrip('.git') # github repo url + branch = subprocess.check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode().strip() # checked out + n = int(subprocess.check_output(f'git rev-list {branch}..origin/master --count', shell=True)) # commits behind + if n > 0: + s = f"⚠️ WARNING: code is out of date by {n} commit{'s' * (n > 1)}. " \ + f"Use 'git pull' to update or 'git clone {url}' to download latest." + else: + s = f'up to date with {url} ✅' + print(emojis(s)) # emoji-safe + except Exception as e: + print(e) + + +def check_requirements(requirements='requirements.txt', exclude=()): + # Check installed dependencies meet requirements (pass *.txt file or list of packages) + import pkg_resources as pkg + prefix = colorstr('red', 'bold', 'requirements:') + if isinstance(requirements, (str, Path)): # requirements.txt file + file = Path(requirements) + if not file.exists(): + print(f"{prefix} {file.resolve()} not found, check failed.") + return + requirements = [f'{x.name}{x.specifier}' for x in pkg.parse_requirements(file.open()) if x.name not in exclude] + else: # list or tuple of packages + requirements = [x for x in requirements if x not in exclude] + + n = 0 # number of packages updates + for r in requirements: + try: + pkg.require(r) + except Exception as e: # DistributionNotFound or VersionConflict if requirements not met + n += 1 + print(f"{prefix} {e.req} not found and is required by YOLOR, attempting auto-update...") + print(subprocess.check_output(f"pip install '{e.req}'", shell=True).decode()) + + if n: # if packages updated + source = file.resolve() if 'file' in locals() else requirements + s = f"{prefix} {n} package{'s' * (n > 1)} updated per {source}\n" \ + f"{prefix} ⚠️ {colorstr('bold', 'Restart runtime or rerun command for updates to take effect')}\n" + print(emojis(s)) # emoji-safe + + +def check_img_size(img_size, s=32): + # Verify img_size is a multiple of stride s + new_size = make_divisible(img_size, int(s)) # ceil gs-multiple + if new_size != img_size: + print('WARNING: --img-size %g must be multiple of max stride %g, updating to %g' % (img_size, s, new_size)) + return new_size + + +def check_imshow(): + # Check if environment supports image displays + try: + assert not isdocker(), 'cv2.imshow() is disabled in Docker environments' + cv2.imshow('test', np.zeros((1, 1, 3))) + cv2.waitKey(1) + cv2.destroyAllWindows() + cv2.waitKey(1) + return True + except Exception as e: + print(f'WARNING: Environment does not support cv2.imshow() or PIL Image.show() image displays\n{e}') + return False + + +def check_file(file): + # Search for file if not found + if Path(file).is_file() or file == '': + return file + else: + files = glob.glob('./**/' + file, recursive=True) # find file + assert len(files), f'File Not Found: {file}' # assert file was found + assert len(files) == 1, f"Multiple files match '{file}', specify exact path: {files}" # assert unique + return files[0] # return file + + +def check_dataset(dict): + # Download dataset if not found locally + val, s = dict.get('val'), dict.get('download') + if val and len(val): + val = [Path(x).resolve() for x in (val if isinstance(val, list) else [val])] # val path + if not all(x.exists() for x in val): + print('\nWARNING: Dataset not found, nonexistent paths: %s' % [str(x) for x in val if not x.exists()]) + if s and len(s): # download script + print('Downloading %s ...' % s) + if s.startswith('http') and s.endswith('.zip'): # URL + f = Path(s).name # filename + torch.hub.download_url_to_file(s, f) + r = os.system('unzip -q %s -d ../ && rm %s' % (f, f)) # unzip + else: # bash script + r = os.system(s) + print('Dataset autodownload %s\n' % ('success' if r == 0 else 'failure')) # analyze return value + else: + raise Exception('Dataset not found.') + + +def make_divisible(x, divisor): + # Returns x evenly divisible by divisor + return math.ceil(x / divisor) * divisor + + +def clean_str(s): + # Cleans a string by replacing special characters with underscore _ + return re.sub(pattern="[|@#!¡·$€%&()=?¿^*;:,¨´><+]", repl="_", string=s) + + +def one_cycle(y1=0.0, y2=1.0, steps=100): + # lambda function for sinusoidal ramp from y1 to y2 + return lambda x: ((1 - math.cos(x * math.pi / steps)) / 2) * (y2 - y1) + y1 + + +def colorstr(*input): + # Colors a string https://en.wikipedia.org/wiki/ANSI_escape_code, i.e. colorstr('blue', 'hello world') + *args, string = input if len(input) > 1 else ('blue', 'bold', input[0]) # color arguments, string + colors = {'black': '\033[30m', # basic colors + 'red': '\033[31m', + 'green': '\033[32m', + 'yellow': '\033[33m', + 'blue': '\033[34m', + 'magenta': '\033[35m', + 'cyan': '\033[36m', + 'white': '\033[37m', + 'bright_black': '\033[90m', # bright colors + 'bright_red': '\033[91m', + 'bright_green': '\033[92m', + 'bright_yellow': '\033[93m', + 'bright_blue': '\033[94m', + 'bright_magenta': '\033[95m', + 'bright_cyan': '\033[96m', + 'bright_white': '\033[97m', + 'end': '\033[0m', # misc + 'bold': '\033[1m', + 'underline': '\033[4m'} + return ''.join(colors[x] for x in args) + f'{string}' + colors['end'] + + +def labels_to_class_weights(labels, nc=80): + # Get class weights (inverse frequency) from training labels + if labels[0] is None: # no labels loaded + return torch.Tensor() + + labels = np.concatenate(labels, 0) # labels.shape = (866643, 5) for COCO + classes = labels[:, 0].astype(np.int32) # labels = [class xywh] + weights = np.bincount(classes, minlength=nc) # occurrences per class + + # Prepend gridpoint count (for uCE training) + # gpi = ((320 / 32 * np.array([1, 2, 4])) ** 2 * 3).sum() # gridpoints per image + # weights = np.hstack([gpi * len(labels) - weights.sum() * 9, weights * 9]) ** 0.5 # prepend gridpoints to start + + weights[weights == 0] = 1 # replace empty bins with 1 + weights = 1 / weights # number of targets per class + weights /= weights.sum() # normalize + return torch.from_numpy(weights) + + +def labels_to_image_weights(labels, nc=80, class_weights=np.ones(80)): + # Produces image weights based on class_weights and image contents + class_counts = np.array([np.bincount(x[:, 0].astype(np.int32), minlength=nc) for x in labels]) + image_weights = (class_weights.reshape(1, nc) * class_counts).sum(1) + # index = random.choices(range(n), weights=image_weights, k=1) # weight image sample + return image_weights + + +def coco80_to_coco91_class(): # converts 80-index (val2014) to 91-index (paper) + # https://tech.amikelive.com/node-718/what-object-categories-labels-are-in-coco-dataset/ + # a = np.loadtxt('data/coco.names', dtype='str', delimiter='\n') + # b = np.loadtxt('data/coco_paper.names', dtype='str', delimiter='\n') + # x1 = [list(a[i] == b).index(True) + 1 for i in range(80)] # darknet to coco + # x2 = [list(b[i] == a).index(True) if any(b[i] == a) else None for i in range(91)] # coco to darknet + x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90] + return x + + +def xyxy2xywh(x): + # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center + y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center + y[:, 2] = x[:, 2] - x[:, 0] # width + y[:, 3] = x[:, 3] - x[:, 1] # height + return y + + +def xywh2xyxy(x): + # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x + y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y + y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x + y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y + return y + + +def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0): + # Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = w * (x[:, 0] - x[:, 2] / 2) + padw # top left x + y[:, 1] = h * (x[:, 1] - x[:, 3] / 2) + padh # top left y + y[:, 2] = w * (x[:, 0] + x[:, 2] / 2) + padw # bottom right x + y[:, 3] = h * (x[:, 1] + x[:, 3] / 2) + padh # bottom right y + return y + + +def xyn2xy(x, w=640, h=640, padw=0, padh=0): + # Convert normalized segments into pixel segments, shape (n,2) + y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) + y[:, 0] = w * x[:, 0] + padw # top left x + y[:, 1] = h * x[:, 1] + padh # top left y + return y + + +def segment2box(segment, width=640, height=640): + # Convert 1 segment label to 1 box label, applying inside-image constraint, i.e. (xy1, xy2, ...) to (xyxy) + x, y = segment.T # segment xy + inside = (x >= 0) & (y >= 0) & (x <= width) & (y <= height) + x, y, = x[inside], y[inside] + return np.array([x.min(), y.min(), x.max(), y.max()]) if any(x) else np.zeros((1, 4)) # xyxy + + +def segments2boxes(segments): + # Convert segment labels to box labels, i.e. (cls, xy1, xy2, ...) to (cls, xywh) + boxes = [] + for s in segments: + x, y = s.T # segment xy + boxes.append([x.min(), y.min(), x.max(), y.max()]) # cls, xyxy + return xyxy2xywh(np.array(boxes)) # cls, xywh + + +def resample_segments(segments, n=1000): + # Up-sample an (n,2) segment + for i, s in enumerate(segments): + s = np.concatenate((s, s[0:1, :]), axis=0) + x = np.linspace(0, len(s) - 1, n) + xp = np.arange(len(s)) + segments[i] = np.concatenate([np.interp(x, xp, s[:, i]) for i in range(2)]).reshape(2, -1).T # segment xy + return segments + + +def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None): + # Rescale coords (xyxy) from img1_shape to img0_shape + if ratio_pad is None: # calculate from img0_shape + gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new + pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding + else: + gain = ratio_pad[0][0] + pad = ratio_pad[1] + + coords[:, [0, 2]] -= pad[0] # x padding + coords[:, [1, 3]] -= pad[1] # y padding + coords[:, :4] /= gain + clip_coords(coords, img0_shape) + return coords + + +def clip_coords(boxes, img_shape): + # Clip bounding xyxy bounding boxes to image shape (height, width) + boxes[:, 0].clamp_(0, img_shape[1]) # x1 + boxes[:, 1].clamp_(0, img_shape[0]) # y1 + boxes[:, 2].clamp_(0, img_shape[1]) # x2 + boxes[:, 3].clamp_(0, img_shape[0]) # y2 + + +def bbox_iou(box1, box2, x1y1x2y2=True, GIoU=False, DIoU=False, CIoU=False, eps=1e-7): + # Returns the IoU of box1 to box2. box1 is 4, box2 is nx4 + box2 = box2.T + + # Get the coordinates of bounding boxes + if x1y1x2y2: # x1, y1, x2, y2 = box1 + b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] + b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] + else: # transform from xywh to xyxy + b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 + b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 + b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 + b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2 + + # Intersection area + inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \ + (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0) + + # Union Area + w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps + w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps + union = w1 * h1 + w2 * h2 - inter + eps + + iou = inter / union + + if GIoU or DIoU or CIoU: + cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width + ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height + if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1 + c2 = cw ** 2 + ch ** 2 + eps # convex diagonal squared + rho2 = ((b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 + + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2) / 4 # center distance squared + if DIoU: + return iou - rho2 / c2 # DIoU + elif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47 + v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / (h2 + eps)) - torch.atan(w1 / (h1 + eps)), 2) + with torch.no_grad(): + alpha = v / (v - iou + (1 + eps)) + return iou - (rho2 / c2 + v * alpha) # CIoU + else: # GIoU https://arxiv.org/pdf/1902.09630.pdf + c_area = cw * ch + eps # convex area + return iou - (c_area - union) / c_area # GIoU + else: + return iou # IoU + + + + +def bbox_alpha_iou(box1, box2, x1y1x2y2=False, GIoU=False, DIoU=False, CIoU=False, alpha=2, eps=1e-9): + # Returns tsqrt_he IoU of box1 to box2. box1 is 4, box2 is nx4 + box2 = box2.T + + # Get the coordinates of bounding boxes + if x1y1x2y2: # x1, y1, x2, y2 = box1 + b1_x1, b1_y1, b1_x2, b1_y2 = box1[0], box1[1], box1[2], box1[3] + b2_x1, b2_y1, b2_x2, b2_y2 = box2[0], box2[1], box2[2], box2[3] + else: # transform from xywh to xyxy + b1_x1, b1_x2 = box1[0] - box1[2] / 2, box1[0] + box1[2] / 2 + b1_y1, b1_y2 = box1[1] - box1[3] / 2, box1[1] + box1[3] / 2 + b2_x1, b2_x2 = box2[0] - box2[2] / 2, box2[0] + box2[2] / 2 + b2_y1, b2_y2 = box2[1] - box2[3] / 2, box2[1] + box2[3] / 2 + + # Intersection area + inter = (torch.min(b1_x2, b2_x2) - torch.max(b1_x1, b2_x1)).clamp(0) * \ + (torch.min(b1_y2, b2_y2) - torch.max(b1_y1, b2_y1)).clamp(0) + + # Union Area + w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps + w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps + union = w1 * h1 + w2 * h2 - inter + eps + + # change iou into pow(iou+eps) + # iou = inter / union + iou = torch.pow(inter/union + eps, alpha) + # beta = 2 * alpha + if GIoU or DIoU or CIoU: + cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1) # convex (smallest enclosing box) width + ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1) # convex height + if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1 + c2 = (cw ** 2 + ch ** 2) ** alpha + eps # convex diagonal + rho_x = torch.abs(b2_x1 + b2_x2 - b1_x1 - b1_x2) + rho_y = torch.abs(b2_y1 + b2_y2 - b1_y1 - b1_y2) + rho2 = ((rho_x ** 2 + rho_y ** 2) / 4) ** alpha # center distance + if DIoU: + return iou - rho2 / c2 # DIoU + elif CIoU: # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47 + v = (4 / math.pi ** 2) * torch.pow(torch.atan(w2 / h2) - torch.atan(w1 / h1), 2) + with torch.no_grad(): + alpha_ciou = v / ((1 + eps) - inter / union + v) + # return iou - (rho2 / c2 + v * alpha_ciou) # CIoU + return iou - (rho2 / c2 + torch.pow(v * alpha_ciou + eps, alpha)) # CIoU + else: # GIoU https://arxiv.org/pdf/1902.09630.pdf + # c_area = cw * ch + eps # convex area + # return iou - (c_area - union) / c_area # GIoU + c_area = torch.max(cw * ch + eps, union) # convex area + return iou - torch.pow((c_area - union) / c_area + eps, alpha) # GIoU + else: + return iou # torch.log(iou+eps) or iou + + +def box_iou(box1, box2): + # https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + box1 (Tensor[N, 4]) + box2 (Tensor[M, 4]) + Returns: + iou (Tensor[N, M]): the NxM matrix containing the pairwise + IoU values for every element in boxes1 and boxes2 + """ + + def box_area(box): + # box = 4xn + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2) + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + return inter / (area1[:, None] + area2 - inter) # iou = inter / (area1 + area2 - inter) + + +def wh_iou(wh1, wh2): + # Returns the nxm IoU matrix. wh1 is nx2, wh2 is mx2 + wh1 = wh1[:, None] # [N,1,2] + wh2 = wh2[None] # [1,M,2] + inter = torch.min(wh1, wh2).prod(2) # [N,M] + return inter / (wh1.prod(2) + wh2.prod(2) - inter) # iou = inter / (area1 + area2 - inter) + + +def box_giou(box1, box2): + """ + Return generalized intersection-over-union (Jaccard index) between two sets of boxes. + Both sets of boxes are expected to be in ``(x1, y1, x2, y2)`` format with + ``0 <= x1 < x2`` and ``0 <= y1 < y2``. + Args: + boxes1 (Tensor[N, 4]): first set of boxes + boxes2 (Tensor[M, 4]): second set of boxes + Returns: + Tensor[N, M]: the NxM matrix containing the pairwise generalized IoU values + for every element in boxes1 and boxes2 + """ + + def box_area(box): + # box = 4xn + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + union = (area1[:, None] + area2 - inter) + + iou = inter / union + + lti = torch.min(box1[:, None, :2], box2[:, :2]) + rbi = torch.max(box1[:, None, 2:], box2[:, 2:]) + + whi = (rbi - lti).clamp(min=0) # [N,M,2] + areai = whi[:, :, 0] * whi[:, :, 1] + + return iou - (areai - union) / areai + + +def box_ciou(box1, box2, eps: float = 1e-7): + """ + Return complete intersection-over-union (Jaccard index) between two sets of boxes. + Both sets of boxes are expected to be in ``(x1, y1, x2, y2)`` format with + ``0 <= x1 < x2`` and ``0 <= y1 < y2``. + Args: + boxes1 (Tensor[N, 4]): first set of boxes + boxes2 (Tensor[M, 4]): second set of boxes + eps (float, optional): small number to prevent division by zero. Default: 1e-7 + Returns: + Tensor[N, M]: the NxM matrix containing the pairwise complete IoU values + for every element in boxes1 and boxes2 + """ + + def box_area(box): + # box = 4xn + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + union = (area1[:, None] + area2 - inter) + + iou = inter / union + + lti = torch.min(box1[:, None, :2], box2[:, :2]) + rbi = torch.max(box1[:, None, 2:], box2[:, 2:]) + + whi = (rbi - lti).clamp(min=0) # [N,M,2] + diagonal_distance_squared = (whi[:, :, 0] ** 2) + (whi[:, :, 1] ** 2) + eps + + # centers of boxes + x_p = (box1[:, None, 0] + box1[:, None, 2]) / 2 + y_p = (box1[:, None, 1] + box1[:, None, 3]) / 2 + x_g = (box2[:, 0] + box2[:, 2]) / 2 + y_g = (box2[:, 1] + box2[:, 3]) / 2 + # The distance between boxes' centers squared. + centers_distance_squared = (x_p - x_g) ** 2 + (y_p - y_g) ** 2 + + w_pred = box1[:, None, 2] - box1[:, None, 0] + h_pred = box1[:, None, 3] - box1[:, None, 1] + + w_gt = box2[:, 2] - box2[:, 0] + h_gt = box2[:, 3] - box2[:, 1] + + v = (4 / (torch.pi ** 2)) * torch.pow((torch.atan(w_gt / h_gt) - torch.atan(w_pred / h_pred)), 2) + with torch.no_grad(): + alpha = v / (1 - iou + v + eps) + return iou - (centers_distance_squared / diagonal_distance_squared) - alpha * v + + +def box_diou(box1, box2, eps: float = 1e-7): + """ + Return distance intersection-over-union (Jaccard index) between two sets of boxes. + Both sets of boxes are expected to be in ``(x1, y1, x2, y2)`` format with + ``0 <= x1 < x2`` and ``0 <= y1 < y2``. + Args: + boxes1 (Tensor[N, 4]): first set of boxes + boxes2 (Tensor[M, 4]): second set of boxes + eps (float, optional): small number to prevent division by zero. Default: 1e-7 + Returns: + Tensor[N, M]: the NxM matrix containing the pairwise distance IoU values + for every element in boxes1 and boxes2 + """ + + def box_area(box): + # box = 4xn + return (box[2] - box[0]) * (box[3] - box[1]) + + area1 = box_area(box1.T) + area2 = box_area(box2.T) + + inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) - torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) + union = (area1[:, None] + area2 - inter) + + iou = inter / union + + lti = torch.min(box1[:, None, :2], box2[:, :2]) + rbi = torch.max(box1[:, None, 2:], box2[:, 2:]) + + whi = (rbi - lti).clamp(min=0) # [N,M,2] + diagonal_distance_squared = (whi[:, :, 0] ** 2) + (whi[:, :, 1] ** 2) + eps + + # centers of boxes + x_p = (box1[:, None, 0] + box1[:, None, 2]) / 2 + y_p = (box1[:, None, 1] + box1[:, None, 3]) / 2 + x_g = (box2[:, 0] + box2[:, 2]) / 2 + y_g = (box2[:, 1] + box2[:, 3]) / 2 + # The distance between boxes' centers squared. + centers_distance_squared = (x_p - x_g) ** 2 + (y_p - y_g) ** 2 + + # The distance IoU is the IoU penalized by a normalized + # distance between boxes' centers squared. + return iou - (centers_distance_squared / diagonal_distance_squared) + + +def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, + labels=()): + """Runs Non-Maximum Suppression (NMS) on inference results + + Returns: + list of detections, on (n,6) tensor per image [xyxy, conf, cls] + """ + + nc = prediction.shape[2] - 5 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + min_wh, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height + max_det = 300 # maximum number of detections per image + max_nms = 30000 # maximum number of boxes into torchvision.ops.nms() + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label &= nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0, 6), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + # Apply constraints + # x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 # width-height + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + l = labels[xi] + v = torch.zeros((len(l), nc + 5), device=x.device) + v[:, :4] = l[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(l)), l[:, 0].long() + 5] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + if nc == 1: + x[:, 5:] = x[:, 4:5] # for models with one class, cls_loss is 0 and cls_conf is always 0.5, + # so there is no need to multiplicate. + else: + x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if multi_label: + i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1) + else: # best class only + conf, j = x[:, 5:].max(1, keepdim=True) + x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres] + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Apply finite constraint + # if not torch.isfinite(x).all(): + # x = x[torch.isfinite(x).all(1)] + + # Check shape + n = x.shape[0] # number of boxes + if not n: # no boxes + continue + elif n > max_nms: # excess boxes + x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence + + # Batched NMS + c = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if i.shape[0] > max_det: # limit detections + i = i[:max_det] + if merge and (1 < n < 3E3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + print(f'WARNING: NMS time limit {time_limit}s exceeded') + break # time limit exceeded + + return output + + +def non_max_suppression_kpt(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False, + labels=(), kpt_label=False, nc=None, nkpt=None): + """Runs Non-Maximum Suppression (NMS) on inference results + + Returns: + list of detections, on (n,6) tensor per image [xyxy, conf, cls] + """ + if nc is None: + nc = prediction.shape[2] - 5 if not kpt_label else prediction.shape[2] - 56 # number of classes + xc = prediction[..., 4] > conf_thres # candidates + + # Settings + min_wh, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height + max_det = 300 # maximum number of detections per image + max_nms = 30000 # maximum number of boxes into torchvision.ops.nms() + time_limit = 10.0 # seconds to quit after + redundant = True # require redundant detections + multi_label &= nc > 1 # multiple labels per box (adds 0.5ms/img) + merge = False # use merge-NMS + + t = time.time() + output = [torch.zeros((0,6), device=prediction.device)] * prediction.shape[0] + for xi, x in enumerate(prediction): # image index, image inference + # Apply constraints + # x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 # width-height + x = x[xc[xi]] # confidence + + # Cat apriori labels if autolabelling + if labels and len(labels[xi]): + l = labels[xi] + v = torch.zeros((len(l), nc + 5), device=x.device) + v[:, :4] = l[:, 1:5] # box + v[:, 4] = 1.0 # conf + v[range(len(l)), l[:, 0].long() + 5] = 1.0 # cls + x = torch.cat((x, v), 0) + + # If none remain process next image + if not x.shape[0]: + continue + + # Compute conf + x[:, 5:5+nc] *= x[:, 4:5] # conf = obj_conf * cls_conf + + # Box (center x, center y, width, height) to (x1, y1, x2, y2) + box = xywh2xyxy(x[:, :4]) + + # Detections matrix nx6 (xyxy, conf, cls) + if multi_label: + i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T + x = torch.cat((box[i], x[i, j + 5, None], j[:, None].float()), 1) + else: # best class only + if not kpt_label: + conf, j = x[:, 5:].max(1, keepdim=True) + x = torch.cat((box, conf, j.float()), 1)[conf.view(-1) > conf_thres] + else: + kpts = x[:, 6:] + conf, j = x[:, 5:6].max(1, keepdim=True) + x = torch.cat((box, conf, j.float(), kpts), 1)[conf.view(-1) > conf_thres] + + + # Filter by class + if classes is not None: + x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)] + + # Apply finite constraint + # if not torch.isfinite(x).all(): + # x = x[torch.isfinite(x).all(1)] + + # Check shape + n = x.shape[0] # number of boxes + if not n: # no boxes + continue + elif n > max_nms: # excess boxes + x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence + + # Batched NMS + c = x[:, 5:6] * (0 if agnostic else max_wh) # classes + boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores + i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS + if i.shape[0] > max_det: # limit detections + i = i[:max_det] + if merge and (1 < n < 3E3): # Merge NMS (boxes merged using weighted mean) + # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) + iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix + weights = iou * scores[None] # box weights + x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes + if redundant: + i = i[iou.sum(1) > 1] # require redundancy + + output[xi] = x[i] + if (time.time() - t) > time_limit: + print(f'WARNING: NMS time limit {time_limit}s exceeded') + break # time limit exceeded + + return output + + +def strip_optimizer(f='best.pt', s=''): # from utils.general import *; strip_optimizer() + # Strip optimizer from 'f' to finalize training, optionally save as 's' + x = torch.load(f, map_location=torch.device('cpu')) + if x.get('ema'): + x['model'] = x['ema'] # replace model with ema + for k in 'optimizer', 'training_results', 'wandb_id', 'ema', 'updates': # keys + x[k] = None + x['epoch'] = -1 + x['model'].half() # to FP16 + for p in x['model'].parameters(): + p.requires_grad = False + torch.save(x, s or f) + mb = os.path.getsize(s or f) / 1E6 # filesize + print(f"Optimizer stripped from {f},{(' saved as %s,' % s) if s else ''} {mb:.1f}MB") + + +def print_mutation(hyp, results, yaml_file='hyp_evolved.yaml', bucket=''): + # Print mutation results to evolve.txt (for use with train.py --evolve) + a = '%10s' * len(hyp) % tuple(hyp.keys()) # hyperparam keys + b = '%10.3g' * len(hyp) % tuple(hyp.values()) # hyperparam values + c = '%10.4g' * len(results) % results # results (P, R, mAP@0.5, mAP@0.5:0.95, val_losses x 3) + print('\n%s\n%s\nEvolved fitness: %s\n' % (a, b, c)) + + if bucket: + url = 'gs://%s/evolve.txt' % bucket + if gsutil_getsize(url) > (os.path.getsize('evolve.txt') if os.path.exists('evolve.txt') else 0): + os.system('gsutil cp %s .' % url) # download evolve.txt if larger than local + + with open('evolve.txt', 'a') as f: # append result + f.write(c + b + '\n') + x = np.unique(np.loadtxt('evolve.txt', ndmin=2), axis=0) # load unique rows + x = x[np.argsort(-fitness(x))] # sort + np.savetxt('evolve.txt', x, '%10.3g') # save sort by fitness + + # Save yaml + for i, k in enumerate(hyp.keys()): + hyp[k] = float(x[0, i + 7]) + with open(yaml_file, 'w') as f: + results = tuple(x[0, :7]) + c = '%10.4g' * len(results) % results # results (P, R, mAP@0.5, mAP@0.5:0.95, val_losses x 3) + f.write('# Hyperparameter Evolution Results\n# Generations: %g\n# Metrics: ' % len(x) + c + '\n\n') + yaml.dump(hyp, f, sort_keys=False) + + if bucket: + os.system('gsutil cp evolve.txt %s gs://%s' % (yaml_file, bucket)) # upload + + +def apply_classifier(x, model, img, im0): + # applies a second stage classifier to yolo outputs + im0 = [im0] if isinstance(im0, np.ndarray) else im0 + for i, d in enumerate(x): # per image + if d is not None and len(d): + d = d.clone() + + # Reshape and pad cutouts + b = xyxy2xywh(d[:, :4]) # boxes + b[:, 2:] = b[:, 2:].max(1)[0].unsqueeze(1) # rectangle to square + b[:, 2:] = b[:, 2:] * 1.3 + 30 # pad + d[:, :4] = xywh2xyxy(b).long() + + # Rescale boxes from img_size to im0 size + scale_coords(img.shape[2:], d[:, :4], im0[i].shape) + + # Classes + pred_cls1 = d[:, 5].long() + ims = [] + for j, a in enumerate(d): # per item + cutout = im0[i][int(a[1]):int(a[3]), int(a[0]):int(a[2])] + im = cv2.resize(cutout, (224, 224)) # BGR + # cv2.imwrite('test%i.jpg' % j, cutout) + + im = im[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 + im = np.ascontiguousarray(im, dtype=np.float32) # uint8 to float32 + im /= 255.0 # 0 - 255 to 0.0 - 1.0 + ims.append(im) + + pred_cls2 = model(torch.Tensor(ims).to(d.device)).argmax(1) # classifier prediction + x[i] = x[i][pred_cls1 == pred_cls2] # retain matching class detections + + return x + + +def increment_path(path, exist_ok=True, sep=''): + # Increment path, i.e. runs/exp --> runs/exp{sep}0, runs/exp{sep}1 etc. + path = Path(path) # os-agnostic + if (path.exists() and exist_ok) or (not path.exists()): + return str(path) + else: + dirs = glob.glob(f"{path}{sep}*") # similar paths + matches = [re.search(rf"%s{sep}(\d+)" % path.stem, d) for d in dirs] + i = [int(m.groups()[0]) for m in matches if m] # indices + n = max(i) + 1 if i else 2 # increment number + return f"{path}{sep}{n}" # update path diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/Dockerfile b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/Dockerfile new file mode 100644 index 000000000..0155618f4 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/Dockerfile @@ -0,0 +1,25 @@ +FROM gcr.io/google-appengine/python + +# Create a virtualenv for dependencies. This isolates these packages from +# system-level packages. +# Use -p python3 or -p python3.7 to select python version. Default is version 2. +RUN virtualenv /env -p python3 + +# Setting these environment variables are the same as running +# source /env/bin/activate. +ENV VIRTUAL_ENV /env +ENV PATH /env/bin:$PATH + +RUN apt-get update && apt-get install -y python-opencv + +# Copy the application's requirements.txt and run pip to install all +# dependencies into the virtualenv. +ADD requirements.txt /app/requirements.txt +RUN pip install -r /app/requirements.txt + +# Add the application source code. +ADD . /app + +# Run a WSGI server to serve the application. gunicorn must be declared as +# a dependency in requirements.txt. +CMD gunicorn -b :$PORT main:app diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/additional_requirements.txt b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/additional_requirements.txt new file mode 100644 index 000000000..5fcc30524 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/additional_requirements.txt @@ -0,0 +1,4 @@ +# add these requirements in your app on top of the existing ones +pip==18.1 +Flask==1.0.2 +gunicorn==19.9.0 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/app.yaml b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/app.yaml new file mode 100644 index 000000000..69b8f68b3 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_app_engine/app.yaml @@ -0,0 +1,14 @@ +runtime: custom +env: flex + +service: yolorapp + +liveness_check: + initial_delay_sec: 600 + +manual_scaling: + instances: 1 +resources: + cpu: 1 + memory_gb: 4 + disk_size_gb: 20 \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_utils.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_utils.py new file mode 100644 index 000000000..f363408e6 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/google_utils.py @@ -0,0 +1,123 @@ +# Google utils: https://cloud.google.com/storage/docs/reference/libraries + +import os +import platform +import subprocess +import time +from pathlib import Path + +import requests +import torch + + +def gsutil_getsize(url=''): + # gs://bucket/file size https://cloud.google.com/storage/docs/gsutil/commands/du + s = subprocess.check_output(f'gsutil du {url}', shell=True).decode('utf-8') + return eval(s.split(' ')[0]) if len(s) else 0 # bytes + + +def attempt_download(file, repo='WongKinYiu/yolov7'): + # Attempt file download if does not exist + file = Path(str(file).strip().replace("'", '').lower()) + + if not file.exists(): + try: + response = requests.get(f'https://api.github.com/repos/{repo}/releases/latest').json() # github api + assets = [x['name'] for x in response['assets']] # release assets + tag = response['tag_name'] # i.e. 'v1.0' + except: # fallback plan + assets = ['yolov7.pt', 'yolov7-tiny.pt', 'yolov7x.pt', 'yolov7-d6.pt', 'yolov7-e6.pt', + 'yolov7-e6e.pt', 'yolov7-w6.pt'] + tag = subprocess.check_output('git tag', shell=True).decode().split()[-1] + + name = file.name + if name in assets: + msg = f'{file} missing, try downloading from https://github.com/{repo}/releases/' + redundant = False # second download option + try: # GitHub + url = f'https://github.com/{repo}/releases/download/{tag}/{name}' + print(f'Downloading {url} to {file}...') + torch.hub.download_url_to_file(url, file) + assert file.exists() and file.stat().st_size > 1E6 # check + except Exception as e: # GCP + print(f'Download error: {e}') + assert redundant, 'No secondary mirror' + url = f'https://storage.googleapis.com/{repo}/ckpt/{name}' + print(f'Downloading {url} to {file}...') + os.system(f'curl -L {url} -o {file}') # torch.hub.download_url_to_file(url, weights) + finally: + if not file.exists() or file.stat().st_size < 1E6: # check + file.unlink(missing_ok=True) # remove partial downloads + print(f'ERROR: Download failure: {msg}') + print('') + return + + +def gdrive_download(id='', file='tmp.zip'): + # Downloads a file from Google Drive. from yolov7.utils.google_utils import *; gdrive_download() + t = time.time() + file = Path(file) + cookie = Path('cookie') # gdrive cookie + print(f'Downloading https://drive.google.com/uc?export=download&id={id} as {file}... ', end='') + file.unlink(missing_ok=True) # remove existing file + cookie.unlink(missing_ok=True) # remove existing cookie + + # Attempt file download + out = "NUL" if platform.system() == "Windows" else "/dev/null" + os.system(f'curl -c ./cookie -s -L "drive.google.com/uc?export=download&id={id}" > {out}') + if os.path.exists('cookie'): # large file + s = f'curl -Lb ./cookie "drive.google.com/uc?export=download&confirm={get_token()}&id={id}" -o {file}' + else: # small file + s = f'curl -s -L -o {file} "drive.google.com/uc?export=download&id={id}"' + r = os.system(s) # execute, capture return + cookie.unlink(missing_ok=True) # remove existing cookie + + # Error check + if r != 0: + file.unlink(missing_ok=True) # remove partial + print('Download error ') # raise Exception('Download error') + return r + + # Unzip if archive + if file.suffix == '.zip': + print('unzipping... ', end='') + os.system(f'unzip -q {file}') # unzip + file.unlink() # remove zip to free space + + print(f'Done ({time.time() - t:.1f}s)') + return r + + +def get_token(cookie="./cookie"): + with open(cookie) as f: + for line in f: + if "download" in line: + return line.split()[-1] + return "" + +# def upload_blob(bucket_name, source_file_name, destination_blob_name): +# # Uploads a file to a bucket +# # https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-python +# +# storage_client = storage.Client() +# bucket = storage_client.get_bucket(bucket_name) +# blob = bucket.blob(destination_blob_name) +# +# blob.upload_from_filename(source_file_name) +# +# print('File {} uploaded to {}.'.format( +# source_file_name, +# destination_blob_name)) +# +# +# def download_blob(bucket_name, source_blob_name, destination_file_name): +# # Uploads a blob from a bucket +# storage_client = storage.Client() +# bucket = storage_client.get_bucket(bucket_name) +# blob = bucket.blob(source_blob_name) +# +# blob.download_to_filename(destination_file_name) +# +# print('Blob {} downloaded to {}.'.format( +# source_blob_name, +# destination_file_name)) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/loss.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/loss.py new file mode 100644 index 000000000..2b1d968f8 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/loss.py @@ -0,0 +1,1697 @@ +# Loss functions + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from utils.general import bbox_iou, bbox_alpha_iou, box_iou, box_giou, box_diou, box_ciou, xywh2xyxy +from utils.torch_utils import is_parallel + + +def smooth_BCE(eps=0.1): # https://github.com/ultralytics/yolov3/issues/238#issuecomment-598028441 + # return positive, negative label smoothing BCE targets + return 1.0 - 0.5 * eps, 0.5 * eps + + +class BCEBlurWithLogitsLoss(nn.Module): + # BCEwithLogitLoss() with reduced missing label effects. + def __init__(self, alpha=0.05): + super(BCEBlurWithLogitsLoss, self).__init__() + self.loss_fcn = nn.BCEWithLogitsLoss(reduction='none') # must be nn.BCEWithLogitsLoss() + self.alpha = alpha + + def forward(self, pred, true): + loss = self.loss_fcn(pred, true) + pred = torch.sigmoid(pred) # prob from logits + dx = pred - true # reduce only missing label effects + # dx = (pred - true).abs() # reduce missing label and false label effects + alpha_factor = 1 - torch.exp((dx - 1) / (self.alpha + 1e-4)) + loss *= alpha_factor + return loss.mean() + + +class SigmoidBin(nn.Module): + stride = None # strides computed during build + export = False # onnx export + + def __init__(self, bin_count=10, min=0.0, max=1.0, reg_scale = 2.0, use_loss_regression=True, use_fw_regression=True, BCE_weight=1.0, smooth_eps=0.0): + super(SigmoidBin, self).__init__() + + self.bin_count = bin_count + self.length = bin_count + 1 + self.min = min + self.max = max + self.scale = float(max - min) + self.shift = self.scale / 2.0 + + self.use_loss_regression = use_loss_regression + self.use_fw_regression = use_fw_regression + self.reg_scale = reg_scale + self.BCE_weight = BCE_weight + + start = min + (self.scale/2.0) / self.bin_count + end = max - (self.scale/2.0) / self.bin_count + step = self.scale / self.bin_count + self.step = step + #print(f" start = {start}, end = {end}, step = {step} ") + + bins = torch.range(start, end + 0.0001, step).float() + self.register_buffer('bins', bins) + + + self.cp = 1.0 - 0.5 * smooth_eps + self.cn = 0.5 * smooth_eps + + self.BCEbins = nn.BCEWithLogitsLoss(pos_weight=torch.Tensor([BCE_weight])) + self.MSELoss = nn.MSELoss() + + def get_length(self): + return self.length + + def forward(self, pred): + assert pred.shape[-1] == self.length, 'pred.shape[-1]=%d is not equal to self.length=%d' % (pred.shape[-1], self.length) + + pred_reg = (pred[..., 0] * self.reg_scale - self.reg_scale/2.0) * self.step + pred_bin = pred[..., 1:(1+self.bin_count)] + + _, bin_idx = torch.max(pred_bin, dim=-1) + bin_bias = self.bins[bin_idx] + + if self.use_fw_regression: + result = pred_reg + bin_bias + else: + result = bin_bias + result = result.clamp(min=self.min, max=self.max) + + return result + + + def training_loss(self, pred, target): + assert pred.shape[-1] == self.length, 'pred.shape[-1]=%d is not equal to self.length=%d' % (pred.shape[-1], self.length) + assert pred.shape[0] == target.shape[0], 'pred.shape=%d is not equal to the target.shape=%d' % (pred.shape[0], target.shape[0]) + device = pred.device + + pred_reg = (pred[..., 0].sigmoid() * self.reg_scale - self.reg_scale/2.0) * self.step + pred_bin = pred[..., 1:(1+self.bin_count)] + + diff_bin_target = torch.abs(target[..., None] - self.bins) + _, bin_idx = torch.min(diff_bin_target, dim=-1) + + bin_bias = self.bins[bin_idx] + bin_bias.requires_grad = False + result = pred_reg + bin_bias + + target_bins = torch.full_like(pred_bin, self.cn, device=device) # targets + n = pred.shape[0] + target_bins[range(n), bin_idx] = self.cp + + loss_bin = self.BCEbins(pred_bin, target_bins) # BCE + + if self.use_loss_regression: + loss_regression = self.MSELoss(result, target) # MSE + loss = loss_bin + loss_regression + else: + loss = loss_bin + + out_result = result.clamp(min=self.min, max=self.max) + + return loss, out_result + + +class FocalLoss(nn.Module): + # Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5) + def __init__(self, loss_fcn, gamma=1.5, alpha=0.25): + super(FocalLoss, self).__init__() + self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss() + self.gamma = gamma + self.alpha = alpha + self.reduction = loss_fcn.reduction + self.loss_fcn.reduction = 'none' # required to apply FL to each element + + def forward(self, pred, true): + loss = self.loss_fcn(pred, true) + # p_t = torch.exp(-loss) + # loss *= self.alpha * (1.000001 - p_t) ** self.gamma # non-zero power for gradient stability + + # TF implementation https://github.com/tensorflow/addons/blob/v0.7.1/tensorflow_addons/losses/focal_loss.py + pred_prob = torch.sigmoid(pred) # prob from logits + p_t = true * pred_prob + (1 - true) * (1 - pred_prob) + alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha) + modulating_factor = (1.0 - p_t) ** self.gamma + loss *= alpha_factor * modulating_factor + + if self.reduction == 'mean': + return loss.mean() + elif self.reduction == 'sum': + return loss.sum() + else: # 'none' + return loss + + +class QFocalLoss(nn.Module): + # Wraps Quality focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5) + def __init__(self, loss_fcn, gamma=1.5, alpha=0.25): + super(QFocalLoss, self).__init__() + self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss() + self.gamma = gamma + self.alpha = alpha + self.reduction = loss_fcn.reduction + self.loss_fcn.reduction = 'none' # required to apply FL to each element + + def forward(self, pred, true): + loss = self.loss_fcn(pred, true) + + pred_prob = torch.sigmoid(pred) # prob from logits + alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha) + modulating_factor = torch.abs(true - pred_prob) ** self.gamma + loss *= alpha_factor * modulating_factor + + if self.reduction == 'mean': + return loss.mean() + elif self.reduction == 'sum': + return loss.sum() + else: # 'none' + return loss + +class RankSort(torch.autograd.Function): + @staticmethod + def forward(ctx, logits, targets, delta_RS=0.50, eps=1e-10): + + classification_grads=torch.zeros(logits.shape).cuda() + + #Filter fg logits + fg_labels = (targets > 0.) + fg_logits = logits[fg_labels] + fg_targets = targets[fg_labels] + fg_num = len(fg_logits) + + #Do not use bg with scores less than minimum fg logit + #since changing its score does not have an effect on precision + threshold_logit = torch.min(fg_logits)-delta_RS + relevant_bg_labels=((targets==0) & (logits>=threshold_logit)) + + relevant_bg_logits = logits[relevant_bg_labels] + relevant_bg_grad=torch.zeros(len(relevant_bg_logits)).cuda() + sorting_error=torch.zeros(fg_num).cuda() + ranking_error=torch.zeros(fg_num).cuda() + fg_grad=torch.zeros(fg_num).cuda() + + #sort the fg logits + order=torch.argsort(fg_logits) + #Loops over each positive following the order + for ii in order: + # Difference Transforms (x_ij) + fg_relations=fg_logits-fg_logits[ii] + bg_relations=relevant_bg_logits-fg_logits[ii] + + if delta_RS > 0: + fg_relations=torch.clamp(fg_relations/(2*delta_RS)+0.5,min=0,max=1) + bg_relations=torch.clamp(bg_relations/(2*delta_RS)+0.5,min=0,max=1) + else: + fg_relations = (fg_relations >= 0).float() + bg_relations = (bg_relations >= 0).float() + + # Rank of ii among pos and false positive number (bg with larger scores) + rank_pos=torch.sum(fg_relations) + FP_num=torch.sum(bg_relations) + + # Rank of ii among all examples + rank=rank_pos+FP_num + + # Ranking error of example ii. target_ranking_error is always 0. (Eq. 7) + ranking_error[ii]=FP_num/rank + + # Current sorting error of example ii. (Eq. 7) + current_sorting_error = torch.sum(fg_relations*(1-fg_targets))/rank_pos + + #Find examples in the target sorted order for example ii + iou_relations = (fg_targets >= fg_targets[ii]) + target_sorted_order = iou_relations * fg_relations + + #The rank of ii among positives in sorted order + rank_pos_target = torch.sum(target_sorted_order) + + #Compute target sorting error. (Eq. 8) + #Since target ranking error is 0, this is also total target error + target_sorting_error= torch.sum(target_sorted_order*(1-fg_targets))/rank_pos_target + + #Compute sorting error on example ii + sorting_error[ii] = current_sorting_error - target_sorting_error + + #Identity Update for Ranking Error + if FP_num > eps: + #For ii the update is the ranking error + fg_grad[ii] -= ranking_error[ii] + #For negatives, distribute error via ranking pmf (i.e. bg_relations/FP_num) + relevant_bg_grad += (bg_relations*(ranking_error[ii]/FP_num)) + + #Find the positives that are misranked (the cause of the error) + #These are the ones with smaller IoU but larger logits + missorted_examples = (~ iou_relations) * fg_relations + + #Denominotor of sorting pmf + sorting_pmf_denom = torch.sum(missorted_examples) + + #Identity Update for Sorting Error + if sorting_pmf_denom > eps: + #For ii the update is the sorting error + fg_grad[ii] -= sorting_error[ii] + #For positives, distribute error via sorting pmf (i.e. missorted_examples/sorting_pmf_denom) + fg_grad += (missorted_examples*(sorting_error[ii]/sorting_pmf_denom)) + + #Normalize gradients by number of positives + classification_grads[fg_labels]= (fg_grad/fg_num) + classification_grads[relevant_bg_labels]= (relevant_bg_grad/fg_num) + + ctx.save_for_backward(classification_grads) + + return ranking_error.mean(), sorting_error.mean() + + @staticmethod + def backward(ctx, out_grad1, out_grad2): + g1, =ctx.saved_tensors + return g1*out_grad1, None, None, None + +class aLRPLoss(torch.autograd.Function): + @staticmethod + def forward(ctx, logits, targets, regression_losses, delta=1., eps=1e-5): + classification_grads=torch.zeros(logits.shape).cuda() + + #Filter fg logits + fg_labels = (targets == 1) + fg_logits = logits[fg_labels] + fg_num = len(fg_logits) + + #Do not use bg with scores less than minimum fg logit + #since changing its score does not have an effect on precision + threshold_logit = torch.min(fg_logits)-delta + + #Get valid bg logits + relevant_bg_labels=((targets==0)&(logits>=threshold_logit)) + relevant_bg_logits=logits[relevant_bg_labels] + relevant_bg_grad=torch.zeros(len(relevant_bg_logits)).cuda() + rank=torch.zeros(fg_num).cuda() + prec=torch.zeros(fg_num).cuda() + fg_grad=torch.zeros(fg_num).cuda() + + max_prec=0 + #sort the fg logits + order=torch.argsort(fg_logits) + #Loops over each positive following the order + for ii in order: + #x_ij s as score differences with fgs + fg_relations=fg_logits-fg_logits[ii] + #Apply piecewise linear function and determine relations with fgs + fg_relations=torch.clamp(fg_relations/(2*delta)+0.5,min=0,max=1) + #Discard i=j in the summation in rank_pos + fg_relations[ii]=0 + + #x_ij s as score differences with bgs + bg_relations=relevant_bg_logits-fg_logits[ii] + #Apply piecewise linear function and determine relations with bgs + bg_relations=torch.clamp(bg_relations/(2*delta)+0.5,min=0,max=1) + + #Compute the rank of the example within fgs and number of bgs with larger scores + rank_pos=1+torch.sum(fg_relations) + FP_num=torch.sum(bg_relations) + #Store the total since it is normalizer also for aLRP Regression error + rank[ii]=rank_pos+FP_num + + #Compute precision for this example to compute classification loss + prec[ii]=rank_pos/rank[ii] + #For stability, set eps to a infinitesmall value (e.g. 1e-6), then compute grads + if FP_num > eps: + fg_grad[ii] = -(torch.sum(fg_relations*regression_losses)+FP_num)/rank[ii] + relevant_bg_grad += (bg_relations*(-fg_grad[ii]/FP_num)) + + #aLRP with grad formulation fg gradient + classification_grads[fg_labels]= fg_grad + #aLRP with grad formulation bg gradient + classification_grads[relevant_bg_labels]= relevant_bg_grad + + classification_grads /= (fg_num) + + cls_loss=1-prec.mean() + ctx.save_for_backward(classification_grads) + + return cls_loss, rank, order + + @staticmethod + def backward(ctx, out_grad1, out_grad2, out_grad3): + g1, =ctx.saved_tensors + return g1*out_grad1, None, None, None, None + + +class APLoss(torch.autograd.Function): + @staticmethod + def forward(ctx, logits, targets, delta=1.): + classification_grads=torch.zeros(logits.shape).cuda() + + #Filter fg logits + fg_labels = (targets == 1) + fg_logits = logits[fg_labels] + fg_num = len(fg_logits) + + #Do not use bg with scores less than minimum fg logit + #since changing its score does not have an effect on precision + threshold_logit = torch.min(fg_logits)-delta + + #Get valid bg logits + relevant_bg_labels=((targets==0)&(logits>=threshold_logit)) + relevant_bg_logits=logits[relevant_bg_labels] + relevant_bg_grad=torch.zeros(len(relevant_bg_logits)).cuda() + rank=torch.zeros(fg_num).cuda() + prec=torch.zeros(fg_num).cuda() + fg_grad=torch.zeros(fg_num).cuda() + + max_prec=0 + #sort the fg logits + order=torch.argsort(fg_logits) + #Loops over each positive following the order + for ii in order: + #x_ij s as score differences with fgs + fg_relations=fg_logits-fg_logits[ii] + #Apply piecewise linear function and determine relations with fgs + fg_relations=torch.clamp(fg_relations/(2*delta)+0.5,min=0,max=1) + #Discard i=j in the summation in rank_pos + fg_relations[ii]=0 + + #x_ij s as score differences with bgs + bg_relations=relevant_bg_logits-fg_logits[ii] + #Apply piecewise linear function and determine relations with bgs + bg_relations=torch.clamp(bg_relations/(2*delta)+0.5,min=0,max=1) + + #Compute the rank of the example within fgs and number of bgs with larger scores + rank_pos=1+torch.sum(fg_relations) + FP_num=torch.sum(bg_relations) + #Store the total since it is normalizer also for aLRP Regression error + rank[ii]=rank_pos+FP_num + + #Compute precision for this example + current_prec=rank_pos/rank[ii] + + #Compute interpolated AP and store gradients for relevant bg examples + if (max_prec<=current_prec): + max_prec=current_prec + relevant_bg_grad += (bg_relations/rank[ii]) + else: + relevant_bg_grad += (bg_relations/rank[ii])*(((1-max_prec)/(1-current_prec))) + + #Store fg gradients + fg_grad[ii]=-(1-max_prec) + prec[ii]=max_prec + + #aLRP with grad formulation fg gradient + classification_grads[fg_labels]= fg_grad + #aLRP with grad formulation bg gradient + classification_grads[relevant_bg_labels]= relevant_bg_grad + + classification_grads /= fg_num + + cls_loss=1-prec.mean() + ctx.save_for_backward(classification_grads) + + return cls_loss + + @staticmethod + def backward(ctx, out_grad1): + g1, =ctx.saved_tensors + return g1*out_grad1, None, None + + +class ComputeLoss: + # Compute losses + def __init__(self, model, autobalance=False): + super(ComputeLoss, self).__init__() + device = next(model.parameters()).device # get model device + h = model.hyp # hyperparameters + + # Define criteria + BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) + BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) + + # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 + self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets + + # Focal loss + g = h['fl_gamma'] # focal loss gamma + if g > 0: + BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) + + det = model.module.model[-1] if is_parallel(model) else model.model[-1] # Detect() module + self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.06, .02]) # P3-P7 + #self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.1, .05]) # P3-P7 + #self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.5, 0.4, .1]) # P3-P7 + self.ssi = list(det.stride).index(16) if autobalance else 0 # stride 16 index + self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, model.gr, h, autobalance + for k in 'na', 'nc', 'nl', 'anchors': + setattr(self, k, getattr(det, k)) + + def __call__(self, p, targets): # predictions, targets, model + device = targets.device + lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) + tcls, tbox, indices, anchors = self.build_targets(p, targets) # targets + + # Losses + for i, pi in enumerate(p): # layer index, layer predictions + b, a, gj, gi = indices[i] # image, anchor, gridy, gridx + tobj = torch.zeros_like(pi[..., 0], device=device) # target obj + + n = b.shape[0] # number of targets + if n: + ps = pi[b, a, gj, gi] # prediction subset corresponding to targets + + # Regression + pxy = ps[:, :2].sigmoid() * 2. - 0.5 + pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i] + pbox = torch.cat((pxy, pwh), 1) # predicted box + iou = bbox_iou(pbox.T, tbox[i], x1y1x2y2=False, CIoU=True) # iou(prediction, target) + lbox += (1.0 - iou).mean() # iou loss + + # Objectness + tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype) # iou ratio + + # Classification + if self.nc > 1: # cls loss (only if multiple classes) + t = torch.full_like(ps[:, 5:], self.cn, device=device) # targets + t[range(n), tcls[i]] = self.cp + #t[t==self.cp] = iou.detach().clamp(0).type(t.dtype) + lcls += self.BCEcls(ps[:, 5:], t) # BCE + + # Append targets to text file + # with open('targets.txt', 'a') as file: + # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] + + obji = self.BCEobj(pi[..., 4], tobj) + lobj += obji * self.balance[i] # obj loss + if self.autobalance: + self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() + + if self.autobalance: + self.balance = [x / self.balance[self.ssi] for x in self.balance] + lbox *= self.hyp['box'] + lobj *= self.hyp['obj'] + lcls *= self.hyp['cls'] + bs = tobj.shape[0] # batch size + + loss = lbox + lobj + lcls + return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach() + + def build_targets(self, p, targets): + # Build targets for compute_loss(), input targets(image,class,x,y,w,h) + na, nt = self.na, targets.shape[0] # number of anchors, targets + tcls, tbox, indices, anch = [], [], [], [] + gain = torch.ones(7, device=targets.device).long() # normalized to gridspace gain + ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) + targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices + + g = 0.5 # bias + off = torch.tensor([[0, 0], + [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m + # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm + ], device=targets.device).float() * g # offsets + + for i in range(self.nl): + anchors = self.anchors[i] + gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain + + # Match targets to anchors + t = targets * gain + if nt: + # Matches + r = t[:, :, 4:6] / anchors[:, None] # wh ratio + j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t'] # compare + # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) + t = t[j] # filter + + # Offsets + gxy = t[:, 2:4] # grid xy + gxi = gain[[2, 3]] - gxy # inverse + j, k = ((gxy % 1. < g) & (gxy > 1.)).T + l, m = ((gxi % 1. < g) & (gxi > 1.)).T + j = torch.stack((torch.ones_like(j), j, k, l, m)) + t = t.repeat((5, 1, 1))[j] + offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] + else: + t = targets[0] + offsets = 0 + + # Define + b, c = t[:, :2].long().T # image, class + gxy = t[:, 2:4] # grid xy + gwh = t[:, 4:6] # grid wh + gij = (gxy - offsets).long() + gi, gj = gij.T # grid xy indices + + # Append + a = t[:, 6].long() # anchor indices + indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices + tbox.append(torch.cat((gxy - gij, gwh), 1)) # box + anch.append(anchors[a]) # anchors + tcls.append(c) # class + + return tcls, tbox, indices, anch + + +class ComputeLossOTA: + # Compute losses + def __init__(self, model, autobalance=False): + super(ComputeLossOTA, self).__init__() + device = next(model.parameters()).device # get model device + h = model.hyp # hyperparameters + + # Define criteria + BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) + BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) + + # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 + self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets + + # Focal loss + g = h['fl_gamma'] # focal loss gamma + if g > 0: + BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) + + det = model.module.model[-1] if is_parallel(model) else model.model[-1] # Detect() module + self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.06, .02]) # P3-P7 + self.ssi = list(det.stride).index(16) if autobalance else 0 # stride 16 index + self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, model.gr, h, autobalance + for k in 'na', 'nc', 'nl', 'anchors', 'stride': + setattr(self, k, getattr(det, k)) + + def __call__(self, p, targets, imgs): # predictions, targets, model + device = targets.device + lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) + bs, as_, gjs, gis, targets, anchors = self.build_targets(p, targets, imgs) + pre_gen_gains = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p] + + + # Losses + for i, pi in enumerate(p): # layer index, layer predictions + b, a, gj, gi = bs[i], as_[i], gjs[i], gis[i] # image, anchor, gridy, gridx + tobj = torch.zeros_like(pi[..., 0], device=device) # target obj + + n = b.shape[0] # number of targets + if n: + ps = pi[b, a, gj, gi] # prediction subset corresponding to targets + + # Regression + grid = torch.stack([gi, gj], dim=1) + pxy = ps[:, :2].sigmoid() * 2. - 0.5 + #pxy = ps[:, :2].sigmoid() * 3. - 1. + pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i] + pbox = torch.cat((pxy, pwh), 1) # predicted box + selected_tbox = targets[i][:, 2:6] * pre_gen_gains[i] + selected_tbox[:, :2] -= grid + iou = bbox_iou(pbox.T, selected_tbox, x1y1x2y2=False, CIoU=True) # iou(prediction, target) + lbox += (1.0 - iou).mean() # iou loss + + # Objectness + tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype) # iou ratio + + # Classification + selected_tcls = targets[i][:, 1].long() + if self.nc > 1: # cls loss (only if multiple classes) + t = torch.full_like(ps[:, 5:], self.cn, device=device) # targets + t[range(n), selected_tcls] = self.cp + lcls += self.BCEcls(ps[:, 5:], t) # BCE + + # Append targets to text file + # with open('targets.txt', 'a') as file: + # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] + + obji = self.BCEobj(pi[..., 4], tobj) + lobj += obji * self.balance[i] # obj loss + if self.autobalance: + self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() + + if self.autobalance: + self.balance = [x / self.balance[self.ssi] for x in self.balance] + lbox *= self.hyp['box'] + lobj *= self.hyp['obj'] + lcls *= self.hyp['cls'] + bs = tobj.shape[0] # batch size + + loss = lbox + lobj + lcls + return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach() + + def build_targets(self, p, targets, imgs): + + #indices, anch = self.find_positive(p, targets) + indices, anch = self.find_3_positive(p, targets) + #indices, anch = self.find_4_positive(p, targets) + #indices, anch = self.find_5_positive(p, targets) + #indices, anch = self.find_9_positive(p, targets) + device = torch.device(targets.device) + matching_bs = [[] for pp in p] + matching_as = [[] for pp in p] + matching_gjs = [[] for pp in p] + matching_gis = [[] for pp in p] + matching_targets = [[] for pp in p] + matching_anchs = [[] for pp in p] + + nl = len(p) + + for batch_idx in range(p[0].shape[0]): + + b_idx = targets[:, 0]==batch_idx + this_target = targets[b_idx] + if this_target.shape[0] == 0: + continue + + txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1] + txyxy = xywh2xyxy(txywh) + + pxyxys = [] + p_cls = [] + p_obj = [] + from_which_layer = [] + all_b = [] + all_a = [] + all_gj = [] + all_gi = [] + all_anch = [] + + for i, pi in enumerate(p): + + b, a, gj, gi = indices[i] + idx = (b == batch_idx) + b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx] + all_b.append(b) + all_a.append(a) + all_gj.append(gj) + all_gi.append(gi) + all_anch.append(anch[i][idx]) + from_which_layer.append((torch.ones(size=(len(b),)) * i).to(device)) + + fg_pred = pi[b, a, gj, gi] + p_obj.append(fg_pred[:, 4:5]) + p_cls.append(fg_pred[:, 5:]) + + grid = torch.stack([gi, gj], dim=1) + pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #/ 8. + #pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i] + pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #/ 8. + pxywh = torch.cat([pxy, pwh], dim=-1) + pxyxy = xywh2xyxy(pxywh) + pxyxys.append(pxyxy) + + pxyxys = torch.cat(pxyxys, dim=0) + if pxyxys.shape[0] == 0: + continue + p_obj = torch.cat(p_obj, dim=0) + p_cls = torch.cat(p_cls, dim=0) + from_which_layer = torch.cat(from_which_layer, dim=0) + all_b = torch.cat(all_b, dim=0) + all_a = torch.cat(all_a, dim=0) + all_gj = torch.cat(all_gj, dim=0) + all_gi = torch.cat(all_gi, dim=0) + all_anch = torch.cat(all_anch, dim=0) + + pair_wise_iou = box_iou(txyxy, pxyxys) + + pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8) + + top_k, _ = torch.topk(pair_wise_iou, min(10, pair_wise_iou.shape[1]), dim=1) + dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1) + + gt_cls_per_image = ( + F.one_hot(this_target[:, 1].to(torch.int64), self.nc) + .float() + .unsqueeze(1) + .repeat(1, pxyxys.shape[0], 1) + ) + + num_gt = this_target.shape[0] + cls_preds_ = ( + p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + ) + + y = cls_preds_.sqrt_() + pair_wise_cls_loss = F.binary_cross_entropy_with_logits( + torch.log(y/(1-y)) , gt_cls_per_image, reduction="none" + ).sum(-1) + del cls_preds_ + + cost = ( + pair_wise_cls_loss + + 3.0 * pair_wise_iou_loss + ) + + matching_matrix = torch.zeros_like(cost, device=device) + + for gt_idx in range(num_gt): + _, pos_idx = torch.topk( + cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False + ) + matching_matrix[gt_idx][pos_idx] = 1.0 + + del top_k, dynamic_ks + anchor_matching_gt = matching_matrix.sum(0) + if (anchor_matching_gt > 1).sum() > 0: + _, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0) + matching_matrix[:, anchor_matching_gt > 1] *= 0.0 + matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0 + fg_mask_inboxes = (matching_matrix.sum(0) > 0.0).to(device) + matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0) + + from_which_layer = from_which_layer[fg_mask_inboxes] + all_b = all_b[fg_mask_inboxes] + all_a = all_a[fg_mask_inboxes] + all_gj = all_gj[fg_mask_inboxes] + all_gi = all_gi[fg_mask_inboxes] + all_anch = all_anch[fg_mask_inboxes] + + this_target = this_target[matched_gt_inds] + + for i in range(nl): + layer_idx = from_which_layer == i + matching_bs[i].append(all_b[layer_idx]) + matching_as[i].append(all_a[layer_idx]) + matching_gjs[i].append(all_gj[layer_idx]) + matching_gis[i].append(all_gi[layer_idx]) + matching_targets[i].append(this_target[layer_idx]) + matching_anchs[i].append(all_anch[layer_idx]) + + for i in range(nl): + if matching_targets[i] != []: + matching_bs[i] = torch.cat(matching_bs[i], dim=0) + matching_as[i] = torch.cat(matching_as[i], dim=0) + matching_gjs[i] = torch.cat(matching_gjs[i], dim=0) + matching_gis[i] = torch.cat(matching_gis[i], dim=0) + matching_targets[i] = torch.cat(matching_targets[i], dim=0) + matching_anchs[i] = torch.cat(matching_anchs[i], dim=0) + else: + matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + + return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs + + def find_3_positive(self, p, targets): + # Build targets for compute_loss(), input targets(image,class,x,y,w,h) + na, nt = self.na, targets.shape[0] # number of anchors, targets + indices, anch = [], [] + gain = torch.ones(7, device=targets.device).long() # normalized to gridspace gain + ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) + targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices + + g = 0.5 # bias + off = torch.tensor([[0, 0], + [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m + # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm + ], device=targets.device).float() * g # offsets + + for i in range(self.nl): + anchors = self.anchors[i] + gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain + + # Match targets to anchors + t = targets * gain + if nt: + # Matches + r = t[:, :, 4:6] / anchors[:, None] # wh ratio + j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t'] # compare + # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) + t = t[j] # filter + + # Offsets + gxy = t[:, 2:4] # grid xy + gxi = gain[[2, 3]] - gxy # inverse + j, k = ((gxy % 1. < g) & (gxy > 1.)).T + l, m = ((gxi % 1. < g) & (gxi > 1.)).T + j = torch.stack((torch.ones_like(j), j, k, l, m)) + t = t.repeat((5, 1, 1))[j] + offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] + else: + t = targets[0] + offsets = 0 + + # Define + b, c = t[:, :2].long().T # image, class + gxy = t[:, 2:4] # grid xy + gwh = t[:, 4:6] # grid wh + gij = (gxy - offsets).long() + gi, gj = gij.T # grid xy indices + + # Append + a = t[:, 6].long() # anchor indices + indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices + anch.append(anchors[a]) # anchors + + return indices, anch + + +class ComputeLossBinOTA: + # Compute losses + def __init__(self, model, autobalance=False): + super(ComputeLossBinOTA, self).__init__() + device = next(model.parameters()).device # get model device + h = model.hyp # hyperparameters + + # Define criteria + BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) + BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) + #MSEangle = nn.MSELoss().to(device) + + # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 + self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets + + # Focal loss + g = h['fl_gamma'] # focal loss gamma + if g > 0: + BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) + + det = model.module.model[-1] if is_parallel(model) else model.model[-1] # Detect() module + self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.06, .02]) # P3-P7 + self.ssi = list(det.stride).index(16) if autobalance else 0 # stride 16 index + self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, model.gr, h, autobalance + for k in 'na', 'nc', 'nl', 'anchors', 'stride', 'bin_count': + setattr(self, k, getattr(det, k)) + + #xy_bin_sigmoid = SigmoidBin(bin_count=11, min=-0.5, max=1.5, use_loss_regression=False).to(device) + wh_bin_sigmoid = SigmoidBin(bin_count=self.bin_count, min=0.0, max=4.0, use_loss_regression=False).to(device) + #angle_bin_sigmoid = SigmoidBin(bin_count=31, min=-1.1, max=1.1, use_loss_regression=False).to(device) + self.wh_bin_sigmoid = wh_bin_sigmoid + + def __call__(self, p, targets, imgs): # predictions, targets, model + device = targets.device + lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) + bs, as_, gjs, gis, targets, anchors = self.build_targets(p, targets, imgs) + pre_gen_gains = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p] + + + # Losses + for i, pi in enumerate(p): # layer index, layer predictions + b, a, gj, gi = bs[i], as_[i], gjs[i], gis[i] # image, anchor, gridy, gridx + tobj = torch.zeros_like(pi[..., 0], device=device) # target obj + + obj_idx = self.wh_bin_sigmoid.get_length()*2 + 2 # x,y, w-bce, h-bce # xy_bin_sigmoid.get_length()*2 + + n = b.shape[0] # number of targets + if n: + ps = pi[b, a, gj, gi] # prediction subset corresponding to targets + + # Regression + grid = torch.stack([gi, gj], dim=1) + selected_tbox = targets[i][:, 2:6] * pre_gen_gains[i] + selected_tbox[:, :2] -= grid + + #pxy = ps[:, :2].sigmoid() * 2. - 0.5 + ##pxy = ps[:, :2].sigmoid() * 3. - 1. + #pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i] + #pbox = torch.cat((pxy, pwh), 1) # predicted box + + #x_loss, px = xy_bin_sigmoid.training_loss(ps[..., 0:12], tbox[i][..., 0]) + #y_loss, py = xy_bin_sigmoid.training_loss(ps[..., 12:24], tbox[i][..., 1]) + w_loss, pw = self.wh_bin_sigmoid.training_loss(ps[..., 2:(3+self.bin_count)], selected_tbox[..., 2] / anchors[i][..., 0]) + h_loss, ph = self.wh_bin_sigmoid.training_loss(ps[..., (3+self.bin_count):obj_idx], selected_tbox[..., 3] / anchors[i][..., 1]) + + pw *= anchors[i][..., 0] + ph *= anchors[i][..., 1] + + px = ps[:, 0].sigmoid() * 2. - 0.5 + py = ps[:, 1].sigmoid() * 2. - 0.5 + + lbox += w_loss + h_loss # + x_loss + y_loss + + #print(f"\n px = {px.shape}, py = {py.shape}, pw = {pw.shape}, ph = {ph.shape} \n") + + pbox = torch.cat((px.unsqueeze(1), py.unsqueeze(1), pw.unsqueeze(1), ph.unsqueeze(1)), 1).to(device) # predicted box + + + + + iou = bbox_iou(pbox.T, selected_tbox, x1y1x2y2=False, CIoU=True) # iou(prediction, target) + lbox += (1.0 - iou).mean() # iou loss + + # Objectness + tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype) # iou ratio + + # Classification + selected_tcls = targets[i][:, 1].long() + if self.nc > 1: # cls loss (only if multiple classes) + t = torch.full_like(ps[:, (1+obj_idx):], self.cn, device=device) # targets + t[range(n), selected_tcls] = self.cp + lcls += self.BCEcls(ps[:, (1+obj_idx):], t) # BCE + + # Append targets to text file + # with open('targets.txt', 'a') as file: + # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] + + obji = self.BCEobj(pi[..., obj_idx], tobj) + lobj += obji * self.balance[i] # obj loss + if self.autobalance: + self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() + + if self.autobalance: + self.balance = [x / self.balance[self.ssi] for x in self.balance] + lbox *= self.hyp['box'] + lobj *= self.hyp['obj'] + lcls *= self.hyp['cls'] + bs = tobj.shape[0] # batch size + + loss = lbox + lobj + lcls + return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach() + + def build_targets(self, p, targets, imgs): + + #indices, anch = self.find_positive(p, targets) + indices, anch = self.find_3_positive(p, targets) + #indices, anch = self.find_4_positive(p, targets) + #indices, anch = self.find_5_positive(p, targets) + #indices, anch = self.find_9_positive(p, targets) + + matching_bs = [[] for pp in p] + matching_as = [[] for pp in p] + matching_gjs = [[] for pp in p] + matching_gis = [[] for pp in p] + matching_targets = [[] for pp in p] + matching_anchs = [[] for pp in p] + + nl = len(p) + + for batch_idx in range(p[0].shape[0]): + + b_idx = targets[:, 0]==batch_idx + this_target = targets[b_idx] + if this_target.shape[0] == 0: + continue + + txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1] + txyxy = xywh2xyxy(txywh) + + pxyxys = [] + p_cls = [] + p_obj = [] + from_which_layer = [] + all_b = [] + all_a = [] + all_gj = [] + all_gi = [] + all_anch = [] + + for i, pi in enumerate(p): + + obj_idx = self.wh_bin_sigmoid.get_length()*2 + 2 + + b, a, gj, gi = indices[i] + idx = (b == batch_idx) + b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx] + all_b.append(b) + all_a.append(a) + all_gj.append(gj) + all_gi.append(gi) + all_anch.append(anch[i][idx]) + from_which_layer.append(torch.ones(size=(len(b),)) * i) + + fg_pred = pi[b, a, gj, gi] + p_obj.append(fg_pred[:, obj_idx:(obj_idx+1)]) + p_cls.append(fg_pred[:, (obj_idx+1):]) + + grid = torch.stack([gi, gj], dim=1) + pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #/ 8. + #pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #/ 8. + pw = self.wh_bin_sigmoid.forward(fg_pred[..., 2:(3+self.bin_count)].sigmoid()) * anch[i][idx][:, 0] * self.stride[i] + ph = self.wh_bin_sigmoid.forward(fg_pred[..., (3+self.bin_count):obj_idx].sigmoid()) * anch[i][idx][:, 1] * self.stride[i] + + pxywh = torch.cat([pxy, pw.unsqueeze(1), ph.unsqueeze(1)], dim=-1) + pxyxy = xywh2xyxy(pxywh) + pxyxys.append(pxyxy) + + pxyxys = torch.cat(pxyxys, dim=0) + if pxyxys.shape[0] == 0: + continue + p_obj = torch.cat(p_obj, dim=0) + p_cls = torch.cat(p_cls, dim=0) + from_which_layer = torch.cat(from_which_layer, dim=0) + all_b = torch.cat(all_b, dim=0) + all_a = torch.cat(all_a, dim=0) + all_gj = torch.cat(all_gj, dim=0) + all_gi = torch.cat(all_gi, dim=0) + all_anch = torch.cat(all_anch, dim=0) + + pair_wise_iou = box_iou(txyxy, pxyxys) + + pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8) + + top_k, _ = torch.topk(pair_wise_iou, min(10, pair_wise_iou.shape[1]), dim=1) + dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1) + + gt_cls_per_image = ( + F.one_hot(this_target[:, 1].to(torch.int64), self.nc) + .float() + .unsqueeze(1) + .repeat(1, pxyxys.shape[0], 1) + ) + + num_gt = this_target.shape[0] + cls_preds_ = ( + p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + ) + + y = cls_preds_.sqrt_() + pair_wise_cls_loss = F.binary_cross_entropy_with_logits( + torch.log(y/(1-y)) , gt_cls_per_image, reduction="none" + ).sum(-1) + del cls_preds_ + + cost = ( + pair_wise_cls_loss + + 3.0 * pair_wise_iou_loss + ) + + matching_matrix = torch.zeros_like(cost) + + for gt_idx in range(num_gt): + _, pos_idx = torch.topk( + cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False + ) + matching_matrix[gt_idx][pos_idx] = 1.0 + + del top_k, dynamic_ks + anchor_matching_gt = matching_matrix.sum(0) + if (anchor_matching_gt > 1).sum() > 0: + _, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0) + matching_matrix[:, anchor_matching_gt > 1] *= 0.0 + matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0 + fg_mask_inboxes = matching_matrix.sum(0) > 0.0 + matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0) + + from_which_layer = from_which_layer[fg_mask_inboxes] + all_b = all_b[fg_mask_inboxes] + all_a = all_a[fg_mask_inboxes] + all_gj = all_gj[fg_mask_inboxes] + all_gi = all_gi[fg_mask_inboxes] + all_anch = all_anch[fg_mask_inboxes] + + this_target = this_target[matched_gt_inds] + + for i in range(nl): + layer_idx = from_which_layer == i + matching_bs[i].append(all_b[layer_idx]) + matching_as[i].append(all_a[layer_idx]) + matching_gjs[i].append(all_gj[layer_idx]) + matching_gis[i].append(all_gi[layer_idx]) + matching_targets[i].append(this_target[layer_idx]) + matching_anchs[i].append(all_anch[layer_idx]) + + for i in range(nl): + if matching_targets[i] != []: + matching_bs[i] = torch.cat(matching_bs[i], dim=0) + matching_as[i] = torch.cat(matching_as[i], dim=0) + matching_gjs[i] = torch.cat(matching_gjs[i], dim=0) + matching_gis[i] = torch.cat(matching_gis[i], dim=0) + matching_targets[i] = torch.cat(matching_targets[i], dim=0) + matching_anchs[i] = torch.cat(matching_anchs[i], dim=0) + else: + matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + + return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs + + def find_3_positive(self, p, targets): + # Build targets for compute_loss(), input targets(image,class,x,y,w,h) + na, nt = self.na, targets.shape[0] # number of anchors, targets + indices, anch = [], [] + gain = torch.ones(7, device=targets.device).long() # normalized to gridspace gain + ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) + targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices + + g = 0.5 # bias + off = torch.tensor([[0, 0], + [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m + # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm + ], device=targets.device).float() * g # offsets + + for i in range(self.nl): + anchors = self.anchors[i] + gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain + + # Match targets to anchors + t = targets * gain + if nt: + # Matches + r = t[:, :, 4:6] / anchors[:, None] # wh ratio + j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t'] # compare + # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) + t = t[j] # filter + + # Offsets + gxy = t[:, 2:4] # grid xy + gxi = gain[[2, 3]] - gxy # inverse + j, k = ((gxy % 1. < g) & (gxy > 1.)).T + l, m = ((gxi % 1. < g) & (gxi > 1.)).T + j = torch.stack((torch.ones_like(j), j, k, l, m)) + t = t.repeat((5, 1, 1))[j] + offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] + else: + t = targets[0] + offsets = 0 + + # Define + b, c = t[:, :2].long().T # image, class + gxy = t[:, 2:4] # grid xy + gwh = t[:, 4:6] # grid wh + gij = (gxy - offsets).long() + gi, gj = gij.T # grid xy indices + + # Append + a = t[:, 6].long() # anchor indices + indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices + anch.append(anchors[a]) # anchors + + return indices, anch + + +class ComputeLossAuxOTA: + # Compute losses + def __init__(self, model, autobalance=False): + super(ComputeLossAuxOTA, self).__init__() + device = next(model.parameters()).device # get model device + h = model.hyp # hyperparameters + + # Define criteria + BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device)) + BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device)) + + # Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3 + self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets + + # Focal loss + g = h['fl_gamma'] # focal loss gamma + if g > 0: + BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) + + det = model.module.model[-1] if is_parallel(model) else model.model[-1] # Detect() module + self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.06, .02]) # P3-P7 + self.ssi = list(det.stride).index(16) if autobalance else 0 # stride 16 index + self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, model.gr, h, autobalance + for k in 'na', 'nc', 'nl', 'anchors', 'stride': + setattr(self, k, getattr(det, k)) + + def __call__(self, p, targets, imgs): # predictions, targets, model + device = targets.device + lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device) + bs_aux, as_aux_, gjs_aux, gis_aux, targets_aux, anchors_aux = self.build_targets2(p[:self.nl], targets, imgs) + bs, as_, gjs, gis, targets, anchors = self.build_targets(p[:self.nl], targets, imgs) + pre_gen_gains_aux = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p[:self.nl]] + pre_gen_gains = [torch.tensor(pp.shape, device=device)[[3, 2, 3, 2]] for pp in p[:self.nl]] + + + # Losses + for i in range(self.nl): # layer index, layer predictions + pi = p[i] + pi_aux = p[i+self.nl] + b, a, gj, gi = bs[i], as_[i], gjs[i], gis[i] # image, anchor, gridy, gridx + b_aux, a_aux, gj_aux, gi_aux = bs_aux[i], as_aux_[i], gjs_aux[i], gis_aux[i] # image, anchor, gridy, gridx + tobj = torch.zeros_like(pi[..., 0], device=device) # target obj + tobj_aux = torch.zeros_like(pi_aux[..., 0], device=device) # target obj + + n = b.shape[0] # number of targets + if n: + ps = pi[b, a, gj, gi] # prediction subset corresponding to targets + + # Regression + grid = torch.stack([gi, gj], dim=1) + pxy = ps[:, :2].sigmoid() * 2. - 0.5 + pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i] + pbox = torch.cat((pxy, pwh), 1) # predicted box + selected_tbox = targets[i][:, 2:6] * pre_gen_gains[i] + selected_tbox[:, :2] -= grid + iou = bbox_iou(pbox.T, selected_tbox, x1y1x2y2=False, CIoU=True) # iou(prediction, target) + lbox += (1.0 - iou).mean() # iou loss + + # Objectness + tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * iou.detach().clamp(0).type(tobj.dtype) # iou ratio + + # Classification + selected_tcls = targets[i][:, 1].long() + if self.nc > 1: # cls loss (only if multiple classes) + t = torch.full_like(ps[:, 5:], self.cn, device=device) # targets + t[range(n), selected_tcls] = self.cp + lcls += self.BCEcls(ps[:, 5:], t) # BCE + + # Append targets to text file + # with open('targets.txt', 'a') as file: + # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] + + n_aux = b_aux.shape[0] # number of targets + if n_aux: + ps_aux = pi_aux[b_aux, a_aux, gj_aux, gi_aux] # prediction subset corresponding to targets + grid_aux = torch.stack([gi_aux, gj_aux], dim=1) + pxy_aux = ps_aux[:, :2].sigmoid() * 2. - 0.5 + #pxy_aux = ps_aux[:, :2].sigmoid() * 3. - 1. + pwh_aux = (ps_aux[:, 2:4].sigmoid() * 2) ** 2 * anchors_aux[i] + pbox_aux = torch.cat((pxy_aux, pwh_aux), 1) # predicted box + selected_tbox_aux = targets_aux[i][:, 2:6] * pre_gen_gains_aux[i] + selected_tbox_aux[:, :2] -= grid_aux + iou_aux = bbox_iou(pbox_aux.T, selected_tbox_aux, x1y1x2y2=False, CIoU=True) # iou(prediction, target) + lbox += 0.25 * (1.0 - iou_aux).mean() # iou loss + + # Objectness + tobj_aux[b_aux, a_aux, gj_aux, gi_aux] = (1.0 - self.gr) + self.gr * iou_aux.detach().clamp(0).type(tobj_aux.dtype) # iou ratio + + # Classification + selected_tcls_aux = targets_aux[i][:, 1].long() + if self.nc > 1: # cls loss (only if multiple classes) + t_aux = torch.full_like(ps_aux[:, 5:], self.cn, device=device) # targets + t_aux[range(n_aux), selected_tcls_aux] = self.cp + lcls += 0.25 * self.BCEcls(ps_aux[:, 5:], t_aux) # BCE + + obji = self.BCEobj(pi[..., 4], tobj) + obji_aux = self.BCEobj(pi_aux[..., 4], tobj_aux) + lobj += obji * self.balance[i] + 0.25 * obji_aux * self.balance[i] # obj loss + if self.autobalance: + self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item() + + if self.autobalance: + self.balance = [x / self.balance[self.ssi] for x in self.balance] + lbox *= self.hyp['box'] + lobj *= self.hyp['obj'] + lcls *= self.hyp['cls'] + bs = tobj.shape[0] # batch size + + loss = lbox + lobj + lcls + return loss * bs, torch.cat((lbox, lobj, lcls, loss)).detach() + + def build_targets(self, p, targets, imgs): + + indices, anch = self.find_3_positive(p, targets) + + matching_bs = [[] for pp in p] + matching_as = [[] for pp in p] + matching_gjs = [[] for pp in p] + matching_gis = [[] for pp in p] + matching_targets = [[] for pp in p] + matching_anchs = [[] for pp in p] + + nl = len(p) + + for batch_idx in range(p[0].shape[0]): + + b_idx = targets[:, 0]==batch_idx + this_target = targets[b_idx] + if this_target.shape[0] == 0: + continue + + txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1] + txyxy = xywh2xyxy(txywh) + + pxyxys = [] + p_cls = [] + p_obj = [] + from_which_layer = [] + all_b = [] + all_a = [] + all_gj = [] + all_gi = [] + all_anch = [] + + for i, pi in enumerate(p): + + b, a, gj, gi = indices[i] + idx = (b == batch_idx) + b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx] + all_b.append(b) + all_a.append(a) + all_gj.append(gj) + all_gi.append(gi) + all_anch.append(anch[i][idx]) + from_which_layer.append(torch.ones(size=(len(b),)) * i) + + fg_pred = pi[b, a, gj, gi] + p_obj.append(fg_pred[:, 4:5]) + p_cls.append(fg_pred[:, 5:]) + + grid = torch.stack([gi, gj], dim=1) + pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #/ 8. + #pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i] + pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #/ 8. + pxywh = torch.cat([pxy, pwh], dim=-1) + pxyxy = xywh2xyxy(pxywh) + pxyxys.append(pxyxy) + + pxyxys = torch.cat(pxyxys, dim=0) + if pxyxys.shape[0] == 0: + continue + p_obj = torch.cat(p_obj, dim=0) + p_cls = torch.cat(p_cls, dim=0) + from_which_layer = torch.cat(from_which_layer, dim=0) + all_b = torch.cat(all_b, dim=0) + all_a = torch.cat(all_a, dim=0) + all_gj = torch.cat(all_gj, dim=0) + all_gi = torch.cat(all_gi, dim=0) + all_anch = torch.cat(all_anch, dim=0) + + pair_wise_iou = box_iou(txyxy, pxyxys) + + pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8) + + top_k, _ = torch.topk(pair_wise_iou, min(20, pair_wise_iou.shape[1]), dim=1) + dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1) + + gt_cls_per_image = ( + F.one_hot(this_target[:, 1].to(torch.int64), self.nc) + .float() + .unsqueeze(1) + .repeat(1, pxyxys.shape[0], 1) + ) + + num_gt = this_target.shape[0] + cls_preds_ = ( + p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + ) + + y = cls_preds_.sqrt_() + pair_wise_cls_loss = F.binary_cross_entropy_with_logits( + torch.log(y/(1-y)) , gt_cls_per_image, reduction="none" + ).sum(-1) + del cls_preds_ + + cost = ( + pair_wise_cls_loss + + 3.0 * pair_wise_iou_loss + ) + + matching_matrix = torch.zeros_like(cost) + + for gt_idx in range(num_gt): + _, pos_idx = torch.topk( + cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False + ) + matching_matrix[gt_idx][pos_idx] = 1.0 + + del top_k, dynamic_ks + anchor_matching_gt = matching_matrix.sum(0) + if (anchor_matching_gt > 1).sum() > 0: + _, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0) + matching_matrix[:, anchor_matching_gt > 1] *= 0.0 + matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0 + fg_mask_inboxes = matching_matrix.sum(0) > 0.0 + matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0) + + from_which_layer = from_which_layer[fg_mask_inboxes] + all_b = all_b[fg_mask_inboxes] + all_a = all_a[fg_mask_inboxes] + all_gj = all_gj[fg_mask_inboxes] + all_gi = all_gi[fg_mask_inboxes] + all_anch = all_anch[fg_mask_inboxes] + + this_target = this_target[matched_gt_inds] + + for i in range(nl): + layer_idx = from_which_layer == i + matching_bs[i].append(all_b[layer_idx]) + matching_as[i].append(all_a[layer_idx]) + matching_gjs[i].append(all_gj[layer_idx]) + matching_gis[i].append(all_gi[layer_idx]) + matching_targets[i].append(this_target[layer_idx]) + matching_anchs[i].append(all_anch[layer_idx]) + + for i in range(nl): + if matching_targets[i] != []: + matching_bs[i] = torch.cat(matching_bs[i], dim=0) + matching_as[i] = torch.cat(matching_as[i], dim=0) + matching_gjs[i] = torch.cat(matching_gjs[i], dim=0) + matching_gis[i] = torch.cat(matching_gis[i], dim=0) + matching_targets[i] = torch.cat(matching_targets[i], dim=0) + matching_anchs[i] = torch.cat(matching_anchs[i], dim=0) + else: + matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + + return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs + + def build_targets2(self, p, targets, imgs): + + indices, anch = self.find_5_positive(p, targets) + + matching_bs = [[] for pp in p] + matching_as = [[] for pp in p] + matching_gjs = [[] for pp in p] + matching_gis = [[] for pp in p] + matching_targets = [[] for pp in p] + matching_anchs = [[] for pp in p] + + nl = len(p) + + for batch_idx in range(p[0].shape[0]): + + b_idx = targets[:, 0]==batch_idx + this_target = targets[b_idx] + if this_target.shape[0] == 0: + continue + + txywh = this_target[:, 2:6] * imgs[batch_idx].shape[1] + txyxy = xywh2xyxy(txywh) + + pxyxys = [] + p_cls = [] + p_obj = [] + from_which_layer = [] + all_b = [] + all_a = [] + all_gj = [] + all_gi = [] + all_anch = [] + + for i, pi in enumerate(p): + + b, a, gj, gi = indices[i] + idx = (b == batch_idx) + b, a, gj, gi = b[idx], a[idx], gj[idx], gi[idx] + all_b.append(b) + all_a.append(a) + all_gj.append(gj) + all_gi.append(gi) + all_anch.append(anch[i][idx]) + from_which_layer.append(torch.ones(size=(len(b),)) * i) + + fg_pred = pi[b, a, gj, gi] + p_obj.append(fg_pred[:, 4:5]) + p_cls.append(fg_pred[:, 5:]) + + grid = torch.stack([gi, gj], dim=1) + pxy = (fg_pred[:, :2].sigmoid() * 2. - 0.5 + grid) * self.stride[i] #/ 8. + #pxy = (fg_pred[:, :2].sigmoid() * 3. - 1. + grid) * self.stride[i] + pwh = (fg_pred[:, 2:4].sigmoid() * 2) ** 2 * anch[i][idx] * self.stride[i] #/ 8. + pxywh = torch.cat([pxy, pwh], dim=-1) + pxyxy = xywh2xyxy(pxywh) + pxyxys.append(pxyxy) + + pxyxys = torch.cat(pxyxys, dim=0) + if pxyxys.shape[0] == 0: + continue + p_obj = torch.cat(p_obj, dim=0) + p_cls = torch.cat(p_cls, dim=0) + from_which_layer = torch.cat(from_which_layer, dim=0) + all_b = torch.cat(all_b, dim=0) + all_a = torch.cat(all_a, dim=0) + all_gj = torch.cat(all_gj, dim=0) + all_gi = torch.cat(all_gi, dim=0) + all_anch = torch.cat(all_anch, dim=0) + + pair_wise_iou = box_iou(txyxy, pxyxys) + + pair_wise_iou_loss = -torch.log(pair_wise_iou + 1e-8) + + top_k, _ = torch.topk(pair_wise_iou, min(20, pair_wise_iou.shape[1]), dim=1) + dynamic_ks = torch.clamp(top_k.sum(1).int(), min=1) + + gt_cls_per_image = ( + F.one_hot(this_target[:, 1].to(torch.int64), self.nc) + .float() + .unsqueeze(1) + .repeat(1, pxyxys.shape[0], 1) + ) + + num_gt = this_target.shape[0] + cls_preds_ = ( + p_cls.float().unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + * p_obj.unsqueeze(0).repeat(num_gt, 1, 1).sigmoid_() + ) + + y = cls_preds_.sqrt_() + pair_wise_cls_loss = F.binary_cross_entropy_with_logits( + torch.log(y/(1-y)) , gt_cls_per_image, reduction="none" + ).sum(-1) + del cls_preds_ + + cost = ( + pair_wise_cls_loss + + 3.0 * pair_wise_iou_loss + ) + + matching_matrix = torch.zeros_like(cost) + + for gt_idx in range(num_gt): + _, pos_idx = torch.topk( + cost[gt_idx], k=dynamic_ks[gt_idx].item(), largest=False + ) + matching_matrix[gt_idx][pos_idx] = 1.0 + + del top_k, dynamic_ks + anchor_matching_gt = matching_matrix.sum(0) + if (anchor_matching_gt > 1).sum() > 0: + _, cost_argmin = torch.min(cost[:, anchor_matching_gt > 1], dim=0) + matching_matrix[:, anchor_matching_gt > 1] *= 0.0 + matching_matrix[cost_argmin, anchor_matching_gt > 1] = 1.0 + fg_mask_inboxes = matching_matrix.sum(0) > 0.0 + matched_gt_inds = matching_matrix[:, fg_mask_inboxes].argmax(0) + + from_which_layer = from_which_layer[fg_mask_inboxes] + all_b = all_b[fg_mask_inboxes] + all_a = all_a[fg_mask_inboxes] + all_gj = all_gj[fg_mask_inboxes] + all_gi = all_gi[fg_mask_inboxes] + all_anch = all_anch[fg_mask_inboxes] + + this_target = this_target[matched_gt_inds] + + for i in range(nl): + layer_idx = from_which_layer == i + matching_bs[i].append(all_b[layer_idx]) + matching_as[i].append(all_a[layer_idx]) + matching_gjs[i].append(all_gj[layer_idx]) + matching_gis[i].append(all_gi[layer_idx]) + matching_targets[i].append(this_target[layer_idx]) + matching_anchs[i].append(all_anch[layer_idx]) + + for i in range(nl): + if matching_targets[i] != []: + matching_bs[i] = torch.cat(matching_bs[i], dim=0) + matching_as[i] = torch.cat(matching_as[i], dim=0) + matching_gjs[i] = torch.cat(matching_gjs[i], dim=0) + matching_gis[i] = torch.cat(matching_gis[i], dim=0) + matching_targets[i] = torch.cat(matching_targets[i], dim=0) + matching_anchs[i] = torch.cat(matching_anchs[i], dim=0) + else: + matching_bs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_as[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gjs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_gis[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_targets[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + matching_anchs[i] = torch.tensor([], device='cuda:0', dtype=torch.int64) + + return matching_bs, matching_as, matching_gjs, matching_gis, matching_targets, matching_anchs + + def find_5_positive(self, p, targets): + # Build targets for compute_loss(), input targets(image,class,x,y,w,h) + na, nt = self.na, targets.shape[0] # number of anchors, targets + indices, anch = [], [] + gain = torch.ones(7, device=targets.device).long() # normalized to gridspace gain + ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) + targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices + + g = 1.0 # bias + off = torch.tensor([[0, 0], + [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m + # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm + ], device=targets.device).float() * g # offsets + + for i in range(self.nl): + anchors = self.anchors[i] + gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain + + # Match targets to anchors + t = targets * gain + if nt: + # Matches + r = t[:, :, 4:6] / anchors[:, None] # wh ratio + j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t'] # compare + # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) + t = t[j] # filter + + # Offsets + gxy = t[:, 2:4] # grid xy + gxi = gain[[2, 3]] - gxy # inverse + j, k = ((gxy % 1. < g) & (gxy > 1.)).T + l, m = ((gxi % 1. < g) & (gxi > 1.)).T + j = torch.stack((torch.ones_like(j), j, k, l, m)) + t = t.repeat((5, 1, 1))[j] + offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] + else: + t = targets[0] + offsets = 0 + + # Define + b, c = t[:, :2].long().T # image, class + gxy = t[:, 2:4] # grid xy + gwh = t[:, 4:6] # grid wh + gij = (gxy - offsets).long() + gi, gj = gij.T # grid xy indices + + # Append + a = t[:, 6].long() # anchor indices + indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices + anch.append(anchors[a]) # anchors + + return indices, anch + + def find_3_positive(self, p, targets): + # Build targets for compute_loss(), input targets(image,class,x,y,w,h) + na, nt = self.na, targets.shape[0] # number of anchors, targets + indices, anch = [], [] + gain = torch.ones(7, device=targets.device).long() # normalized to gridspace gain + ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt) + targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices + + g = 0.5 # bias + off = torch.tensor([[0, 0], + [1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m + # [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm + ], device=targets.device).float() * g # offsets + + for i in range(self.nl): + anchors = self.anchors[i] + gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain + + # Match targets to anchors + t = targets * gain + if nt: + # Matches + r = t[:, :, 4:6] / anchors[:, None] # wh ratio + j = torch.max(r, 1. / r).max(2)[0] < self.hyp['anchor_t'] # compare + # j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2)) + t = t[j] # filter + + # Offsets + gxy = t[:, 2:4] # grid xy + gxi = gain[[2, 3]] - gxy # inverse + j, k = ((gxy % 1. < g) & (gxy > 1.)).T + l, m = ((gxi % 1. < g) & (gxi > 1.)).T + j = torch.stack((torch.ones_like(j), j, k, l, m)) + t = t.repeat((5, 1, 1))[j] + offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] + else: + t = targets[0] + offsets = 0 + + # Define + b, c = t[:, :2].long().T # image, class + gxy = t[:, 2:4] # grid xy + gwh = t[:, 4:6] # grid wh + gij = (gxy - offsets).long() + gi, gj = gij.T # grid xy indices + + # Append + a = t[:, 6].long() # anchor indices + indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices + anch.append(anchors[a]) # anchors + + return indices, anch diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/metrics.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/metrics.py new file mode 100644 index 000000000..6d2f53647 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/metrics.py @@ -0,0 +1,227 @@ +# Model validation metrics + +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +import torch + +from . import general + + +def fitness(x): + # Model fitness as a weighted combination of metrics + w = [0.0, 0.0, 0.1, 0.9] # weights for [P, R, mAP@0.5, mAP@0.5:0.95] + return (x[:, :4] * w).sum(1) + + +def ap_per_class(tp, conf, pred_cls, target_cls, v5_metric=False, plot=False, save_dir='.', names=()): + """ Compute the average precision, given the recall and precision curves. + Source: https://github.com/rafaelpadilla/Object-Detection-Metrics. + # Arguments + tp: True positives (nparray, nx1 or nx10). + conf: Objectness value from 0-1 (nparray). + pred_cls: Predicted object classes (nparray). + target_cls: True object classes (nparray). + plot: Plot precision-recall curve at mAP@0.5 + save_dir: Plot save directory + # Returns + The average precision as computed in py-faster-rcnn. + """ + + # Sort by objectness + i = np.argsort(-conf) + tp, conf, pred_cls = tp[i], conf[i], pred_cls[i] + + # Find unique classes + unique_classes = np.unique(target_cls) + nc = unique_classes.shape[0] # number of classes, number of detections + + # Create Precision-Recall curve and compute AP for each class + px, py = np.linspace(0, 1, 1000), [] # for plotting + ap, p, r = np.zeros((nc, tp.shape[1])), np.zeros((nc, 1000)), np.zeros((nc, 1000)) + for ci, c in enumerate(unique_classes): + i = pred_cls == c + n_l = (target_cls == c).sum() # number of labels + n_p = i.sum() # number of predictions + + if n_p == 0 or n_l == 0: + continue + else: + # Accumulate FPs and TPs + fpc = (1 - tp[i]).cumsum(0) + tpc = tp[i].cumsum(0) + + # Recall + recall = tpc / (n_l + 1e-16) # recall curve + r[ci] = np.interp(-px, -conf[i], recall[:, 0], left=0) # negative x, xp because xp decreases + + # Precision + precision = tpc / (tpc + fpc) # precision curve + p[ci] = np.interp(-px, -conf[i], precision[:, 0], left=1) # p at pr_score + + # AP from recall-precision curve + for j in range(tp.shape[1]): + ap[ci, j], mpre, mrec = compute_ap(recall[:, j], precision[:, j], v5_metric=v5_metric) + if plot and j == 0: + py.append(np.interp(px, mrec, mpre)) # precision at mAP@0.5 + + # Compute F1 (harmonic mean of precision and recall) + f1 = 2 * p * r / (p + r + 1e-16) + if plot: + plot_pr_curve(px, py, ap, Path(save_dir) / 'PR_curve.png', names) + plot_mc_curve(px, f1, Path(save_dir) / 'F1_curve.png', names, ylabel='F1') + plot_mc_curve(px, p, Path(save_dir) / 'P_curve.png', names, ylabel='Precision') + plot_mc_curve(px, r, Path(save_dir) / 'R_curve.png', names, ylabel='Recall') + + i = f1.mean(0).argmax() # max F1 index + return p[:, i], r[:, i], ap, f1[:, i], unique_classes.astype('int32') + + +def compute_ap(recall, precision, v5_metric=False): + """ Compute the average precision, given the recall and precision curves + # Arguments + recall: The recall curve (list) + precision: The precision curve (list) + v5_metric: Assume maximum recall to be 1.0, as in YOLOv5, MMDetetion etc. + # Returns + Average precision, precision curve, recall curve + """ + + # Append sentinel values to beginning and end + if v5_metric: # New YOLOv5 metric, same as MMDetection and Detectron2 repositories + mrec = np.concatenate(([0.], recall, [1.0])) + else: # Old YOLOv5 metric, i.e. default YOLOv7 metric + mrec = np.concatenate(([0.], recall, [recall[-1] + 0.01])) + mpre = np.concatenate(([1.], precision, [0.])) + + # Compute the precision envelope + mpre = np.flip(np.maximum.accumulate(np.flip(mpre))) + + # Integrate area under curve + method = 'interp' # methods: 'continuous', 'interp' + if method == 'interp': + x = np.linspace(0, 1, 101) # 101-point interp (COCO) + ap = np.trapz(np.interp(x, mrec, mpre), x) # integrate + else: # 'continuous' + i = np.where(mrec[1:] != mrec[:-1])[0] # points where x axis (recall) changes + ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) # area under curve + + return ap, mpre, mrec + + +class ConfusionMatrix: + # Updated version of https://github.com/kaanakan/object_detection_confusion_matrix + def __init__(self, nc, conf=0.25, iou_thres=0.45): + self.matrix = np.zeros((nc + 1, nc + 1)) + self.nc = nc # number of classes + self.conf = conf + self.iou_thres = iou_thres + + def process_batch(self, detections, labels): + """ + Return intersection-over-union (Jaccard index) of boxes. + Both sets of boxes are expected to be in (x1, y1, x2, y2) format. + Arguments: + detections (Array[N, 6]), x1, y1, x2, y2, conf, class + labels (Array[M, 5]), class, x1, y1, x2, y2 + Returns: + None, updates confusion matrix accordingly + """ + detections = detections[detections[:, 4] > self.conf] + gt_classes = labels[:, 0].int() + detection_classes = detections[:, 5].int() + iou = general.box_iou(labels[:, 1:], detections[:, :4]) + + x = torch.where(iou > self.iou_thres) + if x[0].shape[0]: + matches = torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1).cpu().numpy() + if x[0].shape[0] > 1: + matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 1], return_index=True)[1]] + matches = matches[matches[:, 2].argsort()[::-1]] + matches = matches[np.unique(matches[:, 0], return_index=True)[1]] + else: + matches = np.zeros((0, 3)) + + n = matches.shape[0] > 0 + m0, m1, _ = matches.transpose().astype(np.int16) + for i, gc in enumerate(gt_classes): + j = m0 == i + if n and sum(j) == 1: + self.matrix[gc, detection_classes[m1[j]]] += 1 # correct + else: + self.matrix[self.nc, gc] += 1 # background FP + + if n: + for i, dc in enumerate(detection_classes): + if not any(m1 == i): + self.matrix[dc, self.nc] += 1 # background FN + + def matrix(self): + return self.matrix + + def plot(self, save_dir='', names=()): + try: + import seaborn as sn + + array = self.matrix / (self.matrix.sum(0).reshape(1, self.nc + 1) + 1E-6) # normalize + array[array < 0.005] = np.nan # don't annotate (would appear as 0.00) + + fig = plt.figure(figsize=(12, 9), tight_layout=True) + sn.set(font_scale=1.0 if self.nc < 50 else 0.8) # for label size + labels = (0 < len(names) < 99) and len(names) == self.nc # apply names to ticklabels + sn.heatmap(array, annot=self.nc < 30, annot_kws={"size": 8}, cmap='Blues', fmt='.2f', square=True, + xticklabels=names + ['background FP'] if labels else "auto", + yticklabels=names + ['background FN'] if labels else "auto").set_facecolor((1, 1, 1)) + fig.axes[0].set_xlabel('True') + fig.axes[0].set_ylabel('Predicted') + fig.savefig(Path(save_dir) / 'confusion_matrix.png', dpi=250) + except Exception as e: + pass + + def print(self): + for i in range(self.nc + 1): + print(' '.join(map(str, self.matrix[i]))) + + +# Plots ---------------------------------------------------------------------------------------------------------------- + +def plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=()): + # Precision-recall curve + fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True) + py = np.stack(py, axis=1) + + if 0 < len(names) < 21: # display per-class legend if < 21 classes + for i, y in enumerate(py.T): + ax.plot(px, y, linewidth=1, label=f'{names[i]} {ap[i, 0]:.3f}') # plot(recall, precision) + else: + ax.plot(px, py, linewidth=1, color='grey') # plot(recall, precision) + + ax.plot(px, py.mean(1), linewidth=3, color='blue', label='all classes %.3f mAP@0.5' % ap[:, 0].mean()) + ax.set_xlabel('Recall') + ax.set_ylabel('Precision') + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left") + fig.savefig(Path(save_dir), dpi=250) + + +def plot_mc_curve(px, py, save_dir='mc_curve.png', names=(), xlabel='Confidence', ylabel='Metric'): + # Metric-confidence curve + fig, ax = plt.subplots(1, 1, figsize=(9, 6), tight_layout=True) + + if 0 < len(names) < 21: # display per-class legend if < 21 classes + for i, y in enumerate(py): + ax.plot(px, y, linewidth=1, label=f'{names[i]}') # plot(confidence, metric) + else: + ax.plot(px, py.T, linewidth=1, color='grey') # plot(confidence, metric) + + y = py.mean(0) + ax.plot(px, y, linewidth=3, color='blue', label=f'all classes {y.max():.2f} at {px[y.argmax()]:.3f}') + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) + ax.set_xlim(0, 1) + ax.set_ylim(0, 1) + plt.legend(bbox_to_anchor=(1.04, 1), loc="upper left") + fig.savefig(Path(save_dir), dpi=250) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/plots.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/plots.py new file mode 100644 index 000000000..fdd8d0e85 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/plots.py @@ -0,0 +1,489 @@ +# Plotting utils + +import glob +import math +import os +import random +from copy import copy +from pathlib import Path + +import cv2 +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import seaborn as sns +import torch +import yaml +from PIL import Image, ImageDraw, ImageFont +from scipy.signal import butter, filtfilt + +from utils.general import xywh2xyxy, xyxy2xywh +from utils.metrics import fitness + +# Settings +matplotlib.rc('font', **{'size': 11}) +matplotlib.use('Agg') # for writing to files only + + +def color_list(): + # Return first 10 plt colors as (r,g,b) https://stackoverflow.com/questions/51350872/python-from-color-name-to-rgb + def hex2rgb(h): + return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)) + + return [hex2rgb(h) for h in matplotlib.colors.TABLEAU_COLORS.values()] # or BASE_ (8), CSS4_ (148), XKCD_ (949) + + +def hist2d(x, y, n=100): + # 2d histogram used in labels.png and evolve.png + xedges, yedges = np.linspace(x.min(), x.max(), n), np.linspace(y.min(), y.max(), n) + hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges)) + xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1) + yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1) + return np.log(hist[xidx, yidx]) + + +def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5): + # https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy + def butter_lowpass(cutoff, fs, order): + nyq = 0.5 * fs + normal_cutoff = cutoff / nyq + return butter(order, normal_cutoff, btype='low', analog=False) + + b, a = butter_lowpass(cutoff, fs, order=order) + return filtfilt(b, a, data) # forward-backward filter + + +def plot_one_box(x, img, color=None, label=None, line_thickness=3): + # Plots one bounding box on image img + tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness + color = color or [random.randint(0, 255) for _ in range(3)] + c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) + cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) + if label: + tf = max(tl - 1, 1) # font thickness + t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] + c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 + cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled + cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA) + + +def plot_one_box_PIL(box, img, color=None, label=None, line_thickness=None): + img = Image.fromarray(img) + draw = ImageDraw.Draw(img) + line_thickness = line_thickness or max(int(min(img.size) / 200), 2) + draw.rectangle(box, width=line_thickness, outline=tuple(color)) # plot + if label: + fontsize = max(round(max(img.size) / 40), 12) + font = ImageFont.truetype("Arial.ttf", fontsize) + txt_width, txt_height = font.getsize(label) + draw.rectangle([box[0], box[1] - txt_height + 4, box[0] + txt_width, box[1]], fill=tuple(color)) + draw.text((box[0], box[1] - txt_height + 1), label, fill=(255, 255, 255), font=font) + return np.asarray(img) + + +def plot_wh_methods(): # from utils.plots import *; plot_wh_methods() + # Compares the two methods for width-height anchor multiplication + # https://github.com/ultralytics/yolov3/issues/168 + x = np.arange(-4.0, 4.0, .1) + ya = np.exp(x) + yb = torch.sigmoid(torch.from_numpy(x)).numpy() * 2 + + fig = plt.figure(figsize=(6, 3), tight_layout=True) + plt.plot(x, ya, '.-', label='YOLOv3') + plt.plot(x, yb ** 2, '.-', label='YOLOR ^2') + plt.plot(x, yb ** 1.6, '.-', label='YOLOR ^1.6') + plt.xlim(left=-4, right=4) + plt.ylim(bottom=0, top=6) + plt.xlabel('input') + plt.ylabel('output') + plt.grid() + plt.legend() + fig.savefig('comparison.png', dpi=200) + + +def output_to_target(output): + # Convert model output to target format [batch_id, class_id, x, y, w, h, conf] + targets = [] + for i, o in enumerate(output): + for *box, conf, cls in o.cpu().numpy(): + targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf]) + return np.array(targets) + + +def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max_size=640, max_subplots=16): + # Plot image grid with labels + + if isinstance(images, torch.Tensor): + images = images.cpu().float().numpy() + if isinstance(targets, torch.Tensor): + targets = targets.cpu().numpy() + + # un-normalise + if np.max(images[0]) <= 1: + images *= 255 + + tl = 3 # line thickness + tf = max(tl - 1, 1) # font thickness + bs, _, h, w = images.shape # batch size, _, height, width + bs = min(bs, max_subplots) # limit plot images + ns = np.ceil(bs ** 0.5) # number of subplots (square) + + # Check if we should resize + scale_factor = max_size / max(h, w) + if scale_factor < 1: + h = math.ceil(scale_factor * h) + w = math.ceil(scale_factor * w) + + colors = color_list() # list of colors + mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8) # init + for i, img in enumerate(images): + if i == max_subplots: # if last batch has fewer images than we expect + break + + block_x = int(w * (i // ns)) + block_y = int(h * (i % ns)) + + img = img.transpose(1, 2, 0) + if scale_factor < 1: + img = cv2.resize(img, (w, h)) + + mosaic[block_y:block_y + h, block_x:block_x + w, :] = img + if len(targets) > 0: + image_targets = targets[targets[:, 0] == i] + boxes = xywh2xyxy(image_targets[:, 2:6]).T + classes = image_targets[:, 1].astype('int') + labels = image_targets.shape[1] == 6 # labels if no conf column + conf = None if labels else image_targets[:, 6] # check for confidence presence (label vs pred) + + if boxes.shape[1]: + if boxes.max() <= 1.01: # if normalized with tolerance 0.01 + boxes[[0, 2]] *= w # scale to pixels + boxes[[1, 3]] *= h + elif scale_factor < 1: # absolute coords need scale if image scales + boxes *= scale_factor + boxes[[0, 2]] += block_x + boxes[[1, 3]] += block_y + for j, box in enumerate(boxes.T): + cls = int(classes[j]) + color = colors[cls % len(colors)] + cls = names[cls] if names else cls + if labels or conf[j] > 0.25: # 0.25 conf thresh + label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j]) + plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl) + + # Draw image filename labels + if paths: + label = Path(paths[i]).name[:40] # trim to 40 char + t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] + cv2.putText(mosaic, label, (block_x + 5, block_y + t_size[1] + 5), 0, tl / 3, [220, 220, 220], thickness=tf, + lineType=cv2.LINE_AA) + + # Image border + cv2.rectangle(mosaic, (block_x, block_y), (block_x + w, block_y + h), (255, 255, 255), thickness=3) + + if fname: + r = min(1280. / max(h, w) / ns, 1.0) # ratio to limit image size + mosaic = cv2.resize(mosaic, (int(ns * w * r), int(ns * h * r)), interpolation=cv2.INTER_AREA) + # cv2.imwrite(fname, cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB)) # cv2 save + Image.fromarray(mosaic).save(fname) # PIL save + return mosaic + + +def plot_lr_scheduler(optimizer, scheduler, epochs=300, save_dir=''): + # Plot LR simulating training for full epochs + optimizer, scheduler = copy(optimizer), copy(scheduler) # do not modify originals + y = [] + for _ in range(epochs): + scheduler.step() + y.append(optimizer.param_groups[0]['lr']) + plt.plot(y, '.-', label='LR') + plt.xlabel('epoch') + plt.ylabel('LR') + plt.grid() + plt.xlim(0, epochs) + plt.ylim(0) + plt.savefig(Path(save_dir) / 'LR.png', dpi=200) + plt.close() + + +def plot_test_txt(): # from utils.plots import *; plot_test() + # Plot test.txt histograms + x = np.loadtxt('test.txt', dtype=np.float32) + box = xyxy2xywh(x[:, :4]) + cx, cy = box[:, 0], box[:, 1] + + fig, ax = plt.subplots(1, 1, figsize=(6, 6), tight_layout=True) + ax.hist2d(cx, cy, bins=600, cmax=10, cmin=0) + ax.set_aspect('equal') + plt.savefig('hist2d.png', dpi=300) + + fig, ax = plt.subplots(1, 2, figsize=(12, 6), tight_layout=True) + ax[0].hist(cx, bins=600) + ax[1].hist(cy, bins=600) + plt.savefig('hist1d.png', dpi=200) + + +def plot_targets_txt(): # from utils.plots import *; plot_targets_txt() + # Plot targets.txt histograms + x = np.loadtxt('targets.txt', dtype=np.float32).T + s = ['x targets', 'y targets', 'width targets', 'height targets'] + fig, ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True) + ax = ax.ravel() + for i in range(4): + ax[i].hist(x[i], bins=100, label='%.3g +/- %.3g' % (x[i].mean(), x[i].std())) + ax[i].legend() + ax[i].set_title(s[i]) + plt.savefig('targets.jpg', dpi=200) + + +def plot_study_txt(path='', x=None): # from utils.plots import *; plot_study_txt() + # Plot study.txt generated by test.py + fig, ax = plt.subplots(2, 4, figsize=(10, 6), tight_layout=True) + # ax = ax.ravel() + + fig2, ax2 = plt.subplots(1, 1, figsize=(8, 4), tight_layout=True) + # for f in [Path(path) / f'study_coco_{x}.txt' for x in ['yolor-p6', 'yolor-w6', 'yolor-e6', 'yolor-d6']]: + for f in sorted(Path(path).glob('study*.txt')): + y = np.loadtxt(f, dtype=np.float32, usecols=[0, 1, 2, 3, 7, 8, 9], ndmin=2).T + x = np.arange(y.shape[1]) if x is None else np.array(x) + s = ['P', 'R', 'mAP@.5', 'mAP@.5:.95', 't_inference (ms/img)', 't_NMS (ms/img)', 't_total (ms/img)'] + # for i in range(7): + # ax[i].plot(x, y[i], '.-', linewidth=2, markersize=8) + # ax[i].set_title(s[i]) + + j = y[3].argmax() + 1 + ax2.plot(y[6, 1:j], y[3, 1:j] * 1E2, '.-', linewidth=2, markersize=8, + label=f.stem.replace('study_coco_', '').replace('yolo', 'YOLO')) + + ax2.plot(1E3 / np.array([209, 140, 97, 58, 35, 18]), [34.6, 40.5, 43.0, 47.5, 49.7, 51.5], + 'k.-', linewidth=2, markersize=8, alpha=.25, label='EfficientDet') + + ax2.grid(alpha=0.2) + ax2.set_yticks(np.arange(20, 60, 5)) + ax2.set_xlim(0, 57) + ax2.set_ylim(30, 55) + ax2.set_xlabel('GPU Speed (ms/img)') + ax2.set_ylabel('COCO AP val') + ax2.legend(loc='lower right') + plt.savefig(str(Path(path).name) + '.png', dpi=300) + + +def plot_labels(labels, names=(), save_dir=Path(''), loggers=None): + # plot dataset labels + print('Plotting labels... ') + c, b = labels[:, 0], labels[:, 1:].transpose() # classes, boxes + nc = int(c.max() + 1) # number of classes + colors = color_list() + x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height']) + + # seaborn correlogram + sns.pairplot(x, corner=True, diag_kind='auto', kind='hist', diag_kws=dict(bins=50), plot_kws=dict(pmax=0.9)) + plt.savefig(save_dir / 'labels_correlogram.jpg', dpi=200) + plt.close() + + # matplotlib labels + matplotlib.use('svg') # faster + ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel() + ax[0].hist(c, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8) + ax[0].set_ylabel('instances') + if 0 < len(names) < 30: + ax[0].set_xticks(range(len(names))) + ax[0].set_xticklabels(names, rotation=90, fontsize=10) + else: + ax[0].set_xlabel('classes') + sns.histplot(x, x='x', y='y', ax=ax[2], bins=50, pmax=0.9) + sns.histplot(x, x='width', y='height', ax=ax[3], bins=50, pmax=0.9) + + # rectangles + labels[:, 1:3] = 0.5 # center + labels[:, 1:] = xywh2xyxy(labels[:, 1:]) * 2000 + img = Image.fromarray(np.ones((2000, 2000, 3), dtype=np.uint8) * 255) + for cls, *box in labels[:1000]: + ImageDraw.Draw(img).rectangle(box, width=1, outline=colors[int(cls) % 10]) # plot + ax[1].imshow(img) + ax[1].axis('off') + + for a in [0, 1, 2, 3]: + for s in ['top', 'right', 'left', 'bottom']: + ax[a].spines[s].set_visible(False) + + plt.savefig(save_dir / 'labels.jpg', dpi=200) + matplotlib.use('Agg') + plt.close() + + # loggers + for k, v in loggers.items() or {}: + if k == 'wandb' and v: + v.log({"Labels": [v.Image(str(x), caption=x.name) for x in save_dir.glob('*labels*.jpg')]}, commit=False) + + +def plot_evolution(yaml_file='data/hyp.finetune.yaml'): # from utils.plots import *; plot_evolution() + # Plot hyperparameter evolution results in evolve.txt + with open(yaml_file) as f: + hyp = yaml.load(f, Loader=yaml.SafeLoader) + x = np.loadtxt('evolve.txt', ndmin=2) + f = fitness(x) + # weights = (f - f.min()) ** 2 # for weighted results + plt.figure(figsize=(10, 12), tight_layout=True) + matplotlib.rc('font', **{'size': 8}) + for i, (k, v) in enumerate(hyp.items()): + y = x[:, i + 7] + # mu = (y * weights).sum() / weights.sum() # best weighted result + mu = y[f.argmax()] # best single result + plt.subplot(6, 5, i + 1) + plt.scatter(y, f, c=hist2d(y, f, 20), cmap='viridis', alpha=.8, edgecolors='none') + plt.plot(mu, f.max(), 'k+', markersize=15) + plt.title('%s = %.3g' % (k, mu), fontdict={'size': 9}) # limit to 40 characters + if i % 5 != 0: + plt.yticks([]) + print('%15s: %.3g' % (k, mu)) + plt.savefig('evolve.png', dpi=200) + print('\nPlot saved as evolve.png') + + +def profile_idetection(start=0, stop=0, labels=(), save_dir=''): + # Plot iDetection '*.txt' per-image logs. from utils.plots import *; profile_idetection() + ax = plt.subplots(2, 4, figsize=(12, 6), tight_layout=True)[1].ravel() + s = ['Images', 'Free Storage (GB)', 'RAM Usage (GB)', 'Battery', 'dt_raw (ms)', 'dt_smooth (ms)', 'real-world FPS'] + files = list(Path(save_dir).glob('frames*.txt')) + for fi, f in enumerate(files): + try: + results = np.loadtxt(f, ndmin=2).T[:, 90:-30] # clip first and last rows + n = results.shape[1] # number of rows + x = np.arange(start, min(stop, n) if stop else n) + results = results[:, x] + t = (results[0] - results[0].min()) # set t0=0s + results[0] = x + for i, a in enumerate(ax): + if i < len(results): + label = labels[fi] if len(labels) else f.stem.replace('frames_', '') + a.plot(t, results[i], marker='.', label=label, linewidth=1, markersize=5) + a.set_title(s[i]) + a.set_xlabel('time (s)') + # if fi == len(files) - 1: + # a.set_ylim(bottom=0) + for side in ['top', 'right']: + a.spines[side].set_visible(False) + else: + a.remove() + except Exception as e: + print('Warning: Plotting error for %s; %s' % (f, e)) + + ax[1].legend() + plt.savefig(Path(save_dir) / 'idetection_profile.png', dpi=200) + + +def plot_results_overlay(start=0, stop=0): # from utils.plots import *; plot_results_overlay() + # Plot training 'results*.txt', overlaying train and val losses + s = ['train', 'train', 'train', 'Precision', 'mAP@0.5', 'val', 'val', 'val', 'Recall', 'mAP@0.5:0.95'] # legends + t = ['Box', 'Objectness', 'Classification', 'P-R', 'mAP-F1'] # titles + for f in sorted(glob.glob('results*.txt') + glob.glob('../../Downloads/results*.txt')): + results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T + n = results.shape[1] # number of rows + x = range(start, min(stop, n) if stop else n) + fig, ax = plt.subplots(1, 5, figsize=(14, 3.5), tight_layout=True) + ax = ax.ravel() + for i in range(5): + for j in [i, i + 5]: + y = results[j, x] + ax[i].plot(x, y, marker='.', label=s[j]) + # y_smooth = butter_lowpass_filtfilt(y) + # ax[i].plot(x, np.gradient(y_smooth), marker='.', label=s[j]) + + ax[i].set_title(t[i]) + ax[i].legend() + ax[i].set_ylabel(f) if i == 0 else None # add filename + fig.savefig(f.replace('.txt', '.png'), dpi=200) + + +def plot_results(start=0, stop=0, bucket='', id=(), labels=(), save_dir=''): + # Plot training 'results*.txt'. from utils.plots import *; plot_results(save_dir='runs/train/exp') + fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True) + ax = ax.ravel() + s = ['Box', 'Objectness', 'Classification', 'Precision', 'Recall', + 'val Box', 'val Objectness', 'val Classification', 'mAP@0.5', 'mAP@0.5:0.95'] + if bucket: + # files = ['https://storage.googleapis.com/%s/results%g.txt' % (bucket, x) for x in id] + files = ['results%g.txt' % x for x in id] + c = ('gsutil cp ' + '%s ' * len(files) + '.') % tuple('gs://%s/results%g.txt' % (bucket, x) for x in id) + os.system(c) + else: + files = list(Path(save_dir).glob('results*.txt')) + assert len(files), 'No results.txt files found in %s, nothing to plot.' % os.path.abspath(save_dir) + for fi, f in enumerate(files): + try: + results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T + n = results.shape[1] # number of rows + x = range(start, min(stop, n) if stop else n) + for i in range(10): + y = results[i, x] + if i in [0, 1, 2, 5, 6, 7]: + y[y == 0] = np.nan # don't show zero loss values + # y /= y[0] # normalize + label = labels[fi] if len(labels) else f.stem + ax[i].plot(x, y, marker='.', label=label, linewidth=2, markersize=8) + ax[i].set_title(s[i]) + # if i in [5, 6, 7]: # share train and val loss y axes + # ax[i].get_shared_y_axes().join(ax[i], ax[i - 5]) + except Exception as e: + print('Warning: Plotting error for %s; %s' % (f, e)) + + ax[1].legend() + fig.savefig(Path(save_dir) / 'results.png', dpi=200) + + +def output_to_keypoint(output): + # Convert model output to target format [batch_id, class_id, x, y, w, h, conf] + targets = [] + for i, o in enumerate(output): + kpts = o[:,6:] + o = o[:,:6] + for index, (*box, conf, cls) in enumerate(o.detach().cpu().numpy()): + targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf, *list(kpts.detach().cpu().numpy()[index])]) + return np.array(targets) + + +def plot_skeleton_kpts(im, kpts, steps, orig_shape=None): + #Plot the skeleton and keypointsfor coco datatset + palette = np.array([[255, 128, 0], [255, 153, 51], [255, 178, 102], + [230, 230, 0], [255, 153, 255], [153, 204, 255], + [255, 102, 255], [255, 51, 255], [102, 178, 255], + [51, 153, 255], [255, 153, 153], [255, 102, 102], + [255, 51, 51], [153, 255, 153], [102, 255, 102], + [51, 255, 51], [0, 255, 0], [0, 0, 255], [255, 0, 0], + [255, 255, 255]]) + + skeleton = [[16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], + [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11], [2, 3], + [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7]] + + pose_limb_color = palette[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]] + pose_kpt_color = palette[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]] + radius = 5 + num_kpts = len(kpts) // steps + + for kid in range(num_kpts): + r, g, b = pose_kpt_color[kid] + x_coord, y_coord = kpts[steps * kid], kpts[steps * kid + 1] + if not (x_coord % 640 == 0 or y_coord % 640 == 0): + if steps == 3: + conf = kpts[steps * kid + 2] + if conf < 0.5: + continue + cv2.circle(im, (int(x_coord), int(y_coord)), radius, (int(r), int(g), int(b)), -1) + + for sk_id, sk in enumerate(skeleton): + r, g, b = pose_limb_color[sk_id] + pos1 = (int(kpts[(sk[0]-1)*steps]), int(kpts[(sk[0]-1)*steps+1])) + pos2 = (int(kpts[(sk[1]-1)*steps]), int(kpts[(sk[1]-1)*steps+1])) + if steps == 3: + conf1 = kpts[(sk[0]-1)*steps+2] + conf2 = kpts[(sk[1]-1)*steps+2] + if conf1<0.5 or conf2<0.5: + continue + if pos1[0]%640 == 0 or pos1[1]%640==0 or pos1[0]<0 or pos1[1]<0: + continue + if pos2[0] % 640 == 0 or pos2[1] % 640 == 0 or pos2[0]<0 or pos2[1]<0: + continue + cv2.line(im, pos1, pos2, (int(r), int(g), int(b)), thickness=2) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/torch_utils.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/torch_utils.py new file mode 100644 index 000000000..1e631b555 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/torch_utils.py @@ -0,0 +1,374 @@ +# YOLOR PyTorch utils + +import datetime +import logging +import math +import os +import platform +import subprocess +import time +from contextlib import contextmanager +from copy import deepcopy +from pathlib import Path + +import torch +import torch.backends.cudnn as cudnn +import torch.nn as nn +import torch.nn.functional as F +import torchvision + +try: + import thop # for FLOPS computation +except ImportError: + thop = None +logger = logging.getLogger(__name__) + + +@contextmanager +def torch_distributed_zero_first(local_rank: int): + """ + Decorator to make all processes in distributed training wait for each local_master to do something. + """ + if local_rank not in [-1, 0]: + torch.distributed.barrier() + yield + if local_rank == 0: + torch.distributed.barrier() + + +def init_torch_seeds(seed=0): + # Speed-reproducibility tradeoff https://pytorch.org/docs/stable/notes/randomness.html + torch.manual_seed(seed) + if seed == 0: # slower, more reproducible + cudnn.benchmark, cudnn.deterministic = False, True + else: # faster, less reproducible + cudnn.benchmark, cudnn.deterministic = True, False + + +def date_modified(path=__file__): + # return human-readable file modification date, i.e. '2021-3-26' + t = datetime.datetime.fromtimestamp(Path(path).stat().st_mtime) + return f'{t.year}-{t.month}-{t.day}' + + +def git_describe(path=Path(__file__).parent): # path must be a directory + # return human-readable git description, i.e. v5.0-5-g3e25f1e https://git-scm.com/docs/git-describe + s = f'git -C {path} describe --tags --long --always' + try: + return subprocess.check_output(s, shell=True, stderr=subprocess.STDOUT).decode()[:-1] + except subprocess.CalledProcessError as e: + return '' # not a git repository + + +def select_device(device='', batch_size=None): + # device = 'cpu' or '0' or '0,1,2,3' + s = f'YOLOR 🚀 {git_describe() or date_modified()} torch {torch.__version__} ' # string + cpu = device.lower() == 'cpu' + if cpu: + os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # force torch.cuda.is_available() = False + elif device: # non-cpu device requested + os.environ['CUDA_VISIBLE_DEVICES'] = device # set environment variable + assert torch.cuda.is_available(), f'CUDA unavailable, invalid device {device} requested' # check availability + + cuda = not cpu and torch.cuda.is_available() + if cuda: + n = torch.cuda.device_count() + if n > 1 and batch_size: # check that batch_size is compatible with device_count + assert batch_size % n == 0, f'batch-size {batch_size} not multiple of GPU count {n}' + space = ' ' * len(s) + for i, d in enumerate(device.split(',') if device else range(n)): + p = torch.cuda.get_device_properties(i) + s += f"{'' if i == 0 else space}CUDA:{d} ({p.name}, {p.total_memory / 1024 ** 2}MB)\n" # bytes to MB + else: + s += 'CPU\n' + + logger.info(s.encode().decode('ascii', 'ignore') if platform.system() == 'Windows' else s) # emoji-safe + return torch.device('cuda:0' if cuda else 'cpu') + + +def time_synchronized(): + # pytorch-accurate time + if torch.cuda.is_available(): + torch.cuda.synchronize() + return time.time() + + +def profile(x, ops, n=100, device=None): + # profile a pytorch module or list of modules. Example usage: + # x = torch.randn(16, 3, 640, 640) # input + # m1 = lambda x: x * torch.sigmoid(x) + # m2 = nn.SiLU() + # profile(x, [m1, m2], n=100) # profile speed over 100 iterations + + device = device or torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') + x = x.to(device) + x.requires_grad = True + print(torch.__version__, device.type, torch.cuda.get_device_properties(0) if device.type == 'cuda' else '') + print(f"\n{'Params':>12s}{'GFLOPS':>12s}{'forward (ms)':>16s}{'backward (ms)':>16s}{'input':>24s}{'output':>24s}") + for m in ops if isinstance(ops, list) else [ops]: + m = m.to(device) if hasattr(m, 'to') else m # device + m = m.half() if hasattr(m, 'half') and isinstance(x, torch.Tensor) and x.dtype is torch.float16 else m # type + dtf, dtb, t = 0., 0., [0., 0., 0.] # dt forward, backward + try: + flops = thop.profile(m, inputs=(x,), verbose=False)[0] / 1E9 * 2 # GFLOPS + except: + flops = 0 + + for _ in range(n): + t[0] = time_synchronized() + y = m(x) + t[1] = time_synchronized() + try: + _ = y.sum().backward() + t[2] = time_synchronized() + except: # no backward method + t[2] = float('nan') + dtf += (t[1] - t[0]) * 1000 / n # ms per op forward + dtb += (t[2] - t[1]) * 1000 / n # ms per op backward + + s_in = tuple(x.shape) if isinstance(x, torch.Tensor) else 'list' + s_out = tuple(y.shape) if isinstance(y, torch.Tensor) else 'list' + p = sum(list(x.numel() for x in m.parameters())) if isinstance(m, nn.Module) else 0 # parameters + print(f'{p:12}{flops:12.4g}{dtf:16.4g}{dtb:16.4g}{str(s_in):>24s}{str(s_out):>24s}') + + +def is_parallel(model): + return type(model) in (nn.parallel.DataParallel, nn.parallel.DistributedDataParallel) + + +def intersect_dicts(da, db, exclude=()): + # Dictionary intersection of matching keys and shapes, omitting 'exclude' keys, using da values + return {k: v for k, v in da.items() if k in db and not any(x in k for x in exclude) and v.shape == db[k].shape} + + +def initialize_weights(model): + for m in model.modules(): + t = type(m) + if t is nn.Conv2d: + pass # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif t is nn.BatchNorm2d: + m.eps = 1e-3 + m.momentum = 0.03 + elif t in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6]: + m.inplace = True + + +def find_modules(model, mclass=nn.Conv2d): + # Finds layer indices matching module class 'mclass' + return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)] + + +def sparsity(model): + # Return global model sparsity + a, b = 0., 0. + for p in model.parameters(): + a += p.numel() + b += (p == 0).sum() + return b / a + + +def prune(model, amount=0.3): + # Prune model to requested global sparsity + import torch.nn.utils.prune as prune + print('Pruning model... ', end='') + for name, m in model.named_modules(): + if isinstance(m, nn.Conv2d): + prune.l1_unstructured(m, name='weight', amount=amount) # prune + prune.remove(m, 'weight') # make permanent + print(' %.3g global sparsity' % sparsity(model)) + + +def fuse_conv_and_bn(conv, bn): + # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/ + fusedconv = nn.Conv2d(conv.in_channels, + conv.out_channels, + kernel_size=conv.kernel_size, + stride=conv.stride, + padding=conv.padding, + groups=conv.groups, + bias=True).requires_grad_(False).to(conv.weight.device) + + # prepare filters + w_conv = conv.weight.clone().view(conv.out_channels, -1) + w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) + fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape)) + + # prepare spatial bias + b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias + b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) + fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) + + return fusedconv + + +def model_info(model, verbose=False, img_size=640): + # Model information. img_size may be int or list, i.e. img_size=640 or img_size=[640, 320] + n_p = sum(x.numel() for x in model.parameters()) # number parameters + n_g = sum(x.numel() for x in model.parameters() if x.requires_grad) # number gradients + if verbose: + print('%5s %40s %9s %12s %20s %10s %10s' % ('layer', 'name', 'gradient', 'parameters', 'shape', 'mu', 'sigma')) + for i, (name, p) in enumerate(model.named_parameters()): + name = name.replace('module_list.', '') + print('%5g %40s %9s %12g %20s %10.3g %10.3g' % + (i, name, p.requires_grad, p.numel(), list(p.shape), p.mean(), p.std())) + + try: # FLOPS + from thop import profile + stride = max(int(model.stride.max()), 32) if hasattr(model, 'stride') else 32 + img = torch.zeros((1, model.yaml.get('ch', 3), stride, stride), device=next(model.parameters()).device) # input + flops = profile(deepcopy(model), inputs=(img,), verbose=False)[0] / 1E9 * 2 # stride GFLOPS + img_size = img_size if isinstance(img_size, list) else [img_size, img_size] # expand if int/float + fs = ', %.1f GFLOPS' % (flops * img_size[0] / stride * img_size[1] / stride) # 640x640 GFLOPS + except (ImportError, Exception): + fs = '' + + logger.info(f"Model Summary: {len(list(model.modules()))} layers, {n_p} parameters, {n_g} gradients{fs}") + + +def load_classifier(name='resnet101', n=2): + # Loads a pretrained model reshaped to n-class output + model = torchvision.models.__dict__[name](pretrained=True) + + # ResNet model properties + # input_size = [3, 224, 224] + # input_space = 'RGB' + # input_range = [0, 1] + # mean = [0.485, 0.456, 0.406] + # std = [0.229, 0.224, 0.225] + + # Reshape output to n classes + filters = model.fc.weight.shape[1] + model.fc.bias = nn.Parameter(torch.zeros(n), requires_grad=True) + model.fc.weight = nn.Parameter(torch.zeros(n, filters), requires_grad=True) + model.fc.out_features = n + return model + + +def scale_img(img, ratio=1.0, same_shape=False, gs=32): # img(16,3,256,416) + # scales img(bs,3,y,x) by ratio constrained to gs-multiple + if ratio == 1.0: + return img + else: + h, w = img.shape[2:] + s = (int(h * ratio), int(w * ratio)) # new size + img = F.interpolate(img, size=s, mode='bilinear', align_corners=False) # resize + if not same_shape: # pad/crop img + h, w = [math.ceil(x * ratio / gs) * gs for x in (h, w)] + return F.pad(img, [0, w - s[1], 0, h - s[0]], value=0.447) # value = imagenet mean + + +def copy_attr(a, b, include=(), exclude=()): + # Copy attributes from b to a, options to only include [...] and to exclude [...] + for k, v in b.__dict__.items(): + if (len(include) and k not in include) or k.startswith('_') or k in exclude: + continue + else: + setattr(a, k, v) + + +class ModelEMA: + """ Model Exponential Moving Average from https://github.com/rwightman/pytorch-image-models + Keep a moving average of everything in the model state_dict (parameters and buffers). + This is intended to allow functionality like + https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage + A smoothed version of the weights is necessary for some training schemes to perform well. + This class is sensitive where it is initialized in the sequence of model init, + GPU assignment and distributed training wrappers. + """ + + def __init__(self, model, decay=0.9999, updates=0): + # Create EMA + self.ema = deepcopy(model.module if is_parallel(model) else model).eval() # FP32 EMA + # if next(model.parameters()).device.type != 'cpu': + # self.ema.half() # FP16 EMA + self.updates = updates # number of EMA updates + self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs) + for p in self.ema.parameters(): + p.requires_grad_(False) + + def update(self, model): + # Update EMA parameters + with torch.no_grad(): + self.updates += 1 + d = self.decay(self.updates) + + msd = model.module.state_dict() if is_parallel(model) else model.state_dict() # model state_dict + for k, v in self.ema.state_dict().items(): + if v.dtype.is_floating_point: + v *= d + v += (1. - d) * msd[k].detach() + + def update_attr(self, model, include=(), exclude=('process_group', 'reducer')): + # Update EMA attributes + copy_attr(self.ema, model, include, exclude) + + +class BatchNormXd(torch.nn.modules.batchnorm._BatchNorm): + def _check_input_dim(self, input): + # The only difference between BatchNorm1d, BatchNorm2d, BatchNorm3d, etc + # is this method that is overwritten by the sub-class + # This original goal of this method was for tensor sanity checks + # If you're ok bypassing those sanity checks (eg. if you trust your inference + # to provide the right dimensional inputs), then you can just use this method + # for easy conversion from SyncBatchNorm + # (unfortunately, SyncBatchNorm does not store the original class - if it did + # we could return the one that was originally created) + return + +def revert_sync_batchnorm(module): + # this is very similar to the function that it is trying to revert: + # https://github.com/pytorch/pytorch/blob/c8b3686a3e4ba63dc59e5dcfe5db3430df256833/torch/nn/modules/batchnorm.py#L679 + module_output = module + if isinstance(module, torch.nn.modules.batchnorm.SyncBatchNorm): + new_cls = BatchNormXd + module_output = BatchNormXd(module.num_features, + module.eps, module.momentum, + module.affine, + module.track_running_stats) + if module.affine: + with torch.no_grad(): + module_output.weight = module.weight + module_output.bias = module.bias + module_output.running_mean = module.running_mean + module_output.running_var = module.running_var + module_output.num_batches_tracked = module.num_batches_tracked + if hasattr(module, "qconfig"): + module_output.qconfig = module.qconfig + for name, child in module.named_children(): + module_output.add_module(name, revert_sync_batchnorm(child)) + del module + return module_output + + +class TracedModel(nn.Module): + + def __init__(self, model=None, device=None, img_size=(640,640)): + super(TracedModel, self).__init__() + + print(" Convert model to Traced-model... ") + self.stride = model.stride + self.names = model.names + self.model = model + + self.model = revert_sync_batchnorm(self.model) + self.model.to('cpu') + self.model.eval() + + self.detect_layer = self.model.model[-1] + self.model.traced = True + + rand_example = torch.rand(1, 3, img_size, img_size) + + traced_script_module = torch.jit.trace(self.model, rand_example, strict=False) + #traced_script_module = torch.jit.script(self.model) + traced_script_module.save("traced_model.pt") + print(" traced_script_module saved! ") + self.model = traced_script_module + self.model.to(device) + self.detect_layer.to(device) + print(" model is traced! \n") + + def forward(self, x, augment=False, profile=False): + out = self.model(x) + out = self.detect_layer(out) + return out \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/__init__.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/__init__.py new file mode 100644 index 000000000..84952a816 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/__init__.py @@ -0,0 +1 @@ +# init \ No newline at end of file diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/log_dataset.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/log_dataset.py new file mode 100644 index 000000000..74cd6c6cd --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/log_dataset.py @@ -0,0 +1,24 @@ +import argparse + +import yaml + +from wandb_utils import WandbLogger + +WANDB_ARTIFACT_PREFIX = 'wandb-artifact://' + + +def create_dataset_artifact(opt): + with open(opt.data) as f: + data = yaml.load(f, Loader=yaml.SafeLoader) # data dict + logger = WandbLogger(opt, '', None, data, job_type='Dataset Creation') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--data', type=str, default='data/coco.yaml', help='data.yaml path') + parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset') + parser.add_argument('--project', type=str, default='YOLOR', help='name of W&B Project') + opt = parser.parse_args() + opt.resume = False # Explicitly disallow resume check for dataset upload job + + create_dataset_artifact(opt) diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/wandb_utils.py b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/wandb_utils.py new file mode 100644 index 000000000..aec7c5f48 --- /dev/null +++ b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/utils/wandb_logging/wandb_utils.py @@ -0,0 +1,306 @@ +import json +import sys +from pathlib import Path + +import torch +import yaml +from tqdm import tqdm + +sys.path.append(str(Path(__file__).parent.parent.parent)) # add utils/ to path +from utils.datasets import LoadImagesAndLabels +from utils.datasets import img2label_paths +from utils.general import colorstr, xywh2xyxy, check_dataset + +try: + import wandb + from wandb import init, finish +except ImportError: + wandb = None + +WANDB_ARTIFACT_PREFIX = 'wandb-artifact://' + + +def remove_prefix(from_string, prefix=WANDB_ARTIFACT_PREFIX): + return from_string[len(prefix):] + + +def check_wandb_config_file(data_config_file): + wandb_config = '_wandb.'.join(data_config_file.rsplit('.', 1)) # updated data.yaml path + if Path(wandb_config).is_file(): + return wandb_config + return data_config_file + + +def get_run_info(run_path): + run_path = Path(remove_prefix(run_path, WANDB_ARTIFACT_PREFIX)) + run_id = run_path.stem + project = run_path.parent.stem + model_artifact_name = 'run_' + run_id + '_model' + return run_id, project, model_artifact_name + + +def check_wandb_resume(opt): + process_wandb_config_ddp_mode(opt) if opt.global_rank not in [-1, 0] else None + if isinstance(opt.resume, str): + if opt.resume.startswith(WANDB_ARTIFACT_PREFIX): + if opt.global_rank not in [-1, 0]: # For resuming DDP runs + run_id, project, model_artifact_name = get_run_info(opt.resume) + api = wandb.Api() + artifact = api.artifact(project + '/' + model_artifact_name + ':latest') + modeldir = artifact.download() + opt.weights = str(Path(modeldir) / "last.pt") + return True + return None + + +def process_wandb_config_ddp_mode(opt): + with open(opt.data) as f: + data_dict = yaml.load(f, Loader=yaml.SafeLoader) # data dict + train_dir, val_dir = None, None + if isinstance(data_dict['train'], str) and data_dict['train'].startswith(WANDB_ARTIFACT_PREFIX): + api = wandb.Api() + train_artifact = api.artifact(remove_prefix(data_dict['train']) + ':' + opt.artifact_alias) + train_dir = train_artifact.download() + train_path = Path(train_dir) / 'data/images/' + data_dict['train'] = str(train_path) + + if isinstance(data_dict['val'], str) and data_dict['val'].startswith(WANDB_ARTIFACT_PREFIX): + api = wandb.Api() + val_artifact = api.artifact(remove_prefix(data_dict['val']) + ':' + opt.artifact_alias) + val_dir = val_artifact.download() + val_path = Path(val_dir) / 'data/images/' + data_dict['val'] = str(val_path) + if train_dir or val_dir: + ddp_data_path = str(Path(val_dir) / 'wandb_local_data.yaml') + with open(ddp_data_path, 'w') as f: + yaml.dump(data_dict, f) + opt.data = ddp_data_path + + +class WandbLogger(): + def __init__(self, opt, name, run_id, data_dict, job_type='Training'): + # Pre-training routine -- + self.job_type = job_type + self.wandb, self.wandb_run, self.data_dict = wandb, None if not wandb else wandb.run, data_dict + # It's more elegant to stick to 1 wandb.init call, but useful config data is overwritten in the WandbLogger's wandb.init call + if isinstance(opt.resume, str): # checks resume from artifact + if opt.resume.startswith(WANDB_ARTIFACT_PREFIX): + run_id, project, model_artifact_name = get_run_info(opt.resume) + model_artifact_name = WANDB_ARTIFACT_PREFIX + model_artifact_name + assert wandb, 'install wandb to resume wandb runs' + # Resume wandb-artifact:// runs here| workaround for not overwriting wandb.config + self.wandb_run = wandb.init(id=run_id, project=project, resume='allow') + opt.resume = model_artifact_name + elif self.wandb: + self.wandb_run = wandb.init(config=opt, + resume="allow", + project='YOLOR' if opt.project == 'runs/train' else Path(opt.project).stem, + name=name, + job_type=job_type, + id=run_id) if not wandb.run else wandb.run + if self.wandb_run: + if self.job_type == 'Training': + if not opt.resume: + wandb_data_dict = self.check_and_upload_dataset(opt) if opt.upload_dataset else data_dict + # Info useful for resuming from artifacts + self.wandb_run.config.opt = vars(opt) + self.wandb_run.config.data_dict = wandb_data_dict + self.data_dict = self.setup_training(opt, data_dict) + if self.job_type == 'Dataset Creation': + self.data_dict = self.check_and_upload_dataset(opt) + else: + prefix = colorstr('wandb: ') + print(f"{prefix}Install Weights & Biases for YOLOR logging with 'pip install wandb' (recommended)") + + def check_and_upload_dataset(self, opt): + assert wandb, 'Install wandb to upload dataset' + check_dataset(self.data_dict) + config_path = self.log_dataset_artifact(opt.data, + opt.single_cls, + 'YOLOR' if opt.project == 'runs/train' else Path(opt.project).stem) + print("Created dataset config file ", config_path) + with open(config_path) as f: + wandb_data_dict = yaml.load(f, Loader=yaml.SafeLoader) + return wandb_data_dict + + def setup_training(self, opt, data_dict): + self.log_dict, self.current_epoch, self.log_imgs = {}, 0, 16 # Logging Constants + self.bbox_interval = opt.bbox_interval + if isinstance(opt.resume, str): + modeldir, _ = self.download_model_artifact(opt) + if modeldir: + self.weights = Path(modeldir) / "last.pt" + config = self.wandb_run.config + opt.weights, opt.save_period, opt.batch_size, opt.bbox_interval, opt.epochs, opt.hyp = str( + self.weights), config.save_period, config.total_batch_size, config.bbox_interval, config.epochs, \ + config.opt['hyp'] + data_dict = dict(self.wandb_run.config.data_dict) # eliminates the need for config file to resume + if 'val_artifact' not in self.__dict__: # If --upload_dataset is set, use the existing artifact, don't download + self.train_artifact_path, self.train_artifact = self.download_dataset_artifact(data_dict.get('train'), + opt.artifact_alias) + self.val_artifact_path, self.val_artifact = self.download_dataset_artifact(data_dict.get('val'), + opt.artifact_alias) + self.result_artifact, self.result_table, self.val_table, self.weights = None, None, None, None + if self.train_artifact_path is not None: + train_path = Path(self.train_artifact_path) / 'data/images/' + data_dict['train'] = str(train_path) + if self.val_artifact_path is not None: + val_path = Path(self.val_artifact_path) / 'data/images/' + data_dict['val'] = str(val_path) + self.val_table = self.val_artifact.get("val") + self.map_val_table_path() + if self.val_artifact is not None: + self.result_artifact = wandb.Artifact("run_" + wandb.run.id + "_progress", "evaluation") + self.result_table = wandb.Table(["epoch", "id", "prediction", "avg_confidence"]) + if opt.bbox_interval == -1: + self.bbox_interval = opt.bbox_interval = (opt.epochs // 10) if opt.epochs > 10 else 1 + return data_dict + + def download_dataset_artifact(self, path, alias): + if isinstance(path, str) and path.startswith(WANDB_ARTIFACT_PREFIX): + dataset_artifact = wandb.use_artifact(remove_prefix(path, WANDB_ARTIFACT_PREFIX) + ":" + alias) + assert dataset_artifact is not None, "'Error: W&B dataset artifact doesn\'t exist'" + datadir = dataset_artifact.download() + return datadir, dataset_artifact + return None, None + + def download_model_artifact(self, opt): + if opt.resume.startswith(WANDB_ARTIFACT_PREFIX): + model_artifact = wandb.use_artifact(remove_prefix(opt.resume, WANDB_ARTIFACT_PREFIX) + ":latest") + assert model_artifact is not None, 'Error: W&B model artifact doesn\'t exist' + modeldir = model_artifact.download() + epochs_trained = model_artifact.metadata.get('epochs_trained') + total_epochs = model_artifact.metadata.get('total_epochs') + assert epochs_trained < total_epochs, 'training to %g epochs is finished, nothing to resume.' % ( + total_epochs) + return modeldir, model_artifact + return None, None + + def log_model(self, path, opt, epoch, fitness_score, best_model=False): + model_artifact = wandb.Artifact('run_' + wandb.run.id + '_model', type='model', metadata={ + 'original_url': str(path), + 'epochs_trained': epoch + 1, + 'save period': opt.save_period, + 'project': opt.project, + 'total_epochs': opt.epochs, + 'fitness_score': fitness_score + }) + model_artifact.add_file(str(path / 'last.pt'), name='last.pt') + wandb.log_artifact(model_artifact, + aliases=['latest', 'epoch ' + str(self.current_epoch), 'best' if best_model else '']) + print("Saving model artifact on epoch ", epoch + 1) + + def log_dataset_artifact(self, data_file, single_cls, project, overwrite_config=False): + with open(data_file) as f: + data = yaml.load(f, Loader=yaml.SafeLoader) # data dict + nc, names = (1, ['item']) if single_cls else (int(data['nc']), data['names']) + names = {k: v for k, v in enumerate(names)} # to index dictionary + self.train_artifact = self.create_dataset_table(LoadImagesAndLabels( + data['train']), names, name='train') if data.get('train') else None + self.val_artifact = self.create_dataset_table(LoadImagesAndLabels( + data['val']), names, name='val') if data.get('val') else None + if data.get('train'): + data['train'] = WANDB_ARTIFACT_PREFIX + str(Path(project) / 'train') + if data.get('val'): + data['val'] = WANDB_ARTIFACT_PREFIX + str(Path(project) / 'val') + path = data_file if overwrite_config else '_wandb.'.join(data_file.rsplit('.', 1)) # updated data.yaml path + data.pop('download', None) + with open(path, 'w') as f: + yaml.dump(data, f) + + if self.job_type == 'Training': # builds correct artifact pipeline graph + self.wandb_run.use_artifact(self.val_artifact) + self.wandb_run.use_artifact(self.train_artifact) + self.val_artifact.wait() + self.val_table = self.val_artifact.get('val') + self.map_val_table_path() + else: + self.wandb_run.log_artifact(self.train_artifact) + self.wandb_run.log_artifact(self.val_artifact) + return path + + def map_val_table_path(self): + self.val_table_map = {} + print("Mapping dataset") + for i, data in enumerate(tqdm(self.val_table.data)): + self.val_table_map[data[3]] = data[0] + + def create_dataset_table(self, dataset, class_to_id, name='dataset'): + # TODO: Explore multiprocessing to slpit this loop parallely| This is essential for speeding up the the logging + artifact = wandb.Artifact(name=name, type="dataset") + img_files = tqdm([dataset.path]) if isinstance(dataset.path, str) and Path(dataset.path).is_dir() else None + img_files = tqdm(dataset.img_files) if not img_files else img_files + for img_file in img_files: + if Path(img_file).is_dir(): + artifact.add_dir(img_file, name='data/images') + labels_path = 'labels'.join(dataset.path.rsplit('images', 1)) + artifact.add_dir(labels_path, name='data/labels') + else: + artifact.add_file(img_file, name='data/images/' + Path(img_file).name) + label_file = Path(img2label_paths([img_file])[0]) + artifact.add_file(str(label_file), + name='data/labels/' + label_file.name) if label_file.exists() else None + table = wandb.Table(columns=["id", "train_image", "Classes", "name"]) + class_set = wandb.Classes([{'id': id, 'name': name} for id, name in class_to_id.items()]) + for si, (img, labels, paths, shapes) in enumerate(tqdm(dataset)): + height, width = shapes[0] + labels[:, 2:] = (xywh2xyxy(labels[:, 2:].view(-1, 4))) * torch.Tensor([width, height, width, height]) + box_data, img_classes = [], {} + for cls, *xyxy in labels[:, 1:].tolist(): + cls = int(cls) + box_data.append({"position": {"minX": xyxy[0], "minY": xyxy[1], "maxX": xyxy[2], "maxY": xyxy[3]}, + "class_id": cls, + "box_caption": "%s" % (class_to_id[cls]), + "scores": {"acc": 1}, + "domain": "pixel"}) + img_classes[cls] = class_to_id[cls] + boxes = {"ground_truth": {"box_data": box_data, "class_labels": class_to_id}} # inference-space + table.add_data(si, wandb.Image(paths, classes=class_set, boxes=boxes), json.dumps(img_classes), + Path(paths).name) + artifact.add(table, name) + return artifact + + def log_training_progress(self, predn, path, names): + if self.val_table and self.result_table: + class_set = wandb.Classes([{'id': id, 'name': name} for id, name in names.items()]) + box_data = [] + total_conf = 0 + for *xyxy, conf, cls in predn.tolist(): + if conf >= 0.25: + box_data.append( + {"position": {"minX": xyxy[0], "minY": xyxy[1], "maxX": xyxy[2], "maxY": xyxy[3]}, + "class_id": int(cls), + "box_caption": "%s %.3f" % (names[cls], conf), + "scores": {"class_score": conf}, + "domain": "pixel"}) + total_conf = total_conf + conf + boxes = {"predictions": {"box_data": box_data, "class_labels": names}} # inference-space + id = self.val_table_map[Path(path).name] + self.result_table.add_data(self.current_epoch, + id, + wandb.Image(self.val_table.data[id][1], boxes=boxes, classes=class_set), + total_conf / max(1, len(box_data)) + ) + + def log(self, log_dict): + if self.wandb_run: + for key, value in log_dict.items(): + self.log_dict[key] = value + + def end_epoch(self, best_result=False): + if self.wandb_run: + wandb.log(self.log_dict) + self.log_dict = {} + if self.result_artifact: + train_results = wandb.JoinedTable(self.val_table, self.result_table, "id") + self.result_artifact.add(train_results, 'result') + wandb.log_artifact(self.result_artifact, aliases=['latest', 'epoch ' + str(self.current_epoch), + ('best' if best_result else '')]) + self.result_table = wandb.Table(["epoch", "id", "prediction", "avg_confidence"]) + self.result_artifact = wandb.Artifact("run_" + wandb.run.id + "_progress", "evaluation") + + def finish_run(self): + if self.wandb_run: + if self.log_dict: + wandb.log(self.log_dict) + wandb.run.finish() diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/camera_manager.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2392816fc2db3207c58574302f98f4e0768796ea GIT binary patch literal 63984 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 Jlq~F}{9jA3t5g60 literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..735fc3efc984ffc63874769eababe34aa13edfbe GIT binary patch literal 1886 zcmaJ>(Qn&C7{7DkoiXgS;k@E)|E`y(Z-kuOn!z#2{`_b|wyg+&oG0c06111M^E5#_~) zOtg^4e)ghL)2VVO!7WqRJ-6^R7 zA4)wrJqiNWY!+`YR~&K|0+kpUk{}FD`~=S4LQUjyEKtq?!u%I=Z(yfSih~5Pzi7rN z(SY32@CrK#(d#jtV2;gyJ{X(t6R(L@0z%TOO{^pw=5eoR++l(p++mwJ{X}V^UlRYP zwfxwgv4guUlV~e?B4$G}p!yS*L{vZA=lO+)%(X^t&NNg*F*QpLvsmt>@x;Ug#F($u zf%F2IM%A6S|z96|D|=x-b;Fn0lWPmWSBGespVTbIC(P-6V(MdFlf};_;hjZi}tVSUWT3rn^auhvE5(AeF)F2A__B G8~*~QD6{zh literal 0 HcmV?d00001 diff --git a/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/enums.cpython-311.pyc b/AgroBase/AgroBase/bin/x64/Debug/Python/Scripts/workers/visual_worker/__pycache__/enums.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db4e6996cdab2a2ef0dfa6c72d6397e4bfbd3bfa GIT binary patch literal 2292 zcmaJ?O>g2x7`DNH0UMH(WRtYneyOTxt36cJuC%RI8o*(#225?IiYz3TWUMS|f>V>U z-J1_R^qAZJgRZLngZ>JR)?9najVL+osqZ_6lC^0(_+j4nnUD8t#;;YiB*61})iYeomT=CsCXirijF7>8KEuE%x?nCdg322?=N`c$H{O`0xp;0vYg8U zS4eRZ=VaiDDK5vk5^&`dm*<=UoSNbav|J^$+7g<3cool0u)1#TK{7;+-H;D@Nl zXlu4>*~H$r+?HCIHtCfU;) zCNbT%Nm`o4YB+{;L2GI7Z*-tX*veh4W$ev1f+;Zb%NE;0-qP9~t!tAeF`(ZeU0ZLG zKZP4`PI&V}^J(AlMs(BzmUnn@-0!_S zf8me%)=@A#J0JId9*!X#?k|4uqZjl&NaXm@*~wWzFwt!U@rMB0wt zsfBw{Z9VKnn_ClUJ=%B>8qvG&O{C4}!RAD&#RES0Xd>;zzu@6~<>dvl0rawhfr5}& z*&d$xv!Vzv_W|aKGS-qF#(U)UO8op$T_q``T}_r7VcqS1gtZ0KUNL!CpYhkEP_j$iJ}y&^#~kagXMQkIJ6p*Y?D|_)~xULHM2!v zN8QwPxJ0C;-XSd4Ao_v9)HxrE%3S1Ql-ZP+E6*gPlI8FS4MV_67;6B@a^N+=V_aPc zpGTEy*p3e|hCDvL{2+5!tA*cUi_?JXE>FX~Wy4P@84XT`V{Z}RB_$)zuf^bV*eU7S z*Qi6lYDg9h_XWMe7F&x$yLFjKc##EJoSj492Om!WuRtCwzHDN@H}W^}hvHfA3emoV@@cZ#KYHt_Rc1A2ug*04#dX3uK1tn+2wW*MX%O}^U wy92&WI^71BSp7mi*Yo5Ee@RbkDt` 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