---
title: "Методология автоматического аннотирования набора данных World-UAV: карты глубины, контурные карты, семантические маски и кросс-вью карты высот"
type: methodology
version: "3.3"
created: 2026-04-07
updated: 2026-04-08
dataset: "World-UAV (UAV-GeoLoc, IEEE RA-L 2025)"
hardware: "RTX 4090 (24GB VRAM)"
downstream_task: "Cross-view geo-localization (drone<->satellite)"
models:
  depth: "Depth Anything V3 LARGE-1.1 (411M)"
  edges: "Sobel filters from depth"
  segmentation: "SegEarth-OV3 (SAM 3.1 + PE-L+)"
  chmv2: "DINOv3-ViTL16-CHMv2-DPT (337M, FP32)"
tags:
  - type/methodology
  - component/dataset
  - method/segmentation
  - dataset/worlduav
  - task/experiment
  - priority/high
---

# Методология автоматического аннотирования набора данных World-UAV

**Содержание (12 разделов):**

- **Раздел 1.** Постановка задачи: формальная формулировка, связь с LUPI-дистилляцией
- **Раздел 2.** World-UAV: структура, объем, неполные сцены
- **Раздел 3.** Модели-генераторы: DA3-LARGE, Sobel, SegEarth-OV3, CHMv2
- **Раздел 4.** Пайплайн обработки: sequential pipeline, формат выхода, manifest
- **Раздел 5.** Протокол обработки: оценка времени, запуск
- **Раздел 6.** Контроль качества: валидация, визуальная инспекция, метрики
- **Раздел 7.** Формат хранения и интеграция с даталоадером
- **Раздел 8.** Гипотезы аннотирования (AH1-AH6)
- **Раздел 9.** Архитектура кода
- **Раздел 10.** Риски и митигации
- **Раздел 11.** Timeline и ресурсы
- **Раздел 12.** Библиография

> **Цель:** разработать воспроизводимую методологию автоматической генерации четырех вспомогательных модальностей (depth, edges, segmentation, cross-view height map) из RGB-изображений набора данных World-UAV для последующего использования в LUPI-дистилляции системы NADEZHDA.

---

## 1. Постановка задачи

### 1.1. Формальная формулировка

Пусть $I \in \mathbb{R}^{H \times W \times 3}$ -- RGB-изображение из World-UAV (drone query или satellite DB crop). Задача автоматического аннотирования формулируется как генерация четырех карт-аннотаций:

$$\mathcal{A}: I \mapsto (D, E, S, M)$$

где:
- $D \in \mathbb{R}^{H' \times W'}$ -- карта глубины (monocular depth), $D_{ij} \in [0, 1]$ (per-frame normalized)
- $E \in \mathbb{R}^{H' \times W'}$ -- контурная карта (structural edges), $E_{ij} \in [0, 1]$ (edge magnitude)
- $S \in \mathbb{Z}^{H' \times W'}$ -- семантическая сегментационная маска, $S_{ij} \in \{0, \ldots, C-1\}$ (class index)
- $M \in \mathbb{R}^{H' \times W'}$ -- кросс-вью карта высот (CHMv2), $M_{ij} \in [0, 1]$ (per-frame normalized)

### 1.2. Связь с downstream-задачей (LUPI-CVGL)

Сгенерированные аннотации используются как **привилегированная информация** при обучении Teacher модели системы NADEZHDA:

- **Teacher** (DINOv3-L, ~356M) обучается на расширенном наборе модальностей: $\{I_{sat}, I_{drone}, D+E, S, M\}$
- **Student** (SOFIA, ~6-8M) при инференсе использует только 2: $\{I_{sat}, I_{drone}\}$

Привилегированные модальности ($D$, $E$, $S$, $M$) доступны **только при обучении** Teacher. Knowledge distillation переносит эту информацию в Student через Loss LUPI:

$$L_{LUPI} = \|z_{Student}^{drone} - \text{sg}(z_{Teacher}^{fused})\|^2$$

### 1.3. Почему edges, а не normals

| Критерий | Edges | Normals |
|:---------|:------|:--------|
| Структурные контуры (дороги, здания) | **Выделяет** | Не фокусируется |
| Инвариантность к освещению | **Высокая** | Зависит от depth quality |
| Cross-view matching utility | **Высокая** -- границы видны с обоих ракурсов | Низкая -- нормали шумные для наклонных снимков |
| Computational cost | **~1 мс/img** (Sobel, CPU) | ~5 мс/img (DSINE, GPU) |
| Число каналов | **1** (magnitude) | 3 (xyz-вектор) |

### 1.4. Зачем CHMv2 (cross-view height map)

CHMv2 (DINOv3-ViTL16-CHMv2-DPT) обучен на DINOv3 backbone специально для оценки высот из спутниковых и аэроснимков. В отличие от DA3, который обучен преимущественно на наземных изображениях:

