OCR всё прочитал, но ничего не понял: как мы научили систему разбирать транспортные накладные

от автора

Материал подготовлен для будущих студентов курса «Компьютерное зрение».

Каждый день склады обрабатывают тысячи транспортных наклеек. FedEx, UPS, DHL, USPS, региональные перевозчики — у каждого свой макет, свои размеры шрифтов и расположение полей. На наклейке FedEx номер отслеживания может находиться наверху, а на наклейке DHL — посередине. Обратный адрес у одного перевозчика выровнен по левому краю, у другого — по центру.

Ручной ввод данных медленный и подвержен ошибкам. Одна переставленная цифра в номере отслеживания — и посылка потеряна. Умножьте это на 5 000 отправлений в день — становится понятно, почему логистические компании тратят миллионы на системы распознавания наклеек.

Мы разработали систему для чтения транспортных наклеек. В статье расскажу, как она устроена.


Система начинается с PaddleOCR (мощный движок распознавания текста с открытым исходным кодом), а затем дополняется пользовательским слоем обнаружения объектов, который преобразует «сырой» текст в структурированные данные. К концу этого руководства у вас будет рабочий пайплайн обработки (pipeline), который принимает фотографию любой транспортной наклейки и возвращает чистый JSON-объект с номерами отслеживания, адресами, информацией о перевозчике и типом доставки.

Код написан на Python, модель обнаружения обучается менее чем за час, а вся система при необходимости работает на CPU.

PaddleOCR за 10 строк

PaddleOCR — один из лучших доступных OCR-движков с открытым исходным кодом. Он выполняет обнаружение текста, распознавание текста и классификацию угла поворота за один вызов. Запустить его можно за считанные минуты.

Установка

# Установка PaddlePaddle (версия для CPU)python -m pip install paddlepaddle==3.2.0 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/# Установка PaddleOCRpython -m pip install paddleocr

В приведённом ниже коде используется класс PaddleOCR() в стиле версии 2.x, который по-прежнему работает в PaddleOCR 3.x для базового использования. В версии 3.x некоторые параметры были изменены (например, удалён show_log, а аргументы det/rec из ocr.ocr() убраны). Зафиксируйте версию paddleocr<3.0, если вам нужно полностью сохранить старое поведение.

Запуск OCR на транспортной наклейке

from paddleocr import PaddleOCRimport cv2# Инициализация OCR-движкаocr = PaddleOCR(use_angle_cls=True, lang="en")# Запуск OCR на изображении транспортной наклейкиresult = ocr.ocr("shipping_label.jpg", cls=True)# Вывод всех найденных текстовых областейfor line in result[0]:   bbox, (text, confidence) = line[0], line[1]   print(f"[{confidence:.2f}] {text}")

Мы запустили PaddleOCR (PP-OCRv5, выпущен в мае 2025 года) на сгенерированной наклейке FedEx Express. Ниже приведён фактический вывод программы:

======================================================================IMAGE: 01_clean_label.pngOCR Time: 5.40s | Lines detected: 15====================================================================== #    Confidence   Text ---- ------------ -------------------------------------------------- 1    0.9994 [OK ]  FROM: 2    0.9848 [OK ]  ACME ELECTRONICS INC 3    0.9995 [OK ]  1234 INNOVATION BLVD STE 500 4    0.9887 [OK ]  SAN FRANCISCO CA 94105 5    0.9672 [OK ]  SHIP TO: 6    0.9999 [OK ]  JOHN SMITH 7    0.9995 [OK ]  5678 OAK AVENUE APT 12B 8    0.9993 [OK ]  PORTLAND OR 97201 9    0.9961 [OK ]  TRACKING #: 10   0.9990 [OK ]  7489 2374 9812 3847 1923 11   0.9857 [OK ]  WEIGHT: 3 LBS 8 OZ 12   0.9854 [OK ]  DATE: 03/19/2026 13   0.9965 [OK ]  DELIVERY CONFIRMATION 14   1.0000 [OK ]  4899132265117561797142 15   0.9999 [OK ]  97201 --- GROUND TRUTH CHECK --- FOUND   [sender_name]: ACME ELECTRONICS INC FOUND   [sender_address]: 1234 INNOVATION BLVD STE 500 FOUND   [sender_city]: SAN FRANCISCO CA 94105 FOUND   [recipient_name]: JOHN SMITH FOUND   [recipient_address]: 5678 OAK AVENUE APT 12B FOUND   [recipient_city]: PORTLAND OR 97201 FOUND   [tracking]: 7489 2374 9812 3847 1923 MISSED  [carrier]: FEDEX MISSED  [service]: EXPRESS FIELD ACCURACY: 7/9 (78%)

