5.29. Codici a barre e Data Matrix¶
Altre due famiglie di codici completano i decoder della camera. I codici a barre unidimensionali – le strisce sul lato di una scatola di cereali, il braccialetto ospedaliero, un’etichetta di spedizione – sono i simboli leggibili dalla macchina più antichi ancora di uso quotidiano. I codici Data Matrix sono bidimensionali come i codici QR, ma più densi a parità di dimensione del payload e pensati per la marcatura industriale – il marchio del produttore inciso a laser su un circuito stampato piuttosto che il poster su un muro. Il modulo image dispone di un decoder dedicato per ciascuno, coprendo le applicazioni industriali, di vendita al dettaglio e di inventario che i codici 2D consumer non hanno mai del tutto raggiunto.
5.29.1. Codici a barre 1D¶
Un codice a barre unidimensionale codifica il suo payload come una sequenza di barre verticali di larghezza variabile, lette da sinistra a destra (o dall’alto verso il basso per i codici orientati verticalmente). Le larghezze si quantizzano in uno di un piccolo insieme di valori, e la sequenza di larghezze compone i caratteri nella simbologia scelta dalla stampante: numerica per un codice prodotto UPC, alfanumerica per un numero di parte di magazzino, o testo arbitrario per un’etichetta Code 128.
find_barcodes() esamina il frame alla ricerca di codici a barre 1D in una qualsiasi delle simbologie supportate e restituisce un elenco di oggetti risultato 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)
Il decoder esamina il frame sia orizzontalmente sia verticalmente in un’unica chiamata, così un codice a barre stampato con qualsiasi angolazione viene trovato in una sola passata senza che l’applicazione debba ruotare l’input. roi limita la ricerca; non esistono altri parametri di regolazione – il decoder è autonomo.
Le simbologie supportate coprono le comuni famiglie consumer e industriali. L’insieme per la vendita al dettaglio comprende image.EAN2, image.EAN5, image.EAN8, image.UPCE, image.UPCA, image.EAN13 (i codici numerici a lunghezza fissa sulla maggior parte degli imballaggi consumer), image.ISBN10 e image.ISBN13 (le stesse famiglie riadattate per i libri). L’insieme generico comprende image.I25 (Interleaved 2 of 5, comune nelle etichette di spedizione), image.CODABAR (usato nelle biblioteche e nelle banche del sangue), image.CODE39, image.CODE93 e image.CODE128 (simbologie alfanumeriche a lunghezza variabile per testo arbitrario). La famiglia per gli scaffali image.DATABAR (RSS-14) e image.DATABAR_EXP (RSS-Expanded) completa l’elenco.
Ogni rilevamento porta con sé il vocabolario del bounding box – x, y, w, h, rect, corners – e il payload decodificato come stringa. type è la costante di simbologia dell’elenco sopra, che un’applicazione controlla quando le interessa specificamente sapere quale famiglia è stata decodificata (ad esempio accettando solo EAN13 per un’applicazione di scanner per la spesa).
I due campi che contano per il filtraggio sono rotation e quality. rotation è l’angolo nel piano dell’immagine del codice a barre in radianti: il decoder gestisce rotazioni arbitrarie, ma il codice a valle che vuole visualizzare il rilevamento in modo pulito potrebbe voler filtrare i codici che risultano inclinati oltre una certa soglia.
quality è il conteggio delle decodifiche: il numero di linee di scansione che hanno decodificato con successo lo stesso payload. Il decoder scorre ogni riga (e colonna) del frame che interseca il codice a barre, e incrementa il contatore ogni volta che la decodifica riesce. Un codice a barre stampato a fuoco nitido e con buona illuminazione produce un quality nell’ordine delle decine; un codice a barre parzialmente occluso o macchiato potrebbe decodificarsi su una o due sole linee di scansione e riportare un quality di 1 – 2. Filtrare i rilevamenti al di sotto di quality > 5 scarta le errate decodifiche transitorie su singola linea di scansione senza alcun costo per i rilevamenti autentici.
Un’applicazione per codici a barre 1D è piccola. Cattura un frame, chiama find_barcodes(), itera sull’elenco restituito, filtra su c.type e c.quality, e inoltra c.payload tramite UART o USB a qualunque fase a valle si occupi di registrare o conteggiare la scansione.
5.29.2. Data Matrix¶
Un codice Data Matrix è un simbolo 2D che codifica il suo payload come una griglia di celle bianche e nere, allo stesso modo di un codice QR. Differisce da un codice QR per due aspetti pratici: è più piccolo a parità di dimensione del payload (la codifica è più densa) ed è destinato all’uso industriale piuttosto che a quello consumer (dove dominano i codici QR). I motivi incisi a laser nelle parti metalliche in una fabbrica, le etichette stampate sui package di circuiti integrati, i marchi apposti sulle siringhe mediche – tutti questi sono tipicamente Data Matrix, non codici QR.
find_datamatrices() esamina il frame alla ricerca di codici Data Matrix e restituisce un elenco di oggetti risultato 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 limita la ricerca nel modo consueto. L’unico parametro di regolazione specifico del decoder è effort, un intero che controlla quanto intensamente il decoder lavora per trovare una corrispondenza. Valori più alti migliorano il rilevamento di codici deboli, danneggiati o obliqui al costo del frame rate; valori più bassi sono più veloci ma possono mancare codici che un effort maggiore avrebbe trovato. Valori inferiori a circa 160 di fatto non riescono a rilevare; valori superiori a circa 240 danno rendimenti decrescenti. Il valore predefinito di 200 è un compromesso ragionevole per un’immagine nitida, e il punto di partenza giusto per una nuova applicazione è il valore predefinito più o meno 20 a seconda che i target siano puliti (più basso) o deteriorati (più alto).
Ogni rilevamento porta con sé il vocabolario del bounding box e i quattro angoli rilevati, il payload decodificato e la rotation nel piano dell’immagine in radianti. I metadati di layout descrivono la dimensione e la densità del simbolo letto dal decoder: rows e columns sono i conteggi delle celle della griglia dati; capacity è il numero massimo di caratteri di payload che il simbolo potrebbe contenere a quella dimensione; padding indica quante di quelle posizioni sono rimaste inutilizzate (capacity - len(payload)).
I campi di layout sono utili per le applicazioni che devono validare il formato di un marchio inciso piuttosto che il suo contenuto. Un sistema di tracciamento delle parti potrebbe richiedere che tutti i marchi siano codici 12 per 12 con al massimo due caratteri di padding; un rilevamento risultato 8 per 8 (un simbolo più piccolo di quanto richiesto dalle specifiche) o con 10 caratteri di padding (per lo più vuoto) viene segnalato per la rimarcatura.
5.29.3. Quale scegliere¶
Mentre QR contro AprilTag si riduceva al tipo di payload (dati arbitrari contro ID piccolo), i codici a barre contro i Data Matrix si riducono alla densità fisica e al settore.
Quando l’applicazione è rivolta al consumatore e i codici esistono già sul campo – generi alimentari, libri, etichette di spedizione, libri di biblioteca – il rilevatore giusto è find_barcodes(). I codici che l’applicazione legge sono stati stampati perché li leggesse un sistema diverso, e le simbologie standardizzate per la vendita al dettaglio sono ciò che quel sistema si aspettava.
Quando l’applicazione è industriale e i codici vengono stampati per l’applicazione stessa – tracciamento dell’inventario in una fabbrica, codici di lotto incisi sulle parti, marchi di tracciabilità sui dispositivi medici – il rilevatore giusto è find_datamatrices() o find_qrcodes(), a seconda che l’applicazione necessiti della maggiore densità del Data Matrix o del più ampio supporto di strumenti del QR.
Una manciata di applicazioni mescola tutti e quattro i rilevatori in un’unica pipeline. Una camera di ispezione pacchi potrebbe eseguire una passata di find_barcodes() per l’UPC stampato, una passata di find_qrcodes() per un codice QR di spedizione sulla stessa scatola, e una passata di find_datamatrices() per un codice di parte inciso, tutte sullo stesso frame catturato; i tre elenchi di risultati vengono correlati per posizione del bounding box e riportati come un unico record di rilevamento. Il costo di ogni rilevatore si somma, quindi le applicazioni che lo fanno tipicamente restringono ogni passata con un roi appropriato invece di cercare ogni tipo di codice nell’intero frame.