- **CHMv2** лучше работает на nadir-view (спутниковые патчи) -- обучен на аэрофотосъемке
- **DA3** лучше работает на oblique-view (drone кадры) -- обучен на ground-level
- Комбинация двух моделей глубины ($D$ + $M$) дает Teacher **ортогональные depth representations**

> **Важно:** CHMv2 **нестабилен в FP16** (выдает NaN). Модель всегда загружается в FP32.

---

## 2. Набор данных: World-UAV (UAV-GeoLoc)

### 2.1. Обзор

| Параметр | Значение |
|:---------|:---------|
| Venue | IEEE Robotics and Automation Letters, Q1 |
| Авторы | Wu et al. (NUDT + Zhejiang University) |
| Число сцен | **372** (171 Country + 200 Terrain + 1 Rot) |
| Всего изображений | **973,256** (фактически найдено при обработке) |
| Query (drone) | 652,744 (512x512 JPEG, синтетические Google Earth Studio) |
| DB (satellite) | 274,683 (PNG, 11 размеров: 100-1000 px) |
| Subset Rot | 7,341 изображений |
| Subset Country | 455,638 изображений |
| Subset Terrain | 510,277 изображений |
| Размер на диске | ~181 GB |

### 2.2. Неполные сцены (исключаются из обработки)

**16 сцен Country** не содержат DB-кропов, positive.json:

| Город | Сцены | Кол-во |
|:------|:------|:-------|
| Edinburgh | CastleHill, Dalry, Haymarket, NewTown, Stockbridge | 5 |
| London | CamdenTown, CoventGarden, Fitzrovia, Mayfair, SoHo | 5 |
| Manchester | Ancoats, Castlefield, Deansgate, NorthernQuarter, Piccadilly | 5 |
| Birmingham | JewelleryQuarter | 1 |

Дополнительно исключаются: `Index`, `charts`, `__MACOSX`.

---

## 3. Модели-генераторы

### 3.1. Depth: Depth Anything V3 LARGE-1.1

| Параметр | Значение |
|:---------|:---------|
| Модель | DA3-LARGE-1.1 (ViT-based unified architecture) |
| HuggingFace ID | `depth-anything/DA3-LARGE-1.1` |
| Параметры | **410.94M** |
| Precision | FP16 (при `use_fp16=True`) |
| VRAM | **1.5 GB** weights |
| Inference speed | **18.4 img/s** (batch=4, 256x256, RTX 4090) -- замерено |
| Выход | grayscale PNG [256, 256] uint8 [0, 255] (per-frame normalized) |
| Fallback | DA V2 Large (transformers API) |
| Код | `src/nn/depth_anything_3/` (вендорированный пакет) |
| Веса | `in/weights/models--depth-anything--DA3-LARGE-1.1/` |

**Архитектура DA3:** DinoV2 backbone (ViT-Large, 24 блока, 1024 dim) + DualDPT head (depth + ray prediction). Поддерживает multi-view inference с camera conditioning, но в пайплайне используется monocular mode (single image).

### 3.2. Edges: Sobel из карты глубины

| Параметр | Значение |
|:---------|:---------|
| Метод | Sobel filters (3x3) на depth map |
| Реализация | `torch.nn.functional.conv2d` с Sobel-ядрами |
| Inference speed | **419.6 img/s** (CPU, batch=32) -- замерено |
| Выход | grayscale PNG [256, 256] uint8 [0, 255] |
| Зависимость | Edges вычисляются из depth в том же batch-цикле |

**Алгоритм:**

$$S_x = \frac{1}{8}\begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix}, \quad E = \sqrt{(S_x * D)^2 + (S_y * D)^2}, \quad E = E / \max(E)$$

### 3.3. Segmentation: SegEarth-OV3 (SAM 3.1)

| Параметр | Значение |
|:---------|:---------|
| Модель | SegEarth-OV3 (training-free OV semantic segmentation) |
| Backbone | SAM 3.1 Multiplex (~3.3 GB checkpoint) |
| VRAM | **3.2 GB** (batch=1), до **~10 GB** (batch=8) |
| Inference speed (per-image) | **3.9 img/s** (batch=1, без батчинга backbone) -- замерено |
| Inference speed (batched) | **~10-12 img/s** (batch=8, batched backbone) -- оценка |
| Макс. batch size | **8** (ограничение в `_infer_segearth_ov3`, hardcoded) |
| Промпты | 5 классов: background, building, road, vegetation, water |
| Fallback | SegFormer-B5 (ADE20K, 150 classes) |
| Код | `src/nn/segearth_ov3/` (вендорированный пакет) |
| Веса | `in/weights/sam3.1/sam3.1_multiplex.pt` |
| BPE vocab | `src/nn/segearth_ov3/sam3/assets/bpe_simple_vocab_16e6.txt.gz` (встроен) |

