# Анализ и оптимизация пайплайна depth_edges_annotate_worlduav

## Обзор

Пайплайн для аннотации датасета World-UAV: четыре стадии (depth, edges, segmentation, chmv2), конфигурация через Gin, fallback-модели, атомарное сохранение, resume-логика. Нейросетевые модели (SegEarth-OV-3, Depth-Anything-3) вендорированы в `src/nn/`.

---

## 1. Инференс GPU -- самые крупные выигрыши

### 1.1 `torch.compile()` для моделей

В `models.py` после загрузки модели можно обернуть в `torch.compile(model, mode="reduce-overhead")`. Для DepthAnything и SegFormer это может дать **20-40% ускорения** на repeated batches за счет fusion ядер и устранения Python overhead. Особенно эффективно при большом количестве батчей.

### 1.2 AMP через `torch.autocast` вместо ручного `.half()`

Сейчас в `inference.py:79` и `inference.py:203` делается ручной каст `x = x.half()` при условии `model.parameters().dtype == torch.float16`. Это неоптимально:

- `torch.autocast("cuda", dtype=torch.float16)` автоматически выберет FP16 для matmul/conv, но оставит FP32 для нормализации и softmax -- меньше потерь точности и обычно быстрее
- Убирает необходимость в ручных проверках dtype

### 1.3 DA3: batch-инференс вместо поштучного цикла

`inference.py:59-74` -- DA3 обрабатывается через `model.inference()` API, который внутри делает preprocess + forward + postprocess. Для максимальной производительности можно вызывать низкоуровневый `model.forward()` напрямую (весь батч тензором на GPU), минуя конвертации tensor -> numpy -> PIL -> обратно.

### 1.4 SegEarth-OV3: batched backbone (реализовано в v3.2)

`predict_pil_batch()` батчит backbone SAM 3.1 (~80% времени) на до 8 изображений. Grounding decoder остается per-image. Дальнейшие оптимизации:

- `torch.cuda.Stream` для overlap compute/data-transfer
- Увеличение max_batch если VRAM позволяет (текущий лимит 8 hardcoded)

---

## 2. Data Pipeline -- ускорение загрузки

### 2.1 `persistent_workers=True` (реализовано)

DataLoader создается с `persistent_workers=True` при `num_workers > 0`. Воркеры не пересоздаются между батчами.

### 2.2 `prefetch_factor` (реализовано)

`prefetch_factor=4` для лучшего overlap загрузки с инференсом.

### 2.3 Decode на GPU через `torchvision.io` или `nvidia-dali`

`dataset.py` -- `Image.open().convert("RGB")` + `transforms.Resize()` работает на CPU через PIL. Для больших датасетов это bottleneck. Варианты:

- `torchvision.io.decode_image` + `torchvision.transforms.v2` -- resize на GPU
- NVIDIA DALI pipeline -- полный decode+resize+normalize на GPU, особенно выгоден при images > 10k

### 2.4 Предвычисление списка файлов

`dataset.py` -- `rglob("*")` обходит файловую систему при каждом запуске. Для больших датасетов (тысячи папок) это минуты. Можно кешировать список в `.file_cache.json` и обновлять только при изменении mtime корня.

---

## 3. I/O -- запись результатов

### 3.1 Асинхронная запись (реализовано)

`io_utils.py` -- `ThreadPoolExecutor` с 4 workers для неблокирующей записи файлов. GPU inference продолжается пока предыдущий батч пишется на диск.

### 3.2 Атомарная запись (реализовано)

Temp file + `os.replace()` для crash-safety. Resume-логика корректно обрабатывает прерванные записи.

### 3.3 Визуализации -- отложить или отключить

`save_vis=False` отключает PNG-визуализации. Для ускорения можно генерировать визуализации отдельным скриптом после всего инференса.

### 3.4 Pre-create output dirs (реализовано)

Все output_dir создаются одним проходом до начала обработки (`run_pipeline()` в `main.py:333-339`), а не per-image.

---

## 4. Edges стадия

### 4.1 Batched Sobel (реализовано)

Edges обрабатываются батчами по 32 изображения. `compute_edges_from_depth` поддерживает `[B, 1, H, W]`.

