Створення штрих‑коду 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 і запустив кожен пресет проти кожного варіанту. Медіана трьох запусків:

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

З цієї таблиці випливають чотири речі, і вони корисніші, ніж окремі числа:

  1. Шум і низька контрастність не є тією проблемою, яку люди вважають. Reed‑Solomon плюс детектор поглинули сильний гаусівський шум і зниження контрастності на 82 % у найшвидшому пресеті. Припиніть налаштовувати це.
  2. Поворот плюс розмиття — це випадок, який розділяє пресети. Це також точний профіль зйомки зручним телефоном, через що 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.

Властивості, які варто знати:

PropertyValuesКоли його використовувати
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 для проміжних файлів.

Читати далі