Files
World-UAV-ds/README.md
Pikaliov 4ff36ce188 Initial import: World-UAV prepro
Add dataloaders (v1/v2), analysis scripts, and documentation for working with UAV-GeoLoc (World-UAV).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 12:44:49 +03:00

176 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<root>/
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
<root>/Terrain/<TerrainType>/<SceneName>/
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
<query_path> <scene_label_int> <positive_db_1> [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`.