Generar un código de barras PDF417 requiere solo cuatro líneas de Python. Leer uno a partir de una foto tomada con el teléfono, un escaneo de 200 DPI o una tarjeta de embarque que pasó un día en el bolsillo de alguien es donde comienza el trabajo real, y es donde la mayoría de las integraciones PDF417 realmente fallan. En este artículo, aprenderá cómo leer códigos de barras PDF417 en Python cuando la imagen de origen es imperfecta. Si lo que busca es la generación, la guía anterior sobre cómo generate PDF417 barcodes using Python cubre la creación de símbolos PDF417 básicos y personalizados, incluido el control de filas y columnas, y la Python barcode reader guide cubre el escaneo de propósito general a través de diferentes simbologías. Lo que sigue aquí se adentra de manera específica y profunda en el reconocimiento de PDF417.

Cada fragmento a continuación se ejecutó contra Aspose.BarCode for Python via .NET 26.7 en Linux con Python 3.12. Los tiempos y los resultados de lectura/omisión se midieron, no se estimaron, y el script que los generó está al final para que pueda volver a ejecutarlo con sus propias imágenes.

Qué realmente rompe el reconocimiento de PDF417

PDF417 es una simbología apilada: filas de palabras de código, cada palabra de código construida a partir de cuatro barras y cuatro espacios que abarcan 17 módulos. Esa estructura lleva corrección de errores Reed‑Solomon, por lo que una esquina rasgada o una mancha de café suele ser sobrevivible. Lo que no es sobrevivible es perder la cuadrícula de módulos.

Tres cosas destruyen la cuadrícula, en orden aproximado de cuán frecuentemente aparecen en producción:

  • Ancho efectivo del módulo por debajo de dos píxeles. Este es el fallo dominante. Ocurre por reducción de escala, por distancia de la cámara y por desenfoque, que extiende la tinta de un módulo a sus vecinos. Un código de barras generado a tres píxeles por módulo y luego desenfocado es funcionalmente un código de barras a un píxel por módulo.
  • Perspectiva y rotación. Un código de barras fotografiado en ángulo tiene filas que ya no son líneas de escaneo paralelas. El detector lo maneja, pero cuesta una pasada de detección que el preset más rápido omite.
  • Colapso de contraste. Papel térmico que se ha desvanecido, una pantalla fotografiada bajo deslumbramiento, o un escaneo con el brillo fijado. Menos fatal de lo que parece, como muestran los números a continuación.

La consecuencia práctica es que los ajustes de reconocimiento son un mecanismo de recuperación, no un sustituto de la calidad de captura. Recuperan la banda media de la calidad de imagen. No recuperan la parte inferior.

Configurar el lector PDF417 de Python

Instale la biblioteca desde PyPI:

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

La API de reconocimiento se encuentra en aspose.barcode.barcoderecognition. Dos clases son importantes para este artículo: BarCodeReader, que realiza la detección y decodificación, y QualitySettings, que controla cuán intensamente intenta el lector.

Leer un código de barras PDF417 en Python

La lectura mínima viable son dos argumentos y un bucle:

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)
Leer un código de barras PDF417 en Python.

Leer un código de barras PDF417 en Python.

Salida en una imagen limpia:

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

El segundo argumento no es opcional en la práctica. Déjalo fuera y BarCodeReader vuelve a DecodeType.ALL_SUPPORTED_TYPES, lo que ejecuta la detección de más de sesenta simbologías contra cada región candidata en la imagen. En una canalización solo de PDF417 eso es un puro desperdicio, y abre la puerta a un falso positivo de simbología lineal dentro de la estructura de filas de PDF417. Nombra el tipo.

Si su entrada llega como bytes desde una carga en lugar de un archivo en disco, pase un flujo y establezca un tiempo de espera para que una imagen patológica no pueda bloquear un trabajador:

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

Comparar la configuración de calidad de reconocimiento PDF417 en Python

QualitySettings incluye cuatro preajustes que vale la pena conocer. Asignar uno al lector es una sola línea:

reader = rec.BarCodeReader("scan.png", rec.DecodeType.PDF417)
reader.quality_settings = rec.QualitySettings.high_quality
PreajusteDestinado aCosto
high_performanceImágenes limpias, generadas por máquinaMás rápido, omite pasadas de recuperación
normal_qualityPredeterminado. Escaneos y exportaciones regularesEquilibrado
high_qualityCapturas de cámara, escaneos degradadosModerado
max_qualityReintentar pasadas en imágenes que ya fallaronMás lento por mucho margen

