Keras-OCR помогает находить текст на фотографиях и сканах, распознавать отдельные слова, получать координаты четырёхугольных областей, наносить подписи на изображение и обучать собственные модели детекции и распознавания через Python API.
Рабочий процесс строится вокруг объекта Pipeline: он принимает список изображений или путей, масштабирует и выравнивает кадры до общего размера, передаёт их детектору CRAFT, вырезает найденные области с учётом наклона и возвращает для каждого кадра список пар слово — четыре угловые точки. Такой результат удобно проверять в Jupyter Notebook, сохранять в JSON, использовать для разметки фотографий или передавать дальше в систему поиска.
Вместо панели с кнопками используются короткие вызовы Python, поэтому пользователь напрямую управляет порогами детекции, максимальным размером изображения, коэффициентом увеличения, алфавитом распознавателя и подготовкой обучающих данных. Это даёт больше контроля, чем фиксированный OCR-диалог, но требует внимательно следить за формой массивов, порядком координат, совместимостью TensorFlow и расходом видеопамяти.
Скачать Keras-OCR
- Редактирование PDF
- Русский интерфейс
- Просто новичкам
- Нет графического интерфейса
- Нет прямого ввода PDF
- Без регистра и пунктуации
Как выглядит базовый рабочий процесс
Минимальный сценарий состоит из четырёх действий: импортировать пакет, создать конвейер, прочитать изображения и вызвать recognize. При создании конвейера автоматически подготавливаются два независимых компонента. Детектор ищет области текста и описывает каждую область четырьмя точками, а распознаватель преобразует выровненный фрагмент в строку. Разделение полезно на практике: можно оставить готовый детектор, но заменить распознаватель моделью с другим алфавитом, либо использовать только координаты без преобразования изображения в текст.
import keras_ocr
pipeline = keras_ocr.pipeline.Pipeline()
images = [
keras_ocr.tools.read("photo_01.jpg"),
keras_ocr.tools.read("scan_02.png"),
]
results = pipeline.recognize(images)
for image_index, predictions in enumerate(results):
for word, box in predictions:
print(image_index, word, box.tolist())
Метод возвращает отдельную группу предсказаний для каждого входного кадра. Внутри группы элемент содержит распознанную строку и массив формы 4 × 2. Точки идут по контуру повёрнутого прямоугольника, поэтому они точнее обычной пары левый верхний — правый нижний для вывесок, наклонных этикеток и текста, снятого под углом. Координаты относятся к исходному масштабу: конвейер корректирует их после внутреннего увеличения и дополнения изображения полями.

Что считать интерфейсом Keras-OCR
Основной интерфейс — это функции и объекты, которые вызываются в скрипте, интерактивной консоли или ноутбуке. Центральный объект Pipeline связывает Detector и Recognizer. Модули tools, data_generation, datasets и evaluation отвечают за чтение файлов, геометрические преобразования, синтетическую разметку, загрузку наборов данных и измерение качества. Такой подход позволяет видеть промежуточные массивы и сохранять любой этап, но не скрывает ошибки за общим сообщением: трассировка Python обычно показывает конкретный вызов, форму массива или несовместимый символ.
Для исследовательской работы удобнее ноутбук: в одной ячейке создаётся модель, в следующей меняются пороги, а ниже Matplotlib показывает рамки и подписи. Для повторяемой обработки лучше обычный скрипт с фиксированным окружением, журналом параметров и сохранением результатов. В серверном приложении конвейер следует создать один раз при старте процесса и переиспользовать, иначе при каждом запросе будут повторяться инициализация графа, чтение весов и выделение памяти.
Подготовка окружения без конфликтов зависимостей
Надёжная установка начинается с отдельного виртуального окружения. Установка через pip добавляет Python-код, однако основные вычисления выполняет TensorFlow, а геометрию и обработку изображений обеспечивают NumPy, OpenCV, Shapely, PyClipper и другие зависимости. Изоляция важна, потому что в существующем проекте могут уже использоваться другие сочетания TensorFlow, Keras, NumPy или OpenCV. Сначала создайте окружение, обновите инструменты упаковки, затем установите Keras-OCR и только после этого проверяйте импорт.
python -m venv .venv
# Windows: .venv\Scripts\activate
# Linux и macOS: source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install keras-ocr
python -c "import keras_ocr; print('import ok')"
Проверка импорта отделяет проблемы установки от проблем загрузки моделей. Если импорт проходит, но создание Pipeline() завершается ошибкой, причина чаще связана с TensorFlow, доступом к файлам весов, сертификатами или памятью. Если ошибка возникает уже на import keras_ocr, зафиксируйте вывод python -m pip freeze и версию интерпретатора, затем воспроизведите сбой в чистом окружении. Это быстрее, чем поочерёдно удалять библиотеки из рабочего проекта.
Загрузка весов и каталог кэша
При первом создании детектора и распознавателя пакет загружает готовые веса и проверяет их контрольные суммы. Файлы помещаются в каталог ~/.keras-ocr, поэтому повторные запуски используют локальный кэш. В среде без постоянного домашнего каталога — например, в одноразовом контейнере — модели будут загружаться заново. Для предсказуемого запуска смонтируйте каталог кэша как постоянный том или заранее перенесите проверенные файлы в домашний каталог пользователя, под которым работает процесс.
Если загрузка прерывается, не заменяйте веса случайным файлом с похожим названием. Встроенная функция download_and_verify умеет сравнивать SHA-256 и повторно использовать только совпадающий файл. При работе через корпоративный прокси сначала проверьте, может ли Python открыть адрес хранилища, и не отключайте проверку сертификата во всём процессе. Для закрытого контура безопаснее скачать веса на машине с доступом, проверить хеш, затем доставить их в кэш контролируемым способом.
Какие изображения подавать на вход
Детектор принимает список путей либо список массивов NumPy формы H × W × 3. Функция keras_ocr.tools.read читает локальный путь, буфер с методом read и сетевой адрес, но в производственной задаче лучше заранее скачать входные файлы и проверять их тип. Трёхканальное RGB-представление — самый безопасный вариант. Изображение с альфа-каналом следует преобразовать в RGB на выбранном фоне, а оттенки серого — развернуть в три канала, если они передаются напрямую как массивы.
from PIL import Image
import numpy as np
with Image.open("label.png") as source:
rgb = source.convert("RGB")
image = np.asarray(rgb)
assert image.ndim == 3 and image.shape[2] == 3
predictions = pipeline.recognize([image])[0]
Формат файла менее важен, чем фактические пиксели после декодирования. JPEG подходит для фотографий, но сильное сжатие создаёт блоки вокруг тонких букв. PNG лучше сохраняет скриншоты, схемы и небольшие надписи. TIFF и многостраничные документы разумно предварительно разбирать специализированной библиотекой на отдельные RGB-страницы. Пакет не строит структуру документа и не извлекает страницы из PDF, поэтому подготовка растров должна быть отдельным, явно протестированным этапом.
Масштабирование, max_size и мелкий текст
Конвейер перед детекцией вызывает resize_image. Параметр scale задаёт максимально допустимое увеличение, а max_size ограничивает длину большей стороны. Значения по умолчанию увеличивают небольшой кадр не более чем вдвое и не позволяют стороне превысить 2048 пикселей. После этого изображения в одном вызове дополняются до общей высоты и ширины. Поэтому один очень крупный кадр может увеличить память, занятую всем пакетом.
Для мелкой надписи сначала сделайте контролируемый кроп вокруг предполагаемой области, затем повышайте scale. Простое увеличение всего снимка не создаёт деталей, но даёт CRAFT больше пикселей для слабых штрихов. Если кадр содержит десятки мегапикселей, полезнее разбить его на перекрывающиеся плитки, распознать плитки и вернуть координаты в систему исходного изображения. Перекрытие должно быть шире типичного слова, иначе надпись на границе будет разрезана.
pipeline = keras_ocr.pipeline.Pipeline(scale=3, max_size=2600)
result = pipeline.recognize(["small_text_photo.jpg"])[0]
Увеличение scale повышает вычислительную нагрузку и может увеличить число ложных областей на текстурном фоне. Изменяйте один параметр за раз и сохраняйте рядом с результатом его значение. Для набора однотипных кадров подберите настройки на отдельной валидационной выборке, а не на одном удачном изображении.
Пакетная обработка изображений
Один вызов может содержать несколько изображений. Перед детекцией они приводятся к общей форме: каждый кадр масштабируется отдельно, затем дополняется белыми полями до максимальной ширины и высоты внутри пакета. Это позволяет выполнить детекцию батчем, но делает невыгодным смешивание маленьких этикеток и панорамных фотографий. Сгруппируйте входы по близким размерам и ориентации, чтобы не расходовать память на большие пустые области.
from pathlib import Path
paths = sorted(Path("incoming").glob("*.jpg"))
chunk_size = 8
for start in range(0, len(paths), chunk_size):
chunk = [str(p) for p in paths[start:start + chunk_size]]
groups = pipeline.recognize(chunk)
for path, predictions in zip(chunk, groups):
save_result(path, predictions)
Размер пакета определяется не только видеопамятью. Большие группы увеличивают время до первого результата и усложняют повтор после ошибки в одном файле. Для фоновой очереди обычно удобен небольшой фиксированный батч, а повреждённые изображения следует отбрасывать до вызова модели. Сохраняйте результат каждого файла сразу после обработки, чтобы перезапуск продолжал работу с незавершённого места.
Настройка детектора CRAFT
Метод Detector.detect принимает четыре ключевых порога. text_threshold превращает карту уверенности текста в бинарную маску. link_threshold выполняет аналогичную роль для связей между символами. detection_threshold дополнительно отбрасывает область, если максимальная уверенность внутри неё слишком мала. size_threshold задаёт минимальную площадь области и помогает убрать крошечные шумовые компоненты.
detection_kwargs = {
"text_threshold": 0.45,
"link_threshold": 0.35,
"detection_threshold": 0.65,
"size_threshold": 12,
}
result = pipeline.recognize(
["signboard.jpg"],
detection_kwargs=detection_kwargs,
)[0]
Снижение text_threshold помогает увидеть бледные буквы, но фоновые линии и текстуры чаще превращаются в рамки. Повышение порога уменьшает шум, одновременно теряя слабые штрихи. Если символы одного слова распадаются на несколько областей, сначала немного снизьте link_threshold и оцените геометрию. Если соседние слова сливаются, двигайтесь в обратную сторону. detection_threshold лучше менять после настройки масок: он действует как финальный фильтр областей, а не исправляет их форму.