Пятнадцать строк, все с уверенностью выше 0.96. PaddleOCR хорошо справляется с распознаванием текста. Он нашёл все адреса, все номера отслеживания, все поля с весом. Пропущены «FEDEX» и «EXPRESS», потому что они находятся внутри белого текста на чёрной плашке — такие области сложнее для OCR-движков. Для скрипта из десяти строк без обучающих данных это весьма достойный результат.

Вот как выглядят ограничивающие рамки PaddleOCR на чистой наклейке. Каждый зелёный многоугольник — это обнаруженная область текста.

Shipping label with detected text regions highlighted by bounding boxes without field labels

Транспортная этикетка с выделенными ограничивающими рамками текстовыми областями без меток полей.

PaddleOCR обнаруживает текстовые области с высокой уверенностью на чистой, хорошо освещённой транспортной наклейке. Проблемы начинаются, когда наклейки далеки от идеала.

Посмотрим внимательнее на этот вывод.

PaddleOCR on clean label with bounding boxes and detected text list with confidence scores

PaddleOCR на чистом тексте с ограничивающими рамками и списком обнаруженного текста с оценками достоверности.

PaddleOCR обнаруживает текстовые области с высокой уверенностью на чистой, хорошо освещённой транспортной наклейке. Проблемы начинаются, когда наклейки далеки от идеала.

Мы протестировали: результаты в 7 сценариях

Чтобы провести нагрузочное тестирование PaddleOCR, мы создали семь сценариев с наклейками, отражающих реальные условия склада: чистые сканы, повернутые наклейки, частично закрытые наклейки, выцветшую печать, помятые поверхности и сцену с несколькими наклейками — три посылки на одном сортировочном столе.

Вот результаты:

Table comparing off-the-shelf OCR and detection plus OCR across different real-world scenarios

Таблица, сравнивающая стандартные системы оптического распознавания символов (OCR) и системы распознавания символов в сочетании с OCR в различных реальных условиях.

Ниже приведено сводное сообщение, которое вывел тестовый скрипт после обработки всех семи изображений:

======================================================================SUMMARY: PaddleOCR Raw Results Across Conditions======================================================================  Image                               Lines    Time       Avg Conf  ----------------------------------- -------- ---------- ----------  01_clean_label.png                  15       5.40s     0.993  02_clean_label_usps.png             15       4.66s     0.993  03_rotated_label.png                18       7.47s     0.918  04_obscured_label.png               16       4.96s     0.992  05_faded_label.png                  15       4.65s     0.992  06_multi_label_scene.png            53       14.28s    0.986  07_wrinkled_label.png               15       4.55s     0.988

Особенно выделяются два вывода.

Распознавание текста в PaddleOCR оказалось лучше, чем ожидалось. Даже на выцветшей наклейке (контраст снижен до 40%) все поля были прочитаны корректно. На повернутой наклейке (смещение на 15 градусов) встроенный классификатор угла исправил ориентацию и нашёл весь текст. Узкое место здесь не в самом OCR-движке.

Сцена с несколькими наклейками — это уже прямое доказательство проблемы. Три наклейки на одной поверхности дали 53 строки текста. Адрес JOHN SMITH с первой наклейки оказался рядом с номером отслеживания ROBERT CHEN со второй, вперемешку с почтовым индексом MARIA GARCIA с третьей. Распутать такой вывод с помощью регулярных выражений невозможно. Нельзя понять, какой адрес относится к какой посылке.

