![]() |
VOOZH | about |
dotnet add package Plugin.Xamarin.OCR --version 1.0.12
NuGet\Install-Package Plugin.Xamarin.OCR -Version 1.0.12
<PackageReference Include="Plugin.Xamarin.OCR" Version="1.0.12" />
<PackageVersion Include="Plugin.Xamarin.OCR" Version="1.0.12" />Directory.Packages.props
<PackageReference Include="Plugin.Xamarin.OCR" />Project file
paket add Plugin.Xamarin.OCR --version 1.0.12
#r "nuget: Plugin.Xamarin.OCR, 1.0.12"
#:package Plugin.Xamarin.OCR@1.0.12
#addin nuget:?package=Plugin.Xamarin.OCR&version=1.0.12Install as a Cake Addin
#tool nuget:?package=Plugin.Xamarin.OCR&version=1.0.12Install as a Cake Tool
Plugin.Xamarin.OCR and Plugin.Maui.OCR provide the ability to do simple text from image OCR using nothing but platform APIs.
YES. Let me know how it works for you.
| Platform | iOS | Android | Windows | macOS |
|---|---|---|---|---|
| Xamarin | Yes | Yes | WIP | WIP |
| MAUI | Yes | Yes | Yes | Yes |
Why am I making this? I'm doing this because I want to make it easier for developers to do OCR in their apps. I want to make it so that you can just use this plugin and not have to worry about the platform specifics.
Too many times I've tried to do OCR and had to wrestle with external dependencies like Tesseract (with its dependencies Leptonica, etc) and these types of native dependencies can be a real pain to work with.
Well, I still have to maintain a Xamarin app that uses Tesseract and I'm tired of all the problems that come with it. I want to make it easier for myself and others to do OCR in their apps.
Available on NuGet for MAUI and Xamarin.
Install with the dotnet CLI: dotnet add package Plugin.Maui.OCR or dotnet add package Plugin.Xamarin.OCR, or through the NuGet Package Manager in Visual Studio.
| Platform | Minimum Version Supported |
|---|---|
| iOS | 13+ |
| macOS | 10.15+ |
| Android | 5.0 (API 21) |
| Windows | 11 and 10 version 1809+ |
One of the more common things I do with OCR is recognize a text pattern. For example, I might want to read a date, a phone number or an email address. This is where the OcrPatternConfig class comes in.
Let's say you want to recognize an Ontario Health Card Number (HCN) in the text of your image. Numbers of those types have some specific qualities that make it easy to match.
To do this, you can create an OcrPatternConfig object like so:
bool IsValidLuhn(string number)
{
// Convert the string to an array of digits
int[] digits = number.Select(d => int.Parse(d.ToString())).ToArray();
int checkDigit = 0;
// Luhn algorithm implementation
for (int i = digits.Length - 2; i >= 0; i--)
{
int currentDigit = digits[i];
if ((digits.Length - 2 - i) % 2 == 0) // check if it's an even index from the right
{
currentDigit *= 2;
if (currentDigit > 9)
{
currentDigit -= 9;
}
}
checkDigit += currentDigit;
}
return (10 - (checkDigit % 10)) % 10 == digits.Last();
}
var ohipPattern = new OcrPatternConfig(@"\d{10}", IsLuhnValid);
var options = new OcrOptions.Builder().SetTryHard(true).SetPatternConfig(ohipPattern).Build();
var result = await OcrPlugin.Default.RecognizeTextAsync(imageData, options);
var patientHcn = result.MatchedValues.FirstOrDefault(); // This will be the HCN (and only the HCN) if it's found
For MAUI, to initialize make sure you use the MauiAppBuilder extension UseOcr() like so:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
}).
UseOcr(); // <-- add this line
return builder.Build();
}
}
And then you can just inject IOcrService into your classes and use it like so:
/// <summary>
/// Takes a photo and processes it using the OCR service.
/// </summary>
/// <param name="photo">The photo to process.</param>
/// <returns>The OCR result.</returns>
private async Task<OcrResult> ProcessPhoto(FileResult photo)
{
// Open a stream to the photo
using var sourceStream = await photo.OpenReadAsync();
// Create a byte array to hold the image data
var imageData = new byte[sourceStream.Length];
// Read the stream into the byte array
await sourceStream.ReadAsync(imageData);
// Process the image data using the OCR service
return await _ocr.RecognizeTextAsync(imageData);
}
For Xamarin, if you have some kind of DI framework in place then you can just register the OcrPlugin with it.
public App()
{
InitializeComponent();
DependencyService.RegisterSingleton(OcrPlugin.Default);
MainPage = new MainPage();
}
If you don't have a DI framework in place, you can use the OcrPlugin.Default property to access the IOcrService instance.
private readonly IOcrService _ocr;
public MainPage(IOcrService? ocr)
{
InitializeComponent();
_ocr = ocr ?? OcrPlugin.Default;
}
The IOcrService interface exposes the following methods:
public interface IOcrService
{
event EventHandler<OcrCompletedEventArgs> RecognitionCompleted;
IReadOnlyCollection<string> SupportedLanguages { get; }
Task InitAsync(CancellationToken ct = default);
Task<OcrResult> RecognizeTextAsync(byte[] imageData, bool tryHard = false, CancellationToken ct = default);
Task<OcrResult> RecognizeTextAsync(byte[] imageData, OcrOptions options, CancellationToken ct = default);
Task StartRecognizeTextAsync(byte[] imageData, OcrOptions options, CancellationToken ct = default);
}
public class OcrResult
{
public bool Success { get; set; }
public string AllText { get; set; }
public IList<OcrElement> Elements { get; set; } = new List<OcrElement>();
public IList<string> Lines { get; set; } = new List<string>();
public class OcrElement
{
public string Text { get; set; }
public float Confidence { get; set; }
// Useful for bounding boxes
public int X { get; set; }
public int Y { get; set; }
public int Height { get; set; }
public int Width { get; set; }
}
}
Before you can start using Feature, you will need to request the proper permissions on each platform.
If you're handling camera, you'll need the usual permissions for that.
If you're handling camera, you'll need the usual permissions for that. The only extra part you'll want in the AndroidManifest.xml is the following:
<application ..>
<meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="ocr" />
</application>
This will cause the model necessary to be installed when the application is installed.
The OcrOptions class provides a flexible way to configure OCR settings. You can use the OcrOptions.Builder class to create instances of OcrOptions with various configurations.
The OcrOptions class holds the configuration for OCR operations.
public class OcrOptions
{
public string? Language { get; }
public bool TryHard { get; }
public List<OcrPatternConfig> PatternConfigs { get; }
public CustomOcrValidationCallback? CustomCallback { get; }
private OcrOptions(string? language, bool tryHard, List<OcrPatternConfig> patternConfigs, CustomOcrValidationCallback? customCallback)
{
Language = language;
TryHard = tryHard;
PatternConfigs = patternConfigs;
CustomCallback = customCallback;
}
public class Builder
{
private string? _language;
private bool _tryHard;
private List<OcrPatternConfig> _patternConfigs = new List<OcrPatternConfig>();
private CustomOcrValidationCallback? _customCallback;
public Builder SetLanguage(string language)
{
_language = language;
return this;
}
public Builder SetTryHard(bool tryHard)
{
_tryHard = tryHard;
return this;
}
public Builder AddPatternConfig(OcrPatternConfig patternConfig)
{
_patternConfigs.Add(patternConfig);
return this;
}
public Builder SetPatternConfigs(List<OcrPatternConfig> patternConfigs)
{
_patternConfigs = patternConfigs ?? new List<OcrPatternConfig>();
return this;
}
public Builder SetCustomCallback(CustomOcrValidationCallback customCallback)
{
_customCallback = customCallback;
return this;
}
public OcrOptions Build()
{
return new OcrOptions(_language, _tryHard, _patternConfigs, _customCallback);
}
}
}
Using the OcrOptions.Builder to create an OcrOptions instance is straightforward and flexible:
var options = new OcrOptions.Builder()
.SetLanguage("en-US")
.SetTryHard(true)
.AddPatternConfig(new OcrPatternConfig(@"\d{10}"))
.SetCustomCallback(myCustomCallback)
.Build();
You will first need to register the OcrPlugin with the MauiAppBuilder following the same pattern that the .NET MAUI Essentials libraries follow.
builder.Services.AddSingleton(OcrPlugin.Default);
You can then enable your classes to depend on IOcrService as per the following example.
public class OcrViewModel
{
readonly IOcrService _ocr;
public OcrViewModel(IOcrService? ocr)
{
_ocr = ocr ?? OcrPlugin.Default;
}
public void DoSomeOcr()
{
byte[] imageData = GetImageData();
var result = await _ocr.RecognizeTextAsync(imageData);
}
}
Alternatively if you want to skip using the dependency injection approach you can use the Feature.Default property.
public class OcrViewModel
{
public void DoSomeOcr()
{
byte[] imageData = GetImageData();
var result = await OcrPlugin.Default.RecognizeTextAsync(imageData);
}
}
Once you have the OCR instance, you can interact with it in the following ways:
RecognitionCompletedThis event is fired when the OCR service has completed recognizing text from an image. The event args contain the OcrResult object. Only fires if the StartRecognizeTextAsync method is called.
SupportedLanguagesA list of supported languages for the OCR service. This is populated after calling InitAsync. Allows you to know what language codes can be used in OcrOptions.
InitAsync(CancellationToken ct = default)Initialize the feature. If supported on the platform (like iOS), SupportedLanguages will be populated with the available languages.
RecognizeTextAsync(byte[] imageData, bool tryHard = false, CancellationToken ct = default)Recognize text from an image. Specify "tryHard" if you want to tell the platform API to do a better job (fast vs accurate, and use language correction (ios/mac)) though it seems very accurate normally.
RecognizeTextAsync(byte[] imageData, OcrOptions options, CancellationToken ct = default)Recognize text from an image. OcrOptions contains options for the OCR service, including the language to use and whether to try hard.
Task StartRecognizeTextAsync(byte[] imageData, OcrOptions options, CancellationToken ct = default)Start recognizing text from an image. This is a task that will fire the RecognitionCompleted event when it completes with the result.
Thanks to the great Gerald Versluis for making an amazing video about using this project: https://www.youtube.com/watch?v=alY_6Qn0_60
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 is compatible. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. monoandroid12.0 monoandroid12.0 is compatible. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 is compatible. tizen60 tizen60 was computed. |
| Universal Windows Platform | uap10.0.19041 uap10.0.19041 is compatible. |
| Xamarin.iOS | xamarinios xamarinios was computed. xamarinios10 xamarinios10 is compatible. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. xamarinmac20 xamarinmac20 is compatible. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. xamarintvos10 xamarintvos10 is compatible. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. xamarinwatchos10 xamarinwatchos10 is compatible. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.12 | 357 | 7/17/2024 |
| 1.0.11 | 211 | 6/3/2024 |
| 1.0.10 | 224 | 5/30/2024 |
| 1.0.9 | 213 | 5/30/2024 |
| 1.0.8 | 198 | 5/27/2024 |
| 1.0.7 | 193 | 5/27/2024 |
| 1.0.0-preview4 | 244 | 4/4/2024 |
| 1.0.0-preview1 | 190 | 4/4/2024 |
| 0.0.0-alpha.0.64 | 132 | 5/26/2024 |