How To
Use .NET / C#
Using RustODotnet in C# and .NET applications across Windows, Linux, and macOS.
The RustODotnet NuGet package provides idiomatic, high-performance C# bindings and embeds native runtimes for Windows (x64, arm64), Linux (x64, arm64), and macOS (x64, arm64).
Installation
Install the core package from NuGet:
dotnet add package RustODotnet
Optionally install an official model bundle to embed model assets automatically:
dotnet add package RustODotnet.Models.PPOCRv6.Tiny
1. Engine Lifecycle & Initialization
RustO manages native unmanaged memory handles through IDisposable. Always use using declarations or dispose the instance when done:
using System;
using RustODotnet;
// 1. Initialize using default bundled PP-OCRv6 models
using var ocr = RustO.Initialize();
// Or configure custom model paths explicitly:
var customConfig = new InitializeConfig
{
Template = "ppv6",
Detection = new DetectionConfig { ModelPath = "models/det.rten", LimitSideLen = 736 },
Recognition = new RecognitionConfig
{
ModelPath = "models/rec.rten",
DictPath = "models/dict.txt"
}
};
using var customOcr = RustO.Initialize(customConfig);
2. Request-Local Options (OcrRunOptions)
Configure options per detection call without reallocating model sessions:
var options = new OcrRunOptions
{
Output = OutputGranularity.Lines, // Lines, Words, or Spatial
LineYThreshold = 0.5f,
WordXThreshold = 0.4f,
TextScore = 0.55f,
MaxSideLen = 1600f,
Detection = new DetectionRunOptions
{
LimitSideLen = 960,
LimitType = "max"
},
Postprocess = new PostprocessRunOptions
{
UseDilation = true,
UnclipRatio = 2.0f
}
};
3. Supported Image Sources
ImageSource accepts filesystem paths/URIs or raw in-memory byte arrays:
using System.IO;
// 1. Filesystem path or file:// URI
var uriSource = new UriImageSource("invoices/sample.jpg");
// 2. In-memory byte array (e.g. from stream or ASP.NET Core IFormFile)
byte[] imageBytes = await File.ReadAllBytesAsync("invoices/sample.jpg");
var bytesSource = new BytesImageSource(imageBytes);
4. Practical Recipes & Output Examples
Recipe A: Structured Line Detection with Bounding Frames
using System;
using RustODotnet;
using var ocr = RustO.Initialize();
var result = ocr.DetectText(new UriImageSource("receipt.jpg"));
if (result is StructuredDetectTextResult structured)
{
Console.WriteLine($"Found {structured.Items.Count} lines:\n");
foreach (var item in structured.Items)
{
Console.WriteLine($"[{item.Frame.Left:F0}, {item.Frame.Top:F0}, {item.Frame.Width:F0}x{item.Frame.Height:F0}]");
Console.WriteLine($" Text: {item.Text}");
Console.WriteLine($" Score: {item.Score:P1}");
}
}
Example Output:
Found 3 lines:
[40, 30, 200x24]
Text: STORE #1029 - DOWNTOWN
Score: 99.1%
[40, 75, 150x20]
Text: Subtotal: $45.00
Score: 98.4%
[40, 105, 150x20]
Text: Total: $48.60
Score: 99.5%
Recipe B: Extracting Document Tables into Spatial Layout
using System;
using RustODotnet;
using var ocr = RustO.Initialize();
var spatialOptions = new OcrRunOptions
{
Output = OutputGranularity.Spatial,
LineYThreshold = 0.5f,
WordXThreshold = 0.4f
};
var spatialResult = ocr.DetectText(new UriImageSource("invoice_table.png"), spatialOptions);
if (spatialResult is SpatialDetectTextResult spatial)
{
Console.WriteLine("--- Reconstructed Spatial Text ---");
Console.WriteLine(spatial.Text);
}
Example Output:
--- Reconstructed Spatial Text ---
Description Qty Rate Amount
Consulting Services 8 $150.00 $1,200.00
Software License 1 $499.00 $499.00
Cloud Hosting 1 $50.00 $50.00
────────────────────────────────────────────────────
Total Due: $1,749.00
Recipe C: ASP.NET Core Web API Controller Example
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using RustODotnet;
using System.IO;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class OcrController : ControllerBase
{
private readonly RustO _ocr;
public OcrController()
{
// For web apps, initialize once as a singleton or scoped service
_ocr = RustO.Initialize();
}
[HttpPost("scan")]
public async Task<IActionResult> ScanDocument(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No image uploaded.");
using var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var result = _ocr.DetectText(
new BytesImageSource(memoryStream.ToArray()),
new OcrRunOptions { Output = OutputGranularity.Spatial }
);
if (result is SpatialDetectTextResult spatial)
{
return Ok(new { text = spatial.Text });
}
return StatusCode(500, "OCR processing failed.");
}
}