scan, split, OCR, prepare for LLM
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

stage2_results.md 12KB

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)
  • Русский текст: ⚠️ ~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 латинских языка, русского нет
  • Вывод: серверной русской модели в PaddleOCR не существует

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}$ — дисконтированная сумма возвратов, получаемых на t-ом периоде
  • ~10s/блок, Anthropic API через zen/go/v1/messages
  • Вывод: лучшее качество, но требует API-доступа

7. Docling (IBM Research)

Результат: ❌ не поддерживает PaddleOCR как OCR-backend.

  • Поддерживает: EasyOCR, Tesseract, RapidOCR, Nemotron
  • Генерирует HTML/Markdown из коробки
  • Вывод: несовместим с нашим пайплайном

8. PaddleOCR-VL (VLM-подход, ERNIE-4.5-0.3B)

Результат: модель загружена (1.79 GB), но ~15 мин/стр на CPU.

  • Одна VLM вместо ансамбля: layout → распознавание единой моделью
  • Теоретически решает проблему русского (языковая модель понимает контекст)
  • Вывод: правильный подход при GPU, нереализуем на CPU

9. Surya 2 (Datalab, 650M VLM)

Результат: ⭐ лучший русский OCR из коробки.

  • 95%+ точность русского текста без коррекции
  • Автоопределение inline-формул: <math>MRec_{it}</math>
  • ~430s/стр на CPU (vs 140s у PaddleOCR, но качество несравнимо)
  • Вывод Markdown с $$...$$ формулами
  • Вывод: лучший выбор при достаточных ресурсах

Итоговая архитектура

                        Входное изображение (JPEG/PNG/TIFF)
                                    │
                      ┌─────────────┴─────────────┐
                      │    src/image_split.py      │
                      │  --slice / --slice-auto    │
                      │  --pre-rotate, --border    │
                      │  --post-crop (default ON)  │
                      └─────────────┬─────────────┘
                                    │
                             JPEG страницы
                                    │
                      ┌─────────────┴─────────────┐
                      │  src/image_to_latex.py     │
                      │  --ocr-engine paddle/surya │
                      │  --llm, --vlm (optional)   │
                      └─────────────┬─────────────┘
                                    │
              ┌─────────────────────┼─────────────────────┐
              ▼                     ▼                     ▼
    ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
    │  src/ocr/        │  │ src/postprocess/ │  │ src/postprocess/ │
    │  paddle_engine   │  │ fixups.py        │  │ llm_corrector    │
    │  surya_engine    │  │ vlm_corrector    │  │ corrector.py     │
    └────────┬────────┘  └────────┬────────┘  └────────┬────────┘
             │                    │                     │
             └────────────────────┼─────────────────────┘
                                  │
                         ParsedBlock[]
                                  │
                    ┌─────────────┴─────────────┐
                    │  src/markdown/             │
                    │  generator.py              │
                    │  Валидация $, {}, \left   │
                    └─────────────┬─────────────┘
                                  │
              ┌───────────────────┼───────────────────┐
              ▼                   ▼                   ▼
         .json (raw)          .md              .tex + .html
     (PP-StructureV3      (Markdown)      (LaTeX + MathJax)
      полный дамп)

Модули

src/
  cli_utils.py           — run_main() общий для CLI
  image_split.py         — CLI разрезания (Stage 1)
  image_to_latex.py      — CLI OCR (Stage 2)
  split/
    slicer.py            — разрезание, поворот, рамка, post-crop
  ocr/
    paddle_engine.py     — PP-StructureV3 + кастом YAML (eslav)
    surya_engine.py      — Surya 2 VLM + llama.cpp
  postprocess/
    fixups.py            — VaR, V^2
    corrector.py         — Corrector Protocol + apply_corrector()
    llm_corrector.py     — Qwen2.5-7B локально (llama-cpp-python)
    vlm_corrector.py     — Qwen3.8 Max через OpenCode API (Anthropic)
  markdown/
    generator.py         — Markdown + валидация LaTeX
  latex/
    tex2html.py          — .tex → .html + MathJax

Модели (локально, ~4 GB)

~/.paddlex/official_models/          (PaddleOCR, ~2.5 GB)
  PP-DocLayout_plus-L                layout detection
  PP-OCRv5_server_det                text detection
  eslav_PP-OCRv5_mobile_rec          RU/EN recognition
  PP-OCRv5_server_rec                EN/CN recognition
  SLANeXt_wired                      table structure
  PP-FormulaNet_plus-L               formula recognition (728 MB)

~/.cache/llama_models/               (LLM, ~4.4 GB)
  Qwen2.5-7B-Instruct-Q4_K_M.gguf    локальный корректор

HF Hub (Surya 2, ~1.5 GB)
  datalab-to/surya-ocr-2-gguf        650M VLM

Сравнение движков

Параметр PaddleOCR (PP-StructV3) Surya 2
Архитектура Ансамбль 5 моделей Один VLM (650M)
Русский текст ~85% 95%+
Формулы PP-FormulaNet (отлично) VLM (отлично)
Таблицы PP-TableMagic + bbox HTML <table>
Inline-формулы Не детектируются <math> тэги
Скорость CPU ~140s ~430s
Размер моделей ~2.5 GB ~1.5 GB
LLM-коррекция Нужна для русского Не нужна
VLM-коррекция Нужна для сложных мест Не нужна

Ключевые цифры

Этап PaddleOCR Surya 2
Image split + post-crop 1-2s 1-2s
OCR 140s 430s
LLM-корректор (Qwen2.5-7B) ~20s/блок не нужен
VLM-корректор (Qwen3.8 Max) ~10s/блок не нужен
Markdown генерация <1s <1s
Итого на страницу ~5 min ~7 min

Неиспользованные подходы

  1. EasyOCR — WER 0.12 vs Paddle 0.056, хуже по метрикам
  2. Tesseract — нет поддержки GPU, хуже качество на русском
  3. RapidOCR — легче, но качество ниже PaddleOCR
  4. OmniParser — избыточен (YOLO + OCR), медленнее
  5. Docling — не поддерживает PaddleOCR как OCR-backend
  6. Fine-tune модели — требует GPU + размеченный датасет, не пробовали

Выводы

  1. PaddleOCR PP-StructureV3 — надёжный выбор для структуры документа и формул. Русский текст требует коррекции (LLM или VLM).

  2. Surya 2 — лучший русский OCR из коробки, авто inline-формулы. Медленнее на CPU, но качество несравнимо выше.

  3. Гибрид Surya 2 + PP-FormulaNet — теоретически лучший вариант (Surya для текста, Paddle для формул), не реализован.

  4. LLM/VLM коррекция — эффективна для PaddleOCR, не нужна для Surya 2.

  5. OpenCode Go API — практичный способ подключить мощную VLM без локального GPU.