This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Thursday, 12 February 2026
In Parte 1 Ho mostrato il tubo di scarto: scaricare i modelli manualmente, scrivere un tokenizzatoreM SK2 collegare l'inferenza ONNX , e decodificare le targhette BIO a mano
Ora è un package NuGet. Una linea di impostazioni, download del modello zero - tutto automatico-downloads on first useM SK2
Nota: Questo paquet è uno strumento semplificato, focalizzato per estrarre il testo e le entità dalle immagini. Se avete bisogno di un'intera multi-pipelina M SK2phase che può leggere il testo da qualsiasi cosa. (photosM SK1documenti,screenshots ,scrittura manuale,,GIF animati e video,)con corrispondenza confusa,MSC6consenso di riproduczione,M SK7e extrazione strutturata,MST8consultare **lucido.RAG dove vive la versione a livello di produzione di questo tubodotto-
Prima di approfondire, ecco cosa significano i termini chiave.
.onnx file, localmente, usando solo il vostro CPUM SK2Tesseract è forte per il testo pulito del documento, ma si abbassa sulle foto rumorose, bassaM SK2 scansioni di contrasto , e mixed MST4 scena MST5 testoM ST6 immaginiM st7 Si ferma anche al testo grezzo S ST8 c'è ancora bisogno di un codice extra per trasformare quel testo in entità strutturate che si possono effettivamente usareM S ST9
Questo pacco elimina quei differenze:
AddOcrNer() E voi avete finito.flowchart LR
subgraph Part1["Part 1: Manual"]
M1[Download models]
M2[Write tokenizer]
M3[Wire ONNX]
M4[BIO decode]
end
subgraph Part2["Part 2: NuGet Package"]
N1["AddOcrNer()"]
N2[Auto-download]
N3[ImageSharp + OpenCV]
N4[Florence-2]
N5[Recognizers]
N6[CLI Tool]
end
Part1 -->|"packaged into"| Part2
style N1 stroke:#090,stroke-width:3px
style N2 stroke:#090,stroke-width:3px
style N3 stroke:#f60,stroke-width:3px
style N4 stroke:#f60,stroke-width:3px
style N5 stroke:#f60,stroke-width:3px
style N6 stroke:#f60,stroke-width:3px
Parte 1 è stato educativo - capire cosa fa ogni pezzo
dotnet add package Mostlylucid.OcrNer
L'informazione figura nella parte dispositiva. AddOcrNer() Il metodo di estensione registra tutto: OCR, NERM SK2 il conduttore combinato , FlorenceMSC4 la vistaMST5 il downloader del modelloMSSK6 e il preprocessore dell'immagineMSM7 Tutti come singole tonalitàMSV8 tutti pigriMSS9initialisatiM SS10
Qui' è il vero codice d'immatriculazione da ServiceCollectionExtensions.cs:
// Option 1: From appsettings.json (reads the "OcrNer" section)
builder.Services.AddOcrNer(builder.Configuration);
// Option 2: Inline configuration
builder.Services.AddOcrNer(config =>
{
config.EnableOcr = true;
config.TesseractLanguage = "eng";
config.MinConfidence = 0.5f;
});
Quello'è questoM SK1 Nessun download del modello, nessun percorso per i file , nessun collegamento ONNXMSC4 sotto il cappotto AddOcrNer() Registra questi servizi:
// From ServiceCollectionExtensions.cs - what gets registered
services.AddSingleton<ModelDownloader>(); // Auto-downloads models on first use
services.AddSingleton<ImagePreprocessor>(); // ImageSharp-based image enhancement
services.AddSingleton<OpenCvPreprocessor>(); // OpenCV advanced preprocessing
services.AddSingleton<INerService, NerService>(); // BERT NER from text
services.AddSingleton<IOcrService, OcrService>(); // Tesseract OCR from images
services.AddSingleton<IOcrNerPipeline, OcrNerPipeline>(); // Combined OCR + NER
services.AddSingleton<ITextRecognizerService, TextRecognizerService>(); // Microsoft.Recognizers
services.AddSingleton<IVisionService, VisionService>(); // Florence-2 vision
{
"OcrNer": {
"EnableOcr": true,
"TesseractLanguage": "eng",
"MinConfidence": 0.5,
"MaxSequenceLength": 512,
"ModelDirectory": "models/ocrner",
"Preprocessing": "Default",
"EnableAdvancedPreprocessing": false,
"EnableRecognizers": false,
"RecognizerCulture": "en-us"
}
}
Qui' è il vero OcrNerConfig Classare queste mappe a:
// From OcrNerConfig.cs
public class OcrNerConfig
{
public string ModelDirectory { get; set; } =
Path.Combine(AppContext.BaseDirectory, "models", "ocrner");
public bool EnableOcr { get; set; } = true;
public string TesseractLanguage { get; set; } = "eng";
public int MaxSequenceLength { get; set; } = 512;
public float MinConfidence { get; set; } = 0.5f;
public string NerModelRepo { get; set; } = "protectai/bert-base-NER-onnx";
public PreprocessingLevel Preprocessing { get; set; } = PreprocessingLevel.Default;
public bool EnableAdvancedPreprocessing { get; set; } = false; // OpenCV pipeline
public bool EnableRecognizers { get; set; } = false; // Microsoft.Recognizers
public string RecognizerCulture { get; set; } = "en-us"; // Recognizer language
}
Tutte le impostazioni hanno dei predefinimenti sensibili. Si può omitare l'intera sezione e tutto funziona. I due optiM SK2in caratteristiche (EnableAdvancedPreprocessing e EnableRecognizers) predefinito a false Quindi il pacchetto rimane leggero per gli utenti che non ne hanno bisogno.
L'informazione figura nella parte dispositiva. Preprocessing l'opzione controlla l'aumento dell'immagine prima di OCR:
| Valore | Cosa fa | Quando usare S |
|---|---|---|
None |
Niente pre-processo | Le immagini sono già ottimizzate |
Minimal |
Solo a scala grigia | Scanne puliti MSC2 |
Default |
A scala grigia + contrasto + spezzare | La maggior parte delle immagini |
Aggressive |
Con un forte contrasto + spezzare + ingrandire | foto di scarsa qualità SSM4 |
Il pacchetto registra cinque servizi, ciascuno usabile in modo indipendente. Prendete quello che va bene per il vostro caso d'uso M SK2 lì ' non c'è bisogno di caricare FlorenceMSC4 se tutto ciò di cui avete bisogno è NER dal testoMNK5
flowchart TD
subgraph Services
NER["INerService<br>Text → Entities"]
OCR["IOcrService<br>Image → Text"]
REC["ITextRecognizerService<br>Text → Signals"]
PIPE["IOcrNerPipeline<br>Image → Entities + Signals"]
VIS["IVisionService<br>Image → Caption"]
end
OCR --> PIPE
NER --> PIPE
REC -.-> PIPE
style PIPE stroke:#090,stroke-width:3px
style VIS stroke:#f60,stroke-width:3px
style REC stroke:#f60,stroke-width:2px,stroke-dasharray: 5 5
Il principio chiave è questo. L'efficienza.: scegliere l'outil più leggero che faccia il lavoroM SK1 Non caricare un modello di visione 450MB quando un motore OCR
| Il servizio | Cosa fa | La dimensione del modello S | La velocità M | Usare quando R... D | |||||
|---|---|---|---|---|---|---|---|---|---|
INerService |
BERT NER dal testo | ~430MB SSK3 ~50ms S | Al momento avete il testo | , | database | , | input degli utenti | ||
IOcrService |
Tesseract OCR dalle immagini | ~4MB | ~100ms SMK5 Avete bisogno del testo dalle scansioni dei documenti, schermi | ||||||
IOcrNerPipeline |
OCR poi NER in una sola chiamata | Entrambi i modelli | 3 | ms | Avete immagini e volete entità in un solo passo | ||||
ITextRecognizerService |
RegolaM SK1estrazione basata (date, telefoniMSC4 ecc. | ||||||||
IVisionService |
FlorenceM SK1 sottotittura + OCR | S~450MB M | ~1-3s | Dovete comprendere l'immagine, non solo leggere il testo R |
Se avete già il testo (in PDF,bassabase di datiM SK2input dell'utente ), potete usare il NER direttamenteMSC4 Questo è il percorso più veloce |- senza OCR | , senza elaborazione di immagini |
L'informazione figura nella parte dispositiva. INerService L'interfaccia è semplice - uno metodo:
// From INerService.cs
public interface INerService
{
Task<NerResult> ExtractEntitiesAsync(string text, CancellationToken ct = default);
}
Ecco come usarlo nel vostro servizio'
public class MyService
{
private readonly INerService _nerService;
public MyService(INerService nerService)
{
_nerService = nerService;
}
public async Task ProcessDocumentAsync(string text)
{
var result = await _nerService.ExtractEntitiesAsync(text);
foreach (var entity in result.Entities)
{
// entity.Label: "PER", "ORG", "LOC", or "MISC"
// entity.Text: "John Smith"
// entity.Confidence: 0.9996
// entity.StartOffset / EndOffset: character positions in the source
}
}
}
I modelli di risultato sono semplici:
// From NerResult.cs / NerEntity.cs
public class NerResult
{
public string SourceText { get; init; } = string.Empty;
public List<NerEntity> Entities { get; init; } = [];
}
public class NerEntity
{
public string Text { get; init; } = string.Empty; // "John Smith"
public string Label { get; init; } = string.Empty; // "PER", "ORG", "LOC", "MISC"
public float Confidence { get; init; } // 0.0 to 1.0
public int StartOffset { get; init; } // Where in the source text
public int EndOffset { get; init; } // End position (exclusive)
}
La prima chiamata scarica il modello BERT NER (~430MBM SK1 da HuggingFace. Le chiamate successive usano il modello cached - lo startup è istantaneo
Per le immagini, il pipeline gestisce il preprocessoM SK1 OCR, e NER in una sola chiamata IOcrNerPipeline Combina IOcrService e INerService:
// From OcrNerPipeline.cs - the actual pipeline code
public async Task<OcrNerResult> ProcessImageAsync(string imagePath, CancellationToken ct = default)
{
// Step 1: OCR (includes preprocessing automatically)
var ocrResult = await _ocrService.ExtractTextAsync(imagePath, ct);
if (string.IsNullOrWhiteSpace(ocrResult.Text))
return new OcrNerResult
{
OcrResult = ocrResult,
NerResult = new NerResult { SourceText = string.Empty }
};
// Step 2: NER on extracted text
var nerResult = await _nerService.ExtractEntitiesAsync(ocrResult.Text, ct);
return new OcrNerResult
{
OcrResult = ocrResult,
NerResult = nerResult
};
}
Usandola:
var pipeline = serviceProvider.GetRequiredService<IOcrNerPipeline>();
var result = await pipeline.ProcessImageAsync("invoice.png");
// What OCR found
var text = result.OcrResult.Text; // The full extracted text
var confidence = result.OcrResult.Confidence; // 0.0 to 1.0
// What NER found in that text
foreach (var entity in result.NerResult.Entities)
{
// [PER] John Smith, [ORG] Microsoft, [LOC] Seattle...
}
flowchart LR
IMG[Image bytes]
PRE["ImageSharp<br>or OpenCV"]
TESS["Tesseract<br>OCR"]
TOK["WordPiece<br>Tokenize"]
BERT["BERT NER<br>ONNX"]
REC["Recognizers<br>(optional)"]
OUT[Result]
IMG --> PRE
PRE --> TESS
TESS --> TOK
TOK --> BERT
BERT --> REC
REC --> OUT
style PRE stroke:#f60,stroke-width:3px
style BERT stroke:#f60,stroke-width:3px
style REC stroke:#f60,stroke-width:2px,stroke-dasharray: 5 5
Parte 1 aveva chiamate di Tesseract crudeM SK1 In pratica, sia Tesserac che FlorenceMSC3 funzionano meglio con immagini preprocessateMスク4 Il preprocesso è di solito. ma completamente opzionale - lo si può disattivare con Preprocessing = "None" nella configurazione o --preprocess none sul CLI.
L'informazione figura nella parte dispositiva. ImagePreprocessor Usazioni ImageSharp (pure CM SK1 nessuna dipendenza nativa):
// From ImagePreprocessor.cs - the actual preprocessing steps
public byte[] Preprocess(byte[] imageBytes, PreprocessingOptions? options = null)
{
options ??= PreprocessingOptions.Default;
using var image = Image.Load<Rgba32>(imageBytes);
image.Mutate(ctx =>
{
// Step 1: Upscale small images (Tesseract wants 300+ DPI equivalent)
if (options.EnableUpscale && (image.Width < options.MinWidth || image.Height < options.MinHeight))
{
var scale = Math.Max(
(float)options.MinWidth / image.Width,
(float)options.MinHeight / image.Height);
scale = Math.Min(scale, options.MaxUpscaleFactor);
ctx.Resize((int)(image.Width * scale), (int)(image.Height * scale),
KnownResamplers.Lanczos3);
}
// Step 2: Grayscale (single channel = faster, more accurate)
if (options.EnableGrayscale)
ctx.Grayscale();
// Step 3: Contrast boost (text stands out from background)
if (options.EnableContrast && options.ContrastAmount != 1.0f)
ctx.Contrast(options.ContrastAmount);
// Step 4: Sharpen (crisp character edges)
if (options.EnableSharpen)
ctx.GaussianSharpen(options.SharpenSigma);
});
using var ms = new MemoryStream();
image.SaveAsPng(ms); // PNG = lossless, no additional artifacts
return ms.ToArray();
}
Tre prese sono costruite in.. PreprocessingOptions la classe li definisce:
// From ImagePreprocessor.cs
public static PreprocessingOptions Default => new(); // Grayscale + 1.5x contrast + sharpen
public static PreprocessingOptions Minimal => new() // Grayscale only
{
EnableContrast = false,
EnableSharpen = false,
EnableUpscale = false
};
public static PreprocessingOptions Aggressive => new() // For poor quality images
{
ContrastAmount = 1.8f,
SharpenSigma = 1.5f,
MinWidth = 1024,
MinHeight = 768,
MaxUpscaleFactor = 4.0f
};
| Preset | Quando usare SSK2 Cosa fa | |
|---|---|---|
Default |
La maggior parte delle immagini | A scala grigia + 1.5 contrasto x S+ affinare leggermente MESK5 |
Minimal |
Scanne puliti | Solo a scala grigia MSC2 |
Aggressive |
Foto di scarsa qualità | 1.8x contrasto S+ forte uccisione M+ zoom più grande R |
Per i documenti gravemente degradati - scansioni skewed, foto rumoroseM SK2 pagine storiche sfocate - il pipeline ImageSharp non è sufficiente EnableAdvancedPreprocessing per passare a un intero pipeline OpenCV trasmesso da ImageSummarizer.
La catena di preprocessori OpenCV ha quattro fasi, ciascuna guidata da una valutazione automatica della qualità:
flowchart LR
IMG[Image]
QA["Quality<br>Assess"]
SK["Deskew"]
DN["Denoise"]
BIN["Binarize"]
OUT[Clean image]
IMG --> QA
QA --> SK
SK --> DN
DN --> BIN
BIN --> OUT
style QA stroke:#f60,stroke-width:2px
Evaluazione della qualità (ImageQualityAssessor) misura l'incertezzaM SK1 l'angolo di inclinazione , il livello di rumore, il contrastoMSC4 la uniformità della luminositàMST5 e la densità del testoM ST6 Basandosi sui risultatiMst7 raccomanda quali fasi di applicazione MST8 in modo che le immagini pulite sfuggino a un processo inutileM st9
Deskew (SkewCorrector) corregge i documenti ruotati usando tre metodiM SK1 Detezione delle linee di pieghe (default), rettangolo di area minimaMSC4 o analisi del profilo di proiezioneMNK5
Denoise (NoiseReducer) offre un'incertezza Gaussia (fastM SK2 filtro bilaterale Piccolo (proiettile -reservazione),non--meastri locali (Qualità più alta),operazioni morfologiche.
Binarize (InkExtractor) converte in nero pulito- eM SK2 bianco usando OtsuMSC3 soglia adattativaMST4 Sauvola MST5per documenti storici degradatiMSSK6 CLAHE MSST7 Otsu SST8per contrasti bassiMSS9 o rimozione di sfondo morfologicoM SS10
Lo accendere nella configurazione o sul CLI:
config.EnableAdvancedPreprocessing = true;
ocrner ocr damaged-scan.png -a
BERT NER trova le persone, le organizzazioni, i luoghiM SK2 e diverse entità . Ma alcuni dati strutturati MSC4 le dateMST5 i numeri telefoniciMst6 gli emailMSt7 le URLM st8 gli indirizzi IP SST9 è meglio catturato dalle regole deterministiche che da una rete neuraleSTS10
Includere EnableRecognizers per aggiungere un secondo passacolo di extrazione usando Microsoft.Recognizers.Texto. Sta funzionando dopo. NER e estratti:
| Tipo | Esempi | ||
|---|---|---|---|
| DateTime | "Giornale M15, \2024", | "Tutorednessì", ♫"La settimana scorsaM SK8 ♪ | |
| Numero | |||
| URL | |||
| Telefono | "555-1234", "+1 | (555) | |
| Indice IP | "192.168.1.1" ≥ |
Il riconoscitore supporta molte culture (enM SK1us, enMSC3gb , deMST5deMS, frMSV7frM SV8 etcMSP9 quindi gestisce localeMSS10formati specifici di data e convenzioni numericheMSSS11
config.EnableRecognizers = true;
config.RecognizerCulture = "en-us";
ocrner ner "John Smith joined Microsoft on January 15, 2024. Call 555-1234." -r
I due metodi di extrazione si completano l'un l'altro: BERT NER capisce il contesto ("AppleM SK2 l'azienda controMSC3 "appleMST5 la fruttaMSR6 mentre i riconoscitori catturano in modo affidabile schemi strutturati che BERT potrebbe perdereMSL7 OcrNerResult Il modello ora include un modello opzionale. Signals proprietà:
public class OcrNerResult
{
public OcrResult OcrResult { get; init; } = new();
public NerResult NerResult { get; init; } = new();
public RecognizedSignals? Signals { get; init; } // Only when EnableRecognizers = true
}
Florence-2 è un approccio completamente diverso da Tesseract. dove Tesserac è un motore specializzato per l'OCR che legge caratteri di testo in caratteri. Il modello visivo. che capisce l'intera immagine.
// From IVisionService.cs
public interface IVisionService
{
Task<VisionCaptionResult> CaptionAsync(string imagePath, bool detailed = true,
CancellationToken ct = default);
Task<VisionOcrResult> ExtractTextAsync(string imagePath,
CancellationToken ct = default);
Task<bool> IsAvailableAsync(CancellationToken ct = default);
}
Usandola:
var vision = serviceProvider.GetRequiredService<IVisionService>();
// Generate a caption describing the image
var caption = await vision.CaptionAsync("photo.jpg", detailed: true);
if (caption.Success)
{
// caption.Caption: "A man in a blue suit standing at a podium"
// caption.DurationMs: how long it took
}
// Extract visible text using Florence-2's built-in OCR
var ocrResult = await vision.ExtractTextAsync("screenshot.png");
if (ocrResult.Success)
{
// ocrResult.Text: the visible text Florence-2 detected
}
| Usazione | Tesseract (IOcrService) |
FlorenceIVisionService) |
|---|---|---|
| Scanne dei documenti | La scelta migliore - veloce, precisa MSC3 OK ma troppo grande MSC4 | |
| Fotografie di cartelli | Decente | Meglio - capisce il contesto della scena S |
| Screenshots | Beno | Bene |
| Copiatura dell'immagine | PuòM SK1non lo fa | La scelta migliore |
| La velocità | veloce (~100ms) | più lento MSC4s |
| La dimensione del modello | ~4MB |
Il punto è questo. L'efficienza.: usare Tesseract per l'estrazione dei documenti e del testo (it's 10x più veloce con un modello più piccolo SSK4x meno grande S). usare FlorenceM SK6 quando si ha davvero bisogno di un'immagine La comprensione..
Florence-2 autoM SK1scarza i suoi modelli (~450MB) durante il primo uso. {ModelDirectory}/florence2/.
Il pipeline del NER segue lo stesso processo di tre fasi -, descritto in dettaglio nel Parte 1: tokenizzare → inferire → decodificare. Parte 1 passa attraverso ogni concezione - Tokenizzazione di WordPiece S, Tensorinferenza ONNX M, Decodazione di targhette BIO D, Softmax fiducia P- da zero con un esempio completo da costruire R.
Qui's quello che il pacchetto aggiunge oltre l'approccio manuale:
Parte 1's tokenizer converte il testo in ID di toccoM SK1 Il pacchetto's BertNerTokenizer Anche i tracciati offset di caratteri - quindi sapete esattamente dove nel testo sorgente è stata trovata ogni entità:
// From BertNerTokenizer.cs
// "John Smith works at Microsoft" becomes:
// [CLS] John Smith works at Micro ##soft [SEP] [PAD] ...
//
// Each token tracks its source position:
// "John" → chars 0-4
// "Smith" → chars 5-10
// "Micro" → chars 20-29 (WordPiece splits "Microsoft")
// "##soft" → chars 20-29 (same source range)
Ecco come. NerEntity.StartOffset e EndOffset Funzionano - ritraggono la mappa alla posizione esatta dei caratteri nel vostro testo originale.
Parte 1' il decoder produce tutte le entità. Il filtro dell'etichetta durante la decodazione - bassaM SK3 il rumore di fiducia non raggiunge mai il vostro codiceMSC4
// From NerService.cs
private void FlushEntity(
List<NerEntity> entities, string text,
string type, int start, int end, float confidence)
{
if (confidence < _config.MinConfidence) return; // Filter low-confidence
var entityText = text[start..end].Trim();
if (string.IsNullOrWhiteSpace(entityText)) return;
entities.Add(new NerEntity
{
Text = entityText,
Label = type,
Confidence = confidence,
StartOffset = start,
EndOffset = end
});
}
Tutti i modelli vengono scaricati automaticamente durante il primo uso. Non c'è bisogno di setup manuale.
flowchart TD
CALL["First API call"]
CHECK{"Files exist<br>in cache?"}
YES[Use cached model]
NO["Download to .tmp file"]
MOVE["Atomic rename<br>.tmp → final"]
CALL --> CHECK
CHECK -->|Yes| YES
CHECK -->|No| NO
NO --> MOVE
MOVE --> YES
style NO stroke:#f60,stroke-width:3px
style MOVE stroke:#090,stroke-width:3px
L'informazione figura nella parte dispositiva. ModelDownloader i download da HuggingFace (NER model) e GitHub (tessdataM SK3 Usa un'atomica .tmp schema - se il download è interrotto, non ci sono file corrottiM SK2
// From ModelDownloader.cs - atomic download pattern
await using var fileStream = new FileStream(tempPath, FileMode.Create,
FileAccess.Write, FileShare.None, 81920, true);
// ... stream download to .tmp file ...
await fileStream.FlushAsync(ct);
fileStream.Close();
File.Move(tempPath, localPath, overwrite: true); // Atomic rename
Localizzazione del caciò di default: {AppBaseDir}/models/ocrner/
models/ocrner/
ner/
model.onnx (~430MB - BERT NER)
vocab.txt (~230KB - WordPiece vocabulary)
config.json (~1KB - label mapping)
tessdata/
eng.traineddata (~4MB - English OCR data)
florence2/
... (~450MB - Vision model files)
Tutto è un singolo tono con l'initializzazione pigra. Ressource costose (ONNX InferenceSession, TesseractEngine, FlorenceM SK1 modello) vengono creati una volta usati per la prima volta e riutilizzati per tutta la vita dell'applicazione
flowchart TD
DI["AddOcrNer()"]
DI --> MD["ModelDownloader<br>(singleton)"]
DI --> PP["ImagePreprocessor<br>(singleton)"]
DI --> CV["OpenCvPreprocessor<br>(singleton)"]
DI --> NER["NerService<br>(singleton)"]
DI --> OCR["OcrService<br>(singleton)"]
DI --> PIPE["OcrNerPipeline<br>(singleton)"]
DI --> REC["TextRecognizerService<br>(singleton)"]
DI --> VIS["VisionService<br>(singleton)"]
MD --> NER
MD --> OCR
PP --> OCR
CV --> OCR
NER --> PIPE
OCR --> PIPE
REC --> PIPE
style DI stroke:#090,stroke-width:3px
Sicurezza dei fili: tutti i servizi usati SemaphoreSlim per l'initializzazione. Molti thread che chiamano simultaneamente il servizio durante la prima utilizzo triggereranno solo un downloadM SK1load:
// From NerService.cs - lazy init pattern used by all services
private async Task EnsureInitializedAsync(CancellationToken ct)
{
if (_initialized) return; // Fast path: already loaded
await _initLock.WaitAsync(ct); // Only one thread enters
try
{
if (_initialized) return; // Double-check after lock
var paths = await _downloader.EnsureNerModelAsync(ct);
_tokenizer = new BertNerTokenizer(paths.VocabPath, _config.MaxSequenceLength);
_session = new InferenceSession(paths.ModelPath, sessionOptions);
_initialized = true;
}
finally { _initLock.Release(); }
}
Il repo contiene un'outil di comando-line costruito con Spectre.Console. E' progettato come un punto di successo.
# NER from text (auto-detected)
ocrner "John Smith works at Microsoft in Seattle"
# OCR from an image (auto-detected)
ocrner invoice.png
# Explicit commands
ocrner ner "Marie Curie won the Nobel Prize in Stockholm"
ocrner ocr scan.png
ocrner caption photo.jpg
Routing intelligente: l'auto CLIM SK1 rileva la vostra intenzione. Da Program.cs:
// From Program.cs - smart routing logic
if (IsImageFile(args2[0]) || IsGlobPattern(args2[0]) || Directory.Exists(args2[0]))
{
args2 = ["ocr", .. args2]; // Image file → ocr command
}
else
{
args2 = ["ner", .. args2]; // Text string → ner command
}
Se passate una stringa di testo, fa funzionare NER. se passate un file d'immagine , globM SK3 o un directoryMSC4 fa fonctionnere OCR MNK5 NER~. Non c'è bisogno di comandoMRK7
| Comandamento | Cosa fa | Motore | velocità | ||||
|---|---|---|---|---|---|---|---|
ner <text> |
Estratto delle entità dal testo | BERT NER ONNXMSC3 S | ~50ms \ | ||||
ocr <path> |
OCR + NER dalle immagini | Tesseract S+ BERT M | P~100-300ms R | ||||
caption <path> |
Copiatura delle immagini + TAC opzionale | FlorenceM SK3 SSK4ONNX |
Tesseract è il motore OCR standard. perché è più veloce e ottimizzato per il testo del documento. Florence-2 è per quando si vuole comprendere l'immagine M SK4captioniMSC5 testo della scena , foto dei segniMST7
Qui' il risultato reale dal fare funzionare la CLI contro i veri documenti campioni.
NER dal testo:
ocrner ner "Marie Curie won the Nobel Prize in Stockholm"
╭──────┬─────────────┬────────────┬──────────╮
│ Type │ Entity │ Confidence │ Position │
├──────┼─────────────┼────────────┼──────────┤
│ PER │ Marie Curie │ 100% │ 0-11 │
│ MISC │ Nobel Prize │ 100% │ 20-31 │
│ LOC │ Stockholm │ 100% │ 35-44 │
╰──────┴─────────────┴────────────┴──────────╯
NER con riconoscitori - combinando le entità BERT con la regola-estrazione di segnali basata su :
ocrner ner "Shelby Lucier from SCS Agency in Cambridge, UK sent an invoice on 13/02/15. Call 07981423683." -r
╭──────┬───────────────┬────────────┬──────────╮
│ Type │ Entity │ Confidence │ Position │
├──────┼───────────────┼────────────┼──────────┤
│ PER │ Shelby Lucier │ 100% │ 0-13 │
│ ORG │ SCS Agency │ 100% │ 19-29 │
│ LOC │ Cambridge │ 100% │ 33-42 │
│ LOC │ UK │ 100% │ 44-46 │
╰──────┴───────────────┴────────────┴──────────╯
── Recognized Signals ─────────────────────────
Type Text Details
DateTime 13/02/15 datetimeV2.date
Phone 07981423683
BERT scopre le persone, le organizzazioni e i luoghi, la data e il numero di telefono, i modelli strutturati che una rete neurale non può estrarre.
OCR da un documento scansionato ( una lettera degli azionisti di AmazonM SK1 scansionata con un buco - marcature perforate):
ocrner ocr shareholder-letter.jpg -q
╭──────┬───────────────┬────────────┬──────────╮
│ Type │ Entity │ Confidence │ Position │
├──────┼───────────────┼────────────┼──────────┤
│ ORG │ Amazon │ 87% │ 285-291 │
│ PER │ Jeff │ 99% │ 293-297 │
│ ORG │ AWS │ 95% │ 984-987 │
│ LOC │ America │ 98% │ 2315-2322│
╰──────┴───────────────┴────────────┴──────────╯
OCR Confidence: 89%
Tesseract extrae near-testo verbale dalla lettera scansionata alla 89%confidenza*, e NER identifica correttamente AmazonM SK3 Jeff (Bezos+), AWS+MSC6 e Nord America+.
La stessa lettera degli azionisti scannata processata da entrambi i motori:
Tesseract (ocrner ocr) |
Florenceocrner caption --ocr) |
||
|---|---|---|---|
| La velocità | |||
| Accuratezza dell'OCR | QuasiM SK1verbattibile, 89% fiducia | fortemente sconvoltoMSC5 frasi allucinate | |
| Il testo chiave | " Negli ultimi 2 anni a Amazon, IM SK4 ho avuto l'opportunitàMSC5 ♫ | " negli ultimi 8 anni aAmazonMST9 I' ho avuto l'occasione di scrivere molte storieMSV11 emailM SV12 \MSV13 | |
| Entità NER | Jeff (PER), Amazon (ORGM SK4 AWS S(ORG+), America SSK7LOC+) M | NMSC10A R(il testo è troppo frammentato per essere affidabile. | |
| Capzione | NM SK1A | "Un foglio con qualche testo |
Florence-2 è una Visione Modello - capisce sceneM SK1 oggetti , e relazioni spaziali. Non è mai stato progettato per competere con Tesseract nel leggere il testo di un documentoMSC4 Lo usate quando avete bisogno di un'immagine La comprensione. ( cos'èM SK1s in questa foto?), non testo L'estrazione. ( cosa dice questo documento?).
L'informazione figura nella parte dispositiva. --json Flag outputs structured JSON to stdout with allgging suppressed - designed for piping into other tools , LLM function callingM SK2 or automation scripts:
ocrner ner "Shelby Lucier from SCS Agency in Cambridge, UK sent an invoice on 13/02/15. Call 07981423683." -r --json
{
"command": "ner",
"success": true,
"sourceText": "Shelby Lucier from SCS Agency in Cambridge, UK...",
"entityCount": 4,
"entities": [
{ "type": "PER", "text": "Shelby Lucier", "confidence": 0.9996, "startOffset": 0, "endOffset": 13 },
{ "type": "ORG", "text": "SCS Agency", "confidence": 0.999, "startOffset": 19, "endOffset": 29 },
{ "type": "LOC", "text": "Cambridge", "confidence": 0.9975, "startOffset": 33, "endOffset": 42 },
{ "type": "LOC", "text": "UK", "confidence": 0.9991, "startOffset": 44, "endOffset": 46 }
],
"signals": {
"dateTimes": [{ "text": "13/02/15", "typeName": "datetimeV2.date" }],
"phoneNumbers": [{ "text": "07981423683" }]
}
}
Questo rende il CLI usabile come L'outil per LLM e gli agenti. Un LLM può chiamare ocrner ner "..." --json, analizzare la risposta JSON, e ragionare sulle entità strutturate - non c'è bisogno di codice personalizzato jq, inserirsi in un sistema agente, o leggere da qualsiasi linguaM SK2
# Pipe to jq for quick filtering
ocrner ocr invoice.png --json | jq '.results[0].entities[] | select(.type == "PER")'
# Use from Python, Node, or any language that can shell out
echo "John Smith at Microsoft" | ocrner ner --json
Per salvare in un file invece, usate -o con un .json estensione - stessi dati strutturati, scritti al discoM SK2
ocrner ocr "scans/*.png" -o results.json
Progettare immagini multiple con schemi o indirizzi di glob:
# All PNGs in a directory
ocrner ocr "scans/*.png" -o results.json
# All images in a folder
ocrner ocr ./documents/
# Batch captioning with Florence-2
ocrner caption "photos/*.jpg" --ocr -o captions.md
| bandiera | Si applica a | Descrizione S |
|---|---|---|
--json |
ner, ocr, caption |
Structured JSON to stdout Simplies --quiet, sopprime l'interazione di tutti gli utentiM SK1 |
-c |
ner, ocr |
Minimale soglia di fiducia delle entità (0.0-1.0) |
--language |
ocr |
Tesseract language ( per esempio eng, fra) |
--max-tokens |
ner, ocr |
Lunghezza máxima della sequenza BERT |
--model-dir |
ner, ocr, caption |
Override il modello di directorio cache |
-p, --preprocess |
ocr, caption |
Presito di preprocesso: none, minimal, default, aggressive |
-a, --advanced-preprocess |
ocr, caption |
Utilizzare il preprocesso OpenCV (deskew, denoiseM SK3 binarizeMSC4 SSK5 |
-r, --recognizers |
ner, ocr |
Spegnere la regola-estrazione basata (dateMSC3 numeriM SK4 URLsMST5 telefoniMst6 emailMSt7 IPsSST8 MSST9 |
--culture |
ner, ocr |
cultura del riconoscitore, eM SK2g . en-us, de-de (default: en-us) |
--brief |
caption |
Generare un sottotipo più breve, meno dettagliato |
-q, --quiet |
ner, ocr, caption |
Modo silenzioso (Output della console ridotta) |
-o |
ner, ocr, caption |
La strada del file di output (.txt, .md, .json) |
--ocr |
caption |
Funzionare anche l'OCR durante il comando di captioning |
--ner |
caption |
Estratto del NER dal testo OCR ( semplifica --ocr) |
Il modello NER attuale è la precisione completa di -. protectai/bert-base-NER-onnx (~430MBM SK1 Per molti casi di uso - specialmente su risorse-macchine limitate o quando si tratta di grandi volumi Quantizzato La versione dello stesso modello sarebbe significativamente più veloce con una perdita di precisione minima.
ONNX Runtime supports INT8 quantization out of the box, which typically reduce model size by M SK2x and improve inference speed by ♫2-3x on CPU ♫ . This is on the roadmap ♫ NerModelRepo L'opzione di configurazione già supporta il puntare ad un altro repo HuggingFace, quindi quando viene pubblicato un modello quantificato voi'd cambiate soloM SK2
{
"OcrNer": {
"NerModelRepo": "protectai/bert-base-NER-onnx-quantized"
}
}
L'architettura è progettata per questo - scambiare il modelloM SK1 mantenere la stessa API.
Questo paquet è un Pipeline single-in fase: un motore OCR , un modello NER, uno modello di visione opzionaleM SK3 È progettato per essere semplice e efficiente nel caso comuneMSC5
Per scenari più complessi - leggere il testo da qualsiasi cosa. (note scritte a manoM SK1 foto di tavole bianche, bassa frequenza - catturature fotografiche ad alta qualitàMSC4 multi-camera - consenso OCR dell'ingegnereMST6 corrispondenza confusaMSST7 e extrazione strutturata |MST8 |Check out the full pipeline at **lucido.RAG. Quello' è dove vive la versione multi-phase di questo lavoro in fase di produzione
Florence-2 è il soffitto attuale per la visione locale in questo pacchetto. Il prossimo passo logico è un LLM multimodale - un modello che può vedere un'immagine e spiegarlo in un linguaggio naturale. Invece di un OCR separato + Passi NERM SK2 voi ' spedite l'immagine direttamente e chiedete un'estrazione strutturataMSC4
Qui' è più o meno come potrebbe apparire l'API:
// Hypothetical future IMultimodalService
public interface IMultimodalService
{
Task<StructuredExtractionResult> ExtractAsync(
string imagePath,
string prompt = "Extract all people, organizations, and locations from this image. Return as JSON.",
CancellationToken ct = default);
}
// Usage
var multimodal = serviceProvider.GetRequiredService<IMultimodalService>();
var result = await multimodal.ExtractAsync("business-card.jpg");
// result.Entities: [{ "John Smith", PER }, { "Acme Corp", ORG }, { "New York", LOC }]
// result.RawText: "John Smith, VP Engineering, Acme Corp, New York, NY 10001"
// result.Summary: "Business card for John Smith at Acme Corp in New York"
Piccoli modelli multimodali locali (come Phi-3.5-vision o LLaVA) stanno diventando abbastanza buoni per questo . Il commercio-off è sempre lo stessoM SK3 modello più grande |= più intelligente ma più lento | . La scelta giusta dipende dal vostro budget di latenza e dalle esigenze di precisione |
flowchart LR
subgraph Staged["Staged Approach: Pick Your Level"]
T1["Tesseract OCR<br>4MB | ~100ms<br>Text extraction"]
T2["BERT NER<br>430MB | ~50ms<br>Entity extraction"]
T3["Florence-2<br>450MB | ~1-3s<br>Image understanding"]
T4["Multimodal LLM<br>2-8GB | ~5-30s<br>Full reasoning"]
end
T1 --> T2
T2 --> T3
T3 -.->|"future"| T4
style T1 stroke:#090,stroke-width:2px
style T2 stroke:#090,stroke-width:2px
style T3 stroke:#f60,stroke-width:2px
style T4 stroke:#999,stroke-width:2px,stroke-dasharray: 5 5
Ogni livello aggiunge capacità al costo della dimensione e della latenza. Il pacchetto attualmente copre i livelli 1-3. Niveau 4 è dove arrivano gli LLM multimodali in |- e dove **lucido.RAG è il titolo.
Questo pacchetto:
Parte 1:
Dipendenze:
Article connessi:
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.