Как интерпретировать четыре точки рамки
Каждая рамка представлена четырьмя вершинами. Для визуализации их можно соединить по порядку, но для сортировки строк нельзя полагаться только на первую точку: на повёрнутом прямоугольнике она может оказаться выше или левее соседнего слова не так, как ожидается в чтении. Удобно вычислить центр как среднее четырёх точек, высоту — как среднее расстояние между верхней и нижней сторонами, а угол — через функцию get_rotated_box или геометрию векторов.
import numpy as np
def box_center(box):
box = np.asarray(box, dtype=float)
return box.mean(axis=0)
for word, box in predictions:
x, y = box_center(box)
print(word, round(x, 1), round(y, 1))
Для экспорта храните исходные плавающие координаты, а округление выполняйте только при рисовании. Преждевременное преобразование в целые числа ухудшает обратное масштабирование и объединение рамок. Если изображение до распознавания поворачивалось или обрезалось, сохраните матрицу преобразования и смещение, чтобы вернуть точки в координаты исходного файла.
Выравнивание наклонного слова перед распознаванием
Распознаватель ожидает уже выделенный фрагмент с одной последовательностью символов. В конвейере это делает recognize_from_boxes: найденная четырёхугольная область преобразуется в прямоугольник перспективным преобразованием. Для собственных сценариев доступна функция warpBox, которая принимает четыре угла, целевую высоту, целевую ширину и поле. Она полезна, когда рамки получены сторонним детектором или размечены человеком.
crop = keras_ocr.tools.warpBox(
image=image,
box=box,
target_height=31,
target_width=200,
margin=2,
)
word = pipeline.recognizer.recognize(crop)
Поле вокруг текста защищает крайние буквы от обрезки, однако слишком большое поле уменьшает относительный размер символов. Для длинных слов фиксированная ширина может приводить к сильному горизонтальному сжатию; это ограничение следует учитывать при проектировании собственного распознавателя. Если строки содержат пробелы и несколько слов, лучше детектировать и распознавать их как отдельные области либо обучать архитектуру под иной формат выхода.
Визуальная проверка результатов
Функция drawAnnotations наносит рамки и подписи на ось Matplotlib. Она предназначена для быстрой проверки, а не для формирования окончательного макета. На сложном кадре линии подписей могут пересекаться, поэтому для отчёта часто удобнее отдельно рисовать контуры через drawBoxes, а распознанные строки выводить в таблицу с индексами. Цвет и толщина рамок задаются в drawBoxes.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(14, 9))
keras_ocr.tools.drawAnnotations(
image=image,
predictions=predictions,
ax=ax,
)
ax.axis("off")
fig.tight_layout()
fig.savefig("checked_result.png", dpi=160)
Сравнивайте не только распознанные строки, но и сами рамки. Ошибка детектора и ошибка распознавателя требуют разных исправлений. Если рамка охватывает половину слова, изменение алфавита не поможет. Если рамка точная, но строка неверна, пороги детектора обычно ни при чём. Полезный журнал содержит исходный кадр, изображение с рамками, выровненные кропы и машинный JSON — тогда причину можно установить без повторного запуска модели.

