using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using Tesseract.Internal;
using Tesseract.Interop;
namespace Tesseract
{
///
/// The tesseract OCR engine.
///
public class TesseractEngine : DisposableBase
{
private static readonly TraceSource trace = new TraceSource("Tesseract");
private HandleRef handle;
private int processCount = 0;
///
/// Creates a new instance of using the mode.
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
public TesseractEngine(string datapath, string language)
: this(datapath, language, EngineMode.Default, new string[0], new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified
/// using the Default Engine Mode.
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// Note: That the config files MUST be encoded without the BOM using unix end of line characters.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
///
/// An optional tesseract configuration file that is encoded using UTF8 without BOM
/// with Unix end of line characters you can use an advanced text editor such as Notepad++ to accomplish this.
///
public TesseractEngine(string datapath, string language, string configFile)
: this(datapath, language, EngineMode.Default, configFile != null ? new[] { configFile } : new string[0], new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified
/// using the Default Engine Mode.
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
///
/// An optional sequence of tesseract configuration files to load, encoded using UTF8 without BOM
/// with Unix end of line characters you can use an advanced text editor such as Notepad++ to accomplish this.
///
public TesseractEngine(string datapath, string language, IEnumerable configFiles)
: this(datapath, language, EngineMode.Default, configFiles, new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified .
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
/// The value to use when initialising the tesseract engine.
public TesseractEngine(string datapath, string language, EngineMode engineMode)
: this(datapath, language, engineMode, new string[0], new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified and .
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// Note: That the config files MUST be encoded without the BOM using unix end of line characters.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
/// The value to use when initialising the tesseract engine.
///
/// An optional tesseract configuration file that is encoded using UTF8 without BOM
/// with Unix end of line characters you can use an advanced text editor such as Notepad++ to accomplish this.
///
public TesseractEngine(string datapath, string language, EngineMode engineMode, string configFile)
: this(datapath, language, engineMode, configFile != null ? new[] { configFile } : new string[0], new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified and .
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
/// The value to use when initialising the tesseract engine.
///
/// An optional sequence of tesseract configuration files to load, encoded using UTF8 without BOM
/// with Unix end of line characters you can use an advanced text editor such as Notepad++ to accomplish this.
///
public TesseractEngine(string datapath, string language, EngineMode engineMode, IEnumerable configFiles)
: this(datapath, language, engineMode, configFiles, new Dictionary(), false)
{
}
///
/// Creates a new instance of with the specified and .
///
///
///
/// The parameter should point to the directory that contains the 'tessdata' folder
/// for example if your tesseract language data is installed in C:\Tesseract\tessdata the value of datapath should
/// be C:\Tesseract. Note that tesseract will use the value of the TESSDATA_PREFIX environment variable if defined,
/// effectively ignoring the value of parameter.
///
///
/// The path to the parent directory that contains the 'tessdata' directory, ignored if the TESSDATA_PREFIX environment variable is defined.
/// The language to load, for example 'eng' for English.
/// The value to use when initialising the tesseract engine.
///
/// An optional sequence of tesseract configuration files to load, encoded using UTF8 without BOM
/// with Unix end of line characters you can use an advanced text editor such as Notepad++ to accomplish this.
///
public TesseractEngine(string datapath, string language, EngineMode engineMode, IEnumerable configFiles, IDictionary initialOptions, bool setOnlyNonDebugVariables)
{
Guard.RequireNotNullOrEmpty("language", language);
DefaultPageSegMode = PageSegMode.Auto;
handle = new HandleRef(this, Interop.TessApi.Native.BaseApiCreate());
Initialise(datapath, language, engineMode, configFiles, initialOptions, setOnlyNonDebugVariables);
}
public string Version
{
get
{
// Get version doesn't work for x64, might be compilation related for now just
// return constant so we don't crash.
return TessApi.BaseApiGetVersion();
}
}
internal HandleRef Handle
{
get { return handle; }
}
///
/// Processes the specific image.
///
///
/// You can only have one result iterator open at any one time.
///
/// The image to process.
/// The page layout analyasis method to use.
public Page Process(Pix image, PageSegMode? pageSegMode = null)
{
return Process(image, null, new Rect(0, 0, image.Width, image.Height), pageSegMode);
}
///
/// Processes a specified region in the image using the specified page layout analysis mode.
///
///
/// You can only have one result iterator open at any one time.
///
/// The image to process.
/// The image region to process.
/// The page layout analyasis method to use.
/// A result iterator
public Page Process(Pix image, Rect region, PageSegMode? pageSegMode = null)
{
return Process(image, null, region, pageSegMode);
}
///
/// Processes the specific image.
///
///
/// You can only have one result iterator open at any one time.
///
/// The image to process.
/// Sets the input file's name, only needed for training or loading a uzn file.
/// The page layout analyasis method to use.
public Page Process(Pix image, string inputName, PageSegMode? pageSegMode = null)
{
return Process(image, inputName, new Rect(0, 0, image.Width, image.Height), pageSegMode);
}
///
/// Processes a specified region in the image using the specified page layout analysis mode.
///
///
/// You can only have one result iterator open at any one time.
///
/// The image to process.
/// Sets the input file's name, only needed for training or loading a uzn file.
/// The image region to process.
/// The page layout analyasis method to use.
/// A result iterator
public Page Process(Pix image, string inputName, Rect region, PageSegMode? pageSegMode = null)
{
if (image == null) throw new ArgumentNullException("image");
if (region.X1 < 0 || region.Y1 < 0 || region.X2 > image.Width || region.Y2 > image.Height)
throw new ArgumentException("The image region to be processed must be within the image bounds.", "region");
if (processCount > 0) throw new InvalidOperationException("Only one image can be processed at once. Please make sure you dispose of the page once your finished with it.");
processCount++;
var actualPageSegmentMode = pageSegMode.HasValue ? pageSegMode.Value : DefaultPageSegMode;
Interop.TessApi.Native.BaseAPISetPageSegMode(handle, actualPageSegmentMode);
Interop.TessApi.Native.BaseApiSetImage(handle, image.Handle);
if (!String.IsNullOrEmpty(inputName))
{
Interop.TessApi.Native.BaseApiSetInputName(handle, inputName);
}
var page = new Page(this, image, inputName, region, actualPageSegmentMode);
page.Disposed += OnIteratorDisposed;
return page;
}
protected override void Dispose(bool disposing)
{
if (handle.Handle != IntPtr.Zero)
{
Interop.TessApi.Native.BaseApiDelete(handle);
handle = new HandleRef(this, IntPtr.Zero);
}
}
private string GetTessDataPrefix()
{
try
{
return Environment.GetEnvironmentVariable("TESSDATA_PREFIX");
}
catch (SecurityException e)
{
trace.TraceEvent(TraceEventType.Error, 0, "Failed to detect if the environment variable 'TESSDATA_PREFIX' is set: {0}", e.Message);
return null;
}
}
private void Initialise(string datapath, string language, EngineMode engineMode, IEnumerable configFiles, IDictionary initialValues, bool setOnlyNonDebugVariables)
{
Guard.RequireNotNullOrEmpty("language", language);
// do some minor processing on datapath to fix some common errors (this basically mirrors what tesseract does as of 3.04)
if (!String.IsNullOrEmpty(datapath))
{
// remove any excess whitespace
datapath = datapath.Trim();
// remove any trialing '\' or '/' characters
if (datapath.EndsWith("\\", StringComparison.Ordinal) || datapath.EndsWith("/", StringComparison.Ordinal))
{
datapath = datapath.Substring(0, datapath.Length - 1);
}
}
if (Interop.TessApi.BaseApiInit(handle, datapath, language, (int)engineMode, configFiles ?? new List(), initialValues ?? new Dictionary(), setOnlyNonDebugVariables) != 0)
{
// Special case logic to handle cleaning up as init has already released the handle if it fails.
handle = new HandleRef(this, IntPtr.Zero);
GC.SuppressFinalize(this);
throw new TesseractException(ErrorMessage.Format(1, "Failed to initialise tesseract engine."));
}
}
///
/// Ties the specified pix to the lifecycle of a page.
///
public class PageDisposalHandle
{
private readonly Page page;
private readonly Pix pix;
public PageDisposalHandle(Page page, Pix pix)
{
this.page = page;
this.pix = pix;
page.Disposed += OnPageDisposed;
}
private void OnPageDisposed(object sender, System.EventArgs e)
{
page.Disposed -= OnPageDisposed;
// dispose the pix when the page is disposed.
pix.Dispose();
}
}
#region Config
///
/// Gets or sets default mode used by .
///
public PageSegMode DefaultPageSegMode
{
get;
set;
}
public bool SetDebugVariable(string name, string value)
{
return Interop.TessApi.BaseApiSetDebugVariable(handle, name, value) != 0;
}
///
/// Sets the value of a string variable.
///
/// The name of the variable.
/// The new value of the variable.
/// Returns True if successful; otherwise False.
public bool SetVariable(string name, string value)
{
return Interop.TessApi.BaseApiSetVariable(handle, name, value) != 0;
}
///
/// Sets the value of a boolean variable.
///
/// The name of the variable.
/// The new value of the variable.
/// Returns True if successful; otherwise False.
public bool SetVariable(string name, bool value)
{
var strEncodedValue = value ? "TRUE" : "FALSE";
return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0;
}
///
/// Sets the value of a integer variable.
///
/// The name of the variable.
/// The new value of the variable.
/// Returns True if successful; otherwise False.
public bool SetVariable(string name, int value)
{
var strEncodedValue = value.ToString("D", CultureInfo.InvariantCulture.NumberFormat);
return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0;
}
///
/// Sets the value of a double variable.
///
/// The name of the variable.
/// The new value of the variable.
/// Returns True if successful; otherwise False.
public bool SetVariable(string name, double value)
{
var strEncodedValue = value.ToString("R", CultureInfo.InvariantCulture.NumberFormat);
return Interop.TessApi.BaseApiSetVariable(handle, name, strEncodedValue) != 0;
}
///
/// Attempts to retrieve the value for a boolean variable.
///
/// The name of the variable.
/// The current value of the variable.
/// Returns True if successful; otherwise False.
public bool TryGetBoolVariable(string name, out bool value)
{
int val;
if (Interop.TessApi.Native.BaseApiGetBoolVariable(handle, name, out val) != 0)
{
value = (val != 0);
return true;
}
else
{
value = false;
return false;
}
}
///
/// Attempts to retrieve the value for a double variable.
///
/// The name of the variable.
/// The current value of the variable.
/// Returns True if successful; otherwise False.
public bool TryGetDoubleVariable(string name, out double value)
{
return Interop.TessApi.Native.BaseApiGetDoubleVariable(handle, name, out value) != 0;
}
///
/// Attempts to retrieve the value for an integer variable.
///
/// The name of the variable.
/// The current value of the variable.
/// Returns True if successful; otherwise False.
public bool TryGetIntVariable(string name, out int value)
{
return Interop.TessApi.Native.BaseApiGetIntVariable(handle, name, out value) != 0;
}
///
/// Attempts to retrieve the value for a string variable.
///
/// The name of the variable.
/// The current value of the variable.
/// Returns True if successful; otherwise False.
public bool TryGetStringVariable(string name, out string value)
{
value = Interop.TessApi.BaseApiGetStringVariable(handle, name);
return value != null;
}
///
/// Attempts to print the variables to the file.
///
///
///
public bool TryPrintVariablesToFile(string filename)
{
return Interop.TessApi.Native.BaseApiPrintVariablesToFile(handle, filename) != 0;
}
#endregion Config
#region Event Handlers
private void OnIteratorDisposed(object sender, EventArgs e)
{
processCount--;
}
#endregion Event Handlers
}
}