Вот что программа фактически вывела для сцены с несколькими наклейками:

======================================================================IMAGE: 06_multi_label_scene.pngOCR Time: 14.28s | Lines detected: 53======================================================================  #    Confidence   Text  ---- ------------ --------------------------------------------------  1    0.9999 [OK ]  FEDEX  2    0.9343 [OK ]  EXPRESS  ...  8    0.9881 [OK ]  USPS  ...  11   0.9975 [OK ]  JOHN SMITH              ← Label 1  12   0.9993 [OK ]  SARAH JOHNSON           ← Label 2  13   0.9686 [OK ]  5678 OAK AVENUE APT 12B ← Label 1  14   0.9924 [OK ]  890 MAPLE STREET        ← Label 2  15   0.9870 [OK ]  AUSTIN TX 78701         ← Label 2  16   0.9938 [OK ]  PORTLAND OR 97201       ← Label 1  ...  37   0.9984 [OK ]  UPS  ...  44   0.9933 [OK ]  MARIA GARCIA            ← Label 3  45   0.9993 [OK ]  789 SUNSET BLVD         ← Label 3  46   0.9827 [OK ]  LOS ANGELES CA 90028    ← Label 3  ...  NOTE: THREE labels on one surface - OCR returns a flat wall of text  The OCR returned 53 lines from ALL labels mixed together.  There is NO way to tell which text belongs to which label.  >>> This is the core 'wall of text' problem. <<<

53 строки. Три разные посылки. Ноль структуры. Адреса со всех трёх наклеек перемешаны без каких-либо разделителей. Это худший вариант проблемы сплошного массива текста, и он возникает каждый раз, когда в кадр камеры попадает больше одной наклейки.

Multiple shipping labels on boxes with OCR bounding boxes showing mixed detections across labels

На коробках обнаружено несколько транспортных этикеток с ограничивающими рамками, полученными с помощью оптического распознавания символов (OCR), что указывает на смешанное обнаружение символов на разных этикетках.

Визуализация проблемы сплошного массива текста. PaddleOCR нашёл 53 текстовые области на трёх наклейках, лежащих на одной поверхности. Весь текст смешан, и понять, какая коробка к какой посылке относится, невозможно.

OCR также должен справляться с деградацией изображения в реальных условиях. Вот как выглядят ограничивающие рамки на повернутой наклейке и на наклейке, частично закрытой стикером «FRAGILE»:

Tilted and partially smudged label with OCR boxes showing detection under imperfect conditions

Поворот на 15°: 18 рамок, часть с низкой уверенностью (красные)
Shipping label with FRAGILE sticker causing OCR to mix sticker text with label content

Частично перекрыта: текст со стикера попал в итоговый вывод

Проблема сплошного массива текста

PaddleOCR выдаёт вам текст. Весь текст. В порядке чтения, то есть примерно сверху вниз и слева направо. Но он никак не понимает, что означает каждый фрагмент этого текста.

Посмотрите на этот вывод ещё раз. Это просто плоский список. Какая строка здесь является номером отслеживания? Какие строки образуют адрес получателя? Где заканчивается адрес отправителя и начинается вес посылки?

Первое инстинктивное намерение — использовать регулярные выражения. И это работает, но лишь отчасти:

import redef extract_tracking(texts):   # Номер отслеживания FedEx: 12 или 15 цифр   for text in texts:       match = re.search(r"\b\d{4}\s?\d{4}\s?\d{4}(\s?\d{4}\s?\d{3})?\b", text)       if match:           return match.group().replace(" ", "")   return None

Это регулярное выражение находит номера отслеживания FedEx. Но оно не срабатывает для UPS (буквенно-цифровой формат 1Z). Не срабатывает для DHL (10-значный числовой формат). Не срабатывает для USPS (формат из 20-22 цифр). Писать отдельное регулярное выражение для формата номера отслеживания каждого перевозчика — это кошмар с точки зрения поддержки, и речь пока только об одном поле.

