Создание штрих‑кода PDF417 занимает всего четыре строки кода на Python. Чтение его из фотографии телефона, сканирования с разрешением 200 DPI или посадочного талона, который провёл день в кармане, — вот где начинается настоящая работа, и именно здесь большинство интеграций PDF417 действительно терпят неудачу. В этой статье вы узнаете, как считывать штрих‑коды PDF417 в Python, когда исходное изображение несовершенно. Если вам интересна сторона генерации, то ранее опубликованный пошаговый материал о том, как генерировать штрих‑коды PDF417 с помощью Python охватывает создание базовых и пользовательских символов PDF417, включая управление строками и столбцами, а руководство по чтению штрих‑кодов на Python описывает универсальное сканирование различных символогий. Далее будет узко и глубоко рассматриваться распознавание PDF417.

Каждый фрагмент ниже был выполнен с использованием Aspose.BarCode for Python via .NET 26.7 на Linux с Python 3.12. Время выполнения и результаты чтения/пропусков измерены, а не оценены, а скрипт, который их создал, находится в конце, чтобы вы могли запустить его на своих собственных изображениях.

Что действительно нарушает распознавание PDF417

PDF417 — это многослойный символьный набор: строки кодовых слов, каждое кодовое слово построено из четырёх штрихов и четырёх пробелов, охватывающих 17 модулей. Эта структура содержит коррекцию ошибок Рида‑Соломона, поэтому порванный угол или пятно от кофе обычно можно восстановить. То, что невозможно восстановить, — это потеря самой сетки модулей.

Три вещи разрушают сетку, в приблизительном порядке того, как часто они встречаются в продакшене:

  • Эффективная ширина модуля менее двух пикселей. Это основная причина сбоя. Это происходит из‑за уменьшения масштаба, расстояния до камеры и размытия, которое растягивает чернила одного модуля на соседние. Штрих‑код, сгенерированный при трёх пикселях на модуль и затем размытый, фактически является штрих‑кодом при одном пикселе на модуль.
  • Перспектива и вращение. Штрих‑код, сфотографированный под углом, имеет строки, которые больше не параллельны сканирующим линиям. Детектор справляется с этим, но это требует прохода обнаружения, который пропускает самый быстрый предустановленный режим.
  • Потеря контраста. Термобумага, которая выцвела, экран, сфотографированный под бликом, или скан с зафиксированной яркостью. Менее критично, чем кажется, как показывают цифры ниже.

Практический вывод состоит в том, что настройки распознавания являются механизмом восстановления, а не заменой качества захвата. Они позволяют восстановить средний диапазон качества изображения. Они не восстанавливают нижний диапазон.

Настройка считывателя PDF417 на Python

Установите библиотеку из PyPI:

pip install aspose-barcode-for-python-via-net

API распознавания находится в aspose.barcode.barcoderecognition. Для этой статьи важны два класса: BarCodeReader, который управляет обнаружением и декодированием, и QualitySettings, который контролирует, насколько усердно работает считыватель.

Чтение штрих‑кода PDF417 в Python

Минимальный рабочий пример чтения состоит из двух аргументов и цикла:

from aspose.barcode import barcoderecognition as rec

reader = rec.BarCodeReader("clean.png", rec.DecodeType.PDF417)

for result in reader.read_bar_codes():
    print("Code text:", result.code_text)
    print("Symbology:", result.code_type_name)
    print("Reading quality:", result.reading_quality)
Считать штрих‑код PDF417 в Python.

Считать штрих‑код PDF417 в Python.

Вывод на чистом изображении:

Code text: PASSENGER|KHAN/M|SEAT14C|LHE-DXB
Symbology: Pdf417
Reading quality: 100.0

Второй аргумент на практике не является необязательным. Оставьте его, и BarCodeReader переходит к DecodeType.ALL_SUPPORTED_TYPES, что запускает обнаружение более чем шести десятков символогий для каждой кандидатской области на изображении. В конвейере, работающем только с PDF417, это просто пустая трата ресурсов, и это открывает возможность ложного срабатывания линейной символогии внутри структуры строки PDF417. Укажите тип.

Если ваш ввод поступает в виде байтов из загрузки, а не из файла на диске, передайте поток и установите тайм‑аут, чтобы патологическое изображение не могло блокировать рабочий процесс:

import io
from aspose.barcode import barcoderecognition as rec

