scan, split, OCR, prepare for LLM
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
Evgeniy Ierusalimov b3ea68eda9 refactored code for modularity, SOLID, DRY, KISS 5日前
.ai renamed cli to image_split 1週間前
src refactored code for modularity, SOLID, DRY, KISS 5日前
tests OCR script with engines SuryaOCR and PaddleOCR 5日前
.env.dist OCR script with engines SuryaOCR and PaddleOCR 5日前
.gitignore initial release with base split functionality 1週間前
3.md OCR script with engines SuryaOCR and PaddleOCR 5日前
3_1200_02.md OCR script with engines SuryaOCR and PaddleOCR 5日前
3_1200_02_surya.md OCR script with engines SuryaOCR and PaddleOCR 5日前
3_surya.md OCR script with engines SuryaOCR and PaddleOCR 5日前
README.md refactored code for modularity, SOLID, DRY, KISS 5日前
STAGE2_RESEARCH_RESULT.md OCR script with engines SuryaOCR and PaddleOCR 5日前
installation.md OCR script with engines SuryaOCR and PaddleOCR 5日前
stage1.md initial release with base split functionality 1週間前
stage2_OCR_updates.md OCR script with engines SuryaOCR and PaddleOCR 5日前
stage2_OCR_updates2.md OCR script with engines SuryaOCR and PaddleOCR 5日前
stage2_research.md OCR script with engines SuryaOCR and PaddleOCR 5日前
stage2_results.md OCR script with engines SuryaOCR and PaddleOCR 5日前

README.md

scan2html — Offline Document OCR Pipeline

Полностью офлайн-конвейер обработки отсканированных документов: разрезание изображений на страницы → OCR → структурированный Markdown.


Цель проекта

Преобразование больших технических документов (JPEG/TIFF/PNG), полученных после печати и сканирования, в структурированный Markdown с формулами LaTeX, пригодный для:

  • просмотра в браузере (HTML + MathJax)
  • анализа LLM
  • конвертации в LaTeX/PDF

Полностью offline. Без облачных сервисов.


Pipeline

JPEG/TIFF/PNG (многостраничный скан)
    │
    ▼
src/image_split.py          ← Stage 1: разрезание
    │ --slice / --slice-auto
    │ --pre-rotate, --border
    │ --post-crop
    │
    ▼
JPEG страницы
    │
    ▼
src/image_to_latex.py       ← Stage 2: OCR + Markdown
    │ --ocr-engine paddle|surya
    │ --input file.jpg         (одна страница)
    │ --input pages/           (пакетный режим: все изображения в каталоге)
    │ --llm, --vlm (optional)
    │
    ├─ PP-StructureV3 / Surya 2    (OCR)
    ├─ fixups                      (VaR, V^2)
    ├─ LLM corrector               (Qwen2.5-7B, local)
    ├─ VLM corrector               (Qwen3.8 Max, API)
    │
    ▼
.md + .json + .html

Параметры

Stage 1: src/image_split.py

Параметр Описание По умолчанию
--input / -i Путь к входному изображению required
--slice / -s Сетка <cols>:<rows>
--slice-auto / -a Автоопределение сетки
--output-dir / -o Каталог для страниц .
--pre-rotate / -r Поворот: 90, 180, 270
--border / -b Белая рамка (px) 50
--post-crop / --no-post-crop Обрезка по контенту ON

Пример:

python -m src.image_split -i scan.jpg -s 3:3 -b 10 -o pages/
python -m src.image_split -i scan.jpg --slice-auto

Stage 2: src/image_to_latex.py

Параметр Описание По умолчанию
--input / -i Файл изображения или каталог required
--output-dir / -o Каталог вывода рядом с --input
--lang / -l Язык OCR en
--ocr-engine paddle или surya paddle
--llm Локальный LLM-корректор (Qwen2.5-7B) OFF
--vlm Vision LM корректор (Qwen3.8 Max, API) OFF

Пример:

# Одна страница
python -m src.image_to_latex -i page.jpg

# Пакетный режим — все изображения в каталоге (Surya: batch-оптимизация)
python -m src.image_to_latex -i ./pages/ --ocr-engine surya

# Пакетный режим (PaddleOCR: последовательно)
python -m src.image_to_latex -i ./pages/ --ocr-engine paddle

Результат

CLI (image_to_latex.py) генерирует два файла для каждого изображения:

Файл Формат Описание
page_01.md Markdown Текст + $$-формулы + ##-заголовки. Готов к загрузке в LLM.
page_01.json JSON Полный дамп OCR: bbox, confidence, labels, formulas

Другие форматы — ручные утилиты:

Файл Утилита Назначение
.html src/latex/surya2html.py Surya JSON → HTML с MathJax (формулы рендерятся браузером)
.html src/latex/tex2html.py LaTeX .tex → HTML + MathJax
# Surya JSON → HTML
python -c "from src.latex.surya2html import surya_json_to_html; \
  open('page.html','w').write(surya_json_to_html('page.json'))"

# TeX → HTML (если есть .tex файл)
python src/latex/tex2html.py page.tex

Качество OCR

