Generowanie kodu kreskowego PDF417 zajmuje cztery linie Pythona. Odczytanie go z zdjęcia telefonu, skanu 200 DPI lub karty pokładowej, która spędziła dzień w czyjejś kieszeni, to miejsce, w którym zaczyna się prawdziwa praca i gdzie większość integracji PDF417 faktycznie zawodzi. W tym artykule dowiesz się, jak odczytywać kody kreskowe PDF417 w Pythonie, gdy obraz źródłowy jest niedoskonały. Jeśli zamiast tego interesuje Cię generowanie, wcześniejszy przewodnik, jak generować PDF417 barcodes using Python opisuje tworzenie podstawowych i niestandardowych symboli PDF417, w tym kontrolę wierszy i kolumn, a Python barcode reader guide obejmuje skanowanie ogólnego przeznaczenia w różnych symbologiach. To, co następuje, jest wąskie i głębokie w rozpoznawaniu PDF417.

Każdy fragment kodu poniżej został uruchomiony na Aspose.BarCode for Python via .NET 26.7 w systemie Linux z Pythonem 3.12. Czasy oraz wyniki odczytu/pominięcia są mierzone, a nie szacowane, a skrypt, który je wygenerował, znajduje się na końcu, abyś mógł ponownie uruchomić go na własnych obrazach.

Co tak naprawdę psuje rozpoznawanie PDF417

PDF417 jest symbologią warstwową: wiersze kodów słów, każdy kod słowa zbudowany z czterech pasków i czterech spacji obejmujących 17 modułów. Ta struktura zawiera korekcję błędów Reed‑Solomon, więc rozerwany róg lub plama po kawie zazwyczaj da się przetrwać. Nie da się natomiast przetrwać utraty samej siatki modułów.

Trzy rzeczy niszczą siatkę, w przybliżonej kolejności, jak często pojawiają się w produkcji:

  • Efektywna szerokość modułu spadająca poniżej dwóch pikseli. To jest dominująca awaria. Dzieje się to przez zmniejszanie skali, odległość kamery oraz rozmycie, które rozprzestrzenia tusz jednego modułu na sąsiednie. Kod kreskowy wygenerowany przy trzech pikselach na moduł, a następnie rozmyty, jest funkcjonalnie kodem kreskowym przy jednym pikselu na moduł.
  • Perspektywa i rotacja. Kod kreskowy sfotografowany pod kątem ma wiersze, które nie są już równoległe do linii skanowania. Detektor radzi sobie z tym, ale wymaga dodatkowego przebiegu wykrywania, który naj szybszy preset pomija.
  • Utrata kontrastu. Papier termiczny, który wyblakł, ekran sfotografowany w odblasku lub skan z ustawioną jasnością. Mniej fatalne niż się wydaje, co pokazują poniższe liczby.

Praktycznym konsekwencją jest to, że ustawienia rozpoznawania są mechanizmem odzyskiwania, a nie zamiennikiem jakości przechwytywania. Przywracają one środkowy zakres jakości obrazu. Nie przywracają one dolnego zakresu.

Skonfiguruj czytnik PDF417 w Pythonie

Zainstaluj bibliotekę z PyPI:

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

API rozpoznawania znajduje się w aspose.barcode.barcoderecognition. Dwie klasy mają znaczenie w tym artykule: BarCodeReader, która steruje wykrywaniem i dekodowaniem, oraz QualitySettings, która kontroluje, jak bardzo czytnik się stara.

Odczyt kodu kreskowego PDF417 w Pythonie

Minimalny wykonalny odczyt to dwa argumenty i pętla:

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)
Odczytaj kod kreskowy PDF417 w Pythonie.

Odczytaj kod kreskowy PDF417 w Pythonie.

Wynik na czystym obrazie:

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

Drugi argument w praktyce nie jest opcjonalny. Pomiń go, a BarCodeReader domyślnie użyje DecodeType.ALL_SUPPORTED_TYPES, co powoduje wykrywanie ponad sześćdziesięciu symbologii w każdym potencjalnym regionie obrazu. W pipeline dedykowanym wyłącznie PDF417 jest to czyste marnotrawstwo i otwiera drzwi do fałszywego alarmu symbologii liniowej wewnątrz struktury wiersza PDF417. Podaj typ.

