5.29. Códigos de barras y códigos Data Matrix¶
Otras dos familias de códigos completan los decodificadores de la cámara. Los códigos de barras unidimensionales -las franjas del lateral de una caja de cereales, una pulsera hospitalaria, una etiqueta de envío- son los símbolos legibles por máquina más antiguos que aún se usan a diario. Los códigos Data Matrix son bidimensionales como los códigos QR, pero más densos para un mismo tamaño de carga útil y orientados al marcado industrial: la marca de fabricante grabada con láser en una placa de circuito en lugar del cartel en una pared. El módulo image tiene un decodificador dedicado para cada uno, que cubre las aplicaciones industriales, minoristas y de inventario que los códigos 2D de consumo nunca llegaron a alcanzar.
5.29.1. Códigos de barras 1D¶
Un código de barras unidimensional codifica su carga útil como una secuencia de barras verticales de anchuras variables, leídas de izquierda a derecha (o de arriba abajo en los códigos orientados verticalmente). Las anchuras se cuantifican a uno de un pequeño conjunto de valores, y la secuencia de anchuras deletrea caracteres en la simbología que el impresor haya elegido: numérica para un código de producto UPC, alfanumérica para un número de pieza de almacén, o texto arbitrario para una etiqueta Code 128.
find_barcodes() explora el fotograma en busca de códigos de barras 1D en cualquiera de las simbologías admitidas y devuelve una lista de objetos resultado BarCode:
codes = img.find_barcodes()
for c in codes:
img.draw_rectangle(c.rect, color=(0, 255, 0))
print(c.payload, c.type, c.quality)
El decodificador explora el fotograma tanto horizontal como verticalmente en una sola llamada, de modo que un código de barras impreso en cualquier ángulo se encuentra en una única pasada sin que la aplicación tenga que rotar la entrada. roi restringe la búsqueda; no existen otros parámetros de ajuste: el decodificador es autosuficiente.
Las simbologías admitidas cubren las familias comunes de consumo e industriales. El conjunto minorista es image.EAN2, image.EAN5, image.EAN8, image.UPCE, image.UPCA, image.EAN13 (los códigos numéricos de longitud fija de la mayoría de los embalajes de consumo), image.ISBN10 e image.ISBN13 (las mismas familias reaprovechadas para libros). El conjunto de uso general es image.I25 (Interleaved 2 of 5, común en etiquetas de envío), image.CODABAR (usado en bibliotecas y bancos de sangre), image.CODE39, image.CODE93 y image.CODE128 (simbologías alfanuméricas de longitud variable para texto arbitrario). La familia de borde de estante image.DATABAR (RSS-14) y image.DATABAR_EXP (RSS-Expanded) completan la lista.
Cada detección lleva el vocabulario del cuadro delimitador -x, y, w, h, rect, corners- y la payload decodificada como cadena. type es la constante de simbología de la lista anterior, que una aplicación comprueba cuando le interesa específicamente saber qué familia se decodificó (por ejemplo, aceptando solo EAN13 en una aplicación de escáner de supermercado).
Los dos campos que importan para el filtrado son rotation y quality. rotation es el ángulo del código de barras en el plano de la imagen, en radianes: el decodificador maneja rotaciones arbitrarias, pero el código posterior que quiera mostrar la detección de forma limpia puede querer descartar los códigos que vuelven inclinados más allá de cierto umbral.
quality es el recuento de decodificaciones: el número de líneas de exploración que decodificaron correctamente la misma carga útil. El decodificador recorre cada fila (y columna) del fotograma que interseca el código de barras, e incrementa el contador cada vez que la decodificación tiene éxito. Un código de barras impreso con enfoque nítido y buena iluminación arroja una quality de decenas; un código de barras parcialmente ocluido o manchado podría decodificarse en solo una o dos líneas de exploración y reportar una quality de 1 a 2. Descartar las detecciones por debajo de quality > 5 elimina las decodificaciones erróneas transitorias de una sola línea de exploración sin coste alguno para las detecciones genuinas.
Una aplicación de código de barras 1D es pequeña. Captura un fotograma, llama a find_barcodes(), itera la lista devuelta, filtra por c.type y c.quality, y reenvía c.payload por UART o USB a la etapa posterior que esté registrando o cobrando el escaneo.
5.29.2. Data Matrix¶
Un código Data Matrix es un símbolo 2D que codifica su carga útil como una cuadrícula de celdas blancas y negras, igual que hace un código QR. Se diferencia de un código QR en dos aspectos prácticos: es más pequeño para un mismo tamaño de carga útil (la codificación es más densa) y está orientado al uso industrial en lugar del uso de consumo (donde dominan los códigos QR). Los patrones grabados con láser en piezas metálicas de una fábrica, las etiquetas impresas en encapsulados de circuitos integrados, las marcas colocadas en jeringas médicas: todos esos suelen ser Data Matrix, no códigos QR.
find_datamatrices() explora el fotograma en busca de códigos Data Matrix y devuelve una lista de objetos resultado DataMatrix:
codes = img.find_datamatrices()
for c in codes:
img.draw_rectangle(c.rect, color=(0, 255, 0))
print(c.payload, c.rows, c.columns)
roi restringe la búsqueda de la forma habitual. El único ajuste específico del decodificador es effort, un entero que controla con cuánta intensidad trabaja el decodificador para encontrar una coincidencia. Los valores más altos mejoran la detección de códigos tenues, dañados u oblicuos a costa de la velocidad de fotogramas; los valores más bajos se ejecutan más rápido, pero pueden pasar por alto códigos que el esfuerzo más alto habría encontrado. Los valores por debajo de unos 160 fallan efectivamente en la detección; los valores por encima de unos 240 ofrecen rendimientos decrecientes. El valor predeterminado de 200 es un equilibrio razonable para una imagen nítida, y el punto de partida adecuado para una nueva aplicación es el valor predeterminado más o menos 20, según si los objetivos están limpios (menos) o deteriorados (más).
Cada detección lleva el vocabulario del cuadro delimitador y las cuatro esquinas detectadas, la payload decodificada y la rotation en el plano de la imagen en radianes. Los metadatos de disposición describen el tamaño y la densidad del símbolo que leyó el decodificador: rows y columns son los recuentos de celdas de la cuadrícula de datos; capacity es el número máximo de caracteres de carga útil que el símbolo podría llevar a ese tamaño; padding es cuántos de esos espacios quedaron sin usar (capacity - len(payload)).
Los campos de disposición son útiles para las aplicaciones que necesitan validar el formato de una marca grabada en lugar de su contenido. Un sistema de seguimiento de piezas podría requerir que todas las marcas sean códigos de 12 por 12 con como máximo dos caracteres de relleno; una detección que vuelve como 8 por 8 (un símbolo más pequeño del que exige la especificación) o con 10 caracteres de relleno (mayormente vacío) se marca para volver a grabarse.
5.29.3. Cuándo elegir cada uno¶
Mientras que la decisión entre QR y AprilTag se reducía al tipo de carga útil (datos arbitrarios frente a un identificador pequeño), la decisión entre códigos de barras y códigos Data Matrix se reduce a la densidad física y a la industria.
Cuando la aplicación está orientada al consumidor y los códigos ya existen sobre el terreno -alimentación, libros, etiquetas de envío, libros de biblioteca- el detector adecuado es find_barcodes(). Los códigos que la aplicación está leyendo se imprimieron para que los leyera otro sistema, y las simbologías minoristas estandarizadas son las que ese sistema esperaba.
Cuando la aplicación es industrial y los códigos se están imprimiendo para la aplicación -seguimiento de inventario en una fábrica, códigos de lote grabados en piezas, marcas de trazabilidad en dispositivos médicos- el detector adecuado es find_datamatrices() o find_qrcodes(), según si la aplicación necesita la mayor densidad de Data Matrix o el soporte de herramientas más amplio de QR.
Un puñado de aplicaciones combina los cuatro detectores en una sola cadena. Una cámara de inspección de paquetes podría ejecutar una pasada de find_barcodes() para el UPC impreso, una pasada de find_qrcodes() para un código QR de envío en la misma caja, y una pasada de find_datamatrices() para un código de pieza grabado, todo sobre el mismo fotograma capturado; las tres listas de resultados se correlacionan por la posición del cuadro delimitador y se reportan como un único registro de detección. El coste de cada detector se suma, por lo que las aplicaciones que hacen esto suelen acotar cada pasada con un roi apropiado en lugar de buscar en todo el fotograma cada tipo de código.