**Архитектура SegEarth-OV3:** SAM3 backbone (ViT-H, 1008x1008, RoPE) + VETextEncoder (CLIP-like, BPE tokenizer) + TransformerEncoderFusion (6 layers, cross-attention) + TransformerDecoder (200 object queries) + PixelDecoder (mask prediction). Для каждого текстового промпта выполняется grounding: fusion instance masks (decoder) + semantic masks (segmentation head), фильтрация по presence score.

**Batched backbone inference (v3.2):**

Основной bottleneck SegEarth-OV3 -- backbone forward pass (SAM 3.1 ViT), занимающий ~80% времени инференса. Реализован `predict_pil_batch`:

1. **`set_image_batch(images)`** -- один backbone pass на весь батч (вместо B отдельных `set_image()`)
2. **`_slice_backbone_out(backbone_out, i)`** -- слайсинг backbone features `[B, 256, H, W] -> [1, 256, H, W]` для каждого изображения
3. **Per-image grounding** -- для каждого изображения: цикл по 5 промптам -> легкий grounding decoder + text encoder

Grounding decoder (~20% времени) остается per-image, но backbone (~80%) амортизируется на весь батч. При OOM или ошибке -- автоматический fallback на per-image `predict_pil()`.

**Палитра сегментации:**

| Класс | ID | Цвет | RGB |
|:------|:--:|:-----|:----|
| background | 0 | Черный | (0, 0, 0) |
| building | 1 | Красный | (220, 40, 40) |
| road | 2 | Серый | (160, 160, 160) |
| vegetation | 3 | Зеленый | (30, 180, 30) |
| water | 4 | Синий | (30, 120, 220) |

### 3.4. CHMv2: DINOv3-ViTL16-CHMv2-DPT

| Параметр | Значение |
|:---------|:---------|
| Модель | CHMv2ForDepthEstimation (DPT head на DINOv3 backbone) |
| HuggingFace ID | `facebook/dinov3-vitl16-chmv2-dpt-head` |
| Параметры | **336.88M** |
| FLOPs | **457.98 GFLOPs** (fvcore, 518x518 input) |
| Precision | **FP32 only** (FP16 выдает NaN -- обнаружено при валидации) |
| VRAM | **1.3 GB** weights (FP32) |
| Inference speed | **31.7 img/s** (batch=4, 256x256, RTX 4090) -- замерено |
| Выход | grayscale PNG [256, 256] uint8 [0, 255] (per-frame normalized) |
| Веса | `in/weights/dinov3-chmv2/` (safetensors, 1.3 GB) |

### 3.5. Сравнение моделей (benchmark на 100 изображениях, RTX 4090)

| | DA3-LARGE | Sobel edges | SegEarth-OV3 (batch=1) | SegEarth-OV3 (batch=8) | CHMv2 (DINOv3) |
|:---|:---------|:-----------|:----------------------|:----------------------|:---------------|
| Params | 411M | 0 | ~1B+ (SAM 3.1) | ~1B+ (SAM 3.1) | 337M |
| VRAM | 1.5 GB | 0 | 3.2 GB | ~10 GB | 1.3 GB |
| Precision | FP16 | CPU | BF16 | BF16 | **FP32** |
| Speed | **18.4 img/s** | **419.6 img/s** | **3.9 img/s** | **~10-12 img/s** (оценка) | **31.7 img/s** |
| 100 img time | 5.44 s | 0.24 s | **25.49 s** | **~8-10 s** (оценка) | 3.15 s |
| Peak VRAM (sequential) | | | | **~10 GB** (SegEarth batch=8 -- наибольшая из загружаемых) | |

---

## 4. Пайплайн обработки

### 4.1. Sequential pipeline (одна модель за раз)

Модели загружаются **последовательно** -- каждая стадия загружает свою модель, обрабатывает все изображения, затем выгружает модель перед следующей стадией. Это позволяет использовать максимальный batch size для каждой модели и упрощает resume по стадиям:

```
Stage 1: DEPTH
   +-- load DA3-LARGE-1.1           [GPU, ~1.5 GB FP16]
   +-- process ALL images (batched, auto_batch_size)
   +-- save_async {stem}_depth.png   [ThreadPool, 4 workers]
   +-- unload_model()               [del + torch.cuda.empty_cache()]

Stage 2: EDGES
   +-- load saved depth from .npy/.png  [CPU]
   +-- compute Sobel edges (CPU, batch=32)
   +-- save_async {stem}_edge.png
   +-- (no GPU model to unload)

Stage 3: SEGMENTATION
   +-- load SegEarth-OV3 (SAM 3.1)   [GPU, ~3.2-10 GB]
   +-- process ALL images (batched backbone, max_batch=8, ~10-12 img/s)
   |   +-- predict_pil_batch(): 1 backbone pass на <=8 изображений
   |   +-- _slice_backbone_out(): слайс features per-image
   |   +-- grounding decoder: per-image x 5 промптов (легкий)
   +-- save_async {stem}_segm.png
   +-- unload_model()

Stage 4: CHMv2
   +-- load DINOv3-ViTL16-CHMv2-DPT [GPU, ~1.3 GB FP32]
   +-- process ALL images (batched, auto_batch_size)
   +-- save_async {stem}_chm.png
   +-- unload_model()
```

