Passiamo ore a costruire database e dashboard perfetti, ma la realtà è che la maggior parte delle informazioni aziendali vitali vive ancora prigioniera in formati impossibili da interrogare: PDF sbilenchi, scansioni di fatture stropicciate, contratti fotocopiati male.
Inserire questi dati a mano non è solo un lavoro alienante, ma è anche la causa principale di errori nei sistemi gestionali.
La soluzione non è assumere più persone per il data entry, ma insegnare alle macchine a “leggere” visivamente i documenti, capendone la struttura.
È qui che entra in gioco il Document Understanding.
In questo tutorial vedremo come combinare Tesseract, LayoutParser e spaCy per trasformare dei semplici pixel in dati strutturati (JSON), pronti per essere integrati nei tuoi sistemi.
Preparazione dell’Ambiente (Google Colab)
Google Colab è perfetto per questo compito, ma dobbiamo installare alcune dipendenze di sistema che non sono presenti di default.
Esegui questa cella per configurare tutto:
# 1. Installazione Tesseract OCR e lingua Italiana
!apt-get update
!apt-get install -y tesseract-ocr tesseract-ocr-ita libtesseract-dev
# 2. Installazione LayoutParser e Detectron2 (per l'analisi del layout)
!pip install layoutparser torchvision
!pip install 'git+https://github.com/facebookresearch/detectron2.git'
# 3. Installazione spaCy e modello italiano
!pip install spacy
!python -m spacy download it_core_news_sm
# 4. Utility aggiuntive
!pip install pytesseract pillow opencv-python
1. OCR: Trasformare i Pixel in Testo
L’OCR (Optical Character Recognition) è il primo mattone.
Utilizzeremo Tesseract, il motore open source di riferimento.
import pytesseract
from PIL import Image
import requests
from io import BytesIO
# Scarichiamo un'immagine di esempio (sostituisci con il tuo file)
# url = "URL_TUA_IMMAGINE_FATTURA"
# response = requests.get(url)
# img = Image.open(BytesIO(response.content))
# Se hai caricato il file su Colab:
# img = Image.open("fattura.png")
def extract_raw_text(image_path):
text = pytesseract.image_to_string(Image.open(image_path), lang="ita")
return text
# Esempio d'uso
# print(extract_raw_text("fattura.png"))
pytesseract (Il traduttore Pixel → Testo)
Pytesseract non è un motore OCR a sé stante, ma un “ponte” (wrapper) Python che comunica con il software Tesseract installato sul tuo computer.
La sua sintassi ruota attorno alla conversione di immagini in diversi formati di output. La funzione regina è image_to_string.
import pytesseract
from PIL import Image
# 1. Caricamento immagine (richiede un oggetto Image di PIL)
img = Image.open('documento.png')
# 2. Estrazione base
testo = pytesseract.image_to_string(img, lang='ita')
I parametri chiave di image_to_string:
- image: L’oggetto immagine (di solito caricato con la libreria PIL/Pillow).
- lang: La lingua. Essenziale per i dizionari integrati. ‘ita’ per l’italiano, ‘eng’ per l’inglese (puoi combinarli: ‘ita+eng’).
- config: (Avanzato) Permette di passare comandi grezzi a Tesseract. Ad esempio, config=’–psm 6′ dice a Tesseract di considerare l’immagine come un singolo blocco di testo uniforme (utile se hai già isolato un paragrafo con LayoutParser).
Altre funzioni utili: Se non vuoi solo il testo, ma anche sapere dove si trova, puoi usare pytesseract.image_to_data(img). Ti restituisce un dizionario con le coordinate (x, y, larghezza, altezza) di ogni singola parola riconosciuta.
cv2 (OpenCV) e il problema dei colori
Anche se è una libreria di supporto in questo script, OpenCV (cv2) gestisce i pixel che diamo in pasto all’AI. C’è una particolarità sintattica fondamentale da ricordare.
import cv2
# Lettura dell'immagine come array multidimensionale
image = cv2.imread("documento.png")
# CORREZIONE CROMATICA (Cruciale!)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
Perché è vitale?
Per motivi storici legati all’hardware delle vecchie fotocamere, OpenCV legge le immagini in formato BGR (Blu, Verde, Rosso). Tuttavia, quasi tutti i modelli moderni di Deep Learning (incluso LayoutParser) si aspettano immagini in RGB (Rosso, Verde, Blu). Se dimentichi cv2.cvtColor, passi al modello un’immagine con i colori sfasati (i rossi diventano blu), facendogli crollare drasticamente l’accuratezza!
Il problema dell’OCR tradizionale è che è ‘cieco’ alla formattazione: spiana tutto. Se la fattura ha due colonne, Tesseract potrebbe leggerle riga per riga, mescolando i dati.
Qui entra in gioco la Layout Analysis.
2. Layout Analysis: Capire la Struttura
La Layout Analysis permette di identificare visivamente le diverse parti di un documento (titoli, tabelle, immagini) prima di estrarne il testo. Utilizzeremo LayoutParser con un modello basato su Deep Learning (Detectron2).
import layoutparser as lp
import cv2
import matplotlib.pyplot as plt
# Carichiamo l'immagine
image = cv2.imread("documento.png")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Inizializziamo il modello pre-addestrato su PubLayNet
model = lp.Detectron2LayoutModel(
config_path="lp://PubLayNet/faster_rcnn_R_50_FPN_3x/config",
label_map={0:"Text", 1:"Title", 2:"List", 3:"Table", 4:"Figure"}
)
# Rilevamento del layout
layout = model.detect(image)
# Visualizzazione dei risultati
lp.draw_layout(image, layout, box_width=3).show()
# Esempio: Estraiamo solo il testo dai blocchi identificati come 'Table'
for block in layout:
if block.type == 'Table':
snippet = block.crop_image(image)
# Qui potresti fare l'OCR solo sulla tabella
Unica piccola nota tecnica per LayoutParser: a seconda della versione esatta di LayoutParser installata, block.crop_image(image) potrebbe richiedere che l’immagine sia in formato PIL e non in array numpy di OpenCV. Nel caso desse errore in esecuzione, basta convertire l’array in immagine PIL.
layoutparser (L’architetto della pagina)
LayoutParser fornisce un’interfaccia elegante e unificata per usare complessi modelli di Deep Learning (come Detectron2 o LayoutLM) senza impazzire con la matematica sottostante.
La logica si divide in due fasi: Inizializzazione del modello e Predizione.
import layoutparser as lp
# 1. Inizializzazione
model = lp.Detectron2LayoutModel(
config_path="lp://PubLayNet/faster_rcnn_R_50_FPN_3x/config",
label_map={0: "Text", 1: "Title", 2: "List", 3: "Table", 4: "Figure"}
)
# 2. Predizione (richiede un array numpy, tipicamente da OpenCV)
layout = model.detect(image)
Anatomia della sintassi:
- config_path: È l’URL interno che dice a LayoutParser quale modello scaricare. lp:// indica il repository ufficiale, PubLayNet è il dataset su cui è stato addestrato (documenti scientifici), e il resto è l’architettura neurale.
- label_map: Mappa i numeri in uscita dal modello a etichette umane (es. il modello dice “Ho trovato un 3”, e tu sai che è una “Table”).
L’oggetto layout: Il risultato di model.detect() è una collezione di blocchi (TextBlock).
Puoi iterare su di essi in modo molto pythonico:
for blocco in layout:
print(blocco.type) # Es: 'Title'
print(blocco.coordinates) # Coordinate del bounding box
print(blocco.score) # Sicurezza del modello (es. 0.98 -> 98%)
Il problema delle tabelle ‘fantasma’: Cosa fare quando mancano i bordi?
Se nello step precedente abbiamo visto come identificare ‘dove’ si trova una tabella, qui vediamo come ‘leggerla’ correttamente se l’estetica del documento è minimale e priva di griglie.
Soluzione Tecnica: Estrarre Tabelle Borderless con img2table
Per gestire i casi in cui i bordi sono assenti, dobbiamo cambiare marcia.
img2table non ha bisogno di pesanti modelli Detectron2 per le tabelle; analizza la disposizione del testo per dedurre la struttura.
# 1. Installazione (aggiungila alla cella di preparazione ambiente)
# !pip install img2table
from img2table.document import Image as TableImage
from img2table.ocr import TesseractOCR
import pandas as pd
# Inizializziamo l'OCR che img2table userà come motore di supporto
ocr = TesseractOCR(n_threads=1, lang="ita")
# Carichiamo il documento
doc = TableImage("fattura_borderless.png")
# Estrazione delle tabelle
# implicit_rows=True è il comando magico per le tabelle senza bordi
extracted_tables = doc.extract_tables(ocr=ocr,
implicit_rows=True,
borderless_tables=True,
min_confidence=50)
# Visualizziamo i risultati in Pandas
for table in extracted_tables:
df = table.df
print("Tabella trovata:")
display(df)
implicit_rows=Trueè il parametro fondamentale. Spesso le tabelle borderless hanno celle che vanno a capo: questo parametro dice all’algoritmo di non creare una nuova riga ogni volta che vede uno spazio bianco orizzontale, ma di guardare l’allineamento complessivo della colonna.
3. Information Extraction: Estrarre il Valore
Una volta isolato il testo, dobbiamo trasformarlo in dati. Possiamo usare due approcci: RegEx (per pattern fissi come gli importi) e NLP/NER (per entità come nomi di persone o aziende).
Approccio Ibrido: RegEx + NLP
import re
import spacy
nlp = spacy.load("it_core_news_sm")
def intelligent_extraction(text):
data = {}
# 1. Estrazione con Regex (per il Totale)
# Cerca pattern come "Totale: € 1.200,50"
total_match = re.search(r"(?:Totale|Importo)\s*[:€]*\s*([\d\.,]+)", text, re.IGNORECASE)
data['total_amount'] = total_match.group(1) if total_match else "Non trovato"
# 2. Estrazione con NLP (per il Cliente)
doc = nlp(text)
entities = [(ent.text, ent.label_) for ent in doc.ents]
# Cerchiamo le organizzazioni o persone
data['entities'] = entities
return data
# Test
testo_esempio = "Fattura inviata a Mario Rossi. Totale dovuto: € 1.540,00"
print(intelligent_extraction(testo_esempio))
spaCy (Il linguista)
spaCy è la libreria di riferimento per la Natural Language Processing (NLP) in produzione.
È veloce e orientata agli oggetti.
Il suo scopo, nel nostro caso, è la NER (Named Entity Recognition): capire chi o cosa sono le parole.
import spacy
# 1. Caricamento del modello linguistico
nlp = spacy.load("it_core_news_sm")
# 2. Processamento del testo
doc = nlp("Mario Rossi lavora presso la Microsoft a Milano.")
Come navigare l’oggetto doc:
Quando passi una stringa a nlp(), spaCy esegue decine di analisi in millisecondi e ti restituisce un oggetto doc.
doc.ents: È la sintassi chiave per l’estrazione dati. Contiene le “entità” trovate.
for entita in doc.ents:
print(f"Testo: {entita.text} | Tipo: {entita.label_}")
Output atteso:
Mario Rossi | PER (Persona)
Microsoft | ORG (Organizzazione)
Milano | LOC (Luogo)
4. Pipeline Completa: Dal File al JSON
Mettiamo tutto insieme in un unico flusso logico:
Fase: Input → Strumento: OpenCV / PIL → Output: Immagine Grezza
Fase: Layout → Strumento: LayoutParser → Output: Coordinate dei blocchi (Tabelle, Testo)
Fase: OCR → Strumento: Tesseract → Output: Stringhe di testo pulite
Fase: Extraction → Strumento: Regex / spaCy → Output: JSON strutturato
La Peculiarità del Metodo a “Cascata” (Pipeline)
Quello che rende interessante questa serie di esercizi è il passaggio da un approccio naive (ingenuo) a uno context-aware (consapevole del contesto).
L’esercizio OCR puro (Step 1) mostra subito i suoi limiti:
se dai in pasto a Tesseract una fattura a due colonne (es. Destinatario a sinistra, Importi a destra), lui leggerà da sinistra a destra saltando il fosso centrale, fondendo il nome del cliente con l’IVA. È un disastro applicativo.
L’esercizio con LayoutParser (Step 2) risolve questo esatto problema aziendale. Isolare visivamente le tabelle o i paragrafi prima di chiamare Tesseract significa preservare il “senso” della posizione. Non stai solo estraendo testo, stai estraendo “Il testo che si trova dentro il riquadro della Tabella”.
L’ibridazione nello Step 3 (RegEx + NLP) è il riflesso esatto di come si lavora in produzione. L’AI (spaCy) non serve a tutto.
Per estrarre un importo monetario (un pattern rigido), una RegEx vecchia scuola è infinitamente più veloce, economica e sicura di un LLM. Si usa il Machine Learning (NLP) solo per ciò che è ambiguo, come distinguere il nome dell’azienda cliente da quello del fornitore. Questo approccio ibrido ottimizza i costi di calcolo.
💡 Casi d’Uso: Dalla Teoria alla Scrivania
L’automazione non è solo una questione di “codice che gira”, ma di tempo restituito alle persone.
Ecco come queste tecnologie cambiano il lavoro quotidiano:
Il caso di Studio Logistica 24
Il problema:
Un’azienda di trasporti riceveva oltre 500 bolle di accompagnamento al giorno, tutte diverse per formato e qualità di stampa. L’inserimento manuale portava a un tasso d’errore del 12% su codici spedizione e pesi, causando ritardi nelle consegne e penali.
La soluzione: Implementando una pipeline di Document Understanding simile a quella vista sopra, l’azienda ha automatizzato l’estrazione dei dati.
Il risultato:
Gli errori di fatturazione sono scesi del 70% nel primo trimestre. Il personale che prima “copiava numeri” ora si occupa della supervisione dei casi ambigui, raddoppiando la capacità operativa dello studio senza nuove assunzioni.
Non si tratta di sostituire l’operatore, ma di dargli un “super-potere” di lettura veloce, lasciando a lui solo le decisioni complesse.
⚠️ Errori comuni e come evitarli
Implementare l’OCR sembra facile finché non incontri il primo “mondo reale”.
Ecco dove la maggior parte dei progetti fallisce e come puoi salvarti:
- Sottovalutare la qualità dell’immagine (DPI): Se dai in pasto a Tesseract una foto sgranata scattata con poca luce, otterrai solo “rumore”.
Fix: Assicurati che le scansioni siano ad almeno 300 DPI. Se sono foto, usa filtri di pre-processing (Thresholding) per aumentare il contrasto tra testo e sfondo. - Ignorare le coordinate relative: Molti estraggono il testo e poi cercano di capire “dov’era” usando solo la logica delle stringhe.
Fix: Usa sempre gli oggetti layout di LayoutParser. Sapere che un testo si trova nel quadrante “Table” è infinitamente più sicuro che sperare che la parola “Prezzo” sia seguita da un numero tramite Regex. - Il “falso amico” della lingua: Usare il modello inglese per documenti italiani porterà a errori su lettere accentate e termini specifici (es. “Società” che diventa “Societ”).
Fix: Non dimenticare mai tesseract-ocr-ita e il modello it_core_news_sm di spaCy. - Gestire i PDF multi-pagina come singole immagini: Un errore classico è convertire un PDF di 50 pagine in un’unica immagine lunghissima che manda in crash la memoria (RAM).
Fix: Itera pagina per pagina. Gestisci ogni foglio come un’entità separata e aggrega i risultati in un unico JSON solo alla fine.
I Prossimi Passi
Costruire un sistema di estrazione dati robusto richiede pazienza: i formati cambiano e le scansioni sono spesso imperfette.
La sfida principale oggi è la Document AI, dove modelli come LayoutLM analizzano simultaneamente il testo e la sua posizione spaziale per una comprensione ancora più profonda.
Post-Processing con LLM
Oggi è molto comune usare l’OCR per estrarre il testo “sporco” e poi passarlo a un modello come GPT-4o o Claude 3 con un prompt del tipo:
“Pulisci questo testo estratto male da un OCR e convertilo in una tabella JSON pulita”.
Questa combinazione è incredibilmente potente.
Consiglio applicativo:
Se vuoi fare un passo avanti immediato, prova a cercare documentazione su LayoutLM. È l’evoluzione naturale del tutorial che hai appena letto, perché “capisce” che una parola scritta in alto a destra è probabilmente una data, indipendentemente dal testo contenuto.
Articoli di approfondimento
👉 Cos’è un file JSON: guida completa
👉 Python e JSON: esercizi pratici
👉 File detection in Python per data engineer
👉 Regex in Python: guida completa




