henrivain / TesseractOcrMaui

Tesseract wrapper for Windows, Android and iOS for .NET MAUI
Apache License 2.0
37 stars 4 forks source link

Add way to configure TessEngine in ITesseract #16

Closed henrivain closed 10 months ago

henrivain commented 1 year ago

Problem

User cannot configure Tesseract parameters of the injected ITesseract api and must use underlying TessEngine api. Because of this dependency injection cannot be used when user wants to change for example tessedit_char_whitelist or tessedit_char_blacklist.

Current way of doing

string traineddataFolder = FileSystem.Current.CacheDirectory;

// Load data
var tranineddataPath = Path.Combine(traineddataFolder , "eng.traineddata");
if (!File.Exists(tranineddataPath)) 
{
    using Stream traineddata = await FileSystem.OpenAppPackageFileAsync("eng.traineddata");
    FileStream fileStream = File.Create(tranineddataPath);
    traineddata.CopyTo(fileStream);
}

// Create and configure engine
using var engine = new TessEngine("eng", traineddataFolder );
bool success = engine.SetVariable("tessedit_char_whitelist", "mychars");

// Recognize text
using var image = Pix.LoadFromFile(@"\path\to\file.png");
using var result = engine.ProcessImage(image);
string text = result.GetText();

Suggested fix

Addition to ITesseract API

Add optional configuration Property to pass into ITesseract

public interface ITesseract
{
    ...

    Action<TessEngine> EngineConfiguration { get; set; }
}

Changes in Tesseract.cs

Method internal RecognizionResult Recognize(Pix pix, string tessDataFolder, string[] traineddataFileNames)

Change

try
{
    using var engine = new TessEngine(languages, tessDataFolder, Logger);
    using var page = engine.ProcessImage(pix);

    confidence = page.GetConfidence();
    text = page.GetText();
}
...

To

try
{
    using var engine = new TessEngine(languages, tessDataFolder, Logger);
    // Configure
    EngineConfiguration(engine);

    using var page = engine.ProcessImage(pix);
    confidence = page.GetConfidence();
    text = page.GetText();
}
...

Intended use

Set configuration func before running process


Tesseract.EngineConfiguration = engine => 
{
    // These characters are not recognized
    engine.SetVariable("tessedit_char_blacklist", "bad");
}
var result = await Tesseract.RecognizeTextAsync(@"my\image\path.png");
henrivain commented 10 months ago

Pull request #19 fixes this issue

Example

private async void DEMO_Recognize_AsConfigured(object sender, EventArgs e)
{
    // Select image (Not important)
    var path = await GetUserSelectedPath();
    if (path is null)
    {
        return;
    }

    // this Tesseract is injected property
    Tesseract.EngineConfiguration = (engine) =>
    {
        // Engine uses DefaultSegmentationMode, if no other is passed as method parameter.
        // If ITesseract is injected to page, this is only way of setting PageSegmentationMode.
        // PageSegmentationMode defines how ocr tries to look for text, for example singe character or single word.
        // By default uses PageSegmentationMode.Auto.
        engine.DefaultSegmentationMode = TesseractOcrMaui.Enums.PageSegmentationMode.Auto;

        engine.SetCharacterWhitelist("abcdefgh");   // These characters ocr is looking for
        engine.SetCharacterBlacklist("abc");        // These characters ocr is not looking for
        // Now ocr should be only finding characters 'defgh'
    };

    // Recognize image 
    var result = await Tesseract.RecognizeTextAsync(path);

    // For this example I reset engine configuration, because same Object is used in other examples
    Tesseract.EngineConfiguration = null;

    // Show output (Not important)
    ShowOutput("FromPath, Configured", result);

}