# Stage 2 Results — OCR Pipeline
## Путь проекта
### Исходная точка
Единый скрипт `src/cli.py` → разрезание изображений на страницы (Stage 1). Нужен Stage 2 — распознавание текста и структуры документа, генерация LaTeX/HTML/Markdown для последующего анализа LLM.
---
## Опробованные варианты
### 1. PaddleOCR базовый (PP-OCRv5)
**Результат:** 66 текстовых блоков, русский текст — ~60% точности.
- `i-WHAeKC AOrOBOpa` вместо `і - индекс договора`
- `t - номер месяцаB дефолте` — латиница смешана с кириллицей
- Нет структуры документа (заголовки, формулы, таблицы слиты в сплошной текст)
- **Вывод:** только для английского, для русского требуется структура
### 2. PP-StructureV3 (ансамбль специализированных моделей)
**Результат:** 22 структурированных блока с layout-разметкой.
- Layout detection: ✅ `[number]`, `[formula]`, `[text]`, `[paragraph_title]`
- Display-формулы: ✅ `\mathrm{C Rec}_{\mathrm{it}}=\sum...`
- Таблицы: ✅ PP-TableMagic с bbox ячеек
- Русский текст: ⚠️ ~85% (eslav_PP-OCRv5_mobile_rec)
- **Вывод:** структура отлично, текст — терпимо
### 3. Замена на cyrillic_PP-OCRv5_mobile_rec
**Результат:** разница ~1-2%, несущественна.
- eslav: `LGD_ехtг;` (кириллица г)
- cyrillic: `LGD_ехtr;` (латиница r) — чуть лучше
- **Вывод:** обе мобильные модели, серверной русской не существует
### 4. PP-OCRv5_server_rec / PP-OCRv6
**Результат:** ❌ не поддерживают русский.
- Server model: только CN/JP/EN
- PP-OCRv6: 52 латинских языка
- **Вывод:** русский — только mobile-версии
### 5. Локальный LLM-корректор (Qwen2.5-7B GGUF)
**Результат:** +25% читаемости текста.
```
До: t - номер месяцаB дефолте, на который прогнозируются возвраты
После: т — номер месяца в диапазоне между текущим сроком в дефолте и горизонтом взыскания
```
- ~4 tok/sec на CPU, ~20s/блок
- **Вывод:** эффективен для пост-обработки
### 6. VLM-корректор (Qwen3.8 Max через OpenCode API)
**Результат:** полное восстановление текста + авто inline-формулы.
```
До: ΣДсе, - дисконтированная сумма возвратов
После: $\sum_t DCF_{i,t}$ — дисконтированная сумма возвратов
```
- ~10s/блок, Anthropic API `zen/go/v1/messages`
- **Вывод:** лучшее качество, требует API-доступа
### 7. Docling (IBM Research)
**Результат:** ❌ не поддерживает PaddleOCR как OCR-backend.
- Поддерживает: EasyOCR, Tesseract, RapidOCR, Nemotron
- **Вывод:** несовместим с нашим пайплайном
### 8. PaddleOCR-VL (ERNIE-4.5-0.3B)
**Результат:** модель загружена (1.79 GB), ~15 мин/стр на CPU.
- Одна VLM вместо ансамбля
- Теоретически решает проблему русского (LM понимает контекст)
- **Вывод:** правильный подход при GPU, нереализуем на CPU
### 9. Surya 2 (Datalab, 650M VLM) ⭐
**Результат:** лучший русский OCR из коробки — но медленно на CPU.
- 95%+ точность русского текста без коррекции
- Авто inline-формулы: `` → `$MRec_{it}$`
- ~300s/стр после CPU-оптимизаций (vs 140s PaddleOCR)
- **Слабость:** таблицы требуют много токенов → таймауты на CPU
- **Вывод:** лучший выбор по качеству, медленнее на CPU
### 10. VLM full-page (Qwen3.8 Max) для таблиц
**Результат:** 118s на страницу с таблицей, качество отличное.
- Таблица → pipe-Markdown: `| col1 | col2 |`
- Формулы → `$...$`
- Структура заголовков сохранена
- **Вывод:** для табличных страниц — лучший вариант
---
## Итоговая архитектура
```
Входное изображение (JPEG/PNG/TIFF)
│
▼
src/image_split.py ← Stage 1: разрезание
│ --slice / --slice-auto
│ --border, --post-crop
│
▼
src/image_ocr.py ← Stage 2: OCR + Markdown
│ --ocr-engine paddle|surya
│ --llm, --vlm
│ --force-ocr, --pause
│ --result-one-document (ON по умолчанию)
│
├─ PaddleEngine / SuryaEngine (OCR)
├─ fix_ocr_errors (VaR, V^2)
├─ LlmCorrector (Qwen2.5-7B, local)
├─ VlmCorrector (Qwen3.8 Max, API)
│
▼
.md + .json (per page) + merged.md
```
### По умолчанию включено
| Функция | Статус |
|---------|:------:|
| `--resume` (пропуск JSON) | ON |
| `--result-one-document` | ON |
| `--pause 5` | ON |
| `--post-crop` (Stage 1) | ON |
### Модули
```
src/
image_split.py — CLI Stage 1
image_ocr.py — CLI Stage 2
cli_utils.py — run_main()
env.py — загрузка .env
split/slicer.py — разрезание, post-crop, border
ocr/
paddle_engine.py — PP-StructureV3 + кастом YAML (eslav)
surya_engine.py — Surya 2 + CPU-оптимизации + health_check
postprocess/
fixups.py — VaR, V^2
corrector.py — Corrector Protocol
llm_corrector.py — Qwen2.5-7B локально
vlm_corrector.py — Qwen3.8 Max API (Anthropic)
markdown/generator.py — Markdown + валидация LaTeX
latex/
json_to_latex.py — Единый JSON→LaTeX (Paddle + Surya)
surya2html.py — Surya JSON → HTML + MathJax
tex2html.py — TeX → HTML
```
---
## Оптимизации Surya 2 для CPU
| Параметр | До | После | Эффект |
|----------|:---:|:---:|--------|
| KEEP_ALIVE | off | on | -23s повторный старт |
| CTX per slot | 12288 | 8192 | -30% RAM |
| Parallel slots | 8 | 1 | -80% слотов |
| Pre-scale | 2552px | 1056px | -60% пикселей |
| Threads | default | `-t 8` | стабильно |
| **Итого** | **430s** | **300s** | **-30%** |
### Health-check + retry
```
llama-server не отвечает → logger.warning → перезапуск SuryaEngine
→ повторный health_check → OK / RuntimeError с инструкцией
```
---
## Сравнение движков
| Параметр | PaddleOCR | Surya 2 | VLM Qwen3.8 |
|----------|:---:|:---:|:---:|
| Архитектура | Ансамбль 5 моделей | Один VLM (650M) | Cloud VLM |
| Русский текст | ~85% | 95%+ | 98%+ |
| Формулы | PP-FormulaNet | VLM (отлично) | VLM (отлично) |
| Таблицы | PP-TableMagic + bbox | ❌ CPU timeout | ✅ Pipe-Markdown |
| Inline-формулы | Нет | `