Все слова распознаны, но текст неверный: порядок чтения двух колонок
OCR находит все слова двухколоночного договора. Посимвольная точность отдельных строк высокая, но итоговый текст читается как «левый пункт, правый пункт, следующий левый пункт». Поиск и извлечение условий получают фразы, которых в документе не было. Ошибка возникает после распознавания символов — при сборке блоков в последовательность чтения.
Координаты нужны для структуры
Простая сортировка строк по верхней координате y годится для одной колонки. В двухколоночной странице строки слева и справа чередуются по высоте. Сначала надо определить колонки или другие зоны макета, затем читать внутри каждой зоны в её порядке. Это не универсальная формула для всех документов: таблица, сноска и заголовок на всю ширину потребуют отдельной логики.
Ниже четыре уже распознанных блока с текстом и координатами. Код сравнивает порядок по y с порядком «сначала колонка, затем y». Границу колонок x // 100 мы задали вручную для маленького примера; в реальном документе её определяют по макету и проверяют на эталоне.
blocks = [
{"text": "Лево 1", "x": 20, "y": 10},
{"text": "Лево 2", "x": 20, "y": 30},
{"text": "Право 1", "x": 220, "y": 12},
{"text": "Право 2", "x": 220, "y": 32},
]
naive = [b["text"] for b in sorted(blocks, key=lambda b: b["y"])]
columns = [b["text"] for b in sorted(blocks, key=lambda b: (b["x"] // 100, b["y"]))]
expected = ["Лево 1", "Лево 2", "Право 1", "Право 2"]
print("по y:", naive)
print("по колонкам:", columns)
print("совпадение последовательности:", columns == expected)
assert naive != expected and columns == expectedСортировка по y чередует «Лево 1» и «Право 1». Сортировка по колонкам возвращает эталонную последовательность. Все слова в обоих вариантах одинаковы, поэтому метрика распознавания отдельных слов не объяснит провал. Сохраняйте координаты и ID блока до этапа сборки текста: по ним можно понять, где именно изменилась последовательность.
Что считать качеством порядка
Подготовьте для нескольких страниц эталонный порядок блоков. Сравните последовательности ID или пары соседних блоков до и после алгоритма макета. Если важны реквизиты договора, дополнительно проверьте, попала ли подпись к нужному пункту и сумма к нужной строке таблицы. Одна цифра WER по плоскому тексту не скажет, испорчена ли связь между полями.
Для поиска по документам сохраняйте и текст, и структуру: страница, блок, координаты, колонка. Тогда найденную цитату можно показать на исходном месте и проверить контекст. Разбор ошибок OCR в поиске показывает, что повреждённые токены мешают извлечению; неправильный порядок способен повредить контекст даже при правильных токенах.
Не делайте колонку из любого большого пробела по x. У акта или счёта рядом могут стоять подпись, сумма и дата, которые читаются не как два независимых текста. Проверьте несколько типов страниц и задайте отдельные правила для таблиц и смешанной верстки. Если распознаватель уже возвращает иерархию блоков, сначала оцените её: самодельная сортировка может потерять полезную структуру.
В курсе «Computer Vision Engineer: от детекции и Doc-AI до продакшна» вы работаете с OCR, структурой страницы и извлечением полей в JSON. Здесь качество означает не только верные буквы, но и сохранённые связи между ними. Посмотрите программу курса.
Продолжить на практике
- Computer Vision Engineer: от детекции и Doc-AI до продакшна ↗
Практический курс по Computer Vision. Соберёте продакшн-сервис: датасет → обучение (YOLO/RT-DETR, SAM), трекинг, OCR/Doc-AI, веб-приложение (UI+API) и мониторинг. Экспорт в ONNX/TensorRT/OpenVINO, батчинг/квантование под CPU/GPU. Плюс профессиональные блоки: mAP/IoU/Dice и калибровка доверия, QA разметки и аугментации, p95/стоимость, дрейф данных и алерты, guardrails (NMS/правила), A/B-тесты, Triton/gRPC/FastAPI.