С адресами всё ещё хуже. Как понять, где начинается и заканчивается адрес получателя? Надпись «SHIP TO:» есть на одних наклейках и отсутствует на других. Где-то используется «DELIVER TO:», а где-то адрес просто напечатан без какого-либо заголовка. Строк адреса может быть две, три или пять — в зависимости от страны.

Вот ключевая мысль: OCR извлекает текст. Обнаружение объектов извлекает смысл.

Вам нужна модель, которая смотрит на изображение наклейки и рисует ограничивающие рамки вокруг конкретных полей: «в этом прямоугольнике находится номер отслеживания», «в этом прямоугольнике адрес получателя», «в этом прямоугольнике штрихкод». Когда у вас есть эти рамки, вы вырезаете каждую область и запускаете OCR отдельно для каждой из них. Вывод OCR перестаёт быть сплошным массивом текста. Он превращается в размеченный, структурированный результат.

Raw OCR text vs structured labeled fields with confidence using detection plus OCR

Сравнение исходного текста, полученного с помощью OCR, со структурированными полями с разметкой с высокой степенью достоверности с использованием обнаружения и OCR.

Сам по себе OCR возвращает плоский список строк. Добавление слоя обнаружения объектов организует тот же самый текст в размеченные, структурированные поля.

Ниже приведён реальный результат из нашего теста. Левая панель показывает необработанный список текста из PaddleOCR, правая — те же данные, но уже организованные моделью обнаружения объектов в размеченные поля.

Raw OCR list of 15 lines vs structured fields with confidence using detection plus OCR

Сравнение реального вывода. Слева — 15 необработанных строк без меток полей. Справа — каждое поле размечено цветными классами и оценками уверенности. Тот же текст, но уже структурированный для промышленного использования.

Почему универсального обнаружения недостаточно

Может возникнуть вопрос: у PaddleOCR уже есть модель обнаружения текста. Почему бы не использовать её?

Встроенный детектор PaddleOCR находит любые текстовые области. Он рисует рамки вокруг каждого слова, строки или абзаца, который видит. Это полезно, чтобы определить, где вообще есть текст, но не даёт понимания, что именно представляет собой каждая область. Для детектора рамка вокруг «John Smith» выглядит точно так же, как рамка вокруг «Memphis, TN 38118». И то и другое для него просто «текст».

А что насчёт универсальной модели сегментации вроде SAM? SAM умеет выделять области наклейки по визуальным границам, но не понимает структуру документа. Она может корректно выделить область штрихкода благодаря высокому визуальному контрасту, но не сможет отличить блок адреса получателя от блока адреса отправителя, если у них одинаковый шрифт и одинаковый цвет фона.

LayoutLM и похожие модели понимания документов ближе к тому, что нам нужно. Они объединяют положение текста, его содержимое и визуальные признаки, чтобы классифицировать области документа. Но обучены они на стандартных бизнес-документах: счетах, чеках, формах. Транспортные наклейки, снятые складской камерой под углом, с бликами, с частичным перекрытием от скотча или упаковки — это уже другая задача. Кроме того, такие модели требуют заметных вычислительных ресурсов и могут работать слишком медленно для сценариев с высокой пропускной способностью.

Самый прямой путь — это пользовательская модель обнаружения объектов, обученная на ваших конкретных типах наклеек. Вы задаёте классы: номер отслеживания, адрес получателя, адрес отправителя, штрихкод, логотип перевозчика, тип доставки, вес, ссылочный номер; размечаете 80-120 изображений; обучаете модель; разворачиваете её. Детектор учится понимать, как выглядит каждое поле и где оно обычно расположено на наклейке, в том числе у разных перевозчиков и при разной ориентации.

Хорошо обученная модель обнаружения объектов, такая как D-FINE или RT-DETR, справляется с повернутыми наклейками, частичным перекрытием и разным качеством печати, потому что во время обучения увидела примеры всех этих условий.

Пайплайн с этапа обнаружения

Полная система состоит из четырёх этапов: обнаружение, обрезка, OCR и структурирование. Каждый этап сам по себе довольно прост. Сила подхода — в их последовательном объединении.

Flowchart showing when to use raw OCR, template matching, or custom detection model