Jeśli Twoje dane wejściowe przychodzą jako bajty z przesyłania, a nie jako plik na dysku, przekaż strumień i ustaw limit czasu, aby patologiczny obraz nie mógł zablokować worker:a

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()]

Porównaj ustawienia jakości rozpoznawania PDF417 w Pythonie

QualitySettings zawiera cztery przydatne presety. Przypisanie jednego do czytnika wymaga jednego wiersza:

reader = rec.BarCodeReader("scan.png", rec.DecodeType.PDF417)
reader.quality_settings = rec.QualitySettings.high_quality
UstawieniePrzeznaczone dlaKoszt
high_performanceCzyste, generowane maszynowo obrazyNajszybszy, pomija przebiegi odzyskiwania
normal_qualityDomyślne. Regularne skany i eksportyZrównoważony
high_qualityZdjęcia z aparatu, zdegradowane skanyUmiarkowany
max_qualityPonowna próba na obrazach, które już nie powiodły sięNajwolniejszy z dużym marginesem

Aby dowiedzieć się, co te opisy oznaczają w praktyce, wygenerowałem PDF417 przy trzech pikselach na moduł, zdegradowałem go na sześć sposobów przy użyciu Pillow i uruchomiłem każdy preset przeciwko każdej wariancie. Mediana z trzech uruchomień:

Obraz źródłowyhigh_performancenormal_qualityhigh_qualitymax_quality
Czystyodczyt, 35 msodczyt, 50 msodczyt, 45 msodczyt, 106 ms
Obrócony o 12°, rozmytypudło, 15 msodczyt, 74 msodczyt, 67 msodczyt, 157 ms
Szum gaussowski (σ 40)odczyt, 31 msodczyt, 49 msodczyt, 52 msodczyt, 123 ms
Połowa rozdzielczościodczyt, 22 msodczyt, 37 msodczyt, 35 msodczyt, 86 ms
Niski kontrastodczyt, 24 msodczyt, 38 msodczyt, 43 msodczyt, 112 ms
Silne rozmycie (σ 2.2)pudło, 5 mspudło, 25 mspudło, 85 mspudło, 180 ms

Z tej tabeli wypadają cztery rzeczy, które są bardziej przydatne niż poszczególne liczby:

  1. Szum i niski kontrast nie są problemem, za który ludzie uważają, że jest. Reed-Solomon plus detektor wchłonęły silny szum Gaussa i 82 % redukcję kontrastu w najszybszym ustawieniu. Przestań dostrajać pod te warunki.
  2. Rotacja plus rozmycie to przypadek, który rozdziela ustawienia. To także dokładny profil przechwytywania z ręcznego telefonu, dlatego high_performance jest niewłaściwym domyślnym wyborem dla dowolnego potoku pochodzącego z urządzeń mobilnych.
  3. max_quality kosztuje trzykrotnie więcej czasu niż normal_quality i nie odczytuje nic dodatkowego. Zasługuje na miejsce jako ścieżka drugiego podejścia, a nie jako domyślna.
  4. Gdy siatka modułów zniknie, żadne ustawienie jej nie przywróci. Ciężki wiersz rozmycia jest porażką we wszystkich przypadkach, a max_quality spędził 180 ms, aby to udowodnić. To problem przechwytywania z rozwiązaniem przechwytywania: więcej pikseli, lepsze ustawienie ostrości, większy x_dimension w czasie generacji.

Dostosuj QualitySettings, aby odczytać rozmyte kody kreskowe PDF417 w Pythonie

Preset to zestaw indywidualnych właściwości, a możesz rozpocząć od presetu i nadpisać jedną z nich. To właśnie tutaj znajdują się prawdziwe korzyści w przypadku znanego, powtarzalnego defektu.

Obrócony i rozmyty obraz powyżej jest dobrą demonstracją. W sekcji high_performance brakuje. Włącz dekonwolucję na tym samym ustawieniu i otrzymasz:

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)
Dostosuj QualitySettings, aby odczytywać rozmyte kody kreskowe PDF417 w Pythonie.

