# Спецификация тензорных форматов

Ответы на ключевые вопросы о формате извлечённых данных для downstream использования в NADEZHDA (cross-view geolocation).

---

## 1. Формат: dense spatial maps [1, H, W]

Все 4 модальности — **dense spatial maps** с сохранением пространственной размерности. Не pooled descriptors, не feature maps промежуточных слоёв.

| Модальность | Key | Shape | Dtype | Диапазон | Тип данных |
|:---|:---|:---|:---|:---|:---|
| depth | `"depth"` | [1, H, W] | float16 | [0, 1] | Непрерывная relative depth |
| edge | `"edge"` | [1, H, W] | float16 | [0, 1] | Непрерывная Sobel magnitude |
| chm | `"chm"` | [1, H, W] | float16 | [0, 1] | Непрерывная relative canopy height |
| segm | `"segm"` | [1, H, W] | uint8 | [0, 16] | Дискретные class IDs (argmax) |

**Что это значит для downstream:**

- Depth, edge, chm — одноканальные continuous maps. Подаются напрямую в lightweight aux-encoder (Conv1x1 или shallow CNN), затем через FiLM injection в DINOv3 patch tokens.
- Segmentation — дискретная карта из 17 классов (argmax, не soft probabilities, не logits). Требует embedding layer (one-hot → Conv или `nn.Embedding(17, C)`) перед injection.
- Промежуточные feature maps моделей (backbone ViT tokens ~1024 dim) **не сохраняются**. Сохраняются только финальные predictions. Обоснование: backbone features трёх ViT-Large моделей сильно коррелируют с features основного DINOv3 в NADEZHDA — мало complementary информации. Ценность auxiliary модальностей именно в **другом представлении** (геометрия, границы, семантика), а не в дублировании ViT features.

---

## 2. Какие слои cached

### Depth (DA3-LARGE-1.1, 411M параметров)

- **Тип:** Relative depth, не метрическая (не Metric3D)
- **Модель:** Depth Anything 3 с DinoV2-ViTL backbone (24 блока, patch 14, embed 1024)
- **Что сохраняется:** Финальный output после DualDPT head — одноканальная карта глубины
- **Нормирование:** Per-frame min-max → [0, 1]. Абсолютные значения глубины (в метрах) **теряются** при нормализации
- **Что НЕ сохраняется:** Промежуточные features из layers [11, 15, 19, 23], DPT pyramid features (256-1024 ch), confidence maps

### Edges (Sobel из depth, CPU)

- **Тип:** Sobel magnitude — непрерывный gradient magnitude, не Canny binary и не learned edges
- **Вычисление:** `sqrt(dz_dx² + dz_dy²)` из depth map, ядра Sobel 3×3 нормализованные (/8.0)
- **Что сохраняется:** Финальная magnitude map после per-frame max normalization → [0, 1]
- **Что НЕ сохраняется:** Направление градиента (dx, dy компоненты по отдельности), ненормализованные значения

### Segmentation (SegEarth-OV3, SAM 3.1 backbone)

- **Тип:** Dense class ID map (argmax), не soft probabilities и не raw logits
- **Модель:** ViTDet-32L backbone (32 блока, patch 14, embed 1024) + mask decoder + text grounding
- **Что сохраняется:** Финальный argmax из 17-class logits → uint8 class IDs [0, 16]
- **Что НЕ сохраняется:**
  - Soft probabilities / logits (потеря информации о confidence и inter-class uncertainty)
  - Backbone features [B, 1024, H/14, W/14] из global attention blocks [7, 15, 23, 31]
  - Neck pyramid features [B, 256, H/14..H/1.75, W/14..W/1.75]
  - Pixel decoder features [B, 256, H, W]
  - Text embeddings от 17 промптов (вычисляются в runtime, кешируются только в GPU memory)
- **Post-processing:** Опционально применяются dark water fix и wetland reclassify **до** сохранения — результат уже post-processed

### CHM (DINOv3-ViTL16 CHMv2, 337M параметров)

- **Тип:** Relative canopy height, не абсолютная высота в метрах
- **Модель:** DINOv3-ViTL16 backbone (24 блока, patch 16, embed 1024) + DPT head
- **Что сохраняется:** Финальный output DPT head → per-frame min-max normalized [0, 1]
- **Что НЕ сохраняется:** Backbone features, DPT pyramid, абсолютные значения высоты
- **Ограничение:** Инференс **только FP32** (NaN в FP16), конвертация в float16 только при сохранении