Пайплайн, начинающийся с обнаружения: найти поля, вырезать их, запустить OCR для каждого фрагмента и собрать структурированный результат.

Вот как выглядит этап обнаружения на нашей тестовой наклейке. Каждый цвет соответствует отдельному классу поля: красный — перевозчик, синий — отправитель, зелёный — получатель, оранжевый — номер отслеживания, фиолетовый — вес и дата.

Shipping label with color-coded bounding boxes for carrier, sender, recipient, tracking, weight, and date

Вывод модели обнаружения: 8 классов полей, каждый с цветной ограничивающей рамкой и меткой. Модель понимает, что находится в каждой области, а не просто фиксирует наличие текста.

Полный код пайплайна

from paddleocr import PaddleOCRfrom ultralytics import YOLOimport cv2import json# Инициализация моделейdetector = YOLO("label_detector.pt")  # Обучена на транспортных наклейкахocr = PaddleOCR(use_angle_cls=True, lang="en")# Определяем классы полей наклейкиFIELD_CLASSES = {   0: "tracking_number",   1: "recipient_name",   2: "recipient_address",   3: "sender_name",   4: "sender_address",   5: "carrier",   6: "service_type",   7: "barcode",}def read_shipping_label(image_path, conf_threshold=0.5):   """Полный пайплайн: находит поля, вырезает их, запускает OCR для каждого и возвращает структурированные данные."""   img = cv2.imread(image_path)   h, w = img.shape[:2]   # Этап 1: находим области на наклейке   results = detector(image_path, conf=conf_threshold)[0]   # Этапы 2 и 3: вырезаем каждую область и запускаем OCR   fields = {}   for box in results.boxes:       cls_id = int(box.cls[0])       confidence = float(box.conf[0])       field_name = FIELD_CLASSES.get(cls_id, "unknown")       # Получаем координаты ограничивающей рамки с отступом 5 пикселей       x1, y1, x2, y2 = box.xyxy[0].tolist()       pad = 5       x1 = max(0, int(x1) - pad)       y1 = max(0, int(y1) - pad)       x2 = min(w, int(x2) + pad)       y2 = min(h, int(y2) + pad)       # Вырезаем найденную область       crop = img[y1:y2, x1:x2]       # Пропускаем OCR для областей со штрихкодом       # Вместо этого используем библиотеку для декодирования штрихкодов       if field_name == "barcode":           fields[field_name] = {               "bbox": [x1, y1, x2, y2],               "detection_confidence": round(confidence, 3),               "note": "Use pyzbar or zxing for barcode decoding"           }           continue       # Запускаем OCR на вырезанной области       ocr_result = ocr.ocr(crop, cls=True)       # Собираем все строки текста из вырезанного фрагмента       text_lines = []       if ocr_result and ocr_result[0]:           for line in ocr_result[0]:               text_lines.append(line[1][0])       fields[field_name] = {           "text": " | ".join(text_lines),           "lines": text_lines,           "bbox": [x1, y1, x2, y2],           "detection_confidence": round(confidence, 3),       }   # Этап 4: возвращаем структурированный JSON   return {       "source_image": image_path,       "fields_detected": len(fields),       "data": fields,   }# Запускаем пайплайнoutput = read_shipping_label("warehouse_photo.jpg")print(json.dumps(output, indent=2))

Фактический вывод пайплайна

Мы запустили этот пайплайн на наших тестовых наклейках, используя смоделированные ограничивающие рамки вместо обученного детектора. Вот фактический вывод программы:

======================================================================DETECTION-FIRST PIPELINE: 01_clean_label.png====================================================================== Step 1: Detect 8 regions (simulated)   [carrier]           -> bbox [5, 5, 795, 70]   [sender_name]       -> bbox [15, 90, 500, 130]   [sender_address]    -> bbox [15, 130, 500, 200]   [recipient_name]    -> bbox [30, 260, 600, 310]   [recipient_address] -> bbox [30, 310, 600, 410]   [tracking_number]   -> bbox [15, 440, 500, 500]   [weight]            -> bbox [15, 590, 300, 625]   [date]              -> bbox [380, 590, 600, 625] Step 2: Crop & OCR each region   [carrier]           -> "EXPRESS FEDEX"                    (0.9998) [OK]   [sender_name]       -> "CME ELECTRONICS INC DM:"          (0.8769) [LOW]   [sender_address]    -> "1234 INNOVATION BLVD STE 500..."  (0.9839) [OK]   [recipient_name]    -> "JOHN SMITH"                       (0.9999) [OK]   [recipient_address] -> "PORTLAND OR 97201"                (0.9998) [OK]   [tracking_number]   -> "7489 2374 9812 3847 1923 J"      (0.7721) [LOW]   [weight]            -> ""                                 (0.0000) [BAD]   [date]              -> "DATE. 0371912026"                 (0.9214) [OK] Step 3: Structured JSON Output {   "carrier":           "EXPRESS FEDEX",   "sender_name":       "CME ELECTRONICS INC DM:",   "sender_address":    "1234 INNOVATION BLVD STE 500 SAN FRANCISCO CA 94 105",   "recipient_name":    "JOHN SMITH",   "recipient_address": "PORTLAND OR 97201",   "tracking_number":   "7489 2374 9812 3847 1923 J",   "date":              "DATE. 0371912026" } Total pipeline time: 3.61s

Каждое поле размечено. Даже с моделируемыми, то есть неидеальными, ограничивающими рамками пайплайн выдаёт структурированный результат за 3,6 секунды на CPU. Сравните это с 15 необработанными строками из PaddleOCR на том же изображении. Тот же текст, но теперь каждый фрагмент имеет своё название.

Эта визуализация точно показывает, что происходит на каждом этапе. Каждая обнаруженная область вырезается из наклейки, по отдельности передаётся в PaddleOCR, а извлечённый текст выводится справа вместе с оценкой уверенности:

Pipeline showing detection, cropped fields, OCR per crop, and structured JSON output

Полный пайплайн с вырезанием областей и OCR. Каждая обнаруженная область вырезается, проходит через PaddleOCR и возвращает размеченный текст с оценкой уверенности. Перевозчик: 0.9998, имя получателя: 0.9997, номер отслеживания: 0.9902.

Этот вывод также наглядно показывает, почему модель обнаружения так важна. Обратите внимание на результаты [LOW] и [BAD]: «CME ELECTRONICS INC» вместо «ACME» (ограничивающая рамка срезала первую букву), лишняя «J» в конце номера отслеживания (рамка захватила край штрихкода) и пустое поле веса (координаты рамки были немного смещены). Это ошибки точности разметки, а не ошибки OCR. Обученный детектор, который строит чуть более свободные рамки вокруг каждого поля, исправил бы все три случая. Именно такие детали настраиваются в процессе разметки и обучения в Datature.

Обучение детектора в Datature

Приведённый выше пайплайн предполагает, что у вас уже есть обученная модель обнаружения (label_detector.pt). Вот как её создать.

Шаг 1: Сбор изображений

Соберите 80-120 фотографий транспортных наклеек. Возьмите разные службы доставки, разные углы съёмки, условия освещения и состояния наклеек: чистые, мятые, заклеенные лентой, частично надорванные. Снимайте на ту же камеру и в той же конфигурации, которая будет использоваться в рабочей среде. Если на складе используется стационарная верхняя камера, снимайте ею. Если сотрудники будут пользоваться камерами телефонов, собирайте изображения с телефонов.

Разнообразие важнее объёма. 100 разнородных изображений обучат модель лучше, чем 500 снимков одной и той же наклейки FedEx при одинаковом освещении. Из приёмов аугментации данных, которые помогают эффективнее использовать небольшой набор данных, для этой задачи наиболее полезны поворот, изменение яркости и имитация размытия.

Шаг 2: Загрузка и разметка

Загрузите изображения в Datature Nexus и создайте восемь классов разметки:

  1. tracking_number

  2. recipient_name

  3. recipient_address

  4. sender_name

  5. sender_address

  6. carrier

  7. service_type

  8. barcode