### 4.2. Формат выхода

Файлы сохраняются в `World-UAV-aug/` с **сохранением структуры** исходного датасета. Исходные изображения **не копируются**. Имя файла = имя исходного изображения + суффикс стадии:

```
World-UAV-aug/
+-- Rot/SouthernSuburbs/DB/img/
|   +-- crop_12_4_depth.png       # grayscale [256, 256] -- DA3 depth
|   +-- crop_12_4_edge.png        # grayscale [256, 256] -- Sobel edges
|   +-- crop_12_4_segm.png        # RGB palette [256, 256, 3] -- semantic seg
|   +-- crop_12_4_chm.png         # grayscale [256, 256] -- CHMv2 height
+-- Country/...
+-- Terrain/...
```

### 4.3. Суффиксы и формат файлов

| Стадия | Суффикс | Формат PNG | Для обучения |
|:-------|:--------|:-----------|:-------------|
| depth | `_depth` | grayscale (L) uint8 | `value = pixel / 255.0` -> float [0,1], 1 канал |
| edges | `_edge` | grayscale (L) uint8 | `value = pixel / 255.0` -> float [0,1], 1 канал |
| segmentation | `_segm` | RGB palette (3ch) | Загружать class ID из int-маски, не из RGB |
| chmv2 | `_chm` | grayscale (L) uint8 | `value = pixel / 255.0` -> float [0,1], 1 канал |

> **Для обучения:** depth, edge, chm используются как grayscale 1-канальные float [0,1]. Colormap-визуализации не нужны -- они теряют информацию при обратном преобразовании. Segm используется через class index (0-4), не через RGB.

### 4.4. Конфигурация (Gin framework)

Все параметры задаются в `in/config_files/*.gin`:

| Файл | Ключевые параметры |
|:-----|:-------------------|
| `pipeline.gin` | input_root, output_root, stages, save_npy, save_vis, save_concat, resume, subset, source, log_level |
| `models.gin` | depth_model_id, depth_fallback_id, chmv2_model_id, seg_model_type, seg_fallback_id, weights_dir |
| `hardware.gin` | profile_name, total_ram_gb, reserve_gb, use_fp16, batch_size (None=auto), num_workers |
| `segmentation.gin` | prompts, threshold (0.3), default_resolution (1008) |
| `input.gin` | image_size (256), sobel_kernel_size (3), edge_normalize, imagenet_mean/std |

### 4.5. Manifest (manifest.json)

По завершении пайплайна генерируется `{output_root}/manifest.json` с полной метаинформацией:

```json
{
  "pipeline_version": "3.3.0-sequential",
  "image_size": 256,
  "profile": "rtx4090",
  "models": {
    "depth": "DA3-LARGE-1.1",
    "edges": "Sobel from depth (CPU)",
    "segmentation": "segearth-ov3",
    "chmv2": "facebook/dinov3-vitl16-chmv2-dpt-head"
  },
  "seg_prompts": ["background", "building", "road", "vegetation", "water"],
  "total_images": 973256,
  "stages": {
    "depth": {"processed": "...", "time_sec": "..."},
    "edges": {"processed": "...", "time_sec": "..."},
    "segmentation": {"processed": "...", "time_sec": "..."},
    "chmv2": {"processed": "...", "time_sec": "..."}
  },
  "timestamp": "..."
}
```

### 4.6. Auto batch size

Batch size определяется автоматически по свободной VRAM:

```python
free_mb = (cuda_total - cuda_reserved) / (1024*1024) - overhead_mb
batch_size = round_down_power_of_2(free_mb / act_per_sample_mb * 0.7)
```

Можно зафиксировать вручную через `hardware.gin` (`batch_size = 8`). При `batch_size = None` используется автоматический расчет.

### 4.7. Подавление логов

Все verbose логи моделей подавлены:
- `DA3_LOG_LEVEL=ERROR` -- кастомный DA3 Logger (не стандартный Python logging)
- `TRANSFORMERS_VERBOSITY=error` -- transformers
- `HF_HUB_DISABLE_PROGRESS_BARS=1` -- tqdm загрузки весов
- Все сторонние Python loggers на уровень `ERROR`

---

## 5. Протокол обработки

### 5.1. Оценка времени (экстраполяция из benchmark 100 img)

