Повернутый скан и OCR: как измерить эффект выравнивания страницы

OCR плохо читает сканы, где строки слегка наклонены. Команда добавляет выравнивание страницы, показывает пару удачных примеров и объявляет улучшение. Но на части документов автоматический поворот может сработать неверно и ухудшить распознавание. Проверять следует на одних и тех же страницах до и после обработки, с одним эталонным текстом и одинаковыми настройками OCR.

Сначала найдите, где возникает ошибка

Посмотрите на исходный скан и входное изображение самого OCR-движка. Наклонённые строки затрудняют сегментацию; руководство Tesseract по качеству входа рекомендует выровнять страницу, когда строки заметно наклонены. Но вращение может обрезать края, изменить разрешение и добавить пустые поля. Если после выравнивания исчез номер в углу, проблема теперь в подготовке, даже если центральный текст распознан лучше.

Для оценки сохраните пары выходов OCR и эталон по каждой странице. В примере ниже результаты до и после заданы как входные данные: код не вызывает OCR-движок, а вычисляет итог по ним. Такую же таблицу можно заполнить результатами реального пакетного прогона с одним и тем же движком. Метрика — число символьных вставок, удалений и замен, делённое на общую длину эталонов.

def distance(a, b):
    row = list(range(len(b) + 1))
    for i, left in enumerate(a, 1):
        new = [i]
        for j, right in enumerate(b, 1):
            new.append(min(new[-1] + 1, row[j] + 1,
                           row[j - 1] + (left != right)))
        row = new
    return row[-1]

pages = [
    ("Счёт 17", "Счт I7", "Счёт 17"),
    ("Итого 1250", "Итго 125O", "Итого 1250"),
]
for label, col in (("до выравнивания", 1), ("после", 2)):
    errors = sum(distance(reference, row[col]) for row in pages for reference in [row[0]])
    chars = sum(len(row[0]) for row in pages)
    print(label, "правок", errors, "CER", round(errors / chars, 3))
assert sum(distance(r[0], r[2]) for r in pages) == 0

На двух учебных страницах после выравнивания ошибок нет, до него есть несколько правок. Этот результат относится только к указанным двум парам строк. Для собственного решения возьмите разные шаблоны документов, углы наклона и качество скана. Сравните общий CER, число страниц с улучшением и число страниц с ухудшением. Большая длинная страница не должна скрыть критичную потерю суммы на маленьком чеке.

Сопоставьте метрику с полями документа

Если OCR передаёт текст в извлечение реквизитов, отдельно сравните точность номера, даты и суммы. Символьная метрика страницы может улучшиться за счёт большого заголовка, пока цифра суммы стала хуже. Разбор CER/WER для русского OCR помогает согласовать регистр и пробелы до сравнения; правила должны быть одинаковыми для обоих прогонов.

Сохраняйте угол поворота и статус шага выравнивания. Это позволит сгруппировать ухудшения: например, сканы с печатью у края или страницы с вертикальными колонками. Если автоматическое определение угла ненадёжно, можно оставлять часть страниц без поворота по заранее заданному критерию и проверить итог на независимом наборе. Решение принимается по полному потоку, а не по двум наиболее удачным картинкам.

В курсе «Computer Vision Engineer: от детекции и Doc-AI до продакшна» вы проходите подготовку документов, OCR/Doc-AI, извлечение полей и проверку качества сервиса. Это помогает связать обработку скана с тем, правильно ли дошли реквизиты до 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.