Порядок чтения и сборка строк
Выход конвейера не является готовым абзацем. Список отражает найденные слова, но не гарантирует порядок, подходящий для колоночного документа, таблицы или вертикальной надписи. Для простого одноколоночного кадра можно группировать центры по близкой координате Y с допуском, зависящим от медианной высоты рамки, затем сортировать слова внутри строки по X. Фиксированный допуск в пикселях плохо переносится между изображениями разных размеров.
def reading_order(predictions):
items = []
for word, box in predictions:
box = np.asarray(box, dtype=float)
center = box.mean(axis=0)
height = (np.linalg.norm(box[3]-box[0]) +
np.linalg.norm(box[2]-box[1])) / 2
items.append({"word": word, "x": center[0], "y": center[1], "h": height})
# Далее элементы группируются в строки по y с допуском от медианной h.
return items
Для многостолбцовой страницы сначала найдите колонки или блоки макета, иначе сортировка по Y будет чередовать левую и правую колонку. Для наклонной строки полезно проецировать центры на направление строки, а не сортировать по экранной оси X. Функция fix_line помогает привести набор символьных рамок к согласованному направлению слева направо или сверху вниз, но структуру документа всё равно определяет вызывающий код.
Экспорт в JSON, CSV и поисковый индекс
Практичный JSON должен хранить имя файла, размер изображения, параметры конвейера, строку и четыре пары координат. Не ограничивайтесь одним объединённым текстом: без рамок невозможно подсветить совпадение, восстановить порядок или повторно обучить модель на исправлениях. Числа NumPy перед сериализацией преобразуйте в обычные списки и типы Python.
import json
payload = {
"image": "photo_01.jpg",
"width": int(image.shape[1]),
"height": int(image.shape[0]),
"words": [
{"text": word, "box": box.astype(float).tolist()}
for word, box in predictions
],
}
with open("photo_01.ocr.json", "w", encoding="utf-8") as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
CSV подходит для аналитики, если каждая строка описывает одно слово и содержит восемь координат. Для полнотекстового поиска добавляйте нормализованный текст отдельно, но сохраняйте исходное предсказание без исправлений. Если бизнес-правило заменяет похожие символы или восстанавливает регистр по словарю, запишите это как новый слой данных, а не перезаписывайте модельный результат.
Работа со сканированными PDF
Keras-OCR не принимает PDF как единый документ. Каждую страницу нужно растеризовать в RGB-изображение, передать в конвейер и затем связать слова с номером страницы. Разрешение выбирают по размеру печатного текста: слишком низкое превращает тонкие штрихи в шум, слишком высокое быстро увеличивает память и время детекции. Начинайте с умеренного разрешения и повышайте его только для страниц, где высота символов получается недостаточной.
При сохранении поискового PDF требуется отдельный этап: координаты рамок переводятся из пикселей в координаты страницы, учитываются поворот и масштаб, затем текстовый слой добавляется PDF-библиотекой. Поскольку готовый распознаватель выдаёт цифры и строчные латинские буквы, он не подходит для точного восстановления русскоязычного документа без обучения другого алфавита. Для обычных книжных сканов специализированный OCR-движок с анализом страницы часто даст более готовый результат.
Если задача заключается в редактировании, объединении, разбиении или конвертации PDF, удобнее использовать редактор PDF. Keras-OCR оправдан, когда требуется программно искать сценический текст на растровых страницах, получать наклонные рамки или обучать модель на специфических этикетках. Не смешивайте эти задачи в один неуправляемый скрипт: растеризация, OCR, проверка и сборка PDF должны иметь отдельные тесты и журналы ошибок.
Фотографии вывесок, упаковки и оборудования
Связка CRAFT и CRNN особенно полезна там, где текст расположен не строго горизонтально: на вывесках, табличках, коробках, ценниках и деталях оборудования. Детектор может описывать повёрнутую область, а warpBox выпрямляет её перед распознаванием. Однако сильная перспектива, блики, тени и рельефные буквы по-разному влияют на два этапа. Сначала добейтесь рамки вокруг полного слова, затем оценивайте строку.
Для серии фотографий создайте стабильный протокол съёмки: одинаковое расстояние, рассеянный свет, минимальный цифровой зум и достаточный контраст. Если положение этикетки известно, обрезка области интереса уменьшит ложные срабатывания и ускорит детекцию. На металлической поверхности поляризационный фильтр или смена угла освещения часто дают больший эффект, чем многократное изменение порогов.
В автоматической линии проверяйте геометрию до OCR. Например, ожидаемая табличка должна находиться в определённой зоне и иметь допустимый размер. Если рамка выходит за границы зоны, отправляйте кадр на повторную съёмку или ручную проверку. Такое правило предотвращает уверенное распознавание случайной надписи на фоне.
Ограничения готового распознавателя
Алфавит готовых весов состоит из цифр и строчных латинских букв. Поэтому регистр не сохраняется, а знаки препинания не входят в стандартный набор. Модель не превращает строку в предложение и не восстанавливает пробелы между несколькими словами. Это не косметическое ограничение: для номера детали AB-120 готовый вывод может потерять дефис и привести буквы к нижнему регистру, а для русского текста символы отсутствуют в алфавите.
Не добавляйте недостающие символы только в строку alphabet и не ожидайте, что готовая верхняя классификационная часть сразу начнёт их распознавать. Когда алфавит отличается, можно использовать веса основы без финального слоя и обучить выход на собственных данных. Количество, разнообразие и качество примеров должны покрывать каждый символ, сочетания, шрифты, фон и геометрические искажения.