Para averiguar qué significan esas descripciones en la práctica, generé un PDF417 a tres píxeles por módulo, lo degradé de seis maneras con Pillow y ejecuté cada preset contra cada variante. Mediana de tres ejecuciones:

Imagen de origenhigh_performancenormal_qualityhigh_qualitymax_quality
Limpialeer, 35 msleer, 50 msleer, 45 msleer, 106 ms
Rotada 12°, desenfocadafallo, 15 msleer, 74 msleer, 67 msleer, 157 ms
Ruido gaussiano (σ 40)leer, 31 msleer, 49 msleer, 52 msleer, 123 ms
Media resoluciónleer, 22 msleer, 37 msleer, 35 msleer, 86 ms
Bajo contrasteleer, 24 msleer, 38 msleer, 43 msleer, 112 ms
Desenfoque intenso (σ 2.2)fallo, 5 msfallo, 25 msfallo, 85 msfallo, 180 ms

Cuatro cosas se desprenden de esta tabla, y son más útiles que los números individuales:

  1. El ruido y el bajo contraste no son el problema que la gente cree que son. Reed‑Solomon más el detector absorbieron un fuerte ruido gaussiano y una reducción del contraste del 82 % en el preset más rápido. Deja de ajustar por esto.
  2. La rotación más el desenfoque es el caso que separa los presets. También es el perfil exacto de una captura con teléfono de mano, por lo que high_performance es la opción predeterminada incorrecta para cualquier canal procedente de móvil.
  3. max_quality costó tres veces el tiempo de normal_quality y no leyó nada extra. Se gana su lugar como una ruta de segundo intento, no como predeterminada.
  4. Una vez que la cuadrícula del módulo desaparece, ningún preset la recupera. La fila con desenfoque intenso es un error en todos los casos, y max_quality gastó 180 ms demostrando ello. Eso es un problema de captura con una solución de captura: más píxeles, mejor enfoque, mayor x_dimension en el momento de la generación.

Ajustar QualitySettings para leer códigos de barras PDF417 borrosos en Python

Un preset es un conjunto de propiedades individuales, y puedes comenzar desde un preset y sobrescribir una. Aquí es donde se encuentran los verdaderos beneficios para un defecto conocido y repetible.

La imagen rotada y desenfocada de arriba es una buena demostración. En high_performance falla. Habilita la deconvolución sobre ese mismo preset y mostrará:

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)
Ajuste QualitySettings para leer códigos de barras PDF417 borrosos en Python.

Ajuste QualitySettings para leer códigos de barras PDF417 borrosos en Python.

Medido: fallo en 12 ms sin la sobrescritura, lectura en 54 ms con ella. Una propiedad invirtió el resultado, y el resultado aún volvió más rápido de lo que habría el preset max_quality.

Las propiedades que vale la pena conocer:

PropiedadValoresCuándo recurrir a ella
deconvolutionFAST, NORMAL, SLOWDesenfoque por movimiento y capturas fuera de foco
barcode_qualityHIGH, NORMAL, LOWDefectos de impresión, desvanecimiento térmico, artefactos de fax
x_dimensionAUTO, SMALL, NORMAL, LARGE, USE_MINIMAL_X_DIMENSIONCódigos de barras densos, o un tamaño de módulo fijo conocido
minimal_x_dimensionfloatCombínelo con USE_MINIMAL_X_DIMENSION para establecer un límite inferior
complex_backgroundAUTO, DISABLED, ENABLEDCódigo de barras impreso sobre una obra de arte o un formulario con patrón
inverse_imageAUTO, DISABLED, ENABLEDCódigos de barras blanco sobre negro, capturas de pantalla
allow_incorrect_barcodesboolSolo para diagnóstico, nunca en producción

Ese último merece una advertencia. allow_incorrect_barcodes devuelve símbolos que fallaron la validación del checksum. Es una ayuda de depuración para responder “¿el detector siquiera encontró algo aquí?” Enviarlo significa enviar cadenas decodificadas que son incorrectas en lugar de ausentes, lo cual es estrictamente peor que una omisión.

Una estrategia de niveles que funciona bien en producción: pruebe normal_quality primero, y si falla, vuelva a intentarlo una vez con high_quality más deconvolution = SLOW. Solo paga la ruta costosa para las imágenes que la necesiten.

Validar resultados de decodificación PDF417 en Python

Una cadena decodificada no es automáticamente una cadena correcta. Tres campos en BarCodeResult ayudan:

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

Reensamblar un Macro PDF417 dividido en varias imágenes

Macro PDF417 divide un mensaje lógico en varios símbolos, lo que se muestra en los manifiestos de envío y documentos de varias páginas. Los metadatos del segmento sobreviven al viaje de ida y vuelta, por lo que el reensamblaje es determinista:

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")
Página 1.

