|
|
преди 4 дни | |
|---|---|---|
| .ai | преди 5 дни | |
| src | преди 4 дни | |
| tests | преди 5 дни | |
| .env.dist | преди 5 дни | |
| .gitignore | преди 1 седмица | |
| 3.md | преди 5 дни | |
| 3_1200_02.md | преди 5 дни | |
| README.md | преди 4 дни | |
| STAGE2_RESEARCH_RESULT.md | преди 5 дни | |
| installation.md | преди 5 дни | |
| requirements.txt | преди 5 дни | |
| stage1.md | преди 1 седмица | |
| stage2_OCR_updates.md | преди 5 дни | |
| stage2_OCR_updates2.md | преди 5 дни | |
| stage2_research.md | преди 5 дни | |
| stage2_results.md | преди 4 дни |
Полностью офлайн-конвейер обработки отсканированных документов: разрезание изображений на страницы → OCR → структурированный Markdown.
Преобразование больших технических документов (JPEG/TIFF/PNG), полученных после печати и сканирования, в структурированный Markdown с формулами LaTeX, пригодный для:
Полностью offline. Без облачных сервисов.
JPEG/TIFF/PNG (многостраничный скан)
│
▼
src/image_prepare.py ← Stage 1: подготовка (разрезание, обрезка)
│ --slice / --slice-auto
│ --pre-rotate, --border
│ --post-crop
│
▼
JPEG страницы
│
▼
src/image_ocr.py ← Stage 2: OCR + Markdown
│ --ocr-engine paddle|surya
│ --input file.jpg (одна страница)
│ --input pages/ (пакетный режим: все изображения в каталоге)
│ --llm, --vlm, --force-ocr
│ --pause 5 (по умолчанию)
│ --result-one-document (по умолчанию ON, объединить в один .md)
│
├─ PP-StructureV3 / Surya 2 (OCR)
├─ fixups (VaR, V^2)
├─ LLM corrector (Qwen2.5-7B, local)
├─ VLM corrector (Qwen3.8 Max, API)
│
▼
.md + .json + .html
src/image_prepare.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_prepare -i scan.jpg -s 3:3 -b 10 -o pages/
python -m src.image_prepare -i scan.jpg --slice-auto
--help Usage: python -m src.image_prepare [OPTIONS]
Подготовить отсканированное изображение: разрезать по сетке, обрезать,
добавить рамку.
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --input -i <file> Путь к входному │
│ изображению (JPEG, │
│ PNG, TIFF) │
│ --slice -s <str> Размер сетки в │
│ формате │
│ <колонки>:<строки>, │
│ например 3:2 │
│ --slice-auto -a Автоматически │
│ определить размер │
│ сетки по содержимому │
│ изображения │
│ --output-dir -o <directory> Каталог для │
│ сохранения │
│ результатов (по │
│ умолчанию — текущий) │
│ [default: .] │
│ --pre-rotate -r <int> Поворот изображения │
│ перед разрезанием: │
│ 90, 180 или 270 │
│ --border -b <int range> [x>=0] Отступ в пикселях │
│ (белая рамка / │
│ отступ при обрезке │
│ содержимого, по │
│ умолчанию 50) │
│ [default: 50] │
│ --post-crop -c --no-post-crop Обрезать каждую │
│ страницу по границам │
│ полезного │
│ содержимого │
│ (включено по │
│ умолчанию) │
│ [default: post-crop] │
│ --help Show this message │
│ and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
src/image_ocr.py| Параметр | Описание | По умолчанию |
|---|---|---|
--input / -i |
Файл изображения или каталог | required |
--output-dir / -o |
Каталог вывода | рядом с --input |
--ocr-engine |
Обязательный: paddle, surya или external-qwen3 |
— |
--fix-by-llm |
Исправить ошибки локальным LLM (Qwen2.5-7B) | OFF |
--fix-by-external-qwen3 |
Исправить ошибки VLM API (Qwen3.8 Max) | OFF |
--pause |
Пауза между изображениями (сек) | 5 |
--no-result-one-document |
Не объединять .md в один документ |
— |
--no-post-crop |
Не обрезать по контенту (Stage 1) | — |
Пример:
# Одна страница — PaddleOCR
python -m src.image_ocr -i page.jpg --ocr-engine paddle
# Одна страница — Surya 2
python -m src.image_ocr -i page.jpg --ocr-engine surya
# Пакетный режим
python -m src.image_ocr -i ./pages/ --ocr-engine surya
# Внешняя VLM как OCR-движок (Qwen3.8 Max)
python -m src.image_ocr -i page.jpg --ocr-engine external-qwen3
# Без объединения в один документ
python -m src.image_ocr -i ./pages/ --ocr-engine surya --no-result-one-document
# С LLM-корректором
python -m src.image_ocr -i page.jpg --ocr-engine paddle --fix-by-llm
--help Usage: python -m src.image_ocr [OPTIONS]
OCR → PostProcess → Markdown. Принимает файл или каталог изображений.
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ * --input -i <path> Путь к входному │
│ изображению или │
│ каталогу (JPEG, PNG, │
│ TIFF) │
│ [required] │
│ * --ocr-engine <str> OCR-движок: paddle │
│ (PP-StructureV3), │
│ surya (Surya 2 VLM) │
│ или external-qwen3 │
│ (Qwen3.8 Max API) │
│ [required] │
│ --output-dir -o <directory> Каталог для │
│ сохранения │
│ результатов (по │
│ умолчанию — рядом с │
│ входным файлом) │
│ --fix-by-llm Исправить OCR-ошибки │
│ локальным LLM │
│ (Qwen2.5-7B). Не │
│ работает с │
│ --ocr-engine │
│ external-qwen3 │
│ --fix-by-external-q… Исправить OCR-ошибки │
│ внешней VLM (Qwen3.8 │
│ Max, API). Не │
│ работает с │
│ --ocr-engine │
│ external-qwen3 │
│ --no-result-one-doc… Не объединять .md │
│ результаты в один │
│ документ │
│ [default: True] │
│ --pause <int range> Пауза между │
│ [0<=x<=600] изображениями в │
│ секундах (по │
│ умолчанию 5) │
│ [default: 5] │
│ --help Show this message and │
│ exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Для отладки зависаний и падений — файл с полным логом всех этапов обработки.
В .env добавить:
OCR_LOG_FILE=image_ocr.log
Формат вывода: ЧЧ:ММ:СС [LEVEL] сообщение. Файл пишется рядом со скриптом или по указанному пути. Уровень DEBUG показывает: начало/конец OCR, сохранение JSON/MD, вызов корректоров.
.env)Скопируй шаблон и заполни своими ключами:
cp .env.dist .env
# Отредактируй .env — вставь свои API-ключи
| Переменная | Назначение | Где взять |
|---|---|---|
OPENCODE_API_KEY |
VLM-корректор через OpenCode Go | opencode.ai/auth → скопировать ключ |
HF_TOKEN |
Скачивание моделей с HuggingFace (Surya 2) | huggingface.co/settings/tokens (read-only) |
OCR_LOG_FILE |
Путь к debug-логу (необязательно) | Например: image_ocr.log |
.env уже добавлен в .gitignore — не коммитится.
CLI (image_ocr.py) генерирует два файла для каждого изображения:
| Файл | Формат | Описание |
|---|---|---|
page_01.md |
Markdown | Текст + $$-формулы + ##-заголовки. Готов к загрузке в LLM. |
page_01.json |
JSON | Полный дамп OCR: bbox, confidence, labels, formulas |
При пакетной обработке (каталог на входе) — дополнительно:
| Файл | Описание |
|---|---|
<dirname>.md |
Объединённый многостраничный Markdown (по умолчанию) |
<dirname>.tex |
LaTeX для всех страниц (через json_to_latex.py) |
Ручные утилиты:
| Файл | Утилита | Назначение |
|---|---|---|
.tex |
src/latex/json_to_latex.py |
JSON (Paddle / Surya / Qwen) → LaTeX + HTML (автодетект) |
# JSON → LaTeX (один файл, автоопределение формата)
python -m src.latex.json_to_latex page.json
# JSON → LaTeX + HTML (каталог — объединённый многостраничный)
python -m src.latex.json_to_latex cbr_en_2/
# Программный вызов
python -c "from src.latex.json_to_latex import json_to_latex; \
open('page.tex','w').write(json_to_latex('page.json'))"
python -c "from src.latex.json_to_latex import multi_json_to_latex; \
multi_json_to_latex('out/', 'out/document.tex')"
| Движок | Русский текст | Формулы | Скорость (CPU) |
|---|---|---|---|
| PaddleOCR (PP-StructV3) | ~85% | ✅ отлично | ~140s |
| + LLM (Qwen2.5-7B) | ~95% | ✅ | +5 min |
| + VLM (Qwen3.8 Max) | ~98% | ✅✅ | +10s/блок |
| Surya 2 | ~95% | ✅ отлично | ~300s |
Рекомендуемый формат: Markdown (.md). Это основной формат вывода CLI.
| Формат | Для LLM | Причина |
|---|---|---|
.md |
⭐⭐⭐⭐⭐ | Нативный для всех LLM. $$-формулы, ##-заголовки — структура сохраняется. Генерируется CLI. |
.json |
⭐⭐⭐⭐ | Полный дамп OCR. Для программной обработки. Генерируется CLI. |
.html |
⭐⭐⭐ | LLM читает HTML, но тратит токены на тэги. Генерируется утилитами. |
Пример запроса к LLM после загрузки .md:
Проанализируй раздел 7.1.10 и объясни формулу $$LGD\_extr_i = \max(...)$$
Surya 2 использует llama-server (llama.cpp) для инференса на CPU. Настройки по умолчанию рассчитаны на GPU-сервер с параллельными запросами. На одном ноутбучном CPU они избыточны и замедляют работу.
SURYA_INFERENCE_KEEP_ALIVE=true)По умолчанию llama-server запускается и останавливается при каждом вызове OCR (старт — 23 секунды). Keep-alive держит сервер живым между запросами.
Эффект: -23s при повторных вызовах.
SURYA_INFERENCE_PARALLEL=1)Количество параллельных «слотов» — сколько запросов llama-server может обрабатывать одновременно. По умолчанию 8 — нужно для сервера с множеством клиентов. Для одной локальной страницы достаточно одного.
Эффект: -80% памяти KV-кэша, меньше переключений контекста.
SURYA_INFERENCE_CTX_PER_SLOT=8192)Размер контекстного окна в токенах на один слот. Одна страница A4 при 96 DPI генерирует ~2000-2500 токенов на выходе Surya 2. Значение 8192 даёт трёхкратный запас, покрывая самые плотные страницы. По умолчанию 12288 — рассчитано на GPU-сервер с большим VRAM.
Как определено: замерено на 3_1200_02.jpg — max_tokens в запросе ~2400. Утроение — стандартный safety-фактор для VLM (input tokens + output tokens). 8192 = 3 × ~2400.
Эффект: -50% памяти KV-кэша относительно 16384 (= 12288 × 8 / 8 → 12288).
_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-энкодера.
-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_prepare.py — CLI Stage 1
image_ocr.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/
json_to_latex.py — Единый JSON→LaTeX+HTML (Paddle + Surya + Qwen)
MIT. Модели PaddleOCR — Apache 2.0, Surya 2 — OpenRAIL-M.