In molte PMI italiane i documenti «vivi» non sono ancora tutti digitali. Arrivano fatture di fornitori in PDF generato, DDT stampati e riscannati, bolle di consegna, moduli compilati a mano, estratti conto, pratiche assicurative. Il gestionale o l’ERP, però, vuole campi: numero documento, data, partita IVA, imponibile, aliquota, codice articolo, quantità.
L’OCR da solo non risolve il problema. Trasforma un’immagine in testo; quello che serve al back-office è estrarre campi affidabili, con una confidenza misurabile e un punto dove una persona può correggere i casi dubbi. In questo articolo raccontiamo come impostiamo una pipeline realistica: preprocessing, riconoscimento, estrazione, validazione e integrazione — con un occhio a privacy, layout italiani e limiti di strumenti open source come Tesseract.
Il problema non è «leggere», è «strutturare»
Una fattura PDF generata dal gestionale del fornitore spesso contiene già testo selezionabile: lì l’OCR non serve, basta un estrattore di testo (o un parser basato su layout). Una scansione a 150 dpi con pieghe, timbri e tabelle storti è un altro mondo.
In ambito PMI italiano i documenti tipici sono:
- fatture e note di credito (anche dopo l’obbligo di fattura elettronica restano molte copie PDF «di cortesia» e documenti esteri);
- DDT e bolle con tabelle di righe;
- moduli e pratiche a campi fissi (key-value);
- documenti misti: logo, intestazione, tabella, pie’ di pagina con IBAN e note.
L’obiettivo operativo non è «avere il testo completo», ma ottenere un JSON (o un Excel) con i campi che alimentano contabilità, magazzino o archiviazione. È il percorso che descriviamo anche sulla pagina dedicata all’OCR documenti per aziende: upload → estrazione e normalizzazione → validazione con log → output verso gestionale.
Architettura a due passi (e un terzo umano)
Una pipeline che regge in produzione separa tre responsabilità.
1. OCR → testo (e, se possibile, layout).
Il motore restituisce stringhe, spesso con bounding box (coordinate della parola o della riga). Qui contano qualità della scansione, lingua (ita + eventuali eng) e preprocessing.
2. Estrazione campi → struttura.
Dal testo (o dal layout) si ricavano i campi di business: con regole (regex, ancore tipo «Partita IVA», «Totale documento»), con modelli addestrati su layout ripetitivi, o con un LLM a cui si passa il testo OCR e uno schema JSON. Le regole sono prevedibili e facili da auditare; i modelli e i LLM gestiscono meglio layout variabili, ma vanno tenuti sotto controllo con esempi e soglie.
3. Revisione umana sui casi sotto soglia.
Ogni campo (o ogni documento) dovrebbe avere una confidenza. Sotto una soglia — o se fallisce un controllo di validazione — il documento finisce in coda di revisione. Solo i casi dubbi costano tempo a una persona; il resto passa in automatico.
Senza il passo 3, «automazione al 100%» diventa spesso «errori silenziosi al 5%», che in contabilità pesano più del lavoro risparmiato.
Preprocessing: dove si guadagna (o si perde) accuratezza
Prima di chiamare Tesseract o un altro motore, vale la pena sistemare l’immagine:
- Risoluzione: mirare ad almeno ~300 dpi sul testo piccolo; sotto i 200 dpi i caratteri sottili (partite IVA, decimali) degradano in fretta.
- Deskew: raddrizzare la pagina riduce errori su colonne e tabelle.
- Binarizzazione / contrasto: utile su scansioni grigie o con sfondo colorato; attenzione a non «mangiare» linee di tabella o timbri che a volte aiutano a capire il layout.
- Ritaglio e pagina: elaborare una pagina alla volta; rimuovere bordi neri dello scanner.
-
PDF multi-pagina: convertire in immagini (es. con
pdf2image/ Poppler) e processare in sequenza, tenendo l’ordine.
Esempio minimale in Python (immagine già preparata o pagina da PDF):
from pathlib import Path
import re
import pytesseract
from PIL import Image, ImageOps, ImageFilter
def prepara(path: Path) -> Image.Image:
img = Image.open(path).convert("L") # scala di grigi
img = ImageOps.autocontrast(img)
# leggero sharpen; il deskew serio richiede OpenCV o simili
img = img.filter(ImageFilter.SHARPEN)
return img
def ocr_ita(img: Image.Image) -> str:
# --psm 6: blocco di testo uniforme; da cambiare per moduli/tabelle
return pytesseract.image_to_string(img, lang="ita", config="--psm 6")
PIVA_RE = re.compile(r"\b(?:IT)?\s*(\d{11})\b")
DATA_RE = re.compile(r"\b(\d{1,2}[/.\-]\d{1,2}[/.\-]\d{2,4})\b")
def estrai_candidati(testo: str) -> dict:
pive = PIVA_RE.findall(testo)
date = DATA_RE.findall(testo)
return {
"partite_iva": pive,
"date": date,
"testo_len": len(testo),
}
Questo snip è un punto di partenza, non un motore di fatture. In produzione aggiungerete: confidenza per parola (image_to_data), normalizzazione date italiane, checksum della partita IVA, matching anagrafica fornitori, gestione multi-pagina.
Limiti di Tesseract su layout e tabelle
Tesseract resta un ottimo motore open source per testo continuo e per molti documenti semplici. Sui layout aziendali italiani mostra limiti noti:
- tabelle: le celle vicine si «attaccano»; quantità e prezzi finiscono sulla riga sbagliata;
- multi-colonna e intestazioni a blocchi: l’ordine di lettura può mescolare colonne;
-
caratteri confusi:
O/0,l/1,S/5, soprattutto a bassa risoluzione; - scrittura a mano: fuori scope, salvo casi molto controllati o motori diversi.
Quando il documento è una tabella densa (DDT con molte righe), spesso conviene un approccio ibrido: OCR a livello di cella dopo aver rilevato la griglia (OpenCV / librerie di table detection), oppure un servizio di document intelligence che restituisce già strutture tabella. La scelta dipende da volume, varietà dei layout e vincoli di privacy.
Cloud API o motore locale?
Non esiste una risposta unica. Criteri che usiamo in pratica:
| Fattore | Motore locale (es. Tesseract + regole) | API cloud di document AI |
|---|---|---|
| Privacy / GDPR | I file restano in azienda o nel vostro server | Serve DPA, valutazione trasferimento dati, eventuale regione UE |
| Costo a volume | Prevedibile (CPU/GPU vostre) | Cresce con pagine/mese |
| Layout variabili | Più lavoro di regole/modelli | Spesso più robusto out-of-the-box |
| Latenza / offline | Controllabile, anche senza Internet | Dipende dalla rete e dal provider |
| Audit | Codice e log sotto il vostro controllo | Va definito cosa logga il fornitore |
Per documenti con dati personali o fiscali sensibili, molte aziende preferiscono OCR on-premise o su cloud privato europeo proprio per tenere sotto controllo dove finiscono le scansioni. Il GDPR non vieta il cloud: impone base giuridica, misure di sicurezza, accordi con il responsabile del trattamento e, se serve, valutazione d’impatto. La domanda utile in kickoff è: questi file possono lasciare il perimetro aziendale?
Validazione: la differenza tra testo e dato utilizzabile
Dopo l’estrazione, i controlli che evitano disastri contabili sono banali ma indispensabili:
- partita IVA: 11 cifre e checksum (algoritmo ufficiale);
- codice fiscale / identificativi esteri quando previsti;
- date in range plausibile e coerenti tra documento e registrazione;
- totali: somma righe ≈ imponibile; IVA ≈ imponibile × aliquota (con tolleranza di arrotondamento);
- anagrafiche: matching fuzzy con anagrafica fornitori già in gestionale;
- obbligatorietà: se manca un campo critico → revisione, non import silenzioso.
def checksum_piva(piva: str) -> bool:
"""Checksum partita IVA italiana (11 cifre)."""
if not piva.isdigit() or len(piva) != 11:
return False
s = 0
for i, ch in enumerate(piva[:10]):
n = int(ch)
if i % 2: # posizioni pari (1-based): raddoppia
n *= 2
if n > 9:
n -= 9
s += n
return (10 - s % 10) % 10 == int(piva[10])
def valida_fattura(campi: dict, soglia: float = 0.85) -> list[str]:
errori = []
if campi.get("confidence", 0) < soglia:
errori.append("confidenza_bassa")
piva = campi.get("piva_fornitore", "")
if not checksum_piva(piva):
errori.append("piva_non_valida")
try:
if abs(float(campi["imponibile"]) * float(campi["aliquota"]) / 100
- float(campi["iva"])) > 0.05:
errori.append("iva_incoerente")
except (KeyError, ValueError, TypeError):
errori.append("importi_mancanti")
return errori
(Il checksum sopra è quello usato per le partite IVA italiane; in produzione usate una funzione collaudata e unit test su casi noti.)
Se il documento è una fattura e l’obiettivo finale è l’invio allo SdI, l’OCR è solo il primo tratto: serve poi mappare i campi nello schema FatturaPA. Per PDF già strutturati o layout fissi spesso si lavora su un convertitore PDF→XML per fattura elettronica; per scansioni il percorso è OCR + estrazione + stessa validazione XSD/semantica vista nelle integrazioni SDI.
Checklist per valutare un progetto OCR in azienda
- Obiettivo: quali campi servono davvero a quale processo (contabilità, magazzino, archivio)?
- Volumi e varietà: quante pagine/mese? Quanti layout distinti? Quanto cambiano nel tempo?
- Origine file: PDF nativi, scan, foto da smartphone, mix?
- Qualità campione: chiedete 20–50 documenti reali (anonimizzati se serve) e misurate l’accuratezza campo per campo, non solo «l’OCR ha letto qualcosa».
- Privacy: i file possono uscire? Serve log degli accessi, cifratura, retention?
- Soglie e coda umana: chi corregge? Con quale interfaccia? Entro quanto tempo?
- Integrazione: JSON/CSV verso ERP, API, o data entry assistito se i volumi sono bassi e irregolari.
- Manutenzione: chi aggiorna le regole quando un fornitore cambia modello di fattura?
- Metriche: precision/recall sui campi critici, % documenti a revisione, tempo medio di correzione.
- Exit strategy: formati di export e proprietà dei modelli/regole, così non restate bloccati su un vendor.
Cosa abbiamo imparato
- OCR ≠ dati strutturati: separate riconoscimento ed estrazione, e misurate la confidenza.
- Il preprocessing paga più di un cambio di motore su scan mediocri.
- Tesseract basta per molti testi semplici; tabelle e layout complessi chiedono più disegno (o un motore diverso).
- Privacy e perimetro vanno decisi in kickoff, non dopo il primo batch in cloud.
- Validazione e revisione umana sono parte del prodotto, non un optional.
- Per le fatture, collegare l’estrazione allo schema FatturaPA evita di rifare il lavoro a valle.
Se state impostando una pipeline simile e volete confrontare scelte su tabelle o su soglie di confidenza, scrivetelo nei commenti: sono dettagli su cui si impara soprattutto dai casi reali.
Scritto dal team di S-Data, software house che sviluppa gestionali e integrazioni su misura.
Top comments (0)