Página 1.

Página 2.

Página 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

Tenga en cuenta que macro_pdf_417_file_id es un entero, no una cadena, tanto en el lado de generación como en el de reconocimiento. El mismo objeto también expone macro_pdf_417_file_name, macro_pdf_417_sender, macro_pdf_417_addressee, macro_pdf_417_time_stamp y macro_pdf_417_checksum cuando el codificador los pobló.

Benchmark de reconocimiento PDF417 en sus propias imágenes

La elección del preset debe ser una medida, no una suposición. Apúntalo a una carpeta de capturas representativas de tu canalización real y te dirá qué preset justifica su costo:

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

Ejecute una vez contra cincuenta capturas reales y el debate preestablecido termina.

Guías Prácticas

  • Restrinja DecodeType a PDF417 a menos que realmente espere simbologías mixtas. Use MACRO_PDF417, COMPACT_PDF417 o MICRO_PDF417 cuando conozca la variante.
  • Recorte antes de decodificar. Pasar un escaneo completo de A4 cuando el código de barras ocupa solo el 5 % desperdicia tiempo de detección y añade superficies de falsos positivos.
  • Mantenga los archivos intermedios sin pérdida. PNG o TIFF. El ringing del JPEG a configuraciones de baja calidad ataca exactamente los bordes de la barra que el decodificador necesita.
  • Escalone sus reintentos en lugar de usar por defecto el ajuste más lento.
  • Corrija la generación antes de afinar el reconocimiento. Si controla el codificador, aumentar x_dimension no cuesta nada y resuelve más fallos que cualquier configuración del lector. El artículo de generación de PDF417 cubre esos parámetros.
  • Establezca timeout en cualquier lector que manipule imágenes suministradas por el usuario.

Obtén una licencia gratuita

La compilación de evaluación marca con una marca de agua el texto decodificado para simbologías distintas de Code 39, lo que interferirá con cualquier prueba de ida y vuelta que realice. Una licencia temporal gratuita elimina eso en la evaluación.

Recursos adicionales gratuitos

Conclusión

Leer códigos de barras PDF417 en Python es una operación de dos líneas en imágenes limpias y un problema de ingeniería en las reales. Las mediciones anteriores apuntan a una postura simple: nombre tu DecodeType, usa normal_quality por defecto, vuelve a intentar una vez con high_quality y deconvolución habilitada, y valida cada resultado antes de actuar sobre él. Guarda los ajustes costosos para la ruta de reintento, y cuando una clase completa de imágenes falla en cada configuración, corrige la captura en lugar del lector. Las preguntas son bienvenidas en el foro de soporte gratuito.

Preguntas frecuentes

  1. ¿Por qué el mismo código de barras PDF417 se lee en una imagen y falla en otra?
    El reconocimiento depende de cuántos píxeles ocupa cada módulo después de la captura. El desenfoque, la reducción de escala y la compresión JPEG reducen el ancho efectivo del módulo, y una vez que un módulo cae por debajo de aproximadamente dos píxeles limpios, ningún preset lo recuperará.

  2. ¿Qué preset de reconocimiento debo usar para PDF417?
    Comience con normal_quality, que es el predeterminado. Cambie a high_quality para capturas con cámara y escaneos, y reserve max_quality para pasadas de reintento porque es varias veces más lento sin leer mucho más.

  3. ¿Restringir DecodeType a PDF417 realmente acelera la lectura?
    Sí. El valor predeterminado es DecodeType.ALL_SUPPORTED_TYPES, que ejecuta el detector para cada simbología compatible. Nombrar PDF417 explícitamente elimina ese trabajo y también elimina la posibilidad de un falso positivo de otra simbología.

  4. ¿Puede Aspose.BarCode leer un mensaje Macro PDF417 dividido en varias imágenes?
    Sí. Lea cada segmento con DecodeType.MACRO_PDF417 y use macro_pdf_417_file_id, macro_pdf_417_segment_id y macro_pdf_417_segments_count en el resultado extendido para volver a ensamblar la carga útil en orden.

  5. ¿Cómo sé si un resultado decodificado es confiable?
    Verifique reading_quality y confidence en el BarCodeResult, y use el cuadrilátero region para confirmar que el código de barras se encontró donde esperaba en la imagen.

  6. ¿Puede leer PDF417 a partir de una foto de una licencia de conducir o una tarjeta de embarque?
    Puede, siempre que la captura esté enfocada y el código de barras no esté recortado. Recorte a la región del código de barras antes de leer, y prefiera PNG sobre JPEG para los archivos intermedios.

Leer más