A simple .NET library for local OCR with automatic model downloading from HuggingFace. Features 2-stage detection+recognition pipeline using PaddleOCR models. Supports CUDA and CoreML GPU acceleration.
Install
| Product | Versions |
|---|---|
| .NET |
README
LMSupply.Ocr
A simple .NET library for local OCR (Optical Character Recognition) with automatic model downloading from HuggingFace. Features a 2-stage detection + recognition pipeline using PaddleOCR ONNX models.
Features
- 2-Stage Pipeline: Text detection (DBNet) followed by text recognition (CRNN with CTC decoding)
- Multi-language Support: 11 script recognizers covering 50+ language codes, including English, Korean, Chinese, Japanese, Arabic, Cyrillic and most Latin-script languages
- Automatic Model Download: Models are downloaded on-demand from HuggingFace (~10MB default)
- GPU Acceleration: Supports CUDA, DirectML, and CoreML
- Pure C# Implementation: No Python dependencies or external processes
- Polygon Support: Precise text region boundaries for rotated or curved text
Quick Start
using LMSupply.Ocr;
// Load default OCR pipeline (English)
await using var ocr = await LocalOcr.LoadAsync();
// Recognize text in an image
var result = await ocr.RecognizeAsync("document.png");
// Get all text
Console.WriteLine(result.FullText);
// Access individual text regions
foreach (var region in result.Regions)
{
Console.WriteLine($"[{region.Confidence:P0}] {region.Text}");
Console.WriteLine($" Location: {region.BoundingBox}");
}
Language-Specific OCR
// Load OCR for Korean text
await using var ocr = await LocalOcr.LoadForLanguageAsync("ko");
// Or specify the recognition model explicitly
await using var ocr = await LocalOcr.LoadAsync(
detectionModel: "default",
recognitionModel: "crnn-korean-v3");
Supported Languages
Recognition is per script: each model reads one script's alphabet, and the caller picks the language. There is no script or language detection step — the models have no identification output to detect with.
| Model | Languages |
|---|---|
crnn-en-v3 |
English |
crnn-korean-v3 |
Korean |
crnn-chinese-v3 |
Chinese (Simplified/Traditional), Japanese |
crnn-latin-v3 |
Spanish, French, German, Italian, Portuguese, Dutch, Polish, Czech, Slovak, Turkish, Hungarian, Nordic, Baltic, Indonesian, Malay, Filipino, and more |
crnn-arabic-v3 |
Arabic, Urdu, Persian |
crnn-cyrillic-v3 |
Russian, Ukrainian, Bulgarian, Belarusian, Serbian, Macedonian |
crnn-devanagari-v3 |
Hindi, Marathi, Nepali, Sanskrit |
crnn-greek-v3 |
Greek |
crnn-thai-v3 |
Thai |
crnn-tamil-v3 |
Tamil |
crnn-telugu-v3 |
Telugu |
Ask the library which codes it can resolve, rather than keeping your own list:
IEnumerable<string> codes = LocalOcr.GetSupportedLanguages(); // "en", "ko", "ja", "tr", ...
Region subtags are ignored ("tr-TR" resolves as "tr"). A code with no model falls back to the English
recognizer and raises a trace warning naming the code — the English model cannot read another script, so
its output for such text would otherwise look like a result.
Configuration Options
var options = new OcrOptions
{
LanguageHint = "en", // Language hint for auto model selection
DetectionThreshold = 0.5f, // Minimum detection confidence
RecognitionThreshold = 0.5f, // Regions below this recognition confidence are dropped (0 keeps all)
BinarizationThreshold = 0.3f, // DBNet binarization threshold
UnclipRatio = 1.5f, // Polygon expansion ratio
UsePolygon = true, // Use polygon coordinates
Provider = ExecutionProvider.Auto, // GPU acceleration
CacheDirectory = null, // Custom cache directory
DisableAutoDownload = false // true: load only from the cache, throw if a file is missing
};
await using var ocr = await LocalOcr.LoadAsync(options: options);
Checking the Cache Before Loading
Loading a language downloads whatever it needs that is not cached. To ask first — before a download the user has not agreed to — check the language's cache status. It makes no network request:
var status = LocalOcr.GetCacheStatusForLanguage("ko");
if (!status.IsCached)
{
foreach (var file in status.Missing)
Console.WriteLine($"Would download {file.RepoId}/{file.Subfolder}/{file.FileName}");
}
The files checked are the ones LoadForLanguageAsync loads: the detection model plus the language's
recognizer and dictionary. Languages of one script share a recognizer, so having English cached says
nothing about Korean. DisableAutoDownload = true makes a load throw instead of downloading.
Detection Only
// Get text regions without recognition
var regions = await ocr.DetectAsync("document.png");
foreach (var region in regions)
{
Console.WriteLine($"Found text at: {region.BoundingBox}");
}
Layout-Aware Text Extraction
var result = await ocr.RecognizeAsync("document.png");
// Get text with layout preserved (same-line regions joined with spaces)
var layoutText = result.GetTextWithLayout(lineTolerancePixels: 10);
Model Information
The library uses PaddleOCR ONNX models from HuggingFace:
- Repository:
monkt/paddleocr-onnx - Detection model: DBNet (~2.3MB)
- Recognition models: CRNN (~7-13MB depending on language)
Models are cached locally after first download.
Changes from Previous Version
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.