Настройка собственного алфавита
Алфавит задаёт допустимые символы и порядок их кодирования. Он должен быть детерминированным: одинаковая строка алфавита используется при обучении, сохранении и восстановлении модели. Удалите дубликаты, заранее решите вопрос регистра и не включайте символы, которые никогда не встречаются в задаче. Слишком широкий алфавит увеличивает число классов и требует больше примеров.
import string
import keras_ocr
alphabet = string.digits + string.ascii_uppercase + "-/."
recognizer = keras_ocr.recognition.Recognizer(
alphabet=alphabet,
weights="kurapan",
)
recognizer.compile()
Если алфавит не совпадает с алфавитом готовых весов, распознаватель использует предварительно обученную основу, но финальный классификатор должен обучиться под новые классы. Это разумная отправная точка для похожих латинских символов, однако кириллица, специальные значки и необычные шрифты требуют репрезентативного набора. Сохраняйте алфавит рядом с весами в отдельном конфигурационном файле, иначе последовательность индексов нельзя надёжно восстановить.
Подготовка данных для распознавателя
Генератор распознавателя ожидает пары изображение одной строки — строковая метка. Все символы метки должны входить в алфавит, пустые строки не допускаются, а длина не должна превышать возможность модели. Встроенный get_recognizer_image_generator умеет брать список меток с путями и рамками, вырезать области, приводить их к размеру входа и применять аугментацию.
Перед обучением проверьте датасет отдельным скриптом. Он должен обнаруживать отсутствующие файлы, декодировать каждое изображение, проверять символы, длину и пустые метки, а также сохранять галерею случайных примеров. Ошибки в разметке часто выглядят как проблема модели: перепутанные символы, обрезанный край или неверная кодировка снижают качество даже при стабильном падении функции потерь.
bad = []
for filepath, box, word in labels:
unknown = sorted(set(word) - set(recognizer.alphabet))
if unknown or not word.strip():
bad.append((filepath, word, unknown))
print("invalid samples:", len(bad))
Дообучение распознавателя на существующем наборе
Практический цикл начинается с разделения данных на обучение и валидацию. В официальном примере готовятся метки Born Digital, строки приводятся к нижнему регистру, затем создаются генераторы изображений и батчей. Аугментация применяется только к обучающей части; валидация должна отражать реальные входы без случайных преобразований. Размер батча выбирается по памяти, а число шагов определяется количеством примеров.
import imgaug
import sklearn.model_selection
train_labels, val_labels = sklearn.model_selection.train_test_split(
labels, test_size=0.2, random_state=42
)
augmenter = imgaug.augmenters.Sequential([
imgaug.augmenters.GammaContrast(gamma=(0.4, 2.2)),
])
train_gen = keras_ocr.datasets.get_recognizer_image_generator(
labels=train_labels,
height=recognizer.model.input_shape[1],
width=recognizer.model.input_shape[2],
alphabet=recognizer.alphabet,
augmenter=augmenter,
)
val_gen = keras_ocr.datasets.get_recognizer_image_generator(
labels=val_labels,
height=recognizer.model.input_shape[1],
width=recognizer.model.input_shape[2],
alphabet=recognizer.alphabet,
augmenter=None,
)
Не оценивайте качество только по loss. После каждой контрольной эпохи распознавайте фиксированный набор и считайте точное совпадение строки, среднее расстояние редактирования и ошибки по символам. Для серийных кодов особенно важна доля полностью правильных строк: одна неверная цифра делает весь код непригодным. Сохраняйте лучший чекпойнт по метрике валидации, а не только последний.
Обучение детектора на собственных изображениях
Детектор обучается на изображениях с символьными рамками, сгруппированными в строки. Его генератор строит тепловые карты символов и связей. В официальном учебном процессе используется упрощённый механизм, который не полностью повторяет обучение исходной реализации CRAFT, поэтому результат нужно проверять на своей задаче. Для нового домена сначала убедитесь, что готовый детектор действительно является узким местом: иногда достаточно кропа, изменения масштаба или порогов.
Разметка детектора должна описывать геометрию последовательно. Четыре точки символа располагаются по часовой стрелке, начиная с левого верхнего угла после выравнивания порядка. Рамки не должны самопересекаться, выходить далеко за изображение или иметь нулевую площадь. Если исходная разметка содержит слова, а генератор ожидает символы, потребуется корректное преобразование или иной обучающий процесс.
Разделяйте набор по сценам, объектам или источникам съёмки, а не случайно по соседним кадрам. Почти одинаковые изображения в обучении и валидации создают завышенную оценку. Для производственной камеры отложите отдельные дни, партии продукции или физические экземпляры. Так проверка покажет перенос на новые данные, а не запоминание фона.
Синтетические изображения для обучения
Модуль data_generation создаёт текст на фоновых изображениях и возвращает точные рамки символов. Он поддерживает наборы шрифтов, алфавит, размеры, повороты вокруг трёх осей, поля, цвет, контуры допустимого размещения, лигатуры и внешний augmenter. Синтетика полезна для редких символов и первоначального обучения, поскольку разметка получается автоматически.