**v3.1 (per-image SegEarth-OV3):**

| Стадия | 100 img | 973K img | % общего времени |
|:-------|:--------|:---------|:-----------------|
| Depth (DA3-LARGE) | 5.44 s | **14.7 ч** | 6% |
| Edges (Sobel) | 0.24 s | **0.6 ч** | <1% |
| Segmentation (SegEarth-OV3, batch=1) | 25.49 s | **69 ч** (~2.9 дней) | **74%** |
| CHMv2 (DINOv3) | 3.15 s | **8.5 ч** | 4% |
| **Итого** | 34.33 s | **~93 ч** (~3.9 дней) | 100% |

**v3.2+ (batched backbone, batch<=8) -- оценка:**

| Стадия | 100 img | 973K img | % общего времени |
|:-------|:--------|:---------|:-----------------|
| Depth (DA3-LARGE) | 5.44 s | **14.7 ч** | 14% |
| Edges (Sobel) | 0.24 s | **0.6 ч** | <1% |
| Segmentation (SegEarth-OV3, batch<=8) | ~8-10 s | **~22-27 ч** (~1 день) | **~55%** |
| CHMv2 (DINOv3) | 3.15 s | **8.5 ч** | 8% |
| **Итого** | ~17-19 s | **~46-51 ч** (~2 дней) | 100% |

> **Примечание:** оценки для batched backbone теоретические (backbone ~80% времени амортизируется на батч, grounding ~20% остается per-image). Реальные цифры зависят от VRAM overhead и размера изображений. Требуется benchmark на реальных данных.

### 5.2. Запуск

```bash
# Полный пайплайн (все subset'ы, все стадии, resume=True):
python -m src.main
```

Resume проверяет наличие `.png` **или** `.npy` файлов **для каждой стадии отдельно**. Пайплайн можно прервать и перезапустить в любой момент -- уже обработанные изображения пропускаются. В sequential-архитектуре resume гранулярный: можно перезапустить только конкретную стадию (например, segmentation), не затрагивая уже завершенные стадии (depth, edges).

### 5.3. Оценка дискового пространства

| Режим | Per-image | 973K images |
|:------|:---------|:-----------|
| Только PNG (save_npy=False) | ~60 KB x 4 | **~228 GB** |
| PNG + NPY (save_npy=True) | ~508 KB | ~484 GB |

---

## 6. Контроль качества

### 6.1. Автоматическая валидация

| Проверка | Уровень | Механизм |
|:---------|:--------|:---------|
| Файл существует | Per-image | `_filter_all_done()` при resumption |
| Shape корректен | Per-stage | Проверка при inference |
| Range корректен | Per-stage | depth/edges/chm in [0,1], seg in [0, C-1] |
| NaN detection | Per-stage | CHMv2 в FP32 (FP16 выдает NaN -- обнаружено и исправлено) |
| Atomic write | Per-image | Temp file + `os.replace()` (POSIX atomic) |
| Per-frame normalization | Per-image | Каждый depth/chm map нормализуется индивидуально |

### 6.2. Визуальная инспекция

Для каждого subset:
- **20 случайных query** (drone) + **20 случайных db** (satellite)
- PNG-визуализации генерируются автоматически рядом с исходными файлами
- Проверка: контуры зданий/дорог видны на edges? Классы корректны на segm?

### 6.3. Обнаруженные проблемы и решения

| Проблема | Причина | Решение |
|:---------|:--------|:--------|
| CHMv2 output полностью черный | FP16 вызывает NaN в DINOv3 DPT head | CHMv2 всегда в FP32 |
| DA3 verbose логи (Processed Images, Model Forward Pass) | Кастомный Logger, не Python logging | `DA3_LOG_LEVEL=ERROR` env var |
| `bpe_simple_vocab_16e6.txt.gz` не найден | SegEarth ищет по относительному пути | BPE vocab встроен в `src/nn/segearth_ov3/sam3/assets/`, дефолтный путь вычисляется от `__file__` |

### 6.4. Количественные метрики

| Метрика | Описание | Threshold |
|:--------|:---------|:----------|
| Depth coverage | % пикселей с depth in (0.01, 0.99) | > 80% |
| Edge density | % ненулевых пикселей edges | 5-40% |
| Seg class balance | Entropy распределения классов | > 1.0 bits |
| Seg coverage | % фоновых (class=0) пикселей | < 70% |

---

## 7. Формат хранения и интеграция с даталоадером

### 7.1. Интеграция с World-UAV DataLoader

