Каждый день склады обрабатывают тысячи транспортных накладных. FedEx, UPS, DHL, USPS, региональные перевозчики — у каждого свой макет, размеры шрифтов и расположение полей. На накладке FedEx номер отслеживания может быть сверху, а на DHL — посередине. Обратный адрес у одного перевозчика выровнен по левому краю, у другого — по центру.
Ручной ввод данных медленный и подвержен ошибкам. Одна переставленная цифра — и посылка потеряна. Умножьте это на 5 000 отправлений в день — становится понятно, почему логистические компании тратят миллионы на автоматизацию.
Мы разработали систему для чтения транспортных накладных. В статье — как она устроена.
Система использует PaddleOCR — мощный OCR-движок с открытым исходным кодом — и дополняется пользовательским слоем обнаружения объектов. Этот слой превращает «сырой» текст в структурированные данные. В итоге система принимает фото любой накладной и возвращает чистый JSON с номерами отслеживания, адресами, информацией о перевозчике и типе доставки.
Код написан на Python. Модель обнаружения обучается менее чем за час. Система работает даже на CPU.
PaddleOCR за 10 строк
PaddleOCR — один из лучших доступных OCR-движков с открытым исходным кодом. Он выполняет обнаружение текста, распознавание и классификацию угла поворота за один вызов. Запустить его можно за считанные минуты.
В приведённом ниже коде используется класс PaddleOCR() в стиле версии 2.x, который по-прежнему работает в PaddleOCR 3.x для базового использования. В версии 3.x некоторые параметры изменены (например, удалён show_log, аргументы det/rec из ocr.ocr() убраны). Зафиксируйте версию paddleocr<3.0, если нужно сохранить старое поведение.
Запуск OCR на транспортной накладной
Мы запустили PaddleOCR (PP-OCRv5, май 2025) на сгенерированной накладке FedEx Express. Ниже — фактический вывод:
Пятнадцать строк, все с уверенностью выше 0.96. PaddleOCR хорошо справляется с распознаванием текста. Он нашёл все адреса, номера отслеживания, поля с весом. Пропущены «FEDEX» и «EXPRESS» — они находятся в белом тексте на чёрной плашке, что сложно для OCR. Для скрипта из десяти строк без обучающих данных — достойный результат.
Вот как выглядят ограничивающие рамки PaddleOCR на чистой накладной. Каждый зелёный многоугольник — обнаруженная текстовая область.
PaddleOCR надёжно работает на чистых, хорошо освещённых накладных. Проблемы начинаются, когда условия далеки от идеала.
Мы протестировали: результаты в 7 сценариях
Чтобы протестировать PaddleOCR в реальных условиях, мы создали семь сценариев: чистые сканы, повёрнутые накладные, частично закрытые, выцветшие, помятые и сцена с несколькими накладными — три посылки на одном столе.
Особенно выделяются два результата.
Распознавание текста оказалось лучше, чем ожидалось. Даже на выцветшей накладной (контраст снижен до 40%) все поля прочитаны корректно. На повёрнутой накладной (смещение на 15 градусов) встроенный классификатор угла исправил ориентацию и нашёл весь текст. Узкое место — не в самом OCR.
Сцена с несколькими накладными — прямое доказательство проблемы. Три накладные дали 53 строки текста. Адрес JOHN SMITH с первой накладной оказался рядом с номером отслеживания ROBERT CHEN со второй, вперемешку с почтовым индексом MARIA GARCIA с третьей. Распутать такой вывод с помощью регулярных выражений невозможно. Нельзя понять, какой адрес к какой посылке относится.
53 строки. Три посылки. Ноль структуры. Адреса перемешаны без разделителей. Это худший вариант проблемы сплошного массива текста — и он возникает каждый раз, когда в кадр попадает больше одной накладной.
Визуализация проблемы: PaddleOCR нашёл 53 текстовые области на трёх накладных. Весь текст смешан. Определить, какая коробка к какой посылке, невозможно.
Проблема сплошного массива текста
PaddleOCR возвращает текст. Весь текст. В порядке чтения — сверху вниз и слева направо. Но он не понимает, что означает каждый фрагмент.
Какая строка — номер отслеживания? Какие строки — адрес получателя? Где заканчивается адрес отправителя и начинается вес?
Первое желание — использовать регулярные выражения. Это работает, но частично.
Регулярное выражение может находить номера отслеживания FedEx. Но не сработает для UPS (1Z), DHL (10 цифр), USPS (20–22 цифры). Писать отдельное выражение для каждого перевозчика — кошмар с точки зрения поддержки. И это только одно поле.
С адресами ещё сложнее. Надпись «SHIP TO:» есть на одних накладных, на других — «DELIVER TO:», а где-то адрес без заголовка. Строк может быть две, три или пять — в зависимости от страны.
Вот ключевая мысль: OCR извлекает текст. Обнаружение объектов извлекает смысл.
Нужна модель, которая смотрит на изображение и рисует рамки вокруг конкретных полей: «здесь номер отслеживания», «здесь адрес получателя», «здесь штрихкод». Когда у вас есть эти рамки, вы вырезаете каждую область и запускаете OCR отдельно. Вывод перестаёт быть сплошным массивом. Он становится структурированным.
OCR возвращает плоский список. Слой обнаружения объектов организует тот же текст в размеченные поля.
Ниже — реальный результат из теста. Слева — необработанный список из PaddleOCR, справа — те же данные, организованные моделью обнаружения.
Почему универсального обнаружения недостаточно
У PaddleOCR уже есть детектор текста. Почему бы не использовать его?
Он находит любые текстовые области — вокруг слов, строк, абзацев. Это помогает найти, где есть текст, но не даёт понимания, что это за текст. Для детектора рамка вокруг «John Smith» выглядит так же, как вокруг «Memphis, TN 38118». Оба — просто «текст».
А что насчёт SAM? SAM выделяет области по визуальным границам, но не понимает структуру документа. Он может выделить штрихкод по контрасту, но не отличит адрес получателя от отправителя, если шрифт и фон одинаковы.
LayoutLM и похожие модели ближе к нужному. Они используют положение, содержимое и визуальные признаки. Но обучены на счётах, чеках, формах. Транспортные накладные, снятые под углом, с бликами, скотчем — это другая задача. Кроме того, такие модели требуют много ресурсов и медленны для высокой пропускной способности.
Самый прямой путь — пользовательская модель обнаружения объектов, обученная на ваших накладных. Вы задаёте классы: номер отслеживания, адрес получателя, отправителя, штрихкод, логотип перевозчика, тип доставки, вес, ссылочный номер. Размечаете 80–120 изображений, обучаете, разворачиваете. Модель учится понимать, как выглядит каждое поле и где оно обычно находится — даже у разных перевозчиков и при разной ориентации.
Хорошо обученная модель, например D-FINE или RT-DETR, справляется с повёрнутыми накладными, частичным перекрытием и разным качеством печати — потому что видела такие примеры на этапе обучения.
Пайплайн с этапа обнаружения
Полная система состоит из четырёх этапов: обнаружение, обрезка, OCR и структурирование. Каждый этап прост. Сила — в их последовательном объединении.
Вот как выглядит этап обнаружения на тестовой накладной. Каждый цвет — отдельный класс: красный — перевозчик, синий — отправитель, зелёный — получатель, оранжевый — номер отслеживания, фиолетовый — вес и дата.
Фактический вывод пайплайна
Мы запустили пайплайн на тестовых накладных, используя смоделированные рамки вместо обученного детектора. Вот результат:
Каждое поле размечено. Даже с неидеальными рамками пайплайн выдаёт структурированный результат за 3,6 секунды на CPU. Сравните с 15 необработанными строками из PaddleOCR. Тот же текст, но теперь каждый фрагмент имеет метку.
Визуализация показывает, что происходит на каждом этапе. Каждая область вырезается, передаётся в PaddleOCR, текст выводится справа с оценкой уверенности.
Этот вывод показывает, почему модель обнаружения так важна. Обратите внимание на результаты [LOW] и [BAD]: «CME ELECTRONICS INC» вместо «ACME» (рамка срезала первую букву), лишняя «J» в номере отслеживания (захватила край штрихкода), пустое поле веса (смещение рамки). Это ошибки точности разметки, а не OCR. Обученный детектор с более свободными рамками исправил бы все три случая. Именно такие детали настраиваются при разметке и обучении.
Обучение детектора в Datature
Шаг 1: Сбор изображений
Соберите 80–120 фотографий транспортных накладных. Возьмите разные службы доставки, углы съёмки, освещение, состояния: чистые, мятые, заклеенные лентой, надорванные. Снимайте той же камерой, что будет использоваться в работе. Если на складе — стационарная камера, снимайте ею. Если сотрудники — используйте телефоны.
Разнообразие важнее объёма. 100 разнородных изображений обучат лучше, чем 500 одинаковых снимков одной накладки FedEx. Полезные аугментации: поворот, изменение яркости, имитация размытия.
Шаг 2: Загрузка и разметка
Загрузите изображения в Datature Nexus и создайте восемь классов:
- tracking_number
- recipient_name
- recipient_address
- sender_name
- sender_address
- carrier
- service_type
- barcode
Нарисуйте плотные рамки вокруг каждого поля. Они должны полностью захватывать текст, но оставлять минимум пустого пространства.
После разметки 20–30 изображений включите разметку с поддержкой модели. Она начнёт предлагать рамки. Вам останется проверять и исправлять. Это сокращает время разметки вдвое.
Общее время на разметку 100 изображений — около 2 часов.
Шаг 3: Обучение
Выберите архитектуру. Для накладных хорошо подходят YOLOv8 и RT-DETR. YOLOv8 быстрее, RT-DETR лучше с нерегулярными макетами. Datature позволяет выбрать и автоматически настраивает параметры.
Обучение на 100 изображениях с предобученной сетью занимает около 45 минут на одной GPU. Следите за матрицей ошибок: типичные проблемы — путаница между адресами отправителя и получателя, пропуск мелкого текста типа доставки.
Шаг 4: Экспорт и развёртывание
Экспортируйте модель в ONNX или TorchScript. Код пайплайна использует интерфейс Ultralytics YOLO, но можно подставить любую модель, выдающую рамки с метками. Для периферийных устройств, например Raspberry Pi, экспортируйте в TFLite и оптимизируйте под оборудование.
Дальнейшее развитие
Пайплайн «обнаружение + OCR» — это база. Вот что можно добавить:
Декодирование штрихкодов
Пайплайн уже находит области штрихкодов. Подключите pyzbar или python-zxing, чтобы декодировать одномерные и двумерные штрихкоды. Сравните значение с номером отслеживания из OCR — это дополнительная проверка. Если не совпадают, помечайте накладную на ручную проверку.
Валидация адресов
Передавайте извлечённый адрес получателя в API валидации (USPS, Google Geocoding, SmartyStreets). Если адрес не проходит проверку, вы обнаружили ошибку до того, как посылка уйдёт не туда.
Интеграция с WMS
Большинство систем управления складом (WMS) принимают JSON через REST API. Структурированный вывод пайплайна напрямую сопоставляется с полями WMS. Настройте вебхук или пакетную обработку, чтобы исключить ручной ввод.
Пакетная обработка
Для высоконагруженных сценариев запускайте пайплайн в пакетном режиме. Получайте изображения, помещайте в очередь, обрабатывайте параллельно. Модель обнаружения работает со скоростью 30+ кадров в секунду на GPU — одна машина может обрабатывать тысячи накладных в час. Узкое место — PaddleOCR; запуск нескольких OCR-процессов параллельно решает проблему.
Не только транспортные накладные
Тот же подход (обнаружение, обрезка, OCR, структурирование) применим к любым документам с полуформализованной структурой: промышленные таблички, счётчики, чеки, удостоверения. Меняются только классы обнаружения — пайплайн остаётся тем же.
Частые вопросы
Работает ли пайплайн на CPU?
Да. Модели PaddleOCR оптимизированы для CPU. Ожидайте 200–500 мс на накладную на современных процессорах (Apple M, Intel i7). Уменьшайте крупные изображения до ~1500 пикселей по длинной стороне. Для высокой пропускной способности GPU сокращает время до менее чем 100 мс.
Сколько изображений нужно для обучения?
Для 8 классов и 3–5 перевозчиков достаточно 80–120 изображений. Для 10+ перевозчиков или сильно повреждённых накладных — 200–300. Качество и разнообразие важнее количества.
Может ли PaddleOCR работать с рукописным текстом?
Базовые модели плохо справляются с рукописью. С печатным текстом — всё хорошо. Рукописные пометки распознаются с низкой уверенностью. Если они часто встречаются, дообучите модель или помечайте результаты с низкой уверенностью на проверку.
Работает ли с накладными не на английском?
PaddleOCR поддерживает более 80 языков. Укажите параметр lang при инициализации. Модель обнаружения не зависит от языка — она определяет визуальные области. OCR отвечает за распознавание. Для многоязычных складов можно запускать несколько движков параллельно или использовать многозадачный режим.
В заключение
OCR — это простая часть. Сложность в том, чтобы понять, где искать.
PaddleOCR хорошо распознаёт текст. Но он не понимает, что он означает. Пользовательская модель обнаружения, обученная на 80–120 размеченных изображениях, закрывает этот разрыв. Она указывает, где находится номер отслеживания, адрес, название перевозчика.
Вся система — около 50 строк на Python. Модель обучается за один вечер. Результат — структурированные данные с каждой накладной, без ручного ввода и хрупких решений на регулярных выражениях.