### 4.2 Sobel-ядра как module-level константы (реализовано)

`_SOBEL_X` и `_SOBEL_Y` -- module-level константы в `inference.py`.

### 4.3 Edges как побочный продукт depth

Если не нужен resume между стадиями -- считать edges прямо в `run_depth_stage` из только что вычисленного depth, не сохраняя и не загружая `.npy`. Текущая архитектура предпочитает гранулярный resume.

---

## 5. Resume и discovery

### 5.1 Completion manifest вместо per-file проверок

`filter_completed()` -- для каждого изображения вызывается `Path.exists()` (syscall). При 100k изображений x 4 стадии = 400k stat-вызовов. Альтернатива:

- `completed.json` / SQLite per stage
- Проверка по set-lookup вместо filesystem

### 5.2 `filter_completed` -- per-stage

Каждая стадия фильтрует отдельно. Можно за один проход собрать статусы всех стадий.

---

## 6. Память и типы данных

### 6.1 FP32 для CHMv2 (вынужденная мера)

CHMv2 (DINOv3 DPT head) выдает NaN в FP16. Всегда FP32 -- это увеличивает VRAM на ~0.65 GB vs FP16, но гарантирует корректность.

### 6.2 uint8 для сегментации (реализовано)

`infer_segmentation_batch` возвращает `uint8` для class IDs. При 5 классах это оптимально.

### 6.3 float16 для .npy (реализовано)

Depth, edges, CHM сохраняются в `.npy` как `float16` (если `save_npy=True`).

---

## 7. Вендорированные пакеты (src/nn/)

### 7.1 Текущая архитектура

SegEarth-OV-3 и Depth-Anything-3 **встроены в проект** как вендорированные пакеты в `src/nn/`. При `import src.nn` автоматически регистрируются пути в `sys.path`:
- `src/nn/` -- для `depth_anything_3.*`
- `src/nn/segearth_ov3/` -- для `sam3.*` и `segearthov3_segmentor`

**Преимущества:**
- Нет зависимости от внешних репозиториев
- Воспроизводимость -- фиксированная версия кода
- Нет конфликтов с системными пакетами

**Ограничения:**
- Обновление до новой версии модели требует ручного копирования
- Дублирование кода (~6.5 MB) -- допустимо для проекта с ~200 GB данных

### 7.2 BPE vocab

Файл `bpe_simple_vocab_16e6.txt.gz` встроен в `src/nn/segearth_ov3/sam3/assets/`. Дефолтный путь в `segearthov3_segmentor.py` вычисляется через `os.path.dirname(__file__)`. Копия в `in/weights/` используется приоритетно (если существует) для совместимости с существующими инсталляциями.

---

## Приоритет по соотношению усилие/выигрыш

| # | Оптимизация | Усилие | Выигрыш | Статус |
|---|-------------|--------|---------|--------|
| 1 | Async I/O через ThreadPool | Низкое | **Высокий** (overlap GPU/disk) | **Реализовано** |
| 2 | Batched backbone SegEarth-OV3 | Среднее | **Высокий** (~2.5-3x segm) | **Реализовано** |
| 3 | Pre-create output dirs | Минимальное | Средний | **Реализовано** |
| 4 | Batched Sobel edges | Низкое | Средний | **Реализовано** |
| 5 | dtype: uint8 для seg, float16 для npy | Низкое | Средний | **Реализовано** |
| 6 | `persistent_workers` + `prefetch_factor` | Минимальное | Низкий-Средний | **Реализовано** |
| 7 | Вендоринг моделей (src/nn/) | Среднее | Средний (reliability) | **Реализовано** |
| 8 | `torch.compile()` | Низкое | Средний (20-40%) | Не реализовано |
| 9 | `torch.autocast` вместо `.half()` | Низкое | Низкий-Средний | Не реализовано |
| 10 | DA3 прямой forward вместо `inference()` | Среднее | Средний | Не реализовано |
| 11 | Completion manifest вместо per-file exists | Среднее | Средний (при >100k img) | Не реализовано |
| 12 | GPU decode (DALI / torchvision.io) | Высокое | Средний | Не реализовано |
