Руководство по использованию Pipeline
Пайплайн последовательно выполняет детекцию, анализ структуры и распознавание, а при необходимости — коррекцию текста. Результаты передаются между этапами в объекте Page. Структура данных описана в Структуры данных, параметры методов — в Pipeline и Stack.
Быстрый старт
from manuscript import Pipeline
pipeline = Pipeline()
result = pipeline.predict("document.jpg")
print(result["page"].get_text())
Выбор этапов
Чтобы заменить стандартные компоненты, передайте свои при создании пайплайна. Значение None отключает этап. Детекция обязательна.
from manuscript.detectors import EAST
from manuscript.layouts import SimpleSorting
from manuscript.recognizers import TRBA
from manuscript.correctors import CharLM
pipeline = Pipeline(
detector=EAST(weights="east_50_g1"),
layout=SimpleSorting(use_columns=False),
recognizer=TRBA(weights="trba_lite_g2", recognition_levels=["word"]),
corrector=CharLM(),
)
Для поиска текста без распознавания используйте Pipeline(layout=None, recognizer=None). Чтобы добавить распознавание — Pipeline(layout=None). Устройство и параметры каждой модели задаются при её создании.
Параметр layout_after задаёт, после какого этапа выполнять анализ структуры: detector (по умолчанию), recognizer или corrector. Анализ структуры выполняется и тогда, когда выбранный предшествующий этап отключён.
pipeline = Pipeline(layout_after="recognizer", corrector=None)
Объединение обработчиков
Stack
Stack запускает обработчики в порядке списка. Каждый детектор получает исходное изображение, а его регионы добавляются в общий Page. Затем этап анализа структуры может определить вложенность. Дубликаты сохраняются.
from manuscript import Stack
from manuscript.layouts import ReadingSorting
# block_detector and line_detector are initialized detector instances.
pipeline = Pipeline(
detector=Stack([block_detector, line_detector]),
layout=ReadingSorting(mode="containment", containment_overlap=0.5),
recognizer=None,
)
Каждый обработчик страницы получает Page от предыдущего. Он может перезаписать текст, но встроенные распознаватели пропускают уже распознанные регионы. Результат последовательного запуска зависит от выбранных обработчиков.
Голосование распознавателей
Используйте ансамбль, когда распознаватели должны независимо читать одни и те же регионы:
from manuscript.recognizers import MajorityVotingRecognizer, MedoidRecognizer
# model_a, model_b and model_c are initialized recognizers.
recognizer = MajorityVotingRecognizer([model_a, model_b, model_c])
pipeline = Pipeline(recognizer=recognizer)
medoid = MedoidRecognizer([model_a, model_b, model_c], parallel=False)
Голосование большинства выбирает наиболее частую нормализованную строку; медоид — существующий ответ с минимальной суммой посимвольных расстояний редактирования. Сравнение выполняется после нормализации NFC, приведения к нижнему регистру и удаления внешних пробелов. Результат сохраняет наиболее частое написание с учётом регистра среди ответов, соответствующих победившей строке; при равенстве выбирается первый участник. Например, Москва, Москва, москва дают Москва. При return_raw=True поле winner содержит это написание, а normalized_winner — его ключ в votes (и distance_sums для медоида).
Согласование детекторов
Используйте ConsensusWBFDetector, чтобы сохранять объекты, подтверждённые несколькими моделями:
from manuscript.detectors import ConsensusWBFDetector
detector = ConsensusWBFDetector(
detectors=[detector_a, detector_b, detector_c],
weights=None, # Equal coordinate weights; alternatively [1, 2, 1].
cluster_iou=0.5,
consensus_fraction=0.5,
minimum_votes=2,
)
pipeline = Pipeline(detector=detector)
raw = detector.predict("page.jpg", return_raw=True)
votes = [item["votes"] for item in raw["detections"]]
Необходимое число голосов: max(minimum_votes, ceil(len(detectors) * consensus_fraction)). Пустые ответы учитываются в общем числе участников; ошибки инференса передаются вызывающему коду. Каждый участник даёт не более одного голоса на кластер. Области сопоставляются отдельно по (type, label); настройте одинаковые семантические метки у участников. Полигоны преобразуются в охватывающие прямоугольники, параллельные осям. Page участников должны использовать координаты и размеры исходного изображения, как у встроенных детекторов. Ансамбль не выполняет дополнительное масштабирование или NMS.
Участники выполняются в заданном порядке. Жадное взаимно однозначное сопоставление использует представителей кластеров, зафиксированных перед каждым участником; при равном IoU предпочтение отдаётся большим индексам объектов и кластеров. Алгоритм зависит от порядка. Представитель и итоговый прямоугольник вычисляются как среднее арифметическое либо взвешенное среднее с одним конечным положительным весом на участника. Веса влияют на геометрию и сопоставление, но не на порог голосов. Уверенность игнорируется; итоговая detection_confidence равна None.
Результат — плоский Page; геометрические потомки Page участников рассматриваются как детекции. Необработанные детекции содержат votes и detector_indices с индексами в заданном порядке участников. Слияние не создаёт маски или тензоры моделей. predict_batch(images) возвращает независимые результаты в порядке изображений, включая пустой список для пустого пакета, последовательно вызывая обработку одного изображения. Pickle сохраняет параметры и вложенных участников, если те поддерживают сериализацию; протокол конфигурации JSON для произвольных моделей не предполагается.
Память моделей
Используйте memory_policy="sequential", чтобы освобождать сессии каждой модели после использования и загружать их заново при необходимости. Значение по умолчанию "keep" сохраняет загруженные сессии.
recognizer = MedoidRecognizer([model_a, model_b, model_c])
pipeline = Pipeline(recognizer=recognizer, memory_policy="sequential")
Политика действует внутри вложенных Stack и ансамблей. Её можно задать отдельно для Stack, MajorityVotingRecognizer или MedoidRecognizer. Последовательный режим перед началом освобождает ранее загруженные сессии участников и очищает активную модель даже при ошибке предсказания. close() явно освобождает всех участников; после этого композицию можно использовать повторно.
Этот режим требует parallel=False во всей управляемой композиции. Пользовательские процессоры должны реализовать идемпотентный close() и поддерживать предсказание после закрытия; для процессоров без состояния допустима пустая реализация. Участники не должны одновременно использоваться другими конвейерами или прямыми вызовами без внешней синхронизации. Вызовы одной последовательной композиции выполняются по очереди.
Изображения, результаты голосования, словари и выделенная средой выполнения память могут продолжать занимать память. Освобождение сессий не гарантирует немедленного снижения RSS процесса или объёма GPU-памяти, отображаемого драйвером. Повторная инициализация сессий уменьшает объём постоянно загруженных моделей ценой скорости инференса.
CUDA-декодирование TrOCR
TrOCR(device="cuda", cuda_decoding="auto") при наличии поддержки сохраняет состояния энкодера, логиты и KV-кеши на GPU. Требуются графы KV-cache с динамическим размером пакета, PyTorch с CUDA, активный CUDAExecutionProvider во всех трёх ONNX-сессиях, привязка ввода-вывода и экспорт DLPack в ONNX Runtime. Установка [gpu,trocr-cuda] не гарантирует поддержку DLPack в wheel среды выполнения.
cuda_decoding="auto" однократно предупреждает и использует NumPy-декодирование, если требования CUDA-моста не выполнены. "required" вместо этого вызывает исключение; "off" отключает декодирование на устройстве. В режиме auto CPU, CoreML и Web используют NumPy. Ошибки активного CUDA-инференса передаются вызывающему коду. Оценки с плавающей точкой могут немного различаться при декодировании на CPU и CUDA.
Области распознавания и память пакетов
При region_preparer="bbox" задавайте padding_x, padding_y, inset_x и inset_y через region_preparer_options как неотрицательные доли исходного размера bbox с каждой стороны. Например, {"inset_y": 0.1} удаляет по 10% сверху и снизу до компенсации ориентации. Отступ внутрь должен быть меньше 0.5. Расширение и сужение по одной оси несовместимы, как и относительные отступы с ненулевым пиксельным pad. Эти параметры сохраняют геометрию Page; другие встроенные способы вырезания их отклоняют.
PPOCRRec поддерживает sort_by_width=True для группировки областей близкой ширины с восстановлением исходного порядка результатов. Опция включается явно, поскольку заполнение справа может влиять на CTC-предсказания. max_batch_pixels ограничивает число входных пикселей пакета с учётом заполнения (batch * height * width, без каналов), не меняя размер областей. Один вход, включая заполнение фиксированного ONNX-пакета, должен укладываться в бюджет, иначе вызывается ValueError. Это не ограничение всей памяти среды выполнения; batch_size по-прежнему ограничивает динамические пакеты. Области распознавания подготавливаются пакетами для снижения пикового потребления памяти изображений.
SimpleSorting теперь по умолчанию определяет колонки (use_columns=True). Используйте SimpleSorting(use_columns=False), чтобы сохранить группировку без определения колонок.
Пользовательские этапы
Свой компонент можно подключить через метод predict; наследоваться от базовых классов Manuscript необязательно. Детектор принимает изображение, остальные компоненты — Page. Результатом должен быть корректный Page. Детектор также может вернуть словарь, в котором Page хранится под ключом page.
Примеры интеграции см. в Примеры в ноутбуках.
Уровни распознавания и коррекции
Текст в дереве документа может храниться в словах, строках и блоках. Параметр recognition_levels выбирает, какие регионы распознавать, а correction_levels — на каких уровнях исправлять текст. Допустимые значения: word, line и block. В примере распознаём слова и строки, а исправляем только слова:
from manuscript import Pipeline
from manuscript.recognizers import TRBA
from manuscript.correctors import CharLM
pipeline = Pipeline(
recognizer=TRBA(
weights="trba_lite_g2", recognition_levels=["word", "line"],
),
corrector=CharLM(correction_levels=["word"]),
)
Выбранные регионы должны уже существовать в Page. Если выбраны и строка, и её слова, приоритет получают слова. Выбирайте уровни распознавания в соответствии с задачей обучения модели. Подробнее — в Распознаватели и Корректоры.
Результаты, визуализация и время выполнения
predict() возвращает {"page": page}. При vis=True результат — (result, preview), где preview — изображение PIL:
result, preview = pipeline.predict("document.jpg", vis=True, profile=True)
print(result["page"].get_text())
preview.save("preview.png")
print(pipeline.last_timings)
С profile=True время каждого этапа выводится на экран. Те же значения в секундах доступны в last_timings. Промежуточные результаты Page хранятся в last_detection_page, last_layout_page, last_recognition_page и last_correction_page. Подробнее — в Pipeline и Stack.