def decode_pdf417(image_bytes, timeout_ms=2000):
    reader = rec.BarCodeReader(io.BytesIO(image_bytes), rec.DecodeType.PDF417)
    reader.timeout = timeout_ms
    return [r.code_text for r in reader.read_bar_codes()]

Сравнение настроек качества распознавания PDF417 в Python

QualitySettings поставляется с четырьмя предустановками, которые стоит знать. Присвоить одну из них читателю — это одна строка:

reader = rec.BarCodeReader("scan.png", rec.DecodeType.PDF417)
reader.quality_settings = rec.QualitySettings.high_quality
ПресетПредназначено дляСтоимость
high_performanceЧистые, сгенерированные машиной изображенияСамый быстрый, пропускает проходы восстановления
normal_qualityПо умолчанию. Обычные сканы и экспортыСбалансировано
high_qualityСнимки с камеры, ухудшенные сканыУмеренно
max_qualityПовторный проход для изображений, которые уже не прошлиСамый медленный с большим отрывом

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

Source imagehigh_performancenormal_qualityhigh_qualitymax_quality
Чистыйчтение, 35 msчтение, 50 msчтение, 45 msчтение, 106 ms
Повернуто на 12°, размытопромах, 15 msчтение, 74 msчтение, 67 msчтение, 157 ms
Гауссов шум (σ 40)чтение, 31 msчтение, 49 msчтение, 52 msчтение, 123 ms
Половинное разрешениечтение, 22 msчтение, 37 msчтение, 35 msчтение, 86 ms
Низкий контрастчтение, 24 msчтение, 38 msчтение, 43 msчтение, 112 ms
Сильное размытие (σ 2.2)промах, 5 msпромах, 25 msпромах, 85 msпромах, 180 ms

Из этой таблицы выводятся четыре вещи, и они более полезны, чем отдельные числа:

  1. Шум и низкий контраст не являются той проблемой, какой их считают. Reed‑Solomon плюс детектор поглотили сильный гауссовский шум и снижение контраста на 82 % в самом быстром пресете. Прекратите настраивать их.
  2. Поворот плюс размытие — это случай, который разделяет пресеты. Это также точный профиль захвата с handheld‑телефона, поэтому high_performance является неправильным значением по умолчанию для любого конвейера, полученного с мобильных устройств.
  3. max_quality требует в три раза больше времени, чем normal_quality, и не читает ничего дополнительно. Он заслуживает место как путь второй попытки, а не как значение по умолчанию.
  4. Как только сетка модуля исчезает, ни один пресет её не восстанавливает. Строка с сильным размытием — провал во всех случаях, и max_quality потратил 180 мс, подтверждая это. Это проблема захвата, решаемая исправлением захвата: больше пикселей, лучшее фокусирование, больший x_dimension во время генерации.

Настройте QualitySettings для чтения размытых штрих‑кодов PDF417 в Python

Пресет — это набор отдельных свойств, и вы можете начать с пресета и переопределить один из них. Именно здесь находятся реальные выгоды для известного, воспроизводимого дефекта.

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

from aspose.barcode import barcoderecognition as rec

reader = rec.BarCodeReader("rotated.png", rec.DecodeType.PDF417)

quality = rec.QualitySettings.high_performance
quality.deconvolution = rec.DeconvolutionMode.NORMAL
reader.quality_settings = quality

for result in reader.read_bar_codes():
    print(result.code_text)
Настройте QualitySettings для чтения размытых штрих‑кодов PDF417 в Python.

Настройте QualitySettings для чтения размытых штрих‑кодов PDF417 в Python.

Измерено: промах за 12 мс без переопределения, чтение за 54 мс с ним. Одна настройка изменила результат, и он всё равно вернулся быстрее, чем предустановка max_quality.

Свойства, которые стоит знать:

СвойствоЗначенияКогда использовать
deconvolutionFAST, NORMAL, SLOWРазмытие движения и снимки с расфокусировкой
barcode_qualityHIGH, NORMAL, LOWДефекты печати, термическое выцветание, артефакты факса
x_dimensionAUTO, SMALL, NORMAL, LARGE, USE_MINIMAL_X_DIMENSIONПлотные штрихкоды или фиксированный известный размер модуля
minimal_x_dimensionfloatВ сочетании с USE_MINIMAL_X_DIMENSION задаёт нижний предел
complex_backgroundAUTO, DISABLED, ENABLEDШтрихкод, напечатанный поверх графики или узорчатой формы
inverse_imageAUTO, DISABLED, ENABLEDШтрихкоды белый‑на‑чёрном, скриншоты
allow_incorrect_barcodesboolТолько для диагностики, никогда в продакшн

