|
|
hace 2 semanas | |
|---|---|---|
| .ai | hace 3 semanas | |
| src | hace 2 semanas | |
| tests | hace 2 semanas | |
| .env.dist | hace 3 semanas | |
| .gitignore | hace 2 semanas | |
| 3.md | hace 3 semanas | |
| 3_1200_02.md | hace 3 semanas | |
| README.md | hace 2 semanas | |
| STAGE2_RESEARCH_RESULT.md | hace 3 semanas | |
| installation.md | hace 3 semanas | |
| requirements.txt | hace 3 semanas | |
| stage1.md | hace 3 semanas | |
| stage2_OCR_updates.md | hace 3 semanas | |
| stage2_OCR_updates2.md | hace 3 semanas | |
| stage2_research.md | hace 3 semanas | |
| stage2_results.md | hace 2 semanas |
Полностью офлайн-конвейер обработки отсканированных документов: разрезание изображений на страницы → OCR → структурированный Markdown.
Преобразование больших технических документов (JPEG/TIFF/PNG), полученных после печати и сканирования, в структурированный Markdown с формулами LaTeX, пригодный для:
Полностью offline. Без облачных сервисов.
JPEG/TIFF/PNG (многостраничный скан)
│
▼
src/image_prepare.py ← Stage 1: подготовка (разрезание, unwarp, denoise)
│ --slice / --slice-auto
│ --pre-rotate, --border
│ --unwarp, --denoise, --post-crop
│
▼
JPEG/PNG страницы
│
▼
src/image_ocr.py ← Stage 2: OCR → сырой ответ
│ --ocr-engine paddle|surya|external-qwen3
│ --main-lang ru|en
│ --fix-by-external-qwen3
│
▼
.raw (сырой ответ VLM, неизменяемый)
│
▼
src/generate_result.py ← Stage 3: постпроцессинг
│ фильтрация номеров страниц
│ восстановление таблиц между страницами
│ очистка формул (\text, \_)
│
▼
.json + .md + объединённый .md + .tex + .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) | 10 |
--unwarp / -u |
Выпрямить перспективные искажения (Canny → 4-угольный контур → warp) | OFF |
--denoise |
Убрать шум сканера (Non-Local Means) | OFF |
--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
python -m src.image_prepare -i photo.jpg --unwarp -s 2:2 -b 10
python -m src.image_prepare -i old_scan.jpg --denoise -s 2:2 -b 10
--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] Отступ в пикселях │
│ (белая рамка / │
│ отступ при обрезке │
│ содержимого, по │
│ умолчанию 10) │
│ [default: 10] │
│ --unwarp -u Выпрямить │
│ перспективные │
│ искажения перед │
│ разрезанием │
│ --denoise Убрать шум сканера │
│ (Non-Local Means) │
│ перед разрезанием │
│ --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 |
— |
--main-lang |
Основной язык: ru (eslav rec) или en (PP-OCRv6 rec) |
ru |
--fix-by-external-qwen3 |
Исправить ошибки VLM API (Qwen3.8 Max) | OFF |
--pause |
Пауза между изображениями (сек) | 5 |
Выход: .raw-файл с сырым ответом движка (write-once, не изменяется при постпроцессинге).
Пример:
# Одна страница — PaddleOCR
python -m src.image_ocr -i page.jpg --ocr-engine paddle
# Внешняя VLM как OCR-движок (Qwen3.8 Max) — лучший для русского
python -m src.image_ocr -i pages/ --ocr-engine external-qwen3
# Английский документ (PP-OCRv6 rec)
python -m src.image_ocr -i page.jpg --ocr-engine paddle --main-lang en
# Пакетный режим — Surya 2 (batch)
python -m src.image_ocr -i ./pages/ --ocr-engine surya
src/generate_result.pyПостпроцессинг .raw → .json + .md + .tex + .html.
| Параметр | Описание | По умолчанию |
|---|---|---|
--input / -i |
Каталог с .raw-файлами |
required |
--skip-latex |
Не генерировать LaTeX и HTML | OFF |
Что делает:
42, [73] в конце страницы удаляются\text{score\_beh} → score_beh для читаемости LLM.md + .tex + .html (MathJax)python -m src.generate_result -i cbr2/
--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-external-q… Исправить OCR-ошибки │
│ внешней VLM (Qwen3.8 │
│ Max, API). Не │
│ работает с │
│ --ocr-engine │
│ external-qwen3 │
│ --main-lang <str> Основной язык: ru │
│ (eslav_PP-OCRv5_mobi… │
│ или en │
│ (PP-OCRv6_medium_rec) │
│ [default: ru] │
│ --pause <int range> Пауза между │
│ [0<=x<=600] изображениями в │
│ секундах (по │
│ умолчанию 5) │
│ [default: 5] │
│ --help Show this message and │
│ exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
src/generate_result.py — --help--help Usage: python -m src.generate_result [OPTIONS]
Постобработка результатов OCR: фильтрация, таблицы, объединение, LaTeX+HTML.
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ * --input -i <path> Путь к каталогу с .raw-файлами │
│ (результаты OCR) │
│ [required] │
│ --skip-latex Не генерировать LaTeX и HTML │
│ --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 — не коммитится.
image_ocr.py) — сырой ответ| Файл | Формат | Описание |
|---|---|---|
page_01.raw |
JSON | Сырой ответ OCR-движка. Неизменяемый — пишется один раз, все постпроцессинги читают его |
generate_result.py) — итоговые файлы| Файл | Формат | Описание |
|---|---|---|
page_01.json |
JSON | Очищенный результат (фильтр номеров страниц, объединённые таблицы) |
page_01.md |
Markdown | Текст + $$-формулы + ##-заголовки. Готов к загрузке в LLM. |
<dirname>.md |
Markdown | Объединённый многостраничный документ |
<dirname>.tex |
LaTeX | LaTeX для всех страниц |
<dirname>.html |
HTML | HTML + MathJax для просмотра в браузере |
# Полный цикл
python -m src.image_prepare -i scan.jpg -s 3:3 -o pages/
python -m src.image_ocr -i pages/ --ocr-engine external-qwen3
python -m src.generate_result -i pages/
| Движок | Русский текст | Английский | Формулы | Скорость |
|---|---|---|---|---|
PaddleOCR --main-lang ru (eslav rec + v6 det) |
~85-90% | ~60% | ✅ PP-FormulaNet | ~100-160s CPU |
PaddleOCR --main-lang en (v6 rec) |
— | ~75-80% | ✅ PP-FormulaNet | ~100-160s CPU |
| Surya 2 | ~95% | ~95% | ✅ VLM | ~300s CPU |
| External VLM (Qwen3.8 Max) | ~98% | ~98% | ✅✅ | ~60-120s API |
Детектор: PP-OCRv6_medium_det (RepLKFPN) — +4.6% vs v5_server.
Рекомендуемый формат: 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/
models.py — ParsedBlock, OcrPageResult
image_utils.py — calc_scale_dims
env.py — загрузка .env
cli_utils.py — run_main()
image_prepare.py — CLI Stage 1 (разрезание, unwarp, denoise)
image_ocr.py — CLI Stage 2 (OCR → .raw)
generate_result.py — CLI Stage 3 (постпроцессинг → .json/.md/.tex/.html)
split/slicer.py — разрезание, post-crop, border, unwarp, denoise
ocr/
paddle_engine.py — PP-StructureV3 + v6-det + ru/en rec
surya_engine.py — Surya 2 + batch + CPU-оптимизации
external_vlm_engine.py — Qwen3.8 Max full-page OCR
postprocess/
fixups.py — OCR-ошибки (VaR, V^2)
corrector.py — apply_corrector (Callable)
vlm_corrector.py — Qwen3.8 Max per-block
qwen_client.py — общий Qwen API-клиент
markdown/
tables.py — общие функции таблиц (parse, detect, ncols)
generator.py — Markdown + валидация LaTeX
latex/
json_to_latex.py — JSON→LaTeX+HTML (Paddle + Surya + Qwen)
MIT. Модели PaddleOCR — Apache 2.0, Surya 2 — OpenRAIL-M.