Dostosuj QualitySettings, aby odczytywać rozmyte kody kreskowe PDF417 w Pythonie.

Zmierzono: miss w 12 ms bez nadpisania, odczyt w 54 ms z nim. Jedna właściwość odwróciła wynik, a rezultat nadal był szybszy niż preset max_quality.

Właściwości, które warto znać:

WłaściwośćWartościKiedy używać
deconvolutionFAST, NORMAL, SLOWRozmycie ruchu i zdjęcia poza ostrością
barcode_qualityHIGH, NORMAL, LOWWady druku, blaknięcie termiczne, artefakty faksu
x_dimensionAUTO, SMALL, NORMAL, LARGE, USE_MINIMAL_X_DIMENSIONGęste kody kreskowe lub stały znany rozmiar modułu
minimal_x_dimensionfloatPołącz z USE_MINIMAL_X_DIMENSION, aby ustawić minimalną wartość
complex_backgroundAUTO, DISABLED, ENABLEDKod kreskowy drukowany na grafice lub wzorzystym formularzu
inverse_imageAUTO, DISABLED, ENABLEDKody kreskowe białe na czarnym tle, zrzuty ekranu
allow_incorrect_barcodesboolTylko diagnostyka, nigdy w produkcji

Ten ostatni zasługuje na ostrzeżenie. allow_incorrect_barcodes zwraca symbole, które nie przeszły weryfikacji sumy kontrolnej. Jest to pomocnicze narzędzie debugowania służące do odpowiedzi na pytanie „czy detektor w ogóle coś tutaj znalazł?”. Udostępnienie go oznacza wysyłanie zdekodowanych ciągów, które są nieprawidłowe zamiast nieobecnych, co jest zdecydowanie gorsze niż pominięcie.

Strategia warstwowania, która dobrze sprawdza się w produkcji: najpierw wypróbuj normal_quality, a w przypadku niepowodzenia spróbuj ponownie raz z high_quality plus deconvolution = SLOW. Płacisz wysoką cenę tylko za obrazy, które tego potrzebują.

Walidacja wyników dekodowania PDF417 w Pythonie

Zdekodowany ciąg nie jest automatycznie poprawnym ciągiem. Trzy pola w BarCodeResult pomagają:

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)]

Ponowne złożenie makro PDF417 podzielonego na obrazy

Macro PDF417 dzieli jedną logiczną wiadomość na kilka symboli, co pojawia się w listach przewozowych i dokumentach wielostronicowych. Metadane segmentu przetrwają podróż w obie strony, więc ponowne składanie jest deterministyczne:

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")
Strona 1.

Strona 1.

Strona 2.

Strona 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

Należy zauważyć, że macro_pdf_417_file_id jest liczbą całkowitą, a nie ciągiem znaków, zarówno po stronie generowania, jak i rozpoznawania. Ten sam obiekt udostępnia również macro_pdf_417_file_name, macro_pdf_417_sender, macro_pdf_417_addressee, macro_pdf_417_time_stamp oraz macro_pdf_417_checksum, gdy enkoder je wypełnił.

Benchmark rozpoznawania PDF417 na własnych obrazach

Wybór presetu powinien być pomiarem, a nie zgadywaniem. Skieruj go na folder z reprezentatywnymi przechwyceniami z Twojego rzeczywistego potoku, a powie Ci, który preset jest wart swojej ceny:

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

Uruchom to raz na pięćdziesięciu rzeczywistych przechwyceniach, a wstępna debata się kończy.