Нарисуйте ограничивающие рамки вокруг каждого поля на каждой наклейке. Лучше всего работают плотные рамки: они должны полностью захватывать текст, но оставлять как можно меньше лишнего пустого пространства вокруг.

После того как вручную будут размечены 20-30 изображений, переходите к разметке с поддержкой модели. Модель начнёт предлагать ограничивающие рамки на основе того, чему уже успела научиться. Вам останется проверять и исправлять эти предложения, а не размечать всё с нуля. Это сокращает время разметки примерно вдвое.

Общее время разметки 100 изображений: около 2 часов.

Шаг 3: Обучение

Выберите архитектуру обнаружения. Для транспортных наклеек хорошо подходят и YOLOv8, и RT-DETR. YOLOv8 быстрее на этапе инференса, а RT-DETR лучше справляется с нерегулярными макетами. Datature позволяет выбрать один из этих вариантов и автоматически настраивает параметры обучения.

Обучение на 100 изображениях с предобученной базовой сетью занимает около 45 минут на одной GPU. Во время обучения следите за матрицей ошибок: типичные проблемы — путаница между адресом отправителя и адресом получателя, поскольку они выглядят похоже, и пропуск текста с типом доставки, если он напечатан мелким шрифтом.

Шаг 4: Экспорт и развёртывание

Экспортируйте обученную модель в формате ONNX или TorchScript. Код пайплайна выше использует интерфейс Ultralytics YOLO, но вместо него можно подставить любую модель обнаружения, которая выдаёт ограничивающие рамки с метками классов. Для развёртывания на периферийных устройствах, например на Raspberry Pi, экспортируйте модель в TFLite и оптимизируйте её под целевое оборудование.

Пошаговое руководство по созданию моделей обнаружения с нуля доступно в туториале по обнаружению объектов.

Если вы обрабатываете наклейки только одного перевозчика в контролируемой среде с использованием планшетного сканера, PaddleOCR в сочетании с несколькими хорошо проверенными регулярными выражениями будет достаточно. Не тратьте лишнее время.

Если вы работаете с разными перевозчиками, используете складские камеры, имеете дело с разными углами съёмки, наклейками, попавшими под дождь или заклеенными скотчем, либо вам нужен надёжный структурированный вывод для последующей системы, пользовательская модель обнаружения оправдывает 2–3 часа на настройку.

Flowchart deciding between raw OCR, template matching, or custom detection based on fields, formats, and image type

Блок-схема, демонстрирующая выбор между распознаванием текста без обработки, сопоставлением с шаблоном или пользовательским обнаружением на основе полей, форматов и типа изображения.

Используйте это дерево решений, чтобы определить, требуется ли в вашем случае пользовательская модель обнаружения или достаточно готового OCR-решения «из коробки».

Стоит упомянуть и промежуточный вариант. Если у вас 2–3 формата наклеек и чистые сканированные изображения, может подойти сопоставление по шаблону: вы определяете, к какому перевозчику относится наклейка, и применяете специфичные для него правила разбора. Но как только добавляется съёмка камерой или четвёртый перевозчик, становится очевидно, что лучше было сразу использовать обнаружение.

Дальнейшее развитие

Пайплайн «обнаружение + OCR» — это база. Вот какие возможности можно добавить сверху.

Декодирование штрихкодов

Пайплайн уже определяет области со штрихкодами. Подключите к нему pyzbar или python-zxing, чтобы декодировать одномерные и двумерные штрихкоды. Сравнивайте полученное значение со значением номера отслеживания, извлечённым через OCR, — это даст дополнительную проверку. Если значения не совпадают, помечайте наклейку для ручной проверки.

from pyzbar import pyzbardef decode_barcode(crop):    barcodes = pyzbar.decode(crop)    if barcodes:        return barcodes[0].data.decode("utf-8")    return None

Валидация адресов

Передавайте извлечённый адрес получателя в API почтовой валидации (USPS, Google Geocoding или SmartyStreets). Если адрес не проходит проверку, значит, вы обнаружили ошибку OCR до того, как посылка отправилась не туда.

