EDI+VA · Esercizio di Programmazione

EP06_08 — ⚫ Segmentazione e Decodifica Reale di QRCode con OpenCV

6.14.8 EP06_08 ⚫ Segmentazione e Decodifica Reale di QRCode con OpenCV

Negli esercizi precedenti, le grandezze intermedie del pipeline di elaborazione delle immagini — come aree, perimetri, varianze e spostamenti — sono state fornite direttamente o calcolate a partire da matrici numeriche, senza la necessità di librerie specializzate di Visione Artificiale. In questo esercizio conclusivo del capitolo, questa restrizione viene rimossa intenzionalmente: verrà utilizzata la libreria OpenCV (cv2) per localizzare e decodificare un QRCode reale presente in una scena.

La proposta riproduce un flusso semplificato di sistemi impiegati nell’ispezione visiva, nell’automazione industriale e nella lettura automatica di documenti. Per mantenere l’input dei dati accessibile al contesto educativo, il caricamento dell’immagine sarà integrato nella libreria didattica morph, tramite la funzione mm.readImg.

La scena è fornita nel formato PGM ASCII (P2) e contiene un singolo QRCode valido, oltre a diversi oggetti distrattori, come rettangoli, regioni di rumore texturizzato e blocchi isolati. La segmentazione basata esclusivamente su proprietà geometriche — come area e forma approssimativamente quadrata — è necessaria per ridurre lo spazio di ricerca, ma non è sufficiente per identificare il codice corretto. La conferma finale sarà effettuata esclusivamente tramite il tentativo di decodifica utilizzando cv2.QRCodeDetector, una procedura compatibile con applicazioni reali di riconoscimento automatico.

6.14.8.1 📋 Linee Guida di Implementazione

  1. Lettura delle dimensioni e dei parametri

    Leggere, in questo ordine, tramite l’input standard:

    • una riga contenente il numero di righe \(L\);
    • una riga contenente il numero di colonne \(C\);
    • una riga contenente i quattro parametri dell’algoritmo separati da spazio:
      • soglia di binarizzazione \(T\) (intero);
      • area minima \(A_{\text{min}}\) (intero);
      • tolleranza di aspetto \(\text{tol}\) (reale);
      • margine \(M\) (intero, in pixel).
  2. Caricamento dell’immagine

    Utilizzare la funzione didattica f = mm.readImg(L, C) per leggere i valori \(L \times C\) dell’immagine in scala di grigi, ottenendo un array NumPy di tipo uint8.

  3. Binarizzazione

    Applicare una sogliatura binaria invertita utilizzando la soglia \(T\). Ogni pixel dell’immagine originale con intensità strettamente maggiore di \(T\) deve essere convertito a 255, mentre i rimanenti devono assumere il valore 0.

  4. Rilevamento dei contorni

    Estrarre i componenti connessi esterni utilizzando cv2.findContours(...) con i parametri:

    • cv2.RETR_EXTERNAL;
    • cv2.CHAIN_APPROX_SIMPLE.
  5. Filtraggio geometrico

    Per ogni contorno trovato:

    • calcolare il rettangolo delimitatore (x, y, w, h) tramite cv2.boundingRect;

    • mantenere solo i candidati che soddisfano simultaneamente:

      Area minima

      \[ w \times h > A_{\text{min}} \]

      Rapporto d’aspetto

      \[ \left|\frac{w}{h}-1\right| \le \text{tol} \]

  6. Ordinamento dei candidati

    Ordinare i candidati per area del rettangolo delimitatore

    \[ w \times h \]

    in ordine decrescente.

    In caso di parità, preservare l’ordine originariamente restituito da cv2.findContours.

  7. Verifica tramite decodifica

    Per ogni candidato, seguendo l’ordine stabilito:

    • espandere il rettangolo di \(M\) pixel nelle quattro direzioni;
    • limitare gli indici per rimanere all’interno dell’immagine;
    • estrarre il ritaglio direttamente dall’immagine originale f;
    • applicare cv2.QRCodeDetector().detectAndDecode(...) su tale ritaglio.
  8. Criterio di arresto

    Interrompere immediatamente l’elaborazione quando il primo candidato produce una stringa decodificata non vuota.

  9. Caso non trovato

    Se nessun candidato viene decodificato con successo, stampare esattamente: QRCODE_NAO_ENCONTRATO

  10. Output (caso trovato)

    Stampare due righe.

    Prima riga: riga colonna altezza larghezza utilizzando il rettangolo delimitatore originale, prima dell’espansione tramite il margine \(M\).

    Seconda riga: testo_decodificato