Синтетический набор должен имитировать конкретную камеру и носитель. Случайные красивые фоны не заменяют шум матрицы, блики, размытие движения, типографику и перспективу реальной линии. Сначала соберите несколько сотен настоящих примеров, измерьте диапазоны высоты символов, контраста и углов, затем настройте генератор под эти диапазоны. Часть реальных данных оставьте только для проверки.
Функция font_supports_alphabet помогает исключить шрифты без нужных глифов. Это особенно важно для кириллицы и специальных символов: отсутствие глифа может дать пустое место или знак замены при корректной текстовой метке. Сохраняйте перечень шрифтов и лицензий вместе с конфигурацией набора.
Аугментация без разрушения меток
Аугментация должна изменять изображение и рамки согласованно. В tools.augment предусмотрена совместная обработка областей, контроль доли рамки, оставшейся внутри кадра, и минимальной площади. Для распознавателя, который получает уже вырезанную строку, допустимы изменения яркости, контраста, умеренное размытие и шум. Для детектора геометрические преобразования требуют корректного пересчёта всех точек.
Не включайте одновременно большой поворот, сильную перспективу, обрезку и размытие без визуального контроля. Комбинация может создать нечитабельный кадр с формально правильной меткой, и модель начнёт подгоняться под шум. Сохраняйте сетку аугментированных примеров при каждом изменении конфигурации. Доля искусственно испорченных изображений не должна значительно превышать долю таких случаев в реальном потоке.
Оценка качества детекции и распознавания
Качество детектора оценивают по совпадению рамок, обычно через площадь пересечения относительно объединения. Качество распознавателя — по точному совпадению, расстоянию редактирования и ошибкам символов. Для полного конвейера нужны обе группы метрик: правильная строка с неверной рамкой может быть непригодна для подсветки, а точная рамка с ошибочной строкой не решает поиск.
Создайте таблицу ошибок по категориям: пропуск текста, лишняя рамка, разделение слова, слияние слов, ошибка символа, потеря знака, неверный порядок. Для каждой категории храните несколько изображений. Когда меняется порог или модель, сравнивайте один и тот же набор. Средняя метрика без анализа категорий может улучшиться, хотя критичные серийные номера станут распознаваться хуже.
Встроенные средства оценки и примеры дают основу, но критерий приёмки должен отражать задачу. Для инвентаризации можно потребовать точное совпадение кода и разрешить пропуски декоративного текста. Для поиска по фотографиям важнее полнота, поэтому допустимы дополнительные кандидаты, которые затем фильтрует словарь. Зафиксируйте критерий до настройки на тестовом наборе.
Управление видеопамятью
TensorFlow может попытаться занять почти всю доступную видеопамять. Keras-OCR предоставляет функцию keras_ocr.config.configure(), которая читает переменную MEMORY_GROWTH и включает постепенное выделение памяти для обнаруженных GPU. Вызов должен происходить до создания моделей и до операций, которые инициализируют устройство.
import os
os.environ["MEMORY_GROWTH"] = "1"
import keras_ocr
keras_ocr.config.configure()
pipeline = keras_ocr.pipeline.Pipeline()
При ошибке нехватки памяти сначала уменьшите число изображений в батче, max_size и scale. Затем убедитесь, что в процессе не создаются новые конвейеры для каждого файла. В ноутбуке повторное выполнение ячейки может оставить старые модели в памяти; надёжнее перезапустить ядро после серии экспериментов. В сервере ограничьте число рабочих процессов с GPU: каждый процесс создаёт собственный граф и копию весов.
Производительность на CPU
Без GPU конвейер остаётся функциональным, но CRAFT и CRNN выполняют много операций свёртки. Основные способы сократить время — уменьшить область интереса, ограничить большую сторону, не повышать scale без необходимости и объединять близкие по размеру кадры в небольшие батчи. Не измеряйте только первый запуск: он включает создание моделей и чтение весов. Отдельно фиксируйте холодный старт и устойчивую скорость после прогрева.
Для большого архива полезна очередь заданий с отдельным процессом распознавания. Подготовка изображений и запись JSON могут выполняться параллельно, но саму модель не стоит бесконтрольно копировать между множеством процессов. Измеряйте пропускную способность на реальных размерах файлов и учитывайте декодирование, растеризацию PDF и сохранение аннотаций, а не только время recognize.
Интеграция в веб-API
В веб-сервисе модель создаётся при старте, а обработчик запроса только декодирует файл, проверяет размер и вызывает распознавание. Ограничьте максимальное число пикселей до полного декодирования, иначе очень большое или специально подготовленное изображение способно занять всю память. Не принимайте путь к файлу от клиента без проверки: работайте с буфером загрузки и временным каталогом с контролируемыми правами.
# Схема обработчика без привязки к конкретному фреймворку
pipeline = keras_ocr.pipeline.Pipeline()
def recognize_upload(file_bytes):
image = decode_checked_rgb(file_bytes)
predictions = pipeline.recognize([image])[0]
return [
{"text": word, "box": box.tolist()}
for word, box in predictions
]
Если GPU не поддерживает одновременные запросы без переполнения памяти, поставьте вызовы модели в очередь или защищайте их семафором. Тайм-аут HTTP не должен прерывать процесс в середине записи результата. Для длинных задач лучше вернуть идентификатор задания, а результат выдавать после завершения. Журналируйте размеры входа и параметры, но не сохраняйте конфиденциальные изображения без явной политики.
Встраивание в настольный или пакетный инструмент
Keras-OCR можно использовать как вычислительный слой внутри собственного интерфейса. Графическая оболочка должна отдельно показывать исходное изображение, рамки, таблицу распознанных слов и поля ручного исправления. Исправления лучше сохранять вместе с координатами и исходным предсказанием: накопленные пары затем превращаются в проверенный обучающий набор.
Для пакетного режима добавьте понятные статусы: ожидает, декодируется, распознаётся, сохранено, ошибка. Не выводите весь traceback конечному пользователю, но сохраняйте его в журнал. Ошибка одного файла не должна останавливать каталог. Для повторного запуска вычисляйте хеш входного изображения и проверяйте, существует ли результат с тем же хешем и конфигурацией модели.
Типовые ошибки импорта и установки
Ошибка импорта TensorFlow, Keras или NumPy обычно означает несовместимое сочетание пакетов в окружении. Не пытайтесь исправить её случайным понижением одной библиотеки. Создайте чистое окружение, установите пакет, сохраните полный список зависимостей и только затем переносите сочетание в проект. Если чистая установка работает, сравните pip freeze и удалите конфликтующее ограничение из основного проекта.
Сообщение об отсутствующем системном модуле или библиотеке OpenCV может зависеть от операционной системы. В серверной среде используется headless-вариант OpenCV; он не содержит оконных функций. Для OCR это обычно достаточно, потому что визуализация выполняется Matplotlib или сохранением файла. Если проекту одновременно требуется GUI-вариант OpenCV, проверьте дерево зависимостей и оставьте один согласованный пакет, а не несколько конкурирующих сборок.
Сбой при создании Pipeline
Первое создание конвейера выполняет больше работы, чем импорт: строит архитектуры, загружает веса, проверяет файлы и выделяет память. Поэтому ошибка сети, повреждённый кэш или нехватка памяти появляется именно здесь. Удаляйте только конкретный файл, который не проходит проверку, а не весь домашний каталог. После повторной загрузки сравните контрольную сумму и убедитесь, что процесс имеет право записи в каталог кэша.
Если traceback указывает на неизвестный аргумент слоя, десериализацию модели или внутренний API Keras, воспроизведите проблему в отдельном окружении с согласованным TensorFlow. Пакет использует API TensorFlow Keras и готовые H5-веса; крупное обновление зависимостей может изменить поведение внутренних слоёв. Зафиксированное окружение с тестом на создание Pipeline() защищает развёртывание от неожиданных обновлений.
Почему детектор не находит текст
Сначала сохраните изображение, фактически переданное модели. Частая причина — неверный порядок каналов после OpenCV, прозрачный текст на неожиданном фоне, слишком маленький кроп или нулевой динамический диапазон. Затем визуально измерьте высоту букв. Если она составляет несколько пикселей, увеличьте исходный кроп или масштаб. После этого осторожно снижайте text_threshold и detection_threshold.
- Проверьте RGB-каналы и фактический размер массива.
- Уберите огромные пустые поля вокруг области интереса.
- Повышайте scale только для действительно мелкого текста.
- Снижайте пороги небольшими шагами и сравнивайте один набор.
- Для бликов и теней улучшайте съёмку или предобработку, а не только пороги.
Если на изображении есть текст, но он имеет необычные изогнутые формы, сильную перспективу или очень большой межбуквенный интервал, готовый детектор может разбивать его или пропускать. Тогда соберите примеры именно такого типа и оцените дообучение детектора. Один удачный кадр недостаточен: нужны разные фоны, углы и экземпляры.
Почему появляется слишком много рамок
Ложные области возникают на повторяющихся текстурах, решётках, кирпичной кладке, ветках и контрастных логотипах. Повышение text_threshold и detection_threshold уменьшает чувствительность, а size_threshold удаляет крошечные компоненты. Однако слишком высокий порог потеряет слабый реальный текст. Более устойчивое решение — ограничить область интереса и отфильтровать рамки по ожидаемой геометрии.
После детекции можно применять правила: минимальное отношение сторон, допустимый угол, зона кадра, диапазон высоты и словарь символов. Эти правила должны быть отделены от модели и покрыты тестами. Не удаляйте рамку только потому, что распознанная строка короткая: ошибка распознавателя не доказывает, что область не является текстом.
Почему слово распознано неправильно при точной рамке
Если рамка охватывает слово полностью, сохраните выровненный кроп. Проверьте, не сжат ли он до нечитаемой полосы, не обрезаны ли крайние символы и соответствует ли строка алфавиту готовой модели. Цифра 0 и буква o, 1 и l, 5 и s часто требуют контекста или специализированного обучения.
Для фиксированного формата кода используйте постпроверку, которая знает позиции букв и цифр, но не подменяйте ею оценку OCR. Сохраняйте исходное предсказание, исправленное значение и причину замены. Когда накопится достаточно исправлений, обучите распознаватель на реальных кропах. Словарь не поможет, если модель систематически теряет крайний символ из-за геометрии.
Ошибки при обучении распознавателя
Сообщение Found illegal character означает, что метка содержит символ вне алфавита. Исправьте данные или алфавит; удаление проверки скроет рассогласование классов. Ошибка о слишком длинной строке указывает, что архитектура не может представить метку при текущей ширине и числе временных шагов. Нужно изменить подготовку данных, ограничить длину или проектировать другую конфигурацию модели.
Нулевая длина строки означает пустую метку после удаления пробелов. Последовательности с несколькими пробелами также не подходят стандартному генератору. Для распознавания отдельных слов лучше заранее разделить строку. Если требуется полноценная строка с пробелами, проектируйте датасет и декодирование под эту задачу, а не маскируйте пробелы произвольным символом.
Сохранение и повторное использование обученной модели
Сохраняйте не только веса. Минимальный комплект включает алфавит, параметры построения, размер входа, сведения о предобработке, контрольную выборку и код загрузки. Два файла весов с одинаковой формой не гарантируют одинаковое соответствие индексов символам. Конфигурация должна быть версионирована вместе с кодом проекта и тестовым изображением с ожидаемым результатом.
При восстановлении сначала создайте распознаватель с тем же алфавитом и параметрами, затем загрузите веса и выполните smoke-тест. Не начинайте обработку архива до проверки нескольких фиксированных кропов. Для полного конвейера также сохраните параметры детекции, scale и max_size: изменение геометрии рамок способно изменить входы распознавателя даже при тех же весах.
Конфиденциальность и контроль данных
Распознавание можно выполнять в собственной вычислительной среде, поэтому изображения не требуется отправлять в облачный OCR. Это полезно для внутренних документов, производственных кадров и закрытых наборов. При этом загрузка весов при первом запуске является отдельным сетевым действием; в изолированном контуре подготовьте кэш заранее и проверьте контрольные суммы.
Локальное выполнение не отменяет защиту файлов. Временные кропы, визуализации и журналы могут содержать больше информации, чем итоговая строка. Задайте срок хранения, права доступа и безопасное удаление. В веб-сервисе не записывайте содержимое изображения в журнал исключений и не включайте входные буферы в диагностические дампы.
Сравнение Keras-OCR с аналогами
| Программа | Лучше подходит для | Главное ограничение |
|---|---|---|
| Keras-OCR | Гибкого Python-конвейера CRAFT + CRNN, наклонных слов и собственного обучения | Готовый распознаватель ограничен цифрами и строчной латиницей |
| EasyOCR | Быстрого старта с готовыми языковыми моделями и простым Python API | Настройка и обучение собственного полного конвейера сложнее типового вызова |
| PaddleOCR | Многоязычного OCR, анализа документов и широкого набора готовых моделей | Экосистема и конфигурация заметно тяжелее для небольшой задачи |
| Tesseract | Печатных сканов, страниц книг и формирования текстового слоя | Хуже приспособлен к произвольному сценическому тексту без отдельной детекции |
| docTR | Современного двухэтапного OCR документов с готовыми архитектурами | Для точной настройки требуется разбираться в моделях и обучающих данных |
| PDF Commander | Ручной работы с PDF, страницами, конвертацией и редактированием | Не предназначен для обучения моделей сценического OCR |
Для экспериментального распознавания вывесок, получения повёрнутых рамок и обучения собственного алфавита выбирайте Keras-OCR. Для готового многоязычного распознавания без длительной подготовки чаще удобнее EasyOCR или PaddleOCR. Tesseract уместен для ровных печатных страниц, особенно когда важен привычный документный процесс. docTR подходит команде, которая хочет развивать современный OCR документов. PDF Commander практичнее, когда основная задача — открыть, исправить, объединить или преобразовать PDF вручную, а не строить модель компьютерного зрения.
Когда Keras-OCR подходит лучше всего
- Нужно получить не только текст, но и четырёхугольные координаты слов.
- Текст расположен на фотографиях, вывесках, упаковке или деталях под углом.
- Требуется заменить детектор или распознаватель независимо друг от друга.
- Есть размеченные данные и задача обучить собственный алфавит или домен.
- Результат должен встраиваться в Python-процесс, JSON, очередь или API.
Наибольшую ценность пакет даёт разработчику, который готов контролировать входы и оценку качества. Он не скрывает геометрию и предоставляет функции для чтения, масштабирования, дополнения, деформации рамок, синтетической генерации и обучения. Это позволяет построить узкоспециализированный процесс, но ответственность за порядок чтения, структуру документа, экспорт и проверку остаётся у приложения.
Когда выбрать другой инструмент
Для распознавания русского текста сразу после установки готовый алфавит не подходит. Для многостраничного PDF с колонками, таблицами, заголовками и текстовым слоем потребуется дополнительный анализ макета и сборка документа. Для пользователя, которому нужен визуальный редактор и кнопка экспорта, программный API создаст лишний порог входа. В этих случаях разумнее начать с системы, где соответствующая функция уже является частью рабочего процесса.
Не выбирайте Keras-OCR только из-за слова Keras в названии, если проект уже построен на другой ML-экосистеме и не нуждается в CRAFT/CRNN. Стоимость поддержки включает совместимость зависимостей, хранение весов, тесты на GPU и собственный код экспорта. Сравнивайте не демонстрационный кадр, а полный путь от входного файла до проверяемого результата.
Контрольный сценарий перед внедрением
- Соберите не менее нескольких десятков реальных кадров каждого типа, включая плохое освещение и мелкий текст.
- Зафиксируйте окружение и убедитесь, что Pipeline создаётся без сети при подготовленном кэше.
- Сохраните базовые результаты с параметрами по умолчанию и изображения с рамками.
- Отдельно настройте детекцию, не меняя распознаватель, затем оцените ошибки строк.
- Определите порядок чтения и формат JSON, сохранив исходные координаты.
- Проверьте холодный старт, устойчивую скорость, память и восстановление после повреждённого файла.
- Сформулируйте критерий ручной проверки и правила отклонения результата.
Такой пилот показывает, где именно находится стоимость решения. Если большинство ошибок вызвано отсутствующим алфавитом, требуется обучение распознавателя. Если текст не попадает в рамки, работа начинается с детектора и съёмки. Если слова правильные, но документ собран неверно, проблема в анализе макета. Разделение этапов предотвращает бессистемное изменение всех параметров одновременно.
Практический шаблон устойчивого скрипта
from pathlib import Path
import json
import os
import numpy as np
from PIL import Image
os.environ.setdefault("MEMORY_GROWTH", "1")
import keras_ocr
keras_ocr.config.configure()
pipeline = keras_ocr.pipeline.Pipeline(scale=2, max_size=2048)
def read_rgb(path: Path) -> np.ndarray:
with Image.open(path) as im:
return np.asarray(im.convert("RGB"))
def run_one(path: Path) -> dict:
image = read_rgb(path)
predictions = pipeline.recognize([image])[0]
return {
"file": path.name,
"width": int(image.shape[1]),
"height": int(image.shape[0]),
"words": [
{"text": word, "box": box.astype(float).tolist()}
for word, box in predictions
],
}
for path in sorted(Path("incoming").glob("*")):
try:
result = run_one(path)
out = Path("results") / f"{path.name}.json"
out.parent.mkdir(exist_ok=True)
out.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
except Exception as exc:
log_failure(path, exc)
Шаблон намеренно разделяет чтение, распознавание и сохранение. В реальном проекте добавьте проверку расширения и числа пикселей, временную запись с последующим атомарным переименованием, журнал конфигурации и повтор для временных ошибок. Функция log_failure должна сохранять тип исключения и traceback, но не останавливать весь каталог. Для батчей замените одиночный вызов группировкой файлов близкого размера.
Ответы на практические вопросы
Можно ли распознавать рукописный текст?
Готовый распознаватель обучен не как универсальная модель рукописи. Отдельные аккуратные символы иногда могут совпасть, но полагаться на это нельзя. Для рукописного домена нужен собственный набор, подходящий алфавит и проверка на реальных авторах. Связный почерк требует архитектуры и разметки, ориентированных на строки, а не только на отдельные печатные слова.
Можно ли получить уверенность для каждого слова?
Стандартный выход Pipeline.recognize содержит строку и рамку, но не выдаёт готовое поле confidence для слова. Если порог принятия обязателен, потребуется расширить декодирование распознавателя или рассчитывать собственную оценку из выходных вероятностей. Нельзя подменять confidence площадью рамки или уверенностью детектора: эти величины описывают разные этапы.
Можно ли распознавать один заранее вырезанный фрагмент?
Да. Для готового прямоугольного кропа используется Recognizer.recognize. Изображение должно содержать одну последовательность символов и соответствовать ожидаемой геометрии после внутренней подготовки. Если фрагмент задан четырьмя углами в большом кадре, сначала примените warpBox или recognize_from_boxes.
Почему результаты меняются после изменения размера?
Масштаб влияет на карту детектора, форму связей между символами и последующий кроп. Даже если распознаватель не менялся, другая рамка создаёт другой вход. Поэтому параметры scale и max_size являются частью конфигурации модели и должны сохраняться вместе с результатом и тестами.
Нужен ли Matplotlib для распознавания?
Matplotlib используется в примерах для отображения аннотаций. Само распознавание выполняют детектор и распознаватель; результат можно сохранить в JSON или рисовать OpenCV и Pillow. На сервере не создавайте фигуры без необходимости, иначе визуализация будет занимать память и замедлять поток.
Итоговый рабочий подход
Начните с небольшого набора реальных изображений и базового Pipeline. Сохраните рамки, строки и выровненные кропы. Затем определите, какой этап даёт ошибку: чтение, масштабирование, детекция, геометрическое выравнивание, распознавание или порядок чтения. Меняйте только относящиеся к нему параметры и сравнивайте одну и ту же контрольную выборку.
Для стабильного применения зафиксируйте Python-окружение, подготовьте кэш весов, ограничьте размеры входов, переиспользуйте модель и храните координаты без округления. Готовый распознаватель подходит для цифр и строчной латиницы; другие языки, регистр и пунктуация требуют собственного алфавита и обучения. При таком разделении Keras-OCR становится управляемым конвейером, а не чёрным ящиком: каждую рамку, строку и ошибку можно воспроизвести, измерить и исправить.
Диагностическая матрица по симптомам
| Симптом | Проверка | Действие |
|---|---|---|
| Импорт завершается ошибкой | Чистое виртуальное окружение и полный traceback | Согласовать TensorFlow, Keras, NumPy и OpenCV, затем зафиксировать зависимости |
| Pipeline долго создаётся | Отдельно измерить холодный старт и наличие файлов в кэше | Подготовить постоянный кэш и создавать модель один раз |
| GPU сразу заполнена | Число процессов, batch, scale и max_size | Включить рост памяти, уменьшить пакет и убрать повторные экземпляры модели |
| Текст не найден | RGB, размер букв, область интереса и пороги | Исправить вход, увеличить кроп, затем осторожно снижать пороги |
| Слишком много рамок | Текстуры фона и значения threshold | Ограничить ROI, повысить пороги или фильтровать геометрию |
| Слово разбито | Форма рамок и link_threshold | Подобрать связь символов или обучить детектор на домене |
| Слова слиты | Расстояние между надписями и пороги связи | Повысить link_threshold и проверить масштаб |
| Рамка точная, строка неверна | Сохранённый warp-кроп и алфавит | Исправить геометрию, расширить данные или обучить распознаватель |
| Неверный порядок | Центры рамок, колонки, наклон строк | Добавить анализ макета и сортировку по блокам |
| PDF обрабатывается медленно | Разрешение растеризации и число страниц в памяти | Обрабатывать страницы потоково и выбирать достаточное, а не максимальное разрешение |
Матрица полезна как правило маршрутизации. Она не заменяет traceback и визуальную проверку, но не даёт команде менять распознаватель при ошибке детекции или повышать разрешение при неверном алфавите. Для каждого симптома сохраните один воспроизводимый пример и ожидаемый результат после исправления.
Проектирование набора для приёмочных испытаний
Приёмочный набор должен включать не только обычные кадры. Разделите его на категории: прямой текст, умеренный наклон, сильная перспектива, мелкие буквы, блики, низкий контраст, частичная окклюзия, повторяющийся фон и отсутствие текста. В каждой категории фиксируйте ожидаемые слова и рамки. Отдельно добавьте негативные изображения, чтобы измерять ложные срабатывания.
Не используйте для финальной приёмки те же изображения, на которых подбирались пороги. После каждого изменения модели или предобработки прогоняйте весь набор автоматически и сохраняйте отчёт по категориям. Для критичных кодов включите список недопустимых ошибок: пропущенный символ, замена цифры буквой, слияние двух кодов и выход рамки за зону объекта.
Приёмка должна учитывать время и память. Один очень крупный кадр, один повреждённый файл и пакет смешанных размеров показывают поведение системы лучше, чем средняя фотография. Измеряйте максимальную память процесса, медиану и высокий процентиль времени, а также число ручных проверок на сто изображений.
Организация ручной проверки
Интерфейс проверки должен показывать увеличенный кроп, исходный контекст, распознанную строку и редактируемое поле. Пользователь подтверждает или исправляет строку, но не должен вручную переносить координаты. Если рамка неверна, предусмотрите отдельный статус ошибка области, иначе исправленная строка будет ошибочно считаться хорошим обучающим примером для распознавателя.
Сохраняйте автора исправления, время, исходное предсказание и тип ошибки. Несколько независимых проверок нужны для сложных символов и слабых фотографий. Перед добавлением исправлений в обучение удаляйте дубликаты и проверяйте согласованность алфавита. Так система постепенно получает реальные доменные данные вместо случайной смеси удачных и спорных правок.
Разбиение больших изображений на плитки
Для чертежа, карты или панорамы ограничение max_size может сделать мелкий текст слишком маленьким. Разбейте исходник на плитки одинакового размера с перекрытием. Каждая плитка распознаётся отдельно, после чего к координатам добавляется её смещение. Области в зоне перекрытия будут продублированы, поэтому требуется объединение по геометрическому совпадению и сходству строк.
tile_size = 1600
overlap = 240
step = tile_size - overlap
for y0 in range(0, image.shape[0], step):
for x0 in range(0, image.shape[1], step):
tile = image[y0:y0+tile_size, x0:x0+tile_size]
group = pipeline.recognize([tile])[0]
for word, box in group:
box = box + np.array([x0, y0], dtype=float)
collect(word, box)
Не делайте плитку меньше типичной длинной надписи. Если слово разрезано, обе половины могут дать ложные строки. После объединения сохраняйте связь с номером плитки для диагностики. Плитки удобно группировать в батчи, потому что они имеют одинаковую форму и требуют меньше пустого дополнения.
Предобработка: что применять осторожно
Автоматическая бинаризация и сильное повышение резкости иногда улучшают скан, но могут ухудшить цветной сценический текст, удалить тонкие штрихи и создать контуры вокруг шума. Всегда сравнивайте исходный RGB с обработанной копией. Если используется несколько вариантов, запускайте модель на каждом только при наличии правила выбора, иначе стоимость удваивается, а дубликаты рамок усложняют результат.
Коррекция перспективы полезна, когда известен контур плоской этикетки. Сначала выпрямите всю этикетку, затем запускайте детектор; это уменьшает разнообразие углов. Нормализация яркости по всему кадру может быть вредна при бликах. Лучше корректировать локальную область интереса или улучшать освещение на этапе съёмки.
Дедупликация рамок после нескольких проходов
Иногда полезно распознать кадр с двумя масштабами или исходный кадр и кропы. Результаты нельзя просто объединить: одинаковое слово появится несколько раз с немного разными рамками. Сначала вычислите геометрическое перекрытие четырёхугольников или их ограничивающих прямоугольников, затем сравните нормализованные строки. При высоком перекрытии оставляйте результат из прохода, которому доверяет ваша валидация.
Не выбирайте строку только по длине. Более длинное предсказание может содержать случайные символы. Если confidence распознавателя не выведен, используйте проверяемые правила домена или отправляйте конфликт на ручную проверку. Сохраняйте оба кандидата в диагностическом поле, чтобы анализировать пользу второго прохода.
Наблюдаемость в длительной обработке
Для каждого задания журналируйте идентификатор, хеш входа, размеры, время декодирования, время OCR, число рамок, параметры и статус сохранения. Резкий рост числа рамок может означать изменение фона или ошибку входного канала, а рост времени — появление больших файлов. Агрегированные метрики позволяют увидеть деградацию до жалоб пользователя.
Храните небольшой набор обезличенных контрольных изображений и запускайте его после обновления окружения. Smoke-тест должен проверять создание модели, распознавание, форму координат и запись JSON. Контрольный хеш выходного файла не всегда стабилен между вычислительными платформами, поэтому сравнивайте ожидаемые слова и допустимую геометрию с разумным допуском.
Безопасное обновление зависимостей
Обновляйте окружение в отдельной ветке или образе. Сначала устанавливается новый набор зависимостей, затем выполняются импорт, создание Pipeline, контрольные изображения, нагрузочный тест и проверка памяти. Только после этого новый образ заменяет рабочий. Возможность отката важнее попытки обновлять пакеты непосредственно на сервере.
Фиксируйте не только верхнеуровневые пакеты, но и транзитивные зависимости. Основной пакет занимает немного места, однако вычислительный стек включает крупные библиотеки, которые могут менять ABI и внутренние API. Сохранённый lock-файл или неизменяемый контейнер делает результат воспроизводимым и упрощает расследование.
Разделение ответственности в команде
Специалист по данным отвечает за разметку, метрики и контрольную выборку. Разработчик интеграции — за входные форматы, очередь, JSON и ошибки. Инженер эксплуатации — за GPU, кэш весов, мониторинг и обновления. Пользователь проверки — за корректные исправления и типизацию ошибок. Когда одна роль меняет пороги без обновления тестов, качество становится непредсказуемым.
Документируйте параметры рядом с кодом, а не в сообщениях чата. Для каждого развёртывания храните идентификатор весов, алфавит, scale, max_size, пороги и правила постобработки. Результат без такой конфигурации трудно воспроизвести даже на том же изображении.
Дополнительные проверки форматов и геометрии
Перед запуском модели проверяйте, что ширина и высота больше нуля, число каналов равно трём, а тип данных поддерживает ожидаемый диапазон. Массив float с диапазоном от нуля до единицы и массив uint8 от нуля до 255 визуально похожи, но вспомогательные функции могут обрабатывать их по-разному. Приводите формат в одном месте и добавьте тест на белое, чёрное и цветное изображение.
После распознавания проверяйте каждую рамку: значения конечны, форма равна 4 × 2, площадь положительна, а координаты находятся в разумном диапазоне относительно изображения. Небольшой выход за границу возможен после геометрических операций, но большой выброс указывает на ошибку преобразования. Перед рисованием ограничивайте координаты копией, сохраняя исходные значения в JSON.
Для путей используйте объект Path и явно задавайте кодировку JSON. Имена файлов могут содержать пробелы и нелатинские символы; не формируйте команды оболочки конкатенацией строк. При пакетной обработке результат лучше писать во временный файл и атомарно переименовывать, чтобы аварийное завершение не оставило корректно названный, но обрезанный JSON.
Разбор качества по длине и типу строк
Сгруппируйте метрики по длине слова, высоте символов, углу и типу фона. Короткие коды труднее проверять словарём, а длинные строки чаще сжимаются при выравнивании. Ошибка, скрытая общей средней, становится заметна в группе из двух-трёх символов или при угле выше заданного порога. Такая аналитика подсказывает, менять ли ширину входа распознавателя, собирать новые данные или улучшать съёмку.
Для смешанного алфавита отдельно считайте цифры, буквы и специальные знаки. Матрица замен показывает систематические пары. Если большинство ошибок связано с одним шрифтом или носителем, добавляйте данные именно этого типа. Не балансируйте набор механическим дублированием одинаковых кадров: разнообразие экземпляров важнее количества копий.
Проверка результата перед автоматическим действием
OCR не должен напрямую запускать необратимое действие без валидации. Серийный номер проверяется регулярным выражением, контрольной суммой или справочником допустимых значений. Координаты сравниваются с ожидаемой зоной. Если одно из условий не выполнено, результат отправляется на повторную съёмку или ручную проверку. Правило отказа должно быть консервативным и измеряться на тестовом наборе.
Даже правильная строка может относиться не к тому объекту, если кадр содержит несколько этикеток. Связывайте OCR с детекцией объекта или заранее определённой областью. При нескольких кандидатах сохраняйте все рамки и причины выбора. Это делает решение объяснимым и позволяет позже изменить бизнес-правило без повторного OCR.
Финальная проверка конфигурации
Перед вводом конвейера в рабочий процесс соберите конфигурацию в один версионируемый файл: алфавит распознавателя, идентификаторы весов, scale, max_size, пороги детектора, способ декодирования изображений, правила сортировки и формат JSON. Параметры командной строки должны переопределять этот файл явно и попадать в журнал задания. Тогда результат можно воспроизвести без догадок о значениях, которые использовал конкретный запуск.
Проверьте обратное преобразование координат на синтетическом прямоугольнике. Создайте изображение с известной рамкой, примените тот же кроп, масштаб или поворот, что используется в обработке, затем верните точки в исходную систему. Расхождение должно укладываться в заданный допуск. Такой тест обнаруживает перепутанные оси, повторное умножение на scale и забытое смещение области интереса до того, как ошибка попадёт в разметку или поиск.
Контрольный запуск должен включать пустой кадр, одно короткое слово, длинную строку, наклонную надпись, изображение с кириллицей и повреждённый файл. Для каждого случая заранее определите допустимое поведение: пустой список, корректная рамка, предсказание в пределах алфавита или зарегистрированная ошибка чтения. После обновления окружения сравнивайте не только строки, но и число рамок, порядок точек и время обработки.
Завершайте проверку сохранением результата во временный каталог, повторным чтением JSON и визуализацией рамок поверх исходного кадра. Только после успешной проверки перемещайте файлы в целевое хранилище. Такой порядок защищает от частично записанных данных и показывает геометрические ошибки, которые не видны в текстовом логе. При зафиксированной конфигурации, контрольной выборке и обратимых координатах поведение Keras-OCR остаётся измеримым на всём пути от изображения до решения.