```python
class UAVGeoLocMultiModal(UAVGeoLocTrain):
    def __init__(self, root, aug_root, ...):
        super().__init__(root, ...)
        self.aug_root = Path(aug_root)

    def __getitem__(self, idx):
        query, positive, negative, label, height, rotation = super().__getitem__(idx)
        query_path = self._get_query_path(idx)

        stem = Path(query_path).stem
        aug_dir = self.aug_root / Path(query_path).parent

        # Загрузка grayscale PNG как float [0, 1], 1 канал
        depth = np.array(Image.open(aug_dir / f"{stem}_depth.png")) / 255.0  # [H, W]
        edges = np.array(Image.open(aug_dir / f"{stem}_edge.png")) / 255.0
        chm   = np.array(Image.open(aug_dir / f"{stem}_chm.png")) / 255.0

        # Сегментация -- class index (НЕ из RGB, а из отдельного источника)
        # Если save_npy=True: seg = np.load(aug_dir / f"{stem}_segm.npy")
        # Если только PNG: нужно обратное преобразование palette -> class ID

        # Конкатенация для Teacher: RGB(3) + depth(1) + edge(1) + chm(1) = 6ch
        aux = np.stack([depth, edges, chm], axis=0)  # [3, H, W]

        return query, positive, negative, label, aux
```

> **Рекомендация:** для сегментации включить `save_npy=True` или сохранять class index напрямую (uint8), т.к. обратное преобразование RGB palette -> class ID ненадежно.

---

## 8. Гипотезы аннотирования (AH1-AH6)

**AH1. Depth+edges дают больший прирост R@1 при LUPI-дистилляции, чем depth alone.**
Основание: edges инвариантны к текстуре/освещению, выделяют структурные контуры -- наиболее устойчивые к кросс-вью трансформации.

**AH2. SegEarth-OV3 превосходит SegFormer-B5 по downstream CVGL utility.**
Основание: OV-сегментация использует RS-специфичные промпты (building, road, water), не ограничена 150 классами ADE20K.

**AH3. Качество monocular depth деградирует для nadir-view спутниковых патчей сильнее, чем для oblique drone-кадров.**
Основание: DA3 обучен преимущественно на наземных изображениях; CHMv2 обучен на аэрофотосъемке -- компенсирует этот разрыв.

**AH4. Edges из depth (Sobel) > Canny edges из RGB для CVGL.**
Основание: Sobel на depth выделяет границы по геометрии (перепад высот), а не по текстуре (которая различается drone<->sat).

**AH5. CHMv2 + DA3 depth вместе > каждый по отдельности.**
Основание: DA3 и CHMv2 обучены на разных доменах (ground-level vs aerial). Комбинация дает Teacher два ортогональных depth representation, каждый из которых лучше работает на "своем" типе изображений.

**AH6. Автоматическая аннотация 973K изображений за ~2 дня на одном GPU экономически оправдана.**
Основание: ручная разметка -- десятки тысяч человеко-часов. Шумная автоматическая аннотация при LUPI допустима, т.к. Student не использует эти модальности при инференсе.

---

## 9. Архитектура кода

### 9.1. Структура модулей

