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 } }