Praktyczne wytyczne

  • Ogranicz DecodeType do PDF417, chyba że naprawdę spodziewasz się mieszanych symbologii. Użyj MACRO_PDF417, COMPACT_PDF417 lub MICRO_PDF417, gdy znasz wariant.
  • Przytnij przed dekodowaniem. Przesyłanie pełnego skanu A4, gdy kod kreskowy zajmuje tylko 5 % powierzchni, marnuje czas wykrywania i zwiększa liczbę fałszywych trafień.
  • Zachowuj pliki pośrednie bezstratnie. PNG lub TIFF. JPEG przy niskich ustawieniach jakości wprowadza artefakty dokładnie na krawędziach pasków, które potrzebuje dekoder.
  • Warstwuj ponowne próby, zamiast domyślnie używać najwolniejszego ustawienia.
  • Napraw generowanie przed dostrajaniem rozpoznawania. Jeśli kontrolujesz enkoder, zwiększenie x_dimension nie kosztuje nic i rozwiązuje więcej pominięć niż jakiekolwiek ustawienie czytnika. Artykuł o generowaniu PDF417 opisuje te parametry.
  • Ustaw timeout w każdym czytniku, który przetwarza obrazy dostarczone przez użytkownika.

Uzyskaj darmową licencję

Wersja ewaluacyjna nakłada znak wodny na zdekodowany tekst dla symbologii innych niż Code 39, co może zakłócić wszelkie testy typu round‑trip, które przeprowadzasz. Darmowa tymczasowa licencja usuwa to w wersji ewaluacyjnej.

Darmowe dodatkowe zasoby

Wnioski

Odczytywanie kodów kreskowych PDF417 w Pythonie to dwuliniowa operacja na czystych obrazach i problem inżynierski na rzeczywistych. Powyższe pomiary wskazują prostą zasadę: nazwij swój DecodeType, domyślnie użyj normal_quality, spróbuj ponownie raz z włączonym high_quality i dekonwolucją, oraz zweryfikuj każdy wynik przed podjęciem działania. Zachowaj kosztowne presety na ścieżkę ponownego próbowania, a gdy cała klasa obrazów nie przechodzi żadnego ustawienia, napraw sposób przechwytywania zamiast czytnika. Pytania są mile widziane na bezpłatnym forum wsparcia.

Najczęściej zadawane pytania

  1. Dlaczego ten sam kod kreskowy PDF417 jest odczytywany na jednym obrazie, a w innym nie?
    Rozpoznawanie zależy od liczby pikseli, które każdy moduł zajmuje po przechwyceniu. Rozmycie, zmniejszanie rozdzielczości i kompresja JPEG wszystkie zmniejszają efektywną szerokość modułu, a gdy moduł spadnie poniżej około dwóch czystych pikseli, żadne ustawienie nie przywróci go.

  2. Jakie ustawienie rozpoznawania powinienem używać dla PDF417?
    Zacznij od normal_quality, które jest domyślne. Przejdź do high_quality przy przechwytywaniu z kamery i skanach, a max_quality zarezerwuj na dodatkowe próby, ponieważ jest kilka razy wolniejsze, nie przynosząc znacząco lepszych wyników.

  3. Czy ograniczenie DecodeType do PDF417 naprawdę przyspiesza odczyt?
    Tak. Domyślnie jest DecodeType.ALL_SUPPORTED_TYPES, co uruchamia detektor dla każdej obsługiwanej symbologii. Jawne określenie PDF417 usuwa to dodatkowe obciążenie i eliminuje ryzyko fałszywego wykrycia innej symbologii.

  4. Czy Aspose.BarCode może odczytać wiadomość Macro PDF417 podzieloną na kilka obrazów?
    Tak. Odczytaj każdy segment przy użyciu DecodeType.MACRO_PDF417 i skorzystaj z macro_pdf_417_file_id, macro_pdf_417_segment_id oraz macro_pdf_417_segments_count w rozszerzonym wyniku, aby ponownie złożyć ładunek w odpowiedniej kolejności.

  5. Jak mogę sprawdzić, czy odczytany wynik jest wiarygodny?
    Sprawdź reading_quality i confidence w obiekcie BarCodeResult, a także użyj czworokąta region, aby potwierdzić, że kod kreskowy został znaleziony w oczekiwanym miejscu na obrazie.

  6. Czy można odczytać PDF417 ze zdjęcia prawa jazdy lub karty pokładowej?
    Tak, pod warunkiem że zdjęcie jest ostre i kod kreskowy nie jest obcięty. Przed odczytem przytnij obszar kodu kreskowego i preferuj format PNG zamiast JPEG dla plików pośrednich.

Czytaj więcej