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

Tre 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.

WeVisOne-VL genera anch'esso la sua risposta come testo con tag, caricato con LibreVLM("wevisone-vl", task="ocr"). La sua istruzione rilasciata chiede ogni parola in formato box, e ogni box torna come un quadrilatero allineato agli assi con la sua trascrizione. .conf e .det_conf sono una costante 1.0, perché il modello non emette nessun punteggio. Richiede l'extra vlm, e i suoi pesi sono Apache-2.0.

Predizione

I pesi si scaricano da Hugging Face al primo uso e restano in cache in locale.

Leggere il testo in un'immagine
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))
Leggere i quadrilateri
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 .conf
Filtrare per confidenza di riconoscimento
import 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.jsonl

Ogni riga nomina un'immagine ed elenca le sue regioni:

json
{"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 famiglia OCR ha un'implementazione dell'addestramento: train() solleva NotImplementedError su tutte e tre. La validazione copre PP-OCRv5; val() solleva un errore sulle due famiglie vision-language. 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.

Validare e leggere le chiavi delle metriche
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 tutte e tre le famiglie. Per mettere in produzione fuori da LibreYOLO, fai fine-tuning upstream e usa il percorso di deployment upstream.

Verificato con LibreYOLO v1.5.0.