```
src/
├── nn/                            # Вендорированные нейросетевые пакеты
│   ├── __init__.py                # Регистрация sys.path при import src.nn
│   ├── segearth_ov3/              # SegEarth-OV-3 (полная копия репозитория)
│   │   ├── segearthov3_segmentor.py  # SegEarthOV3Segmentation class
│   │   ├── sam3/                  # SAM 3.1 backbone (134 .py файла)
│   │   │   ├── __init__.py        # build_sam3_image_model()
│   │   │   ├── model_builder.py   # Сборка модели из компонентов
│   │   │   ├── model/             # Sam3Image, Sam3Processor, ViT, encoder/decoder
│   │   │   └── assets/bpe_simple_vocab_16e6.txt.gz  # BPE tokenizer vocab
│   │   └── pamr.py               # Probabilistic Affinity Mask Refinement
│   └── depth_anything_3/          # Depth-Anything-3 (полная копия пакета)
│       ├── api.py                 # DepthAnything3 class (main API)
│       ├── model/                 # DA3 архитектура
│       │   ├── da3.py             # DepthAnything3Net, NestedDepthAnything3Net
│       │   ├── dinov2/            # DinoV2 backbone (ViT)
│       │   ├── dpt.py, dualdpt.py # DPT heads
│       │   └── cam_enc.py, cam_dec.py  # Camera encoder/decoder
│       ├── configs/               # YAML-конфиги моделей (da3-large.yaml и др.)
│       ├── utils/                 # I/O processors, export, geometry, alignment
│       └── registry.py            # Реестр моделей
├── augmentor/
│   ├── dataset.py          # ImageRecord, discover_images, INCOMPLETE_SCENES,
│   │                       #   attach_output_dirs, filter_completed, STAGE_SUFFIX,
│   │                       #   stage_filename, AugmentDataset
│   ├── inference.py        # infer_depth_batch, infer_chmv2_batch (FP32 only),
│   │                       #   compute_edges_from_depth, infer_segmentation_batch,
│   │                       #   _infer_segearth_ov3 (batched backbone, max_batch=8),
│   │                       #   _infer_segformer
│   ├── io_utils.py         # save_depth_async, save_chmv2_async, save_edges_async,
│   │                       #   save_segmentation_async, _save_float16_map,
│   │                       #   _atomic_save_npy, make_palette, _FIXED_PALETTE
│   └── models.py           # load_depth_model (DA3 via src.nn),
│                           #   load_chmv2_model (FP32, NaN в FP16),
│                           #   load_segmentation_model (SegEarth via src.nn),
│                           #   _resolve_cache_dir, unload_model
├── conf/                   # Gin-configurable dataclasses
│   ├── config_loader.py    # load_all_configs()
│   ├── hardware_conf.py    # HardwareConfig + auto_batch_size
│   ├── input_conf.py       # InputConfig (image_size=256, sobel, imagenet stats)
│   ├── models_conf.py      # ModelsConfig (depth, chmv2, seg IDs, fallbacks, weights_dir)
│   ├── pipeline_conf.py    # PipelineConfig (paths, stages, resume, subset, source)
│   └── seg_conf.py         # SegConfig (prompts, threshold, default_resolution)
├── utils/
│   ├── benchmark.py        # BenchmarkResult, benchmark_depth, benchmark_chmv2,
│   │                       #   benchmark_edges, benchmark_segmentation, benchmark_full_pipeline
│   ├── configure_gpu.py    # GPU configuration utilities
│   ├── profiler.py         # log_system_info, log_disk_info, log_vram_snapshot
│   └── utils_file_dir.py   # get_proj_dir
├── tests/                  # 125 тестов (pytest), 6 test-файлов
│   ├── test_config.py      # Config dataclass tests (12 tests)
│   ├── test_models_and_benchmark.py  # Model loading + benchmarking (62 tests)
│   ├── test_inference.py   # Inference functions (18 tests)
│   ├── test_io_utils.py    # I/O and save functions (16+ tests)
│   ├── test_dataset.py     # Discovery and filtering (12 tests)
│   └── test_pipeline_integration.py  # End-to-end stage tests (8 tests)
└── main.py                 # Sequential pipeline:
                            #   run_pipeline -> run_depth_stage, run_edges_stage,
                            #   run_segmentation_stage, run_chmv2_stage
                            #   manifest generation, _silence_model_loggers(),
                            #   log_system_info(), log_disk_info()
```

### 9.2. Механизм вендоринга (src/nn/)

Нейросетевые пакеты **встроены внутрь проекта** и не требуют внешних репозиториев:

