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.");
    }
}