6.14.8.2 📌 Vincoli Computazionali

  • Utilizzare funzioni OpenCV per eseguire la binarizzazione, il rilevamento dei contorni, il calcolo del rettangolo delimitatore e la decodifica del QRCode.
  • Il filtraggio geometrico deve avvenire obbligatoriamente prima della fase di decodifica.
  • Utilizzare esclusivamente la soglia fissa \(T\) fornita in input. Non è consentito utilizzare metodi automatici di sogliatura, come Otsu o sogliatura adattativa.
  • Garantire che i ritagli inviati al decodificatore rimangano all’interno dei limiti dell’immagine.

6.14.8.3 🧠 Fondamenti Teorici

Fase Ruolo nel pipeline Conseguenza se omessa
Filtraggio geometrico Riduce lo spazio di ricerca selezionando solo regioni compatibili con la geometria attesa di un QRCode. Il decodificatore elaborerebbe tutti i contorni, inclusi rumori e oggetti distrattori.
Decodifica Conferma semanticamente se il candidato contiene un QRCode valido. Oggetti geometricamente simili potrebbero essere classificati erroneamente come QRCode.
Margine \(M\) Preserva la zona di silenzio attorno al codice, facilitandone il rilevamento. L’assenza di tale margine può impedire l’allineamento e la corretta lettura del codice.

Questo esercizio integra concetti studiati durante il capitolo in un unico pipeline di Visione Artificiale. La segmentazione riduce l’insieme delle regioni candidate tramite caratteristiche geometriche, mentre la fase di decodifica valida il contenuto della regione utilizzando un algoritmo specializzato di riconoscimento.

6.14.8.4 📦 Specifica di Input e Output (VPL)

Struttura di Input

L
C
T A_min tol M
[matrice dell'immagine]

Struttura di Output (Successo)

riga colonna altezza larghezza
testo_decodificato

Struttura di Output (Fallimento)

QRCODE_NAO_ENCONTRATO

6.14.8.5 📌 File di Riferimento (.pgm)

A scopo di validazione, debug locale e analisi di matrici reali di pixel, i file immagine generati nel formato ASCII P2 sono disponibili nella directory del progetto. Puoi utilizzarli per testare con i decoder del tuo cellulare l’aderenza del tuo codice (salvare i *.pgm localmente per visualizzarli):

  • 📥 Caso 1: Pattern Normale – Contiene un singolo codice perfettamente centralizzato con semplici distrattori geometrici alla periferia.
  • 📥 Caso 2: Scenario Complesso – Presenta una maggiore densità di rumore texturizzato e molteplici candidati distrattori che mettono alla prova i limiti del filtraggio per aspetto.
  • 📥 Caso 3: Messaggio Esteso – Contiene un QRCode strutturato a partire da una stringa di caratteri di lunghezza maggiore, generando una maggiore densità di moduli interni.
  • 📥 Caso 4: Geometria Compatta – Valuta il comportamento del pipeline in condizioni ottimizzate di contrasto e posizionamento limite.
  • 📥 Caso 5: Scenario di Esclusione – Immagine composta puramente da elementi distrattori di alta area, progettata per validare il comportamento di fallimento controllato del programma.
📋 Simulatore EP06_08: Segmentazione e Decodifica di QRCode Filtro Geometrico → Criterio di Arresto Semantico

Regola interattivamente i parametri di ingresso dell'algoritmo (A_min e tol) per verificare quali componenti vengono filtrati geometricamente e come il criterio di arresto tramite analisi semantica interrompe la scansione della coda.

250
0.22
Visualizzazione della Scena (Matrice f)
Componenti Connessi nella Coda
Figura 6.28: Simulatore EP06_08: Segmentazione Geometrica + Verifica tramite Decodifica di QRCode
%%writefile EP06_08.py
# Codice Python
Overwriting EP06_08.py
TestSuite("EP06_08.py").run()
✔️ EP06_08.cases esiste già in casos/
📋 5 caso/i caricato/i da casos/EP06_08.cases

🔍 Test di Python: EP06_08.py
⚠️ EP06_08.py: file vuoto (meno di 3 righe). Test saltati.