| Пакет | Источник | Расположение | Размер |
|:------|:---------|:-------------|:-------|
| SegEarth-OV-3 | [earth-insights/SegEarth-OV-3](https://github.com/earth-insights/SegEarth-OV-3) | `src/nn/segearth_ov3/` | ~5 MB (134 .py + BPE vocab) |
| Depth-Anything-3 | [ByteDance-Seed/Depth-Anything-3](https://github.com/ByteDance-Seed/Depth-Anything-3) | `src/nn/depth_anything_3/` | ~1.6 MB (85 .py + YAML configs) |

**Как работает:** `src/nn/__init__.py` при импорте добавляет в `sys.path`:
- `src/nn/` -- делает доступным `from depth_anything_3.api import DepthAnything3`
- `src/nn/segearth_ov3/` -- делает доступным `from segearthov3_segmentor import SegEarthOV3Segmentation` и `from sam3 import build_sam3_image_model`

Все внутренние импорты обоих пакетов работают **без изменений** -- код идентичен оригинальным репозиториям.

**Загрузка моделей:** `src/augmentor/models.py` содержит `import src.nn` (регистрация путей) и далее использует стандартные импорты:
```python
from depth_anything_3.api import DepthAnything3          # depth
from segearthov3_segmentor import SegEarthOV3Segmentation  # segmentation
```

### 9.3. Ключевые design decisions

| Решение | Обоснование |
|:--------|:-----------|
| **Вендорированные пакеты в src/nn/** | Нет зависимости от внешних репозиториев; воспроизводимость; нет конфликтов sys.path |
| **Sequential pipeline** (одна модель за раз) | Максимальный batch size для каждой модели; гранулярный resume по стадиям; упрощенная обработка ошибок |
| **Batched backbone для SegEarth-OV3** | `predict_pil_batch()`: 1 backbone pass на <=8 img, затем per-image grounding. Backbone ~80% времени -> ~2.5-3x ускорение стадии. Fallback на per-image при OOM |
| Stem-based file naming | `{stem}_{suffix}.png` -- файлы в той же директории, структура 1:1 с исходным датасетом |
| Grayscale PNG для depth/edge/chm | 1 канал, пригодный для обучения (`pixel / 255.0`); colormapped PNG теряет информацию |
| CHMv2 в FP32 | FP16 вызывает NaN (обнаружено при валидации на реальных данных) |
| Per-image atomic save | Crash-safe: temp file + `os.replace()` |
| Async I/O (ThreadPoolExecutor) | GPU inference не блокируется записью на диск |
| BPE vocab встроен в src/nn/ | Файл `bpe_simple_vocab_16e6.txt.gz` в `sam3/assets/`, дефолтный путь вычисляется от `__file__` |
| 125 unit tests (6 test-файлов) | Полное покрытие: inference, I/O, pipeline, benchmark, config, dataset (все mocked) |

### 9.4. Веса моделей

Все веса хранятся в `in/weights/`:

```
in/weights/
├── models--depth-anything--DA3-LARGE-1.1/                    # HF cache (safetensors)
├── models--nvidia--segformer-b5-finetuned-ade-640-640/       # SegFormer fallback
├── sam3.1/sam3.1_multiplex.pt                                # 3.3 GB (SAM 3.1 checkpoint)
├── dinov3-chmv2/                                             # 1.3 GB (safetensors + config)
└── bpe_simple_vocab_16e6.txt.gz                              # CLIP BPE (опциональная копия)
```

> BPE vocab уже встроен в `src/nn/segearth_ov3/sam3/assets/`. Копия в `in/weights/` используется приоритетно, если существует.

---

## 10. Риски и митигации

| Риск | Вероятность | Митигация |
|:-----|:-----------|:----------|
| **R1:** SegEarth-OV3 не загрузится | Низкая (вендорирован) | Автоматический fallback на SegFormer-B5 |
| **R2:** ~2 дней на GPU (batched) | Средняя | Resume позволяет прерывать/продолжать; можно запускать по subset'ам |
| **R2a:** SegEarth-OV3 OOM при batch=8 | Низкая (RTX 4090, ~10 GB) | Автоматический fallback на per-image `predict_pil()` при ошибке `predict_pil_batch()` |
| **R3:** DA3 не загрузится | Низкая (вендорирован) | Fallback на DA V2 Large (transformers) |
| **R4:** Диск закончится (~228 GB только PNG) | Низкая | `save_vis = False` для экономии |
| **R5:** CHMv2 NaN в FP16 | **Решено** | Модель загружается в FP32 |
| **R6:** Segmentation palette неинтуитивная | **Решено** | Фиксированная палитра: red=building, gray=road, green=vegetation, blue=water |

---

## 11. Timeline и ресурсы

### 11.1. Фазы

| Фаза | Длительность | Статус |
|:-----|:-------------|:-------|
| **0. Setup** | -- | Выполнено: DA3, SegEarth-OV3, CHMv2 установлены, веса скачаны |
| **1. Smoke test (100 img)** | -- | Выполнено: 34.33 s, все стадии работают |
| **2. Валидация качества** | -- | Выполнено: CHMv2 FP16->FP32 fix, palette fix |
| **2a. Вендоринг моделей** | -- | Выполнено: SegEarth-OV3 и DA3 перенесены в src/nn/ |
| **3. Полный запуск (973K)** | ~2 дней (batched backbone) | В процессе |
| **4. Final validation** | 0.5 дня | -- |

### 11.2. Ресурсы

| Ресурс | Требование |
|:-------|:-----------|
| GPU | RTX 4090 (24 GB VRAM), peak ~10 GB (SegEarth batch=8, sequential) |
| RAM | >= 32 GB |
| SSD | >= 250 GB свободно (только PNG) |
| GPU-часы | ~46-51 ч (batched) / ~93 ч (per-image) |

---

## 12. Библиография

1. Wu et al., "UAV-GeoLoc: A Large-Vocabulary Dataset and Geometry-Transformed Method for UAV Geo-Localization," IEEE RA-L, 2025.
2. Yang et al., "Depth Anything V3: Recovering the Visual Space from Any Views," arXiv:2511.10647, 2025.
3. Li et al., "SegEarth-OV3: Exploring SAM 3.1 for Open-Vocabulary Semantic Segmentation in Remote Sensing Images," arXiv:2512.08730, 2025.
4. Ravi et al., "SAM 3.1: Segment Anything in Images and Videos," Meta AI, 2025.
5. Oquab et al., "DINOv3: Learning Robust Visual Features without Supervision," Meta AI, 2025.
6. Vapnik & Vashist, "A New Learning Paradigm: Learning Using Privileged Information," Neural Networks, 2009.
7. Xie et al., "SegFormer: Simple and Efficient Design for Semantic Segmentation with Transformers," NeurIPS, 2021.
8. Lin et al., "Sample4Geo: Hard Negative Sampling For Cross-View Geo-Localisation," ICCV, 2023.