---

## 3. Captions и text embeddings

**Не сохраняются.** Ни raw text, ни text embeddings.

- **SegEarth-OV3** использует text embeddings от 17 промптов (через VETextEncoder) для open-vocabulary grounding. Эти embeddings вычисляются в runtime из строковых промптов и кешируются **только в GPU memory** во время инференса. В SafeTensors файлы не попадают.
- **Raw text промптов** хранится в `scripts/seg_classes.py:UNIFIED_PROMPTS` — это исходный код, не часть выходных данных.
- Никакие caption/description для изображений не генерируются и не сохраняются.

**Следствия для downstream:**
- Если нужны text-conditioned fusion варианты (F0/F1/F2), text embeddings нужно вычислять заново из raw промптов в `seg_classes.py`. Это не проблема — промпты фиксированы (17 строк), embeddings вычисляются за доли секунды.
- Привязки к конкретному text encoder нет — можно использовать любой (CLIP, SigLIP, и т.д.) для вычисления embeddings из тех же промптов.

---

## 4. Нормирование

| Модальность | Нормирование | Per-sample | Диапазон | Исходные значения восстановимы? |
|:---|:---|:---:|:---|:---|
| depth | min-max: `(x - min) / (max - min + 1e-8)` | Да | [0, 1] | Нет — min/max теряются |
| edge | max-normalize: `x / max(x)` | Да | [0, 1] | Нет — max теряется |
| chm | min-max: `(x - min) / (max - min + 1e-8)` | Да | [0, 1] | Нет — min/max теряются |
| segm | Нет (class IDs) | N/A | {0, 1, ..., 16} | N/A |

### Влияние на FiLM heads initialization

**Depth, edge, chm (float16, [0, 1]):**
- Диапазон стабилен между изображениями (всегда [0, 1]), но **распределение внутри диапазона сильно варьируется**: пустыня — равномерное, город — бимодальное (здания vs земля), вода — сконцентрировано около одного значения.
- Рекомендуемый init: `gamma=1, beta=0` (identity FiLM) — безопасный старт, нормализация уже в [0, 1].
- Альтернатива: предварительно вычислить dataset-level mean/std по каждой модальности для BatchNorm перед FiLM.

**Segmentation (uint8, class IDs):**
- Дискретные значения [0, 16] — нельзя подавать как continuous input в FiLM напрямую.
- Варианты encoding перед FiLM injection:
  1. `nn.Embedding(17, C)` → lookup table → spatial [C, H, W] — компактно, learned
  2. `F.one_hot(segm, 17)` → Conv1x1 → [C, H, W] — явное, deterministic init возможен
- Для варианта 2 init: Xavier uniform для Conv1x1 weights, zero bias.

### Статистика входных тензоров (для калибровки)

Все три continuous модальности нормализованы per-frame в [0, 1], поэтому:
- **Global mean** по датасету ≈ 0.3–0.5 (зависит от модальности и датасета)
- **Global std** ≈ 0.2–0.35
- Для точной калибровки рекомендуется пройти по subset (1000-5000 изображений) и вычислить running mean/std

---

## Сводная таблица

| Свойство | Depth | Edge | Segm | CHM |
|:---|:---|:---|:---|:---|
| **SafeTensors key** | `"depth"` | `"edge"` | `"segm"` | `"chm"` |
| **Shape** | [1, H, W] | [1, H, W] | [1, H, W] | [1, H, W] |
| **Dtype** | float16 | float16 | uint8 | float16 |
| **Value range** | [0, 1] | [0, 1] | [0, 16] | [0, 1] |
| **Type** | Relative depth | Sobel magnitude | Class IDs (argmax) | Relative height |
| **Model** | DA3-LARGE-1.1 | Sobel from depth | SegEarth-OV3 | DINOv3 CHMv2 |
| **Backbone** | DinoV2-ViTL (24L) | — | ViTDet (32L) | DINOv3-ViTL (24L) |
| **Normalization** | Per-frame min-max | Per-frame max | None | Per-frame min-max |
| **Dense/Pooled** | Dense spatial | Dense spatial | Dense spatial | Dense spatial |
| **Features cached** | Final output only | Final output only | Final output only | Final output only |
| **Captions/embeddings** | — | — | Not saved | — |