Интеграция с WMS

Большинство систем управления складом (WMS, warehouse management system) принимают JSON через REST API. Структурированный вывод пайплайна напрямую сопоставляется с полями WMS. Настройте вебхук или пакетную обработку, чтобы передавать разобранные данные наклеек в WMS и исключить ручной ввод.

Пакетная обработка

Для высоконагруженных сценариев запускайте пайплайн в пакетном режиме. Непрерывно получайте изображения, помещайте их в очередь и обрабатывайте параллельно. Модель обнаружения работает со скоростью 30+ кадров в секунду на GPU, поэтому одна машина может обрабатывать тысячи наклеек в час. Узкое место — PaddleOCR; запуск нескольких OCR-процессов параллельно решает эту проблему.

Не только транспортные наклейки

Этот же подход (обнаружение областей, обрезка, OCR, структурирование) применим к любым документам с полуформализованной структурой. Промышленные таблички с серийными номерами и характеристиками. Счётчики коммунальных ресурсов с окнами показаний. Кассовые чеки с позициями. Государственные удостоверения с полями имени и даты. Меняются только классы обнаружения — сам пайплайн остаётся тем же.

FAQ

Работает ли этот пайплайн на CPU?

Да. Модели PaddleOCR оптимизированы для выполнения на этапе предсказания (inference) на CPU. Ожидайте 200–500 мс на одну транспортную наклейку на современных процессорах (Apple серии M, последние Intel i7). Время увеличивается с ростом разрешения изображения и плотности текста, поэтому перед обработкой крупные изображения лучше уменьшать до ~1500 пикселей по длинной стороне. Для систем с высокой пропускной способностью (тысячи наклеек в час) использование GPU сокращает время инференса до менее чем 100 мс.

Сколько изображений нужно для обучения?

Для 8 классов полей и 3–5 типов перевозчиков достаточно 80–120 размеченных изображений, чтобы получить рабочую модель. Если нужно покрыть 10+ перевозчиков или обрабатывать сильно повреждённые наклейки, ориентируйтесь на 200–300. Качество и разнообразие данных важнее их количества. Включайте разные условия освещения, углы съёмки, состояния наклеек и типы перевозчиков.

Может ли PaddleOCR работать с рукописным текстом на наклейках?

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

Работает ли это с наклейками не на английском языке?

PaddleOCR поддерживает более 80 языков. Укажите параметр lang при инициализации движка. Модель обнаружения не зависит от языка, так как она определяет визуальные области, а не содержание текста. За распознавание языка отвечает этап OCR. Для многоязычных складов можно запускать несколько OCR-движков параллельно или использовать многозадачный режим PaddleOCR.

В заключение

OCR — это простая часть. Сложность в том, чтобы понять, где искать.

PaddleOCR хорошо распознаёт текст. Но он не понимает, что этот текст означает. Пользовательская модель обнаружения, обученная на 80–120 размеченных изображениях, закрывает этот разрыв. Она указывает PaddleOCR, где находится номер отслеживания, где адрес, где название перевозчика.

Вся система — это около 50 строк на Python. Модель обнаружения обучается за один вечер. Результат — структурированные, размеченные данные с каждой транспортной наклейки, проходящей через ваш склад, без ручного ввода и без хрупких решений на регулярных выражениях.

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

  • 28 апреля в 20:00. «Как работают современные модели компьютерного зрения “из коробки”». Поговорим про Hugging Face, OpenCV и YOLO: что реально значит “запустить за 5 минут”, как устроен базовый inference-пайплайн и где заканчиваются возможности готовых моделей. Записаться

  • 19 мая в 20:00. «Событийная видеоаналитика: превращаем отслеживание в бизнес-логику». Как из детекции и трекинга собрать рабочую аналитику: зоны, пересечения, время в области и превращение координат в понятные события и метрики. Записаться

Немного практики в тему — пройдите вступительный тест по компьютерному зрению и узнаете, есть ли пробелы в знаниях. Бонус за прохождение теста: скидка 15% на курс.

ссылка на оригинал статьи https://habr.com/ru/articles/1026088/