# Caption Quality Test for Cross-View Geo-Localization ## Архитектура системы (v3, 2026-04-21) — GTA-UAV эксперимент ``` QUERY BRANCH (drone + L1/L2/L3 captions): drone_img --> DINOv3 ViT-L/16 LVD-1689M (frozen) --> CLS [B,1024] | proj_drone (1024->512) | L1 (overview) --> LRSCLIP (248 tok) --> z_L1 [768] --\ L2 (full desc) --> LRSCLIP (248 tok) --> z_L2 [768] ---+-- concat [B,2304] L3 (fingerprint) --> LRSCLIP (248 tok) --> z_L3 [768] --/ | MLP(2304->768->512) | GatedFusion(drone_512, text_512) --> L2-norm --> query [B,512] GALLERY BRANCH (satellite only): sat_img --> DINOv3 ViT-L/16 SAT-493M (frozen) --> CLS [B,1024] | proj_sat (1024->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: 10.9M из 733M (1.49%) - proj_drone: 1024x512 = ~524K - proj_sat: 1024x512 = ~524K - TextFusionMLP: 2304->768->512 = ~2.2M - gate alpha: 1 scalar - LRSCLIP partial unfreeze (last block + ln_final + text_projection): ~7.6M - Backbones DINOv3 x2 (303M each): frozen ### Image input: 256x256 DINOv3 ViT-L/16 с patch_size=16 → 16x16=256 patches на 256x256. Resize(256) + CenterCrop(256) + ImageNet normalization. ### Предыдущая архитектура (v2) — UAV-GeoLoc эксперимент Использовала GeoRSCLIP ViT-B/32 (512-dim) для обеих веток + template captions. Код в `src/models/dual_encoder.py`, `src/datasets/visloc_with_captions.py`. ## Ключевые файлы ### V2 (UAV-GeoLoc, GeoRSCLIP) | Файл | Назначение | |------|-----------| | `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) | ### V3 (GTA-UAV, DINOv3 + LRSCLIP) — DONE | Файл | Назначение | |------|-----------| | `src/models/asymmetric_encoder.py` | DINOv3ViT + LRSCLIPTextEncoder + TextFusionMLP + AsymmetricEncoder + GatedFusion | | `src/datasets/gtauav_dataset.py` | GTA-UAV-LR loader + L1/L2/L3 caption parsing из VLM JSON | | `src/training/train_gtauav.py` | Training loop с eval, AMP, CLI args (--baseline, --filter-meta) | | `scripts/filter_segmentation.py` | Scan segm masks, output meta JSON (exclude >=90% bg+water) | ## Backbones (v3) ### DINOv3 ViT-L/16 — Drone (web pretrained) - **Checkpoint:** `nn_models/DINO_WEB/dinov3-vitl16-pretrain-lvd1689m.pth` - **Arch:** ViT-L/16, 24 layers, 16 heads, hidden=1024, MLP=4096, 303M params - **Input:** 256x256, ImageNet normalization, patch=16 → 256 patches - **Register tokens:** 4, RoPE theta=100.0 - **Status:** frozen ### DINOv3 ViT-L/16 — Satellite (sat pretrained) - **Checkpoint:** `nn_models/DINO_SAT/model.safetensors` - **HuggingFace:** `facebook/dinov3-vitl16-pretrain-sat493m` - **Arch:** идентична DINO_WEB (ViT-L/16, hidden=1024, 303M params) - **Input:** 256x256 - **Config:** `nn_models/DINO_SAT/config.json` — BROKEN (auth error), используем конфиг от DINO_WEB - **Status:** frozen ### DGTRS-CLIP ViT-L-14 (LRSCLIP) — Text encoder - **Checkpoint:** `nn_models/LRSCLIP/DGTRS-CLIP-ViT-L-14.pt` - **Text dim:** 768, max tokens: 248 (KPS stretched from CLIP's 77) - **Содержит:** полную CLIP модель (visual + text), используем только text encoder - **GitHub:** `github.com/MitsuiChen14/DGTRS` - **Status:** partial unfreeze (last block + text_projection) ### GeoRSCLIP ViT-B/32 (v2, legacy) - **Checkpoint:** `checkpoints/RS5M_ViT-B-32.pt` - **Image encoder:** ViT-B/32, 224x224, 512-dim, ~86M params — frozen - **Text encoder:** 77 tokens, 512-dim — partial unfreeze ## 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 логируется каждую эпоху для интерпретации вклада текста ## Text Hierarchy (L1/L2/L3) Три уровня описаний из VLM-generated captions: | Уровень | Контент | Длина | Источник | |---|---|---|---| | L1 overview | Краткое описание сцены | <=30 tok | Конденсация P1 | | L2 full description | Детальное описание через Qwen3-VL | <=200 tok | Полный P1+P2 | | L3 fingerprint | Ключевые landmark'ы | <=30 tok | Конденсация P3 | Все три уровня кодируются одним LRSCLIP (248 tok max). Альтернатива (Stage 2): RemoteCLIP для L1/L3 + LRSCLIP для L2. ## Датасет: GTA-UAV-LR - **RGB:** `/home/servml/Документы/datasets/GTA-UAV-LR/` - Drone: 33,763 PNG (512x384), altitudes 100-600m - Satellite: 14,640 PNG (256x256 RGBA) - Pairs: `cross-area-drone2sate-{train,test}.json` (primary split) - Metadata: `*_drone_meta.csv` (height, yaw, roll, pitch) - Origin: GTA V simulation (Los Santos) - **Captions:** `/home/servml/Документы/datasets/GTA-UAV-LR-captions/` - Drone: 33,411 JSON (32,635 multi-paragraph P1/P2/P3 + 776 short water-only) - Satellite: 6,546 JSON (все multi-paragraph) - Формат: 3 абзаца (P1 Inventory + P2 Spatial + P3 Fingerprint) - Token counts: ~430 output tokens per caption - **Segmentation:** `/home/servml/Документы/datasets/GTA-UAV-LR-aug/` - 48,403 images, 17 классов (background, building, road, vegetation, water, ...) - Modalities: segm/, depth/, edge/, chm/, safetensors/ - Query: 512x512, DB: 256x256 ### Фильтрация сегментации Meta-файл `meta/seg_filter.json`: исключение изображений с >=90% background(class 0) + water(class 4). - **Total:** 48,403 → **Passed:** 37,498 (77.5%) / **Excluded:** 10,905 (22.5%) - Drone: 31,188 passed / 2,575 excluded - Satellite: 6,310 passed / 8,330 excluded (преимущественно open water tiles) ## Датасет: UAV-GeoLoc (v2, legacy) - **Путь:** `/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) - **Index:** `Index/train_query.txt` — `query_path 0 pos_crop1 pos_crop2 ...` ## Конфигурации ### V3 (GTA-UAV) Параметры: - 10 epochs, batch 64, AMP, image 256x256 - **Optimizer:** AdamW, per-group LR: proj=1e-4, text=1e-5 (10x lower) - **Scheduler:** linear warmup (2 epochs) + cosine annealing (per-step) - **Loss:** InfoNCE с learnable temperature (CLIP logit_scale), init=0.07, clamp [0.01, 0.5] - **Augmentations:** - Drone: RandomResizedCrop(0.7-1.0), HFlip, Rotation(15°), ColorJitter, Grayscale(5%), GaussianBlur - Satellite: RandomResizedCrop(0.7-1.0), HFlip, ColorJitter, Grayscale(5%) - Eval: Resize+CenterCrop (clean, no augmentation) - **Split:** 80/20 random из всех 33,708 пар (`meta/train_80.json` / `meta/test_20.json`) - Train: 26,966 → 24,891 after seg filter - Test: 6,742 → 6,252 after seg filter - Скрипт: `python -m scripts.make_split --ratio 0.8 --seed 42` ### V2 (UAV-GeoLoc, 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 | ## Запуск ### V3 (GTA-UAV) ```bash # 1. Filter segmentation (exclude 90%+ background/water) python -m scripts.filter_segmentation --output meta/seg_filter.json # 2. Baseline (no text) python -m src.training.train_gtauav --baseline --filter-meta meta/seg_filter.json # 3. With captions (L1/L2/L3) python -m src.training.train_gtauav --filter-meta meta/seg_filter.json # 4. Compare python -m scripts.compare_runs \ --baseline_report out/gtauav/baseline/eval_report.json \ --full_report out/gtauav/with_text/eval_report.json \ --output out/gtauav/comparison.md ``` ### V2 (UAV-GeoLoc) ```bash python -m src.training.train --config conf/baseline_no_text.gin python -m src.training.train --config conf/balanced.gin 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 (drone -> satellite) | 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 | **Eval metrics:** R@1, R@5, R@10 для drone->satellite и satellite->drone **Splits (GTA-UAV):** cross-area (primary, harder) и same-area (sanity check) **Logged per epoch:** loss, temperature (tau), gate value (sigma(alpha)), lr ## Бюджет времени (RTX 4090, 24 GB) ### V3 (GTA-UAV, DINOv3 ViT-L/16, 256x256) | Фаза | Оценка | |------|--------| | VRAM: 2x DINOv3-L + LRSCLIP + batch 64 | ~18-22 GB | | GPU mem (smoke test, batch 4) | 3.1 GB | | Batch size | 64 (default) | | Total params | 733M (10.9M trainable, 1.49%) | ### V2 (UAV-GeoLoc, GeoRSCLIP) | Фаза | Время | |------|-------| | Один 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` - **Статус:** выполнен (2026-04-17), данные в `/home/servml/Документы/datasets/UAV_VisLoc_processed/` (25 GB) - **Задача:** нарезка satellite кропов 512x512, stride 256 + resize drone -> 512x512 - **Подробности:** см. ниже --- ## Downstream: NADEZHDA Teacher (DINOv3 + Multi-FiLM) caption-test — первый этап валидации. При Δ R@1 >= +3% переход к полному teacher'у: ``` Этап 1 (caption-test): GeoRSCLIP + GatedFusion(text) → валидация текста Этап 2 (teacher): DINOv3-L + Multi-FiLM(depth, seg, CHM, normals, text) Этап 3 (distillation): teacher ~300M → student ~5M → Jetson Orin NX ``` ### Auxiliary modalities (предвычисляются из 512x512 офлайн) | Модальность | Модель | Формат для teacher | Каналы | |---|---|---|---| | Depth | DepthAnything V2 | continuous, log(1+d) | 1 | | Normals | Sobel от depth | continuous | 3 | | Segmentation | SegFormer-B5 | binary per-class masks (top-K) | 16-17 | | Canopy Height | Meta HRCH | binary bins (1-5m, 5-15m, >15m) + occupancy | 4-5 | | Text | Qwen3-VL-8B / MobileCLIP2 | embedding | - | ### Асимметрия sat/drone - CHM: только satellite (модель обучена на nadir, на oblique drone не работает) - Satellite: ~27 aux каналов, Drone: ~21 aux каналов ### Fusion: Multi-FiLM ``` aux_features → FiLM(γ, β) → γ * DINOv3_tokens + β ``` Binary masks — natural FiLM gates. Modality dropout: text 0.3, CHM 0.5, seg 0.15, depth 0.1. ### Планируемый эксперимент H5 Сравнение T_bin (binary masks) vs T_pyr (native feature pyramids) vs T_hybrid. Прогноз: binary masks лучше на cross-domain из-за робастности к aux-model artifacts. --- ## Датасеты (справочник) ### 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` - **Статус:** выполнен (2026-04-17) ### Запуск ```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 512 ``` ### Pipeline 1. Resize drone -> 512x512 (JPEG, quality=95) 2. Stitch satellite tiles для маршрута 09 (4 тайла -> 44800x33280) 3. Нарезка satellite -> кропы 512x512, stride 256, сохранение без downscale (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) | ### Результаты (target-size 512) - Drone: 6,744 images 512x512 (без маршрута 07: 30 excluded) - Satellite кропов: 74,807 (512x512, без downscale) - Размер на диске: 25 GB - Median distance drone->crop: 25.9m, P99: 45.7m - Память при генерации: до ~8.7 GB RAM (маршрут 09 stitched 44800x33280) - Разрешение 512 для downstream задач (сегментация, depth, normals); resize до 256x256 в dataloader ### Ревью и исправления (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) - 6 drone в маршруте 06 (06_0093-06_0098) за пределами спутниковой карты (distance >1000m) - Большие спутниковые карты загружаются целиком в RAM при генерации (до 8.7 GB для route 09)