# World-UAV-prepro Эта папка — **мой слой препроцесса/аналитики** поверх датасета **UAV-GeoLoc (World-UAV)**. Здесь нет обучения моделей. Основные артефакты: - `dataloader.py`: компактный PyTorch `Dataset`/`DataLoader` для train/eval по индекс-файлам `Index/*.txt`. - `dataloader_v2.py`: расширенная версия лоадера с парсингом метаданных (height/rotation), утилитами GPS/локализационной ошибки и scene-based лоадером для кастомных сплитов. - `analyze/`: оффлайн-скрипты, которые **проверяют структуру датасета**, **схему нарезки спутника**, и **генерируют графики/примерные картинки**. ## Формат данных (ожидаемая структура датасета) Оба лоадера предполагают, что корень датасета (`root`) выглядит примерно так: ```text / Country/... Terrain/... Rot/... Index/ train_query.txt train_db.txt val_query.txt val_db.txt test_query.txt test_db.txt ... и варианты *_country.txt, *_all.txt ``` На уровне сцены (примерно): ```text /Terrain/// positive.json semi_positive.json DB/ merge.tif db_postion.txt img/crop_X_Y.png query/ height100_rot0/footage/*.jpeg ... ``` ## Index-файлы (ключевой интерфейс) ### DB index (`*_db*.txt`) По 1 пути на строку, путь **относительно `root`**: ```text Terrain/Mountain/Andes/DB/img/crop_0_0.png Terrain/Mountain/Andes/DB/img/crop_0_1.png ... ``` ### Query index (`*_query*.txt`) Формат строки: ```text [positive_db_2 ...] ``` Пример: ```text Terrain/Mountain/Andes/query/height100_rot0/footage/height100_rot0_00.jpeg 12 Terrain/Mountain/Andes/DB/img/crop_10_7.png Terrain/Mountain/Andes/DB/img/crop_10_8.png ``` Важно: в путях могут встречаться пробелы (в вариантах/папках). Парсер в обоих лоадерах извлекает DB-пути по паттерну `*/DB/img/crop_*.png`, а `label` берёт как последний числовой токен перед DB-путями. ## `dataloader.py` (базовый) ### Что даёт - **Train**: - `UAVGeoLocTrain`: `(query, positive, negative)` triplets (негатив берётся случайно из `train_db.txt`, исключая positives этого scene label). - `UAVGeoLocPair`: `(query, positive)` пары (под contrastive без explicit negative). - **Eval**: - `UAVGeoLocEval(mode="query")`: одиночные query-изображения + `label` + список `positives`. - `UAVGeoLocEval(mode="db")`: одиночные DB-изображения. - `eval_collate_fn`: collate, который оставляет `positives` списком (variable-length). - `build_dataloaders(...)`: собирает набор лоадеров на train/val/test. ### Мини-пример использования ```python from dataloader import build_dataloaders root = "/path/to/UAV-GeoLoc" loaders = build_dataloaders(root, split="terrain", batch_size=32, img_size=512, num_workers=4, mode="triplet") batch = next(iter(loaders["train"])) print(batch["query"].shape, batch["positive"].shape, batch["negative"].shape, batch["label"]) ``` ## `dataloader_v2.py` (расширенный) ### Отличия от базовой версии - **Метаданные query**: - парсит `height` и `rotation` из пути вида `height125_rot270/...`. - возвращает их в батчах train/eval. - **GPS / локализационная ошибка**: - `load_db_positions(...)`, `haversine_m(...)` - `compute_localization_error(...)`: метрики ошибки в метрах по retrieval predictions (top-1 индексы DB). - **Утилита нарезки спутника**: - `tile_satellite_image(...)`: генерирует кропы в стиле UAV-GeoLoc (stride по умолчанию `crop_size // 2`). - **Scene-based loader**: - `UAVGeoLocScene(scene_dir=...)`: читает сцену напрямую из папки (без `Index/*.txt`), удобно для кастомных выборок/проверок (в т.ч. Rot subset). - `build_rot_loader(...)`: convenience лоадер для `Rot/SouthernSuburbs`. ### Мини-пример: посчитать error (м) по top-1 предсказаниям ```python import numpy as np from dataloader_v2 import build_dataloaders, compute_localization_error root = "/path/to/UAV-GeoLoc" loaders = build_dataloaders(root, split="terrain", batch_size=64, img_size=224, num_workers=4, mode="triplet") q_ds = loaders["test_query"].dataset d_ds = loaders["test_db"].dataset # допустим, у вас есть top1 predictions как индексы DB: pred = np.zeros(len(q_ds), dtype=np.int64) stats = compute_localization_error(q_ds, d_ds, pred) print(stats["mean_error_m"], stats["median_error_m"], stats["num_evaluated"]) ``` ## Какой лоадер использовать - **Бери `dataloader.py`**, если тебе нужен минимальный, предсказуемый интерфейс: - train triplets/pairs - eval query/db - без метаданных и без геометрии/географии - **Бери `dataloader_v2.py`**, если: - в модели/логах важно `height` и `rotation` (например, для condition/аблаций) - хочешь считать **ошибку локализации в метрах** (по `db_postion.txt`) - нужно грузить сцены **напрямую из папки** (без `Index/*.txt`) или работать с `Rot` subset - нужен helper для tiling спутника `tile_satellite_image(...)` На практике: **для базовых retrieval-экспериментов достаточно `dataloader.py`**, а `v2` — когда переходишь к “исследовательским” метрикам/контролю условий. ## Какие `Index/*.txt` ожидаются для `terrain/country/all` Оба лоадера используют один и тот же принцип: в `Index/` лежат файлы вида: - `train_query*.txt`, `train_db*.txt` - `val_query*.txt`, `val_db*.txt` - `test_query*.txt`, `test_db*.txt` Суффиксы: - **`terrain`**: суффикс пустой (`train_query.txt`, `train_db.txt`, …) - **`country`**: суффикс `_country` (`train_query_country.txt`, …) - **`all`**: суффикс `_all` (`train_query_all.txt`, …) ### Fallback-логика в `build_dataloaders` В `dataloader.py` и `dataloader_v2.py` `build_dataloaders(...)` делает сборку так: - **train**: берёт строго `Index/train_query{suffix}.txt` и `Index/train_db{suffix}.txt` для выбранного `split` - **val/test**: - сначала пытается открыть `Index/{phase}_query{suffix}.txt` / `Index/{phase}_db{suffix}.txt` - если этих файлов нет, откатывается на **несуффиксные** `Index/{phase}_query.txt` / `Index/{phase}_db.txt` Это удобно, если у тебя, например, `train_*_country.txt` есть, а `val_*_country.txt` ещё не сгенерен. ## `analyze/` (оффлайн анализ датасета) Скрипты ориентированы на запуск “как есть”, но почти везде нужно поменять `BASE`/`ROOT` (путь к датасету). Подробности см. `analyze/README.md`.