Files
caption-test/CLAUDE.md
pikaliov 814586ce3b Add g2q eval metrics and asymmetric MONA-24 configs
- _evaluate: compute R@K + AP for both directions (q2g and g2q) via inverted
  ground truth; g2q denominator counts only sat-tiles with at least one positive
  drone in the (sub)sampled query set. Surfaces in train.csv, val.csv,
  train_recall.csv, W&B summary, and final log.
- conf/gtauav_balanced_asym.gin: asymmetric WEB+SAT encoders, MONA in all 24
  ViT blocks (~17.6M trainable / ~733M total).
- conf/gtauav_baseline_asym.gin: same architecture, baseline_mode=True for
  Δ R@1 against balanced_asym.
- CLAUDE.md / README.md: document new configs, clarify that g2q is now
  computed (was claimed but missing).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:47:02 +03:00

448 lines
26 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.
# Caption Quality Test for Cross-View Geo-Localization
## Архитектура системы (v3, 2026-04-24) — GTA-UAV эксперимент
```
Shared DINOv3 ViT-L/16 (LVD-1689M, frozen + MONA in last 12/24 blocks)
для обеих веток — drone и satellite кодируются одним encoder.
QUERY BRANCH (drone + L1/L2/L3 captions):
drone_img [B,3,256,256] --> DINOv3 ViT-L/16 (shared) --> d_img [B,1024]
|
L1 --> DGTRS-CLIP (248 tok) --> z₁ [768] --\ |
L2 --> DGTRS-CLIP (248 tok) --> z₂ [768] ---+-- cat --> MLP(2304→1024→1024) --> d_txt [B,1024]
L3 --> DGTRS-CLIP (248 tok) --> z₃ [768] --/ |
|
q = σ(α_q)·d_img + (1σ(α_q))·d_txt GatedFusion_q
|
q̂ = q/‖q‖₂ --> query [B,1024]
GALLERY BRANCH (satellite + satellite captions):
sat_img [B,3,256,256] --> DINOv3 ViT-L/16 (shared, same weights) --> s_img [B,1024]
|
sat_L1 --> DGTRS-CLIP --> z₁ --\ |
sat_L2 --> DGTRS-CLIP --> z₂ ---+-- cat --> MLP (shared) --> s_txt [B,1024]
sat_L3 --> DGTRS-CLIP --> z₃ --/ |
|
g = σ(α_g)·s_img + (1σ(α_g))·s_txt GatedFusion_g
|
ĝ = g/‖g‖₂ --> gallery [B,1024]
Retrieval space: 1024-dim (DINOv3 native, без projection layers)
TextFusionMLP shared между query и gallery (одинаковый формат captions)
Для sat images без captions: s_txt=None → g = s_img (gate passthrough)
LOSS: L = 0.6·CE(q̂·ĝᵀ/τ, targets) + 0.4·CE(ĝ·q̂ᵀ/τ, targets)
τ = 1/exp(logit_scale), learnable, clamped [0.01, 0.1], init=0.07
label_smoothing=0.1
BATCH SAMPLING: MutuallyExclusiveSampler — в одном батче нет двух drone'ов
с пересекающимися sat_candidates (исключает false negatives, которые
иначе появляются из-за multi-positive структуры GTA-UAV).
BASELINE: σ(α_q)=σ(α_g)=1.0, text disabled, DGTRS not loaded
```
### Text hierarchy (L1/L2/L3)
- **L1 overview:** первое предложение P1 — краткое описание land-cover (15-30 tok)
- **L2 full:** полные P1 + P2 — inventory + spatial layout (100-200 tok)
- **L3 fingerprint:** P3 — уникальные landmarks для matching (20-50 tok)
- **Fusion:** z_text = MLP([z₁; z₂; z₃]) — concat 3×768 → Linear(2304,1024) → GELU → Linear(1024,1024)
- **Shared MLP** между query и gallery ветками (одинаковый формат captions)
- **Satellite captions:** 6,546 из 14,640 sat images имеют captions. Для остальных gate passthrough (g = s_img) — **per-sample mask** в `_fuse_with_mask` возвращает чистые image features для samples без caption (без шума от пустых строк)
### Text encoder: DGTRS-CLIP (official architecture)
- Код: `src/models/dgtrs/` — из github.com/MitsuiChen14/DGTRS (Apache-2.0)
- KPS positional embedding: mask1 (pos 0-19, frozen) + mask2 (pos 20-247, trainable)
- Transformer: sequence-first (LND), nn.MultiheadAttention, 12 layers
- Tokenizer: BPE SimpleTokenizer (248 tokens, vocab 49408)
### Trainable parameters: 7.06M из 434M (1.63%)
- **MONA adapters** (shared DINOv3): 3.5M (2 per block × 12 last blocks, bottleneck=64)
- **LoRA** (DGTRS-CLIP): 147K (Q+V, rank=4, 12 blocks)
- TextFusionMLP (shared): Linear(2304,1024)+GELU+Linear(1024,1024) = ~3.4M
- gate α_q + α_g: 2 scalars
- logit_scale: 1 scalar (learnable temperature)
- DINOv3 (1 encoder) + DGTRS: frozen backbone weights
- **Без projection layers** — retrieval space = DINOv3 native 1024-dim
- **AMP:** frozen layers fp16, adapters + loss fp32
- **Примечание:** ранее была asymmetric setup (2×DINOv3 WEB+SAT, MONA во всех 24 блоках) с 17.6M trainable / 733M total. Упростили до shared + last-12 MONA.
### Optimizer & Scheduler
- **AdamW** с per-group LR: projections lr=1e-4, text encoder lr=1e-5
- **Linear warmup** (2 epochs) + **cosine annealing** (per-step)
- **Gradient clipping:** max_norm=1.0
- **AMP:** fp16 для model forward, fp32 для loss (learnable temperature overflow fix)
### Image input: 256x256
DINOv3 ViT-L/16 с patch_size=16 → 16x16=256 patches на 256x256.
Train: augmentations (drone: crop+flip+rot+jitter+blur, sat: crop+flip+jitter).
Eval: 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 + DGTRS-CLIP) — DONE
| Файл | Назначение |
|------|-----------|
| `src/models/dgtrs/model.py` | Официальная архитектура DGTRS-CLIP text encoder (Apache-2.0) |
| `src/models/dgtrs/simple_tokenizer.py` | BPE tokenizer (248 tokens, vocab 49408) |
| `src/models/asymmetric_encoder.py` | DINOv3ViT + TextFusionMLP + AsymmetricEncoder + GatedFusion + encode_query/encode_gallery (per-sample caption mask) |
| `src/datasets/gtauav_dataset.py` | GTA-UAV-LR loader + L1/L2/L3 captions + GTAUAVSatGallery/GTAUAVDroneQuery (full retrieval eval) |
| `src/datasets/mutually_exclusive_sampler.py` | BatchSampler: drone'ы в батче не делят sat_candidates (no false negatives) |
| `src/datasets/dynamic_similarity_sampler.py` | DSS: embedding-kNN + mutex — батчи из визуально похожих drone'ов (GPU/CPU, опциональный LSH) |
| `src/datasets/lsh_index.py` | Random-projection cosine-LSH для approximate kNN (opt-in; `dss_use_lsh=True`) |
| `src/datasets/embedding_cache.py` | Дисковый кеш для drone embeddings — skip re-embed на resume |
| `src/losses/multi_infonce.py` | **Primary:** SymmetricInfoNCE + MoCo queue, learnable τ clamp [0.01, 0.1], weights q2g=0.6 g2q=0.4, `hard_mining_k` для top-K hardest negatives |
| `src/losses/weighted_infonce.py` | Alternative: per-sample adaptive label smoothing (активируется `loss_type="weighted"`) |
| `src/losses/hard_negatives.py` | NegativeMemoryBank (MoCo-style FIFO queue 4096 × 1024) |
| `src/training/train_gtauav.py` | Training loop: full-gallery `_evaluate`, mutex sampler wiring, loss_type switch |
| `scripts/smoke_eval.py` / `scripts/smoke_train.py` | Регрессионные smoke-тесты для eval и train pipeline |
| `src/training/trackers.py` | Unified experiment tracker: W&B + TensorBoard + CSV |
| `src/training/grad_monitor.py` | Gradient norm monitoring per param group |
| `src/training/gradcam.py` | Grad-CAM visualization для DINOv3 encoders |
| `src/training/profiling.py` | PyTorch Profiler wrapper + torchinfo model summary |
| `src/training/plot_metrics.py` | Seaborn/matplotlib plots (каждую эпоху) |
| `conf/gtauav_balanced.gin` | Shared encoder, MONA 12/24, with text, gate=0.7, 10 epochs |
| `conf/gtauav_baseline.gin` | Shared encoder, MONA 12/24, no text, gate=1.0 |
| `conf/gtauav_balanced_asym.gin` | Asymmetric (WEB+SAT), MONA 24/24, with text — overrides gtauav_balanced.gin |
| `conf/gtauav_baseline_asym.gin` | Asymmetric (WEB+SAT), MONA 24/24, no text |
| `conf/gtauav_text_heavy.gin` | Text-heavy, gate=0.3 |
| `conf/gtauav_image_heavy.gin` | Image-heavy, gate=0.9 |
| `scripts/make_split.py` | 80/20 random split из всех пар |
| `scripts/filter_segmentation.py` | Scan segm masks, output meta JSON (exclude >=90% bg+water) |
## Backbones (v3)
### DINOv3 ViT-L/16 — Shared (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
- **MONA:** 24 адаптера в последних 12 блоках (blocks 12-23), bottleneck=64, 3.5M trainable
- **Status:** frozen кроме MONA
- **Примечание:** ранее asymmetric — использовался отдельно `nn_models/DINO_SAT/model.safetensors` (sat493m pretrain) для satellite ветки. Упростили до shared WEB-энкодера.
### DGTRS-CLIP ViT-L-14 (LRSCLIP) — Text encoder
- **Checkpoint:** `nn_models/LRSCLIP/DGTRS-CLIP-ViT-L-14.pt`
- **Код:** `src/models/dgtrs/` — официальная архитектура из github.com/MitsuiChen14/DGTRS
- **Text dim:** 768, max tokens: 248 (KPS: mask1 pos 0-19 frozen + mask2 pos 20-247 trainable)
- **Transformer:** 12 layers, 12 heads, sequence-first (LND), QuickGELU
- **Tokenizer:** BPE SimpleTokenizer (vocab 49408), 248 token context
- **Содержит:** полную CLIP модель (visual + text), используем только text encoder (124M params)
- **Status:** partial unfreeze (last resblock + ln_final + text_projection, ~7.6M trainable)
### 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:** SymmetricInfoNCE (q2g=0.6, g2q=0.4) с learnable τ (init=0.07, clamp [0.01, 0.1])
- **Hard mining:** top-K=512 hardest negatives per query из MoCo queue (размер 4096); `hard_mining_k=0` отключает
- **Batch sampler:** `sampler_type="dss"` (default) — DynamicSimilaritySampler с re-embedding каждую эпоху: пакует визуально похожих drone'ов в один батч (+hardness) с mutex-constraint (no false negatives). Первая эпоха warmup mutex-only. Средний in-batch cosine sim ~0.71 vs 0.26 у mutex. kNN на GPU (`dss_knn_device="cuda"`) — 1.6s vs 17s на CPU. Опциональный LSH (`dss_use_lsh=True`) для scale 100K+. Embedding cache (`dss_cache_dir`) — skip re-embed на resume.
- **Eval:** full satellite gallery (~2684 unique tiles для test_20) с multi-match R@K (учитывает все positive/semi-positive)
- **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`
### V3 (GTA-UAV, gin)
| Конфиг | Gate init | Описание |
|--------|-----------|----------|
| `conf/gtauav_balanced.gin` | 0.7 (30% text) | **Primary test** — shared DINOv3 WEB, MONA 12/24 |
| `conf/gtauav_baseline.gin` | 1.0 (no text) | Reference baseline (shared, MONA 12/24) |
| `conf/gtauav_balanced_asym.gin` | 0.7 (30% text) | Asymmetric (WEB+SAT), MONA 24/24 — full-capacity variant |
| `conf/gtauav_baseline_asym.gin` | 1.0 (no text) | Asymmetric baseline for Δ R@1 vs balanced_asym |
| `conf/gtauav_text_heavy.gin` | 0.3 (70% text) | Stress test |
| `conf/gtauav_image_heavy.gin` | 0.9 (10% text) | Image-dominant |
### 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. Train with gin config (recommended)
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
--filter-meta meta/seg_filter.json
# 3. Baseline (no text)
python -m src.training.train_gtauav --config conf/gtauav_baseline.gin \
--filter-meta meta/seg_filter.json
# 4. With diagnostics (W&B + Grad-CAM + Profiler)
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
--filter-meta meta/seg_filter.json --wandb --gradcam --profile
# 5. CLI overrides (gin params take priority)
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
--filter-meta meta/seg_filter.json \
--gin-param 'TrainConfigGTAUAV.batch_size=16'
# 6. 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
# 7. TensorBoard
tensorboard --logdir out/gtauav/with_text/tb_logs
```
### 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 + AP (MRR) для обоих направлений: q2g (drone→satellite) и g2q (satellite→drone). g2q считается через инвертированный GT (для каждого sat-tile собираются drone-индексы из `valid_idx_per_query`); знаменатель — sat-tiles, у которых есть хотя бы один positive 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: DINOv3-L (shared) + LRSCLIP + batch 64 | ~10-14 GB (было ~18-22 с 2× DINOv3) |
| GPU mem (smoke test, batch 4) | 3.1 GB |
| Batch size | 64 (default) |
| Total params | 434M (7.06M trainable, 1.63%) — shared encoder + MONA в last 12/24 blocks |
### 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)