# Caption Quality Test for Cross-View Geo-Localization ## Архитектура системы (v2, 2026-04-17) ``` QUERY BRANCH (drone + caption): drone_img --> GeoRSCLIP ViT-B/32 (frozen) --> drone_feat [B,512] caption --> GeoRSCLIP Text (partial unfreeze) --> text_feat [B,512] | GatedFusion: q = sigma(alpha)*drone + (1-sigma(alpha))*text | proj_query (Linear 512->512) --> L2-norm --> query [B,512] GALLERY BRANCH (satellite only): sat_img --> GeoRSCLIP ViT-B/32 (frozen) --> sat_feat [B,512] | proj_gallery (Linear 512->512) --> L2-norm --> gallery [B,512] LOSS: InfoNCE(query, gallery) — symmetric, asymmetric weights (0.6 q->g, 0.4 g->q) BASELINE: gate = 1.0 (text ignored) ``` ### Trainable parameters: ~1.2M из ~151M (proj_query + proj_gallery + fusion alpha + text last_block) ## Ключевые файлы | Файл | Назначение | |------|-----------| | `src/models/dual_encoder.py` | GeoRSCLIP + GatedFusion + projection heads | | `src/losses/multi_infonce.py` | InfoNCE с cosine temperature schedule | | `src/datasets/visloc_with_captions.py` | UAV-GeoLoc loader + template captions из path metadata | | `src/training/train.py` | Training loop, логирование loss/gate/tau | | `src/eval/evaluate.py` | R@K metrics, delta_r_at_1 | | `scripts/compare_runs.py` | Markdown/JSON сравнение baseline vs caption runs | | `scripts/generate_captions.py` | Offline caption generation (template/VLM/hybrid) | ## Backbone: GeoRSCLIP ViT-B/32 - **Checkpoint:** `checkpoints/RS5M_ViT-B-32.pt` (скачать с github.com/om-ai-lab/RS5M) - **Image encoder:** ViT-B/32, 224x224, 512-dim, ~86M params — frozen - **Text encoder:** CLIP text transformer, 77 tokens, 512-dim — partial unfreeze (last_block + text_projection) - **Throughput:** ~4000 img/s на RTX 4090 (AMP, batch 128) - **Выбран вместо SigLIP 2** (ViT-SO400M, 384px, ~400M): в 7-10x быстрее, domain-specific (обучен на 5M RS-изображений), больше batch = больше негативов в InfoNCE ## GatedFusion - `query = sigma(alpha) * drone_feat + (1 - sigma(alpha)) * text_feat` - `alpha` — один learnable scalar в logit-space - `init_gate = 0.7` → начальный вес image = 70%, text = 30% - `baseline_mode = True` → gate = 1.0, text полностью игнорируется - Gate value логируется каждую эпоху для интерпретации вклада текста ## Датасет: UAV-GeoLoc - **Путь:** `/mnt/data1tb/cvgl_datasets/UAV-GeoLoc/` - **Train:** 206,108 queries, 94,709 DB crops (140 scenes, Terrain split) - **Val:** 62,368 queries, 26,597 DB crops (40 scenes) - **Test:** 33,472 queries, 11,684 DB crops (20 scenes) - **Структура:** `Terrain/{type}/{scene}/query/height{N}_rot{M}/footage/{file}.jpeg` - **Index:** `Index/train_query.txt` — `query_path 0 pos_crop1 pos_crop2 ...` ### Template captions (из path metadata) Формат: `"Aerial view at {height}m facing {heading} over {terrain} terrain near {scene}. Plan-view features: {features}."` Пример: `"Aerial view at 100m facing northwest over volcanic terrain near KilaueaVolcano. Plan-view features: lava flows, crater edges, volcanic rock."` Metadata извлекается из пути: - `Terrain/Volcano/KilaueaVolcano/query/height100_rot315/...` → terrain=Volcano, scene=KilaueaVolcano, height=100, heading=northwest - 27 terrain типов с predefined features (Volcano, Mountain, Hill, Desert, Plain, ...) - Country subset: features = "buildings, roads, urban blocks, rooftops, intersections" ## Конфигурации (gin) | Конфиг | Gate init | Описание | |--------|-----------|----------| | `conf/balanced.gin` | 0.7 (30% text) | **Primary test** | | `conf/baseline_no_text.gin` | 1.0 (no text) | Reference baseline | | `conf/text_heavy.gin` | 0.3 (70% text) | Stress test | Общие параметры: 10 epochs, batch 128, lr=1e-4, AMP, cosine LR schedule, eval every 2 epochs. ## Запуск ```bash # 1. Baseline (no text) python -m src.training.train --config conf/baseline_no_text.gin # 2. With captions (primary test) python -m src.training.train --config conf/balanced.gin # 3. Text-heavy (stress test) python -m src.training.train --config conf/text_heavy.gin # 4. Compare python -m scripts.compare_runs \ --baseline_report out/caption_test/baseline_no_text/eval_report.json \ --full_report out/caption_test/balanced/eval_report.json \ --output out/caption_test/comparison.md ``` ## Метрики и Decision rule **Primary metric:** Delta R@1 (query -> gallery) | Delta R@1 | Verdict | |-----------|---------| | >= +3% | PASS — captions informative, proceed to production | | +1% to +3% | MARGINAL — add VLM refinement, re-run | | 0 to +1% | WEAK — redesign caption pipeline | | < 0 | HARMFUL — critical bug | **Logged per epoch:** loss, temperature (tau), gate value (sigma(alpha)), lr **Eval metrics:** R@1, R@5, R@10 для query->gallery и gallery->query ## Бюджет времени (RTX 4090, 24 GB) | Фаза | Время | |------|-------| | Один training run (10 epochs, 206K queries, batch 128) | ~15-30 мин | | Full test (3 варианта) | ~1-1.5 ч | | Evaluation per run | ~2-5 мин | ## Связанные проекты ### Text Annotation Pipeline - **Путь:** `/home/servml/Документы/pikaliov_obsidian/(Полякова ВЕ_Система для генерации текстовых описаний для БПЛА)/2_work/2_text_annotation/code/` - **VLM:** Qwen3-VL-8B AWQ (1.68 s/img) - **Scoring:** SigLIP 2 (drone P3) + CLIP-RSICD (satellite P1+P2) - **Формат описаний:** 3 абзаца (P1 Inventory + P2 Spatial Map + P3 Fingerprint) - **Метрики:** FDR, FNR, NumAcc, LLaVA-Critic C1-C6 ### UAV-VisLoc Prepare - **Путь:** `/home/servml/Документы/code/Yaroslav/UAV-VisLoc-prepare/scripts/prepare_dataset.py` - **Статус:** код готов, ещё не запускался - **Задача:** нарезка satellite кропов 512x512, stride 256 для UAV-VisLoc dataset - **Подробности:** см. ниже --- ## Датасеты (справочник) ### UAV-VisLoc - **Путь:** `/home/servml/Документы/datasets/UAV_VisLoc_dataset/` - **Структура:** 11 маршрутов (папки `01`-`11`), каждая содержит: - `drone/` — drone-снимки (`XX_NNNN.JPG`) - `satelliteXX.tif` — спутниковая карта - `XX.csv` — GPS-метаданные drone (num, filename, date, lat, lon, height, Omega, Kappa, Phi1, Phi2) - **Исключение:** маршрут `09` — спутник разбит на 4 тайла (`satellite09_01-01.tif` и т.д.) - **Satellite coordinates:** `satellite_ coordinates_range.csv` — bbox каждой карты (LT_lat_map, LT_lon_map, RB_lat_map, RB_lon_map) - **Splits:** `visloc_train.csv`, `visloc_test.csv` — списки drone-снимков (TSV, full absolute paths) - **Размеры:** Drone 3976x2652 / 3000x2000 (6774 снимков), Satellite от 3000x170 до 43421x38408 - **GSD спутника:** ~0.30 м/px (единый zoom level Google Earth для всех карт). GSD по долготе варьируется 0.23-0.27 м/px из-за косинусного эффекта широты (40°N vs 25°N), но это не разная высота съёмки. Кроп 512x512 покрывает ~154x154 м везде. ### UAV-GeoLoc - **Путь:** `/mnt/data1tb/cvgl_datasets/UAV-GeoLoc/` - **Подмножества:** Country (171 scene), Terrain (200 scenes), Rot (1 scene) - **Формат пар:** `positive.json`, `semi_positive.json`, `db_postion.txt` - **Index:** `train_query.txt` — `query_path label pos_crop1 pos_crop2 ...` - **Drone:** синтетика Google Earth Studio 3D, 512x512, FOV 30deg, heights 100/125/150m - **Satellite:** кропы из merge.tif, доминирующий размер 200x200 ## Скрипт подготовки UAV-VisLoc - **Путь:** `/home/servml/Документы/code/Yaroslav/UAV-VisLoc-prepare/scripts/prepare_dataset.py` - **Статус:** код готов, ещё не запускался ### Запуск ```bash python scripts/prepare_dataset.py \ --src /home/servml/Документы/datasets/UAV_VisLoc_dataset \ --dst /home/servml/Документы/datasets/UAV_VisLoc_processed \ --crop-size 512 --stride 256 --target-size 256 ``` ### Pipeline 1. Resize drone -> 256x256 (JPEG, quality=95) 2. Stitch satellite tiles для маршрута 09 (4 тайла -> 44800x33280) 3. Нарезка satellite -> кропы 512x512, stride 256, resize -> 256x256 (PNG) 4. GPS для каждого кропа из bbox карты + позиция в grid 5. Match drone->crop через vectorized haversine (positive = ближайший, semi-positive = +/-1 в grid) 6. Metadata: `positive.json`, `semi_positive.json`, `db_postion.txt` (per route) 7. Index: `train_query.txt`, `test_query.txt`, `train_db.txt`, `test_db.txt`, `all_db.txt` ### Форматы выходных файлов (совместимость с UAV-GeoLoc) | Файл | Формат | |------|--------| | `positive.json` | `{frame_id: [crop_name]}`, ключ = frame ID без route prefix (`"0001"`) | | `semi_positive.json` | `{frame_id: [crop1, crop2, ...]}`, соседи +/-1 в grid | | `db_postion.txt` | tab-separated: `name\tlon\tlat\tscale_lon\tscale_lat` | | `train_query.txt` | `route/drone/file.JPG 0 route/DB/img/crop1.png ...` | | `train_db.txt` / `test_db.txt` | все кропы всех маршрутов (gallery одинаковая, split по query) | ### Ожидаемые объёмы - Drone: 6744 (без маршрута 07: 30 images excluded) - Satellite кропов: ~74,807 - Память: до ~4.5 GB RAM (маршрут 09 stitched 44800x33280) ### Ревью и исправления (2026-04-17) 1. `train_db.txt`/`test_db.txt` содержали только matched кропы -> теперь все ~74K (полная gallery) 2. `db_position.txt` -> `db_postion.txt` (совместимость с UAV-GeoLoc), добавлены scale_lon/scale_lat, tab-separator 3. `positive.json` ключи были filename (`01_0001.JPG`) -> теперь frame_id (`0001`) 4. Semi-positive поиск O(n) -> O(1) через dict lookup по (x,y) grid 5. Удален мёртвый код (`haversine_m`, `defaultdict` import) ### Известные ограничения - Нет val split (только train/test, как в оригинальном UAV-VisLoc) - Большие спутниковые карты загружаются целиком в RAM (до 4.5 GB для route 09)