OCR
L'OCR localizza il testo in un'immagine e lo legge. LibreYOLO lo espone come task ocr, che restituisce un poligono a quattro punti più una trascrizione per ogni regione di testo, in ordine di lettura.
Definizione
Il task ocr fa due cose in una sola chiamata: localizza ogni regione di testo
di un'immagine e la trascrive. Le regioni tornano come poligoni a quattro punti
invece che come box allineati agli assi, perché il testo nelle scene reali è
spesso ruotato, e in ordine di lettura, dall'alto verso il basso e poi da
sinistra a destra.
Una predizione riempie result.ocr, un payload OCRRegions. .data è un
array float (N, 4, 2) di poligoni in pixel dell'immagine originale, ordinati
in alto a sinistra, in alto a destra, in basso a destra, in basso a sinistra;
.texts è la lista delle N trascrizioni; .conf è il punteggio di
riconoscimento per regione e .det_conf quello di rilevamento; .xyxy dà
l'inviluppo allineato agli assi di ogni poligono. Poiché i quadrilateri sono
poligoni veri e propri, non popolano result.boxes. Lo slicing di un
OCRRegions porta con sé le trascrizioni e i due array di punteggi oltre alla
geometria.
Modelli
Due famiglie servono ocr.
PP-OCRv5 è la pipeline dedicata: un rilevatore a
binarizzazione differenziabile trova i quadrilateri di testo e un riconoscitore
SVTR/CTC li legge, con entrambi gli stadi impacchettati in un unico file .pt
insieme al charset di riconoscimento. È disponibile in due tier, uno più leggero
per la CPU e uno server per un'accuratezza più alta, e un solo dizionario copre
cinese semplificato e tradizionale, inglese, giapponese e pinyin.
SenseNova-Vision affronta l'OCR generando le
parole come testo con tag dallo stesso checkpoint da 7B che serve gli altri sei
task, caricato con LibreVLM("sensenova-vision", task="ocr"). Richiede l'extra
sensenova e i suoi pesi sono limitati all'uso non commerciale; la licenza è
sulla sua pagina.
Predizione
I pesi si scaricano da Hugging Face al primo uso e restano in cache in locale.
from libreyolo import LibreYOLO, SAMPLE_IMAGE # Il tier t è il più leggero dei due, pensato per la CPU. SAMPLE_IMAGE# mantiene lo snippet eseguibile; puntalo a una tua immagine con del testo.model = LibreYOLO("LibrePPOCRt-ocr.pt")result = model(SAMPLE_IMAGE) regions = result.ocrprint(len(regions), "regions")for text, score in zip(regions.texts, regions.conf): print(repr(text), float(score))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibrePPOCRt-ocr.pt")result = model(SAMPLE_IMAGE) regions = result.ocrprint(regions.data.shape) # poligoni (N, 4, 2), TL TR BR BLprint(regions.xyxy) # inviluppi allineati agli assi di quei poligoniprint(regions.det_conf) # punteggio di rilevamento, distinto da .confimport numpy as npfrom libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibrePPOCRt-ocr.pt")result = model(SAMPLE_IMAGE) # Indicizza con le posizioni, non con una maschera booleana: lo slicing# porta con sé le trascrizioni e i due array di punteggi oltre alla geometria.regions = result.ocr.numpy()keep = regions[np.flatnonzero(regions.conf >= 0.9)]print(keep.texts)PP-OCRv5 esegue il rilevamento con un limite fisso sul lato lungo e poi
riconosce a batch le regioni ritagliate, con rec_batch che controlla quanti
ritagli passano nel riconoscitore a ogni forward pass. Le sorgenti
multi-immagine vengono elaborate in sequenza, perché una pipeline a due stadi
non fa batch tra immagini diverse. Vedi predizione per
sorgenti, streaming e gestione dei risultati.
Formato del dataset
Le etichette OCR sono un file JSONL per split, un oggetto JSON per immagine, accanto alle immagini stesse.
my-ocr-dataset/
images/
val/receipt.jpg
labels/
val.jsonlOgni riga nomina un'immagine ed elenca le sue regioni:
{"image": "receipt.jpg", "regions": [{"polygon": [[10, 12], [118, 14], [117, 40], [9, 38]], "text": "TOTAL 12.50"}]}polygon è un quadrilatero a quattro punti in coordinate pixel assolute,
ordinate in alto a sinistra, in alto a destra, in basso a destra, in basso a
sinistra. Una regione il cui testo non è leggibile si etichetta con
"text": "###", la convenzione don't-care di ICDAR: viene esclusa dal punteggio
di riconoscimento, e una predizione che vi si sovrappone viene ignorata invece
che contata come falso positivo.
Basta passare la directory radice come data=. L'alternativa è uno YAML di
dataset, con path più i nomi opzionali delle directory images e labels, e
nc: 1 con names: {0: text} come segnaposto di schema, dato che un modello
OCR restituisce Results.ocr invece di rilevamenti. Vedi
formati dei dataset per il contratto completo.
Addestramento
Nessuna delle due famiglie OCR ha un'implementazione dell'addestramento:
train() solleva NotImplementedError su entrambe, e il supporto OCR copre
solo predizione e validazione. La pagina di PP-OCRv5 indica il codice di
addestramento upstream sotto Apache-2.0 e lo script di conversione che riporta
in LibreYOLO un checkpoint sottoposto a fine-tuning.
Validazione
val() valuta l'intera pipeline, rilevamento e riconoscimento insieme,
accoppiando uno a uno i poligoni predetti con quelli del ground truth a un IoU
superiore a 0.5.
from libreyolo import LibreYOLO model = LibreYOLO("LibrePPOCRt-ocr.pt")metrics = model.val(data="my-ocr-dataset") print(metrics["metrics/det_precision"], metrics["metrics/det_recall"])print(metrics["metrics/det_hmean"])print(metrics["metrics/e2e_f1"]) # fitnessprint(metrics["metrics/rec_1-NED"])metrics/det_precision, metrics/det_recall e metrics/det_hmean valutano
solo la localizzazione: per una corrispondenza basta la sovrapposizione dei
poligoni, qualunque cosa dica la trascrizione. metrics/e2e_precision,
metrics/e2e_recall e metrics/e2e_f1 aggiungono la lettura: serve la stessa
sovrapposizione dei poligoni più una trascrizione identica dopo la
normalizzazione NFKC e la rimozione degli spazi, e il confronto resta sensibile
alle maiuscole. metrics/e2e_f1 è anche fitness, il numero letto dalla
selezione del checkpoint migliore.
metrics/rec_1-NED valuta il riconoscitore da solo, sulle coppie che il
rilevamento ha già associato: uno meno la distanza di edit normalizzata, così
una trascrizione sbagliata di un carattere ottiene quasi 1 dove l'F1 end-to-end
le dà 0.
Esportazione
Per questo task non è disponibile nessun formato di esportazione. PP-OCRv5 è
fatto di due reti che lavorano insieme e non di un unico grafo tracciabile, e
export() solleva un errore per ogni formato su entrambe le famiglie. Per
mettere in produzione fuori da LibreYOLO, fai fine-tuning upstream e usa il
percorso di deployment upstream.