Последний из них заслуживает предупреждения. allow_incorrect_barcodes возвращает символы, которые не прошли проверку контрольной суммы. Это средство отладки, помогающее ответить на вопрос «обнаружил ли детектор что‑то здесь?». Поставляя его, вы отправляете декодированные строки, которые неверны, а не отсутствуют, что однозначно хуже, чем пропуск.

Стратегия уровней, которая хорошо работает в продакшене: сначала попробуйте normal_quality, а при неудаче повторите попытку один раз с high_quality плюс deconvolution = SLOW. Вы платите за дорогой путь только для изображений, которым это необходимо.

Проверка результатов декодирования PDF417 в Python

Декодированная строка не автоматически является корректной строкой. Три поля в BarCodeResult помогают:

for result in reader.read_bar_codes():
    print("Text:", result.code_text)
    print("Reading quality:", result.reading_quality)
    print("Confidence:", result.confidence)
    print("Region:", [(p.x, p.y) for p in result.region.points])
Text: PASSENGER|KHAN/M|SEAT14C|LHE-DXB
Reading quality: 100.0
Confidence: 100
Region: [(7, 6), (261, 7), (261, 276), (7, 277)]

Воссоздать макрос PDF417, разбитый на несколько изображений

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

from aspose.barcode import barcoderecognition as rec

segments = {}
total = None

for path in ["page1.png", "page2.png"]:
    reader = rec.BarCodeReader(path, rec.DecodeType.MACRO_PDF417)
    for result in reader.read_bar_codes():
        meta = result.extended.pdf417
        segments[meta.macro_pdf_417_segment_id] = result.code_text
        total = meta.macro_pdf_417_segments_count
        print(f"{path}: segment {meta.macro_pdf_417_segment_id + 1} of {total}, "
              f"file id {meta.macro_pdf_417_file_id}")

if total and len(segments) == total:
    payload = "".join(segments[i] for i in sorted(segments))
    print("Reassembled:", payload)
else:
    print("Incomplete: missing segments")
Страница 1.

Страница 1.

Страница 2.

Страница 2.

Output:

Data\pdf417\page1.png: segment 1 of 2, file id 42
Data\pdf417\page2.png: segment 2 of 2, file id 42
Reassembled: SEGMENT-A-DATASEGMENT-B-DATA

Обратите внимание, что macro_pdf_417_file_id является целым числом, а не строкой, как при генерации, так и при распознавании. Тот же объект также предоставляет macro_pdf_417_file_name, macro_pdf_417_sender, macro_pdf_417_addressee, macro_pdf_417_time_stamp и macro_pdf_417_checksum, когда кодировщик заполняет их.

Оценка распознавания PDF417 на ваших собственных изображениях

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

import time, statistics, pathlib
from aspose.barcode import barcoderecognition as rec

PRESETS = ["high_performance", "normal_quality", "high_quality", "max_quality"]

def probe(path, preset, reps=3):
    times, found = [], False
    for _ in range(reps):
        reader = rec.BarCodeReader(str(path), rec.DecodeType.PDF417)
        reader.quality_settings = getattr(rec.QualitySettings, preset)
        start = time.perf_counter()
        results = list(reader.read_bar_codes())
        times.append((time.perf_counter() - start) * 1000)
        found = found or bool(results)
    return found, statistics.median(times)

for image in sorted(pathlib.Path("samples").glob("*.png")):
    row = []
    for preset in PRESETS:
        found, ms = probe(image, preset)
        row.append(f"{preset}: {'read' if found else 'miss'} {ms:.0f}ms")
    print(f"{image.name:28} " + " | ".join(row))
clean.png                    high_performance: read 32ms | normal_quality: read 48ms | high_quality: read 51ms | max_quality: read 130ms
faded_thermal.png            high_performance: read 26ms | normal_quality: read 43ms | high_quality: read 46ms | max_quality: read 144ms
half_resolution.png          high_performance: read 27ms | normal_quality: read 41ms | high_quality: read 41ms | max_quality: read 89ms
heavy_blur.png               high_performance: miss 7ms | normal_quality: miss 24ms | high_quality: miss 69ms | max_quality: miss 180ms
noisy_scan.png               high_performance: read 26ms | normal_quality: read 42ms | high_quality: read 47ms | max_quality: read 116ms
Page1.png                    high_performance: read 21ms | normal_quality: read 34ms | high_quality: read 38ms | max_quality: read 90ms
Page2.png                    high_performance: read 21ms | normal_quality: read 33ms | high_quality: read 38ms | max_quality: read 89ms
rotated.png                  high_performance: miss 16ms | normal_quality: read 43ms | high_quality: read 57ms | max_quality: read 147ms