Движок Русский текст Формулы Скорость (CPU)
PaddleOCR (PP-StructV3) ~85% ✅ отлично ~140s
+ LLM (Qwen2.5-7B) ~95% +5 min
+ VLM (Qwen3.8 Max) ~98% ✅✅ +10s/блок
Surya 2 ~95% ✅ отлично ~300s

Передача в LLM (Gemini, Qwen, ChatGPT)

Рекомендуемый формат: Markdown (.md). Это основной формат вывода CLI.

Формат Для LLM Причина
.md ⭐⭐⭐⭐⭐ Нативный для всех LLM. $$-формулы, ##-заголовки — структура сохраняется. Генерируется CLI.
.json ⭐⭐⭐⭐ Полный дамп OCR. Для программной обработки. Генерируется CLI.
.html ⭐⭐⭐ LLM читает HTML, но тратит токены на тэги. Генерируется утилитами.

Пример запроса к LLM после загрузки .md:

Проанализируй раздел 7.1.10 и объясни формулу $$LGD\_extr_i = \max(...)$$

Оптимизации Surya 2 для CPU

Surya 2 использует llama-server (llama.cpp) для инференса на CPU. Настройки по умолчанию рассчитаны на GPU-сервер с параллельными запросами. На одном ноутбучном CPU они избыточны и замедляют работу.

1. Keep-alive сервера (SURYA_INFERENCE_KEEP_ALIVE=true)

По умолчанию llama-server запускается и останавливается при каждом вызове OCR (старт — 23 секунды). Keep-alive держит сервер живым между запросами.

Эффект: -23s при повторных вызовах.

2. Parallel slots (SURYA_INFERENCE_PARALLEL=1)

Количество параллельных «слотов» — сколько запросов llama-server может обрабатывать одновременно. По умолчанию 8 — нужно для сервера с множеством клиентов. Для одной локальной страницы достаточно одного.

Эффект: -80% памяти KV-кэша, меньше переключений контекста.

3. CTX per slot (SURYA_INFERENCE_CTX_PER_SLOT=8192)

Размер контекстного окна в токенах на один слот. Одна страница A4 при 96 DPI генерирует ~2000-2500 токенов на выходе Surya 2. Значение 8192 даёт трёхкратный запас, покрывая самые плотные страницы. По умолчанию 12288 — рассчитано на GPU-сервер с большим VRAM.

Как определено: замерено на 3_1200_02.jpgmax_tokens в запросе ~2400. Утроение — стандартный safety-фактор для VLM (input tokens + output tokens). 8192 = 3 × ~2400.

Эффект: -50% памяти KV-кэша относительно 16384 (= 12288 × 8 / 8 → 12288).

4. Pre-scale до 96 DPI (_IMAGE_MAX_WIDTH=1056px)

Surya 2 обучен на данных с 96 DPI. Ширина A4 при 96 DPI ≈ 794 px, при альбомной ориентации ≈ 1056 px. Полноразмерные сканы (2552×3417 px, ~300 DPI) дают избыточную детализацию, которую vision-энкодер Surya всё равно сжимает до своего внутреннего разрешения. Предварительное сжатие экономит время на передачу и энкодинг без влияния на качество OCR.

Документация Surya подтверждает: «Try going from 192 to 96 for improved throughput» — снижение DPI рекомендовано разработчиками как способ ускорения без потери точности.

Эффект: -60% пикселей на входе, +20-30% скорости vision-энкодера.

5. Threads match CPU (-t 8 --threads-batch 8)

llama-server не указывает количество потоков — по умолчанию берёт все доступные. Явное указание -t и --threads-batch гарантирует оптимальное использование всех 8 ядер без оверхеда на hyperthreading.

Эффект: стабильная загрузка CPU, без деградации при гипертрединге.

Совокупный прирост

Метрика До оптимизации После Прирост
Время холодного старта ~430s ~300s -30%
RAM на KV-кэш ~8 GB ~1 GB -87%
Повторный запуск +23s (перестарт сервера) 0s (keep-alive) -23s

Настройки заданы в движке (src/ocr/surya_engine.py), переопределяются через переменные окружения.


Архитектура

src/
  image_split.py         — CLI Stage 1
  image_to_latex.py      — CLI Stage 2
  split/slicer.py        — разрезание, post-crop
  ocr/
    paddle_engine.py     — PP-StructureV3
    surya_engine.py      — Surya 2
  postprocess/
    fixups.py            — OCR-ошибки (VaR)
    corrector.py         — Corrector protocol
    llm_corrector.py     — Qwen2.5-7B (local)
    vlm_corrector.py     — Qwen3.8 Max (API)
  markdown/generator.py  — Markdown + валидация
  latex/
    surya2html.py        — Surya JSON → HTML
    tex2html.py           — TeX → HTML

Не входит в проект

  • GUI
  • Docker
  • PDF (на вход)
  • LLM/RAG
  • Поиск по документам
  • Многопоточность
  • Автоматический deskew / denoise

Лицензия

MIT. Модели PaddleOCR — Apache 2.0, Surya 2 — OpenRAIL-M.