Запустите его один раз против пятидесяти реальных захватов, и предустановленная дискуссия завершится.

Практические рекомендации

  • Ограничьте DecodeType значением PDF417, если только вы действительно ожидаете смешанные символьные наборы. Используйте MACRO_PDF417, COMPACT_PDF417 или MICRO_PDF417, когда знаете вариант.
  • Обрезайте изображение перед декодированием. Передача полного скана формата A4, когда штрих‑код занимает лишь 5 % площади, тратит время на обнаружение и увеличивает количество ложных срабатываний.
  • Сохраняйте промежуточные файлы без потерь. PNG или TIFF. JPEG с артефактами при низком качестве повреждает именно те края штрихов, которые нужны декодеру.
  • Структурируйте повторные попытки, вместо того чтобы по умолчанию использовать самый медленный набор параметров.
  • Сначала исправьте генерацию, а затем настраивайте распознавание. Если вы контролируете кодировщик, увеличение x_dimension ничего не стоит и решает больше проблем, чем любые настройки считывателя. В статье Статья о генерации PDF417 рассматриваются эти параметры.
  • Установите timeout для любого считывателя, работающего с изображениями, предоставленными пользователем.

Получить бесплатную лицензию

Оценочная сборка накладывает водяные знаки на декодированный текст для всех символогий, кроме Code 39, что будет мешать любому тестированию с обратным проходом. Бесплатная временная лицензия устраняет это в оценочной версии.

Бесплатные дополнительные ресурсы

Заключение

Чтение штрихкодов PDF417 в Python — это двухстрочная операция на чистых изображениях и инженерная задача на реальных. Вышеуказанные измерения указывают на простую стратегию: назовите ваш DecodeType, по умолчанию используйте normal_quality, попробуйте ещё раз с high_quality и включённой деконволюцией, и проверяйте каждый результат перед тем, как действовать. Сохраните дорогие пресеты для пути повторной попытки, и когда целый класс изображений не проходит ни при одной настройке, исправьте захват вместо считывателя. Вопросы приветствуются на форум бесплатной поддержки.

Часто задаваемые вопросы

  1. Почему один и тот же штрих‑код PDF417 читается на одном изображении и не читается на другом?
    Распознавание зависит от того, сколько пикселей занимает каждый модуль после захвата. Размытие, уменьшение масштаба и JPEG‑компрессия уменьшают эффективную ширину модуля, и как только модуль становится уже примерно двух чистых пикселей, ни один пресет не сможет его восстановить.

  2. Какой пресет распознавания следует использовать для PDF417?
    Начните с normal_quality, который является значением по умолчанию. Перейдите к high_quality для захватов с камеры и сканирований, а max_quality оставьте для повторных проходов, поскольку он работает в несколько раз медленнее и не даёт заметного прироста чтения.

  3. Действительно ли ограничение DecodeType только PDF417 ускоряет чтение?
    Да. По умолчанию используется DecodeType.ALL_SUPPORTED_TYPES, который запускает детектор для каждой поддерживаемой символьной системы. Явное указание PDF417 исключает эту работу и также устраняет возможность ложного срабатывания от другой символьной системы.

  4. Может ли Aspose.BarCode читать сообщение Macro PDF417, разбитое на несколько изображений?
    Да. Читайте каждый сегмент с помощью DecodeType.MACRO_PDF417 и используйте macro_pdf_417_file_id, macro_pdf_417_segment_id и macro_pdf_417_segments_count в расширенном результате для последовательной сборки полезной нагрузки.

  5. Как понять, можно ли доверять декодированному результату?
    Проверьте reading_quality и confidence в объекте BarCodeResult, а также используйте квадрант region, чтобы убедиться, что штрих‑код найден в ожидаемом месте изображения.

  6. Можно ли читать PDF417 с фотографии водительского удостоверения или посадочного талона?
    Можно, при условии, что захват выполнен в фокусе и штрих‑код не обрезан. Обрежьте область штрих‑кода перед чтением и отдавайте предпочтение PNG вместо JPEG для промежуточных файлов.

Читать далее