Need Help with my C# Wrapper

1,749 views
Skip to first unread message

EricD

unread,
Mar 2, 2008, 6:33:28 PM3/2/08
to tesseract-ocr
Hi all,

once again I'm having some difficulties while trying to use tessdll
from C#.

I dont know if im doing the marshaling correctly. Let me present the
code for my wrapper before I start explaining my problem:
<CODE>
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;

[StructLayout(LayoutKind.Sequential)]
public struct EANYCODE_CHAR /*single character */
{
UInt16 char_code; /*character itself */
Int16 left; /*of char (-1) */
Int16 right; /*of char (-1) */
Int16 top; /*of char (-1) */
Int16 bottom; /*of char (-1) */
Int16 font_index; /*what font (0) */
byte confidence; /*0=perfect, 100=reject (0/100) */
byte point_size; /*of char, 72=i inch, (10) */
sbyte blanks; /*no of spaces before this char
(1) */
byte formatting; /*char formatting (0) */

} /*single character */

[StructLayout(LayoutKind.Sequential)]
public struct ETEXT_DESC /*output header */
{
Int16 count; /*chars in this buffer(0) */
Int16 progress; /*percent complete increasing (0-100)
*/
sbyte more_to_come; /*true if not last */
sbyte ocr_alive; /*ocr sets to 1, HP 0 */
sbyte err_code; /*for errcode use */
unsafe void* cancel; /*returns true to cancel */
unsafe void* cancel_this; /*this or other data for
cancel*/
Int32 end_time; /*time to stop if not 0*/
EANYCODE_CHAR text; /*character data */
}

namespace testingWrapper
{
class WrapperTesseract
{
[DllImport("tessdll.dll")]
private unsafe static extern int
TessDllBeginPageUpright(UInt32 xsize, UInt32 ysize, byte* buf, string
lang);

[DllImport("tessdll.dll")]
private unsafe static extern IntPtr
TessDllRecognize_all_Words();

private static byte[]
ConvertImageToByteArray(System.Drawing.Image imageToConvert,
ImageFormat formatOfImage)
{
byte[] Ret;
using (MemoryStream ms = new MemoryStream())
{
imageToConvert.Save(ms, formatOfImage);
Ret = ms.ToArray();
}
return Ret;
}

public static void recognizeChars(System.Drawing.Bitmap bmp,
string language)
{
//This is a test image to see if i have some output as the
dlltest
bmp = new Bitmap("test5.tif");

//Convertion from image to a byte array
byte[] byteBmp = ConvertImageToByteArray(bmp,
bmp.RawFormat);

unsafe
{
//Convertion from a byte array to a pointer
fixed (byte* imgPtr = &byteBmp[0])
{

BitmapData bmd1 = bmp.LockBits(new Rectangle(0, 0,
bmp.Size.Width, bmp.Size.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
ETEXT_DESC d = new ETEXT_DESC();

TessDllBeginPageUpright((uint)bmp.Width,
(uint)bmp.Height, imgPtr, "eng"); //(void*)bmd1.Scan0.ToPointer(),
"eng");

IntPtr data = TessDllRecognize_all_Words();

d = (ETEXT_DESC)(Marshal.PtrToStructure(data,
typeof(ETEXT_DESC)));
data = IntPtr.Zero;


}
}
}
}
}

</CODE>

Alright so what i've done is to redefine the ETEXT_DESC as a managed
struct (and i've also specify [StructLayout(LayoutKind.Sequential)] to
order the struct as i defined it) but the difference is that it
contains a EANYCODE_CHAR text an not an array (i can't seem to find
how to do it without having a AccessViolation crash).

And i had to find a way to convert a C# image into a "unsigned char*",
so i thought that a byte* would be my best candidate. But i'm not sure
if i've done it correctly.

Right now the results between dlltess and my wrapper for the same tiff
is different and the text variable never seem correct.


Anyone could tell me what i'm doing wrong?

If i manage to write the code for this wrapper, ill post the solution
and the .dll on the group.


Thanks for any help.


twknox

unread,
Mar 4, 2008, 1:09:50 PM3/4/08
to tesseract-ocr
I am also looking into this trying to find a way to call this from
managed code. If anyone has any ideas please chime up.

Please someone?

Thanks!

EricD

unread,
Mar 4, 2008, 5:37:25 PM3/4/08
to tesseract-ocr
OK I was able to fix 1 bug, my old struct had 1 byte missing before
the cancel attribute (probably because of the typedef signature)
so my new structure is:

public struct ETEXT_DESC /*output header */
{
public Int16 count; /*chars in this buffer(0) */
public Int16 progress; /*percent complete increasing
(0-100) */
public SByte more_to_come; /*true if not last */
public SByte ocr_alive; /*ocr sets to 1, HP 0 */
public SByte err_code; /*for errcode use */
public SByte cancelSignature; /*for errcode use */
public unsafe void* cancel; /*returns true to cancel */
public unsafe void* cancel_this; /*this or other data
for cancel*/
public Int32 end_time; /*time to stop if not 0*/
unsafe public EANYCODE_CHAR text ; /*character data */
}



So i've add "public SByte cancelSignature; /*for
errcode use */"

But now im pretty sure i'm having problem with my image conversion.
The value of the arrays for the image from dlltest and my wrapper is
different.

I'm guessing that maybe .Net doesnt remove the header of my image
which is done in tesseract?! Anytips on how to remove this header?(if
this is my problem)

Another thing, I dont seem to be able to create an array for the text
variable in my structure. Is it because managed array aren't the same
size in unmanged code?

EricD

unread,
Mar 4, 2008, 9:26:27 PM3/4/08
to tesseract-ocr
Alright, I think my wrapper is working now. I need to clean the code
up and make some tests to make sure its ok.

If anyone is really in need of the code contact me and ill give you
the correct code.

Julien Benoit

unread,
Mar 5, 2008, 2:28:44 AM3/5/08
to tesser...@googlegroups.com
Could you please post here your wrapper, or at least the correct p/invokes signatures and structures. I think it will be very useful for persons (including me) wanting to integrate tesseract with the .Net framework. After the hard work you have done, it would be sad if everyone else had to reinvent the wheel.

--
olorin

twknox

unread,
Mar 5, 2008, 9:50:17 AM3/5/08
to tesseract-ocr
Eric, way to go! Please share your hard earned knowledge :)

Did you end up using an intptr for the char array? I was going down
this path and having some grief....

Please post your changes and we'll gladly help you test.

jc

unread,
Mar 5, 2008, 10:55:27 AM3/5/08
to tesseract-ocr
Very good Eric. Could you please post your code, or email it to me.

Thanks,
Jeff

EricD

unread,
Mar 5, 2008, 11:50:00 AM3/5/08
to tesseract-ocr
Ok before i start, i want to tell everyone that this code work to
invoke only 3 fonctions of tesseract
(TessDllBeginPageUpright,TessDllBeginPage and
TessDllRecognize_all_Words) but it can be easily expanded if you
follow the logic.

Plus this code is only a startup, this is why i wont post the .cs to
the group right now since its not finished (I'm posting it only to
help those who need a wrapper fast)

Another thing i realized is that SOME images will have different (and
ugly) results than if you used tesseract.exe or the .dll. This is
probably cause because tesseract.exe and the .dll must do some image
preprocessing before it is analysed (am i right?)
So the next version of the code will fix this problem

BUGS: Here's a list of bugs that i found when using the wrapper
1-Changing language between some extraction make the application
crash.
2-Results vary from the wrapper and the dlltest for SOME image
(Preprocessing missing?)
...more to come


And remember this is only a prototype of the wrapper and it may
contains some bug so do not use it without testing it to make sure it
does what you want.

<CODE SNIPPET>
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;


/
**********************************************************************
* CharDescription
* Description of a single character. The character code is defined by
* the character set of the current font.
* Output text is sent as an array of these structures.
* Spaces and line endings in the output are represented in the
* structures of the surrounding characters. They are not directly
* represented as characters.
* The first character in a word has a positive value of blanks.
* Missing information should be set to the defaults in the comments.
* If word bounds are known, but not character bounds, then the top
and
* bottom of each character should be those of the word. The left of
the
* first and right of the last char in each word should be set. All
other
* lefts and rights should be set to -1.
* If set, the values of right and bottom are left+width and top
+height.
* Most of the members come directly from the parameters to
ocr_append_char.
* The formatting member uses the enhancement parameter and combines
the
* line direction stuff into the top 3 bits.
* The coding is 0=RL char, 1=LR char, 2=DR NL, 3=UL NL, 4=DR Para,
* 5=UL Para, 6=TB char, 7=BT char. API users do not need to know what
* the coding is, only that it is backwards compatible with the
previous
* version.

**********************************************************************/
[StructLayout(LayoutKind.Sequential)]
public struct CharDescription /*single character */
{

public UInt16 char_code; /*character itself */
public Int16 left; /*of char (-1) */
public Int16 right; /*of char (-1) */
public Int16 top; /*of char (-1) */
public Int16 bottom; /*of char (-1) */
public Int16 font_index; /*what font (0) */
public Byte confidence; /*0=perfect, 100=reject
(0/100) */
public Byte point_size; /*of char, 72=i inch, (10) */
public SByte blanks; /*no of spaces before this
char (1) */
public Byte formatting; /*char formatting (0) */

} /*single character */

[StructLayout(LayoutKind.Sequential)]
public struct GeneralInfos /*output header */
{
public Int16 count; /*chars in this buffer(0) */
public Int16 progress; /*percent complete
increasing (0-100) */
public SByte more_to_come; /*true if not last */
public SByte ocr_alive; /*ocr sets to 1, HP 0 */
public SByte err_code; /*for errcode use */
public Byte cancelSignature;
public unsafe void* cancel; /*returns true to cancel */
public unsafe void* cancel_this; /*this or other data
for cancel*/
public Int32 end_time; /*time to stop if not 0*/
}

/// <summary>
/// Used for returning a managed structure contains OCR extraction
results
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class AnalysisResults
{
public GeneralInfos generalInfos=new GeneralInfos();
public List<CharDescription> charactersFound = new
List<CharDescription>();
public string stringFound = "";
}

namespace tesseractOCR
{
/// <summary>
/// A wrapper that expose some fonctionnalies of TesseractOCR
/// </summary>
class WrapperTesseract
{

[DllImport("tessdll.dll")]
private unsafe static extern int
TessDllBeginPageUpright(UInt32 xsize, UInt32 ysize, IntPtr buf, string
lang);

[DllImport("tessdll.dll")]
private unsafe static extern int
TessDllBeginPageUprightBPP(UInt32 xsize, UInt32 ysize, IntPtr buf,
string lang, Byte bpp);

[DllImport("tessdll.dll")]
private unsafe static extern IntPtr
TessDllRecognize_all_Words();


/// <summary>
/// Take an image and extract any text found using the
specified language with TesseractOCR.
/// </summary>
/// <param name="bmp">The image to analyse.</param>
/// <param name="language">The language to be used to extract
text.</param>
/// <param name="bpp">The number of bits per pixel(bpp) of the
image.</param>
/// <returns>A AnalysisResults structure that contains the
results of the Tesseract
/// extraction.</returns>
public static AnalysisResults
recognizeChars(System.Drawing.Bitmap bmp, string language, Byte bpp)
{

//TODO: IMAGE RESIZING TO INCREASE TESSERACT ACCURACY

//Create a BitmapData and Lock all pixels to be read
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, bmp.PixelFormat);


TessDllBeginPageUprightBPP((uint)bmp.Width,
(uint)bmp.Height, bmpData.Scan0, language, bpp);

//Unlock the pixels
bmp.UnlockBits(bmpData);

IntPtr data = TessDllRecognize_all_Words();

return(BuildAnalysisResults(data));
}

/// <summary>
/// Build and return a AnalysisResults object that contains
the results of the Tesseract
/// extraction.
/// </summary>
/// <param name="data">A pointer to the data to be
transformed.</param>
/// <returns>A AnalysisResults structure that contains the
results of the Tesseract
/// extraction.</returns>
private static AnalysisResults BuildAnalysisResults(IntPtr
data)
{
AnalysisResults analysisResults = new AnalysisResults();

//Retreives the structure starting at the address of the
pointer
analysisResults.generalInfos = (GeneralInfos)
(Marshal.PtrToStructure(data, typeof(GeneralInfos)));

//Move to the text data (next data is actually at + 20 not
4, but 4 will be
//add up with 16 in the for loop (and make 20)
data = new IntPtr(data.ToInt32() + 4);
for (int i = 0; i < analysisResults.generalInfos.count; i+
+)
{
//Move to the next character
data = new IntPtr(data.ToInt32() + 16);

CharDescription charInfos = (CharDescription)
(Marshal.PtrToStructure(data, typeof(CharDescription)));
analysisResults.charactersFound.Add(charInfos);

//TODO: ADD CHARACTER ANALYSIS TO FORMAT THE STRING OR/
AND TO REMOVE UNWANTED CHARACTERS

//Lets add all the white space detected
for(int j = 0; j<charInfos.blanks;j++)
analysisResults.stringFound += " ";

//Now lets add the char found
analysisResults.stringFound +=
(char)charInfos.char_code;

}
//Unaffect the pointer
data = IntPtr.Zero;
return analysisResults;

}
}
}


</CODE SNIPPET>

Knowing the offset of each value returned by my structure and because
of the complexity of it, i decided that it would be better to build
the structure myselft. But if the structure was without any array that
can grows i would suggest to use the marshal.PtrToStructure fonction.



Plus the first methods required a pointer to the first pixel of an
image. In my first code i was using the .save method, but it save the
content of the header as well, so what i neeeded to do is to use
the .Scan0 which return this pointer.



Oh and it seems that the typedef required an additionnal byte for
storing the def signature, so this is why i have a byte
cancelSignature in my structure.


REMARK: This code is only a beginning

I hope this code can help someone with interop because its not an easy
subject and it can get confusing sometimes...

p.s: suggestion of amelioration is very welcome.

twknox

unread,
Mar 5, 2008, 2:29:05 PM3/5/08
to tesseract-ocr
OK congrats on the progress so far! I can run all the way through now
without any memory errors :)

Now I just need to get my image to a point where it can be processed
correctly. I've tried reading a bitmap from a file and passing that,
but I never get the results expected. Usually I just get a single
tilde '~' back from the call.

Ultimately I want to be able to capture part of the screen and process
that. Any ideas on how to get this to work reliably? Any idea on
exactly what format the bitmap needs to be in to get tesseract to like
it?

Thanks!

EricD

unread,
Mar 6, 2008, 12:05:54 PM3/6/08
to tesseract-ocr
try this code to transform your image to a tiff file with no
compression :

private void saveImageTIFF(Image img, string fname)
{
ImageCodecInfo myImageCodecInfo;
System.Drawing.Imaging.Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;



// Get an ImageCodecInfo object that represents the TIFF
codec.
myImageCodecInfo = GetEncoder(ImageFormat.Tiff);

// Create an Encoder object based on the GUID
// for the Compression parameter category.
myEncoder = System.Drawing.Imaging.Encoder.Compression;

// Create an EncoderParameters object.
// An EncoderParameters object has an array of
EncoderParameter
// objects. In this case, there is only one
// EncoderParameter object in the array.
myEncoderParameters = new EncoderParameters(1);

// Save the bitmap as a TIFF file with no compression.
myEncoderParameter = new EncoderParameter(myEncoder,
(long)System.Drawing.Imaging.EncoderValue.CompressionNone);

myEncoderParameters.Param[0] = myEncoderParameter;

img.Save(fname, myImageCodecInfo, myEncoderParameters);
}

private ImageCodecInfo GetEncoder(ImageFormat format)
{

ImageCodecInfo[] codecs =
ImageCodecInfo.GetImageDecoders();

foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}


(this save the image to a file but you can easily just rebuild the
image with the new compression infos without saving it)
> ...
>
> read more »- Hide quoted text -
>
> - Show quoted text -

EricD

unread,
Mar 6, 2008, 12:14:59 PM3/6/08
to tesseract-ocr
After some tests with the dll, the .exe and my wrapper i notice that
the data retrieve are not always the same.

In fact the .exe is the one giving me the best results, after its
the .dll and finally my wrapper.

Ok maybe my wrapper is buggy at the moment but why isn't the .dll
results not the same as the .exe results.

What is so different between the .dll and the .exe


I've done the test on different image and below is the results found
(i could also upload my image in case someone want to help me) (with
eng language)

Actual Word | Res .dll | Res wrapper| Res .exe
--------------------------------------------------------------------
MFD | unru- | {S! | nrt!
FMS1 | ~ | ~ | FHS1
HDG | HUG | HUG | HDG
PFD | PFD | REU | PFD
113.80 | 113.80 | ~ | 113.80

The image are small and maybe not that clear, so i'm expecting that
the value retreived wont be 100% accurate. But I want at least to see
that value extracted to have the same results as the .exe.


Why are the results so different?!

Francesco

unread,
Mar 7, 2008, 6:41:42 AM3/7/08
to tesseract-ocr
Hi all,
I have written code that interacts with Tessdll.dll through PInvoke
but it is still too slow when it is passed a tiff image from an A4
page.
An article will soon be published in italian, on factory.wavegroup.it
and shortly after that, I will post my code there.

P.S.There are differences between my code and Eric's code and I can't
get all the fields of the
ETEXT_DESC structure although i get the fields of EANYCODE_CHAR
correctly.

twknox

unread,
Mar 7, 2008, 9:08:05 AM3/7/08
to tesseract-ocr
Thanks Eric, I'll try this when I get home this eve. I tried creating
an uncompressed bmp and it didn't like that from the wrapper, though
it worked fine from dlltest. I have modified a copy of "dlltest" that
I am currently calling from my C# application. It's clunky, so
calling directly is preferred.

My application is actually quite simple, it grabs a grid of 25
characters from the screen and just needs to do OCR on these. I
currently take the color image, and convert to 1bpp, inverting the
black/white so that the background is white, and the actual text is
black. This I am writing out to a .bmp, and calling the modified exe
that returns a string of 25 chars (or so I hope for). I am getting 90%
+ accuracy this way so far....


On Mar 6, 12:05 pm, EricD <eri...@videotron.ca> wrote:
> ...
>
> read more »

EricD

unread,
Mar 8, 2008, 12:04:37 AM3/8/08
to tesseract-ocr

Ok, there IS a difference between the way the .exe and the .dll
extract the data.

I've created a new dll fonction that does the same logic as the .exe.
(i wont post it right now because once again its not complete and ill
post it tomorow maybe)

But now i'm sure my wrapper doesnt convert the image to a const char*
correctly because whe i try to open from a file the result is correct
but when i pass the buffer, result is messed up.

I need help from anyone interested by the wrapper to see whats my
error in my code?

The dll requires that the pointer is the top pixel of the image, so
the header of the image must be removed!


Please help.


But the thing is

EricD

unread,
Mar 8, 2008, 9:42:33 PM3/8/08
to tesseract-ocr
Ok everything seems to be working correctly.

My wrapper now use my new dll methods that actually call the same
thing as the exe would.

For the problem with the image, I've realized that i had to build the
byte[] myselft since that i have to remove the stride (padding added)
of the image.

So I would suggest adding and reviewing my dll methods to futur
release of Tesseract. What you guys think about that?

The method that was added to tessdll.cpp is:

<CODE>
char* TessDllAPI::ExtractDataWithExeLogic(UINT32 xsize,UINT32
ysize,unsigned char *buf,const char* lang,UINT8 bpp)
{
char *fake_argv[] = { "api_config" };

TessBaseAPI::InitWithLanguage("TESSEDIT", "tessapi", lang, 0L, false,
0, fake_argv);

int bytes_per_line = check_legal_image_size(xsize, ysize, bpp);

return TessBaseAPI::TesseractRect(buf, bpp/8, bytes_per_line, 0, 0,
xsize, ysize);

}
</CODE>

And now the wrapper looks like this:

<CODE>
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.IO;

[StructLayout(LayoutKind.Sequential)]
public struct CharDescription /*single character */
{

public UInt16 char_code; /*character itself */
public Int16 left; /*of char (-1) */
public Int16 right; /*of char (-1) */
public Int16 top; /*of char (-1) */
public Int16 bottom; /*of char (-1) */
public Int16 font_index; /*what font (0) */
public Byte confidence; /*0=perfect, 100=reject
(0/100) */
public Byte point_size; /*of char, 72=i inch, (10) */
public SByte blanks; /*no of spaces before this
char (1) */
public Byte formatting; /*char formatting (0) */

} /*single character */

[StructLayout(LayoutKind.Sequential)]
public struct GeneralInfos /*output header */
{
public Int16 count; /*chars in this buffer(0) */
public Int16 progress; /*percent complete
increasing (0-100) */
public SByte more_to_come; /*true if not last */
public SByte ocr_alive; /*ocr sets to 1, HP 0 */
public SByte err_code; /*for errcode use */
public Byte cancelSignature;
public IntPtr cancel; /*returns true to cancel */
public IntPtr cancel_this; /*this or other data for
cancel*/
public Int32 end_time; /*time to stop if not 0*/
}

/// <summary>
/// Used for returning a managed structure contains OCR extraction
results
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class AnalysisResults
{
public GeneralInfos generalInfos=new GeneralInfos();
public List<CharDescription> charactersFound = new
List<CharDescription>();
public string stringFound = "";
}

namespace tesseractOCR
{
/// <summary>
/// A wrapper that expose some fonctionnalies of TesseractOCR
/// </summary>
class WrapperTesseract
{
private static bool firstRun = true;

[DllImport("tessdll.dll",
CallingConvention=CallingConvention.Cdecl)]
private unsafe static extern int
TessDllBeginPageUpright(UInt32 xsize, UInt32 ysize, byte* buf, string
lang);

[DllImport("tessdll.dll", CallingConvention =
CallingConvention.Cdecl)]
private unsafe static extern int
TessDllBeginPageUprightBPP(UInt32 xsize, UInt32 ysize, byte* buf,
string lang, Byte bpp);

[DllImport("tessdll.dll", CallingConvention =
CallingConvention.Cdecl)]
private static extern IntPtr TessDllRecognize_all_Words();

[DllImport("tessdll.dll", CallingConvention =
CallingConvention.Cdecl)]
private static extern void TessDllEndPage();

[DllImport("tessdll.dll", CallingConvention =
CallingConvention.Cdecl)]
private static extern void TessDllRelease();

[DllImport("tessdll.dll", CallingConvention =
CallingConvention.Cdecl, SetLastError=true)]
private static unsafe extern IntPtr
ExtractDataWithExeLogic(UInt32 xsize, UInt32 ysize, byte* buf, string
lang, Byte bpp);


/// <summary>
/// Converts the image to byte array without Strides data..
/// </summary>
/// <param name="imageToConvert">The image to convert.</param>
/// <returns>A array of bytes</returns>
private static byte[]
ConvertImageToByteArray(System.Drawing.Bitmap imageToConvert, Byte
bpp)
{
//Create a BitmapData and Lock all pixels to be read
BitmapData bmpData = imageToConvert.LockBits(
new Rectangle(0, 0, imageToConvert.Width,
imageToConvert.Height),
ImageLockMode.ReadOnly,
imageToConvert.PixelFormat);

byte[] imgBytes = new byte[bmpData.Height * bmpData.Width
* 3];

unsafe
{
IntPtr imgPtr = (bmpData.Scan0);

int indexImg = 0;

int pointerOffset = 0;

for (int i = 0; i < bmpData.Height; i++)
{
for (int j = 0; j < bmpData.Width; j++)
{
imgBytes[indexImg] =
Marshal.ReadByte(imgPtr,pointerOffset);
indexImg++;
pointerOffset += (bpp / 8) / 3;

imgBytes[indexImg] =
Marshal.ReadByte(imgPtr,pointerOffset);
indexImg++;
pointerOffset += (bpp / 8) / 3;

imgBytes[indexImg] =
Marshal.ReadByte(imgPtr,pointerOffset);
indexImg++;
pointerOffset += (bpp / 8) / 3;

}
pointerOffset += bmpData.Stride - bmpData.Width *
(bpp/8);
}
}

//Unlock the pixels
imageToConvert.UnlockBits(bmpData);

return imgBytes;
}

/// <summary>
/// Take an image and extract any text found using the
specified language with TesseractOCR DLL logic.
/// </summary>
/// <param name="bmp">The image to analyse.</param>
/// <param name="language">The language to be used to extract
text.</param>
/// <param name="bpp">The number of bits per pixel(bpp) of the
image.</param>
/// <returns>The extracted string </returns>
public static String RecognizeCharsDll(System.Drawing.Bitmap
bmp, string language)
{

if (firstRun)
firstRun = false;
else
TessDllEndPage();

Byte bpp = 0;
AnalysisResults tmp = new AnalysisResults();
IntPtr data = new IntPtr();


switch (bmp.PixelFormat)
{
case PixelFormat.Format1bppIndexed: bpp = 1; break;
case PixelFormat.Format8bppIndexed: bpp = 8; break;
case PixelFormat.Format48bppRgb: bpp = 48; break;
case PixelFormat.Format32bppRgb: bpp = 32; break;
case PixelFormat.Format24bppRgb: bpp = 24; break;
default: throw new BadImageFormatException("Please use
an image format of.... TODO");//TODO

}


unsafe
{
byte[] imgArray = ConvertImageToByteArray(bmp, bpp);

fixed (byte* ptr = &imgArray[0])
{
TessDllBeginPageUprightBPP((uint)bmp.Width,
(uint)bmp.Height, ptr, language, bpp);

data = TessDllRecognize_all_Words();
tmp = BuildAnalysisResults(data);


}
}

//TODO: Character analysis...


return (BuildAnalysisResults(data)).stringFound;
}

/// <summary>
/// Take an image and extract any text found using the
specified language with TesseractOCR EXE Logic.
/// </summary>
/// <param name="bmp">The image to analyse.</param>
/// <param name="language">The language to be used to extract
text.</param>
/// <param name="bpp">The number of bits per pixel(bpp) of the
image.</param>
/// <returns>The extracted string </returns>
public static String RecognizeChars(System.Drawing.Bitmap bmp,
string language)
{
IntPtr res= new IntPtr();
Byte bpp = 0;

switch (bmp.PixelFormat)
{
case PixelFormat.Format1bppIndexed: bpp = 1; break;
case PixelFormat.Format8bppIndexed: bpp = 8; break;
case PixelFormat.Format48bppRgb: bpp = 48; break;
case PixelFormat.Format32bppRgb: bpp = 32; break;
case PixelFormat.Format24bppRgb: bpp = 24; break;
//TEMPORARY TEST
case PixelFormat.Format32bppArgb: bpp = 32; break;
default: throw new BadImageFormatException("Please use
an image format of.... TODO");//TODO

}

unsafe
{
byte[] imgArray = ConvertImageToByteArray(bmp, bpp);

fixed (byte* ptr = &imgArray[0])
{
res = ExtractDataWithExeLogic((uint)bmp.Width,
(uint)bmp.Height, ptr, language, bpp);
}
}

//TODO: Character analysis...

return PointerToString(res);
}


/// <summary>
/// Convert a pointer to a String.
/// </summary>
/// <param name="data">The pointer to be converted.</param>
/// <returns>The string converted.</returns>
private static String PointerToString(IntPtr data)
{
String result = "";
int i=0;

if (data != IntPtr.Zero)
{
char characterExtracted = (char)Marshal.ReadByte(data,
i);

while (characterExtracted.ToString() != "\0")
{
result += characterExtracted.ToString();
i++;
characterExtracted = (char)Marshal.ReadByte(data,
i);
}

//Unaffect the pointer
data = IntPtr.Zero;
}

return result;



}
/// <summary>
/// Build and return a AnalysisResults object that contains
the results of the Tesseract
/// extraction.
/// </summary>
/// <param name="data">A pointer to the data to be
transformed.</param>
/// <returns>A AnalysisResults structure that contains the
results of the Tesseract
/// extraction.</returns>
private static AnalysisResults BuildAnalysisResults(IntPtr
data)
{
AnalysisResults analysisResults = new AnalysisResults();

if (data != IntPtr.Zero)
{


//Retreives the structure starting at the address of
the pointer
analysisResults.generalInfos = (GeneralInfos)
(Marshal.PtrToStructure(data, typeof(GeneralInfos)));

//Move to the text data (next data is actually at + 20
not 4, but 4 will be
//add up with 16 in the for loop (and make 20)
data = new IntPtr(data.ToInt32() + 4);
for (int i = 0; i <
analysisResults.generalInfos.count; i++)
{
//Move to the next character
data = new IntPtr(data.ToInt32() + 16);

CharDescription charInfos = (CharDescription)
(Marshal.PtrToStructure(data, typeof(CharDescription)));
analysisResults.charactersFound.Add(charInfos);

//TODO: ADD CHARACTER ANALYSIS TO FORMAT THE
STRING OR/AND TO REMOVE UNWANTED CHARACTERS

//Lets add all the white space detected
for (int j = 0; j < charInfos.blanks; j++)
analysisResults.stringFound += " ";

//Now lets add the char found
analysisResults.stringFound +=
(char)charInfos.char_code;

}

//Unaffect the pointer
data = IntPtr.Zero;
}
return analysisResults;

}
}
}

</CODE>

Once again this code ain't perfect but it should work fine.


Would it be possible to add this code to version control that way
people could make amelioration and share it to the rest of the
community?

Francesco

unread,
Apr 4, 2008, 3:17:54 PM4/4/08
to tesseract-ocr
Hi all,
this is my code. I use the delegate to call the DLL and I created
classes OCRPage, OCRRow and OCRWord to represent the page processed
from Tesseract.
If you are interested I have written two small posts about my code:
http://factory.wavegroup.it/blog/2008/4/4/ancora-su-tesseract
http://factory.wavegroup.it/blog/2008/3/17/nell-era-dell-indicizzazione-risorge-tesseract-seconda-parte

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.IO;

namespace Wave.OCR.Test
{

public class WaveRec
{

const string dllfile = @"tessdll.dll";

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int TessDllBeginPageUprightBPPDelegate(uint
xsize, uint ysize, System.IntPtr buf,
[System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
string lang, byte bpp);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate System.IntPtr
TessDllRecognize_all_WordsDelegate();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TessDllReleaseDelegate();

public delegate string TransformWordDelegate(string text);

static private int MAX_CHAR_RECOGNIZE = 32000;
static private int DISTANCE_WORDS = 5;


public static OCRPage CreatePageWD(Image file)
{

IntPtr pDll = WrapperUnManaged.LoadLibrary(dllfile);

IntPtr pAddressOfFunctionToCall =
WrapperUnManaged.GetProcAddress(pDll, "TessDllBeginPageUprightBPP");

TessDllBeginPageUprightBPPDelegate BeginPage =
(TessDllBeginPageUprightBPPDelegate)Marshal.GetDelegateForFunctionPointer(

pAddressOfFunctionToCall,

typeof(TessDllBeginPageUprightBPPDelegate));

pAddressOfFunctionToCall =
WrapperUnManaged.GetProcAddress(pDll, "TessDllRecognize_all_Words");

TessDllRecognize_all_WordsDelegate Recognize_all_Words =
(TessDllRecognize_all_WordsDelegate)Marshal.GetDelegateForFunctionPointer(

pAddressOfFunctionToCall,

typeof(TessDllRecognize_all_WordsDelegate));

pAddressOfFunctionToCall =
WrapperUnManaged.GetProcAddress(pDll, "TessDllRelease");

TessDllReleaseDelegate Release =
(TessDllReleaseDelegate)Marshal.GetDelegateForFunctionPointer(

pAddressOfFunctionToCall,

typeof(TessDllReleaseDelegate));


Image i = null;

Image filetmp = null;

MemoryStream ms = null;

ETEXT_DESC[] structUN = new
ETEXT_DESC[MAX_CHAR_RECOGNIZE];

BitmapData data = null;

try
{

OCRPage page = new OCRPage();

int numbersPages =
file.GetFrameCount(FrameDimension.Page);

for (int pageF = 0; pageF < numbersPages; pageF++)
{

file.SelectActiveFrame(FrameDimension.Page,
pageF);

ms = new MemoryStream();

filetmp = file;

data = ((Bitmap)filetmp).LockBits(new Rectangle(0,
0, filetmp.Width, filetmp.Height), ImageLockMode.ReadOnly,
filetmp.PixelFormat);// PixelFormat.Format1bppIndexed);

((Bitmap)filetmp).UnlockBits(data);

char[] words = new char[MAX_CHAR_RECOGNIZE];

byte bpp = 1;

if (filetmp.PixelFormat ==
PixelFormat.Format8bppIndexed) bpp = 8;
else if (filetmp.PixelFormat ==
PixelFormat.Format24bppRgb) bpp = 24;

int f = BeginPage((uint)filetmp.Width,
(uint)filetmp.Height, data.Scan0, "eng", bpp);

IntPtr oi = Recognize_all_Words();

IntPtr currentP = IntPtr.Zero;

currentP = oi;

for (int cont = 0; cont < MAX_CHAR_RECOGNIZE; cont+
+)
{

structUN[cont] =
(ETEXT_DESC)Marshal.PtrToStructure(currentP, typeof(ETEXT_DESC));

Marshal.DestroyStructure(currentP,
typeof(ETEXT_DESC));

currentP = new IntPtr(currentP.ToInt32() +
16);
}

FillPageFromStruct(page, structUN);

Release();
bool result = WrapperUnManaged.FreeLibrary(pDll);

}

return page;

}
catch (Exception)
{
throw;
}
finally
{

filetmp.Dispose();
ms.Dispose();

}

}


/// <summary>
/// Visita la struttura della pagina ed esegue la funzione
callback per ogni parola
/// </summary>
/// <param name="page"></param>
public static void VisitPage(OCRPage page,
TransformWordDelegate transformWord)
{
int countw = 1;
foreach (OCRRow riga in page.rows.Values)
{
foreach (OCRWord word in riga.words)
{
//delegate callback
//string s =
word.Text = transformWord(word.Text);
}

countw++;
}

}



/// <summary>
/// Estrae le parole contenute in una certa riga della pagina
e in un determinato intervallo di coordinata
/// </summary>
/// <param name="page">oggetto di tipo OCRPage che rappresenta
la pagina</param>
/// <param name="row">riga nella quale cercare</param>
/// <param name="inflim">limite inferiore dell'intervallo</
param>
/// <param name="suplim">limite superiore</param>
/// <returns>Array di parole trovate nell'intervallo</returns>
/// <author>Francesco Sinopoli</author>
public static List<OCRWord> ExtractWordsFromRange(OCRPage
page, int indexrow, decimal inflim, decimal suplim)
{
indexrow--; //l'indice ha base zero

List<OCRWord> wordsList = new List<OCRWord>();

if (!page.rows.ContainsKey(indexrow)) return wordsList;

OCRRow row = page.rows[indexrow];

foreach (OCRWord word in row.words)
{
if ((word.Left < inflim && word.Right < inflim) ||
(word.Left > suplim && word.Right > suplim)) continue;

wordsList.Add(word);

}

return wordsList;

}

/// <summary>
/// Riempie la struttura della pagina dopo il marshaling delle
strutture ritornate dal codice non gestito
/// </summary>
/// <param name="pagina">Oggetto di tipo OCRPage da popolare</
param>
/// <param name="words">Array di caratteri dal quale
estrapolare le parole</param>
/// <author>Francesco Sinopoli</author>
internal static void FillPageFromStruct(OCRPage page,
ETEXT_DESC[] words)
{
string word = string.Empty;
int numRow = 0;
int startRectWord = 0;
int endRectWord = 0;
int upRectWord = 0;
int downRectWord = 0;
decimal leftPre = 0;
decimal heightPre = 0;

OCRWord tmpword = null;

if (words.Length > 0)
{
startRectWord = words[0].text[0].left;
upRectWord = words[0].text[0].top;
}
else return;

page.rows.Add(numRow, new OCRRow()); //aggiungo una nuova
riga alla pagina

foreach (ETEXT_DESC myChar in words) // scorro tutti i
caratteri trovati
{

EANYCODE_CHAR[] infoChar1 = new EANYCODE_CHAR[1];
infoChar1 = myChar.text;
EANYCODE_CHAR infoChar = infoChar1[0];

decimal code =
Convert.ToDecimal(infoChar.char_code.ToString());
decimal left = Convert.ToDecimal(infoChar.left);
decimal height = Convert.ToDecimal(infoChar.bottom);

if (code == 0) //caratteri finiti
{

//trucco: recupero i precedenti
if (tmpword != null)
{
infoChar.bottom = (short)tmpword.Bottom;
infoChar.top = (short)tmpword.Top;
infoChar.left = (short)leftPre;
infoChar.right = (short)tmpword.Right;
}
tmpword = new OCRWord(word);

tmpword.Bottom = infoChar.bottom;
tmpword.Top = infoChar.top;
tmpword.Left = infoChar.left;
tmpword.Right = infoChar.right;

page.rows[numRow].words.Add(tmpword);

break;
}

object charvalue = (char)code;

decimal numblanks =
Convert.ToDecimal(infoChar.blanks.ToString());

if (numblanks >= 1 || left < leftPre)
{

tmpword = new OCRWord(word);

tmpword.Bottom = downRectWord;
tmpword.Top = upRectWord;
tmpword.Left = startRectWord;
tmpword.Right = endRectWord;

page.rows[numRow].words.Add(tmpword);

if (left < leftPre)
{
numRow++;
page.rows.Add(numRow, new OCRRow());

if (heightPre != 0)
page.distancesRows.Add(infoChar.bottom - heightPre);
heightPre = infoChar.bottom;
}

word = charvalue.ToString(); //inizializza parola

startRectWord = infoChar.left;
upRectWord = infoChar.top;
downRectWord = infoChar.bottom;
endRectWord = infoChar.right;


}
else
{

word += charvalue.ToString();

endRectWord = infoChar.right; //la fine della
parola coincide con il margine sinistro del carattere aggiunto

if (infoChar.top <= upRectWord) upRectWord =
infoChar.top;
if (infoChar.bottom >= downRectWord) downRectWord
= infoChar.bottom;

}

leftPre = left;
}

decimal med = 0;
foreach (decimal num in page.distancesRows) med += num;
if (page.distancesRows.Count != 0) med = med /
page.distancesRows.Count;
page.DistanceRows = med;
}


public static int GetRowSentence(string[] sentence, OCRPage
page, StringComparison comparisonType, out decimal inflim, out decimal
suplim)
{
int countR = 1;
int numberWordOS = 0;

inflim = 0;
suplim = 0;

string wordSearch = sentence[numberWordOS];

foreach (OCRRow row in page.rows.Values)
{
foreach (OCRWord word in row.words)
{
if (numberWordOS == sentence.Length) return
countR;

wordSearch = sentence[numberWordOS];

if (word.Text.Equals(wordSearch, comparisonType))
{
numberWordOS++;

if (numberWordOS == 1) inflim = word.Left;
}
else
{
numberWordOS = 0;
}

suplim = word.Right;

}
countR++;
}

return -1;

}


public static OCRWord GetWord(string wordSearch, OCRPage page,
StringComparison comparisonType)
{
int countR = 1;
foreach (OCRRow row in page.rows.Values)
{
//log.AppendText("Numero di parole della {0} riga:
{1}", countw, riga.words.Count);
foreach (OCRWord word in row.words)
{
if (word.Text.Equals(wordSearch, comparisonType))
return word;

}
countR++;
}
return null;
}


public static int SearchSentence(string[] sentence, OCRPage
page, StringComparison comparisonType)
{
int countR = 1;
int numberWordOS = 0;
string wordSearch = sentence[numberWordOS];

foreach (OCRRow row in page.rows.Values)
{
foreach (OCRWord word in row.words)
{
if (numberWordOS == sentence.Length) return
countR;

wordSearch = sentence[numberWordOS];

if (word.Text.Equals(wordSearch, comparisonType))
{
numberWordOS++;
}
else
{
numberWordOS = 0;
}

}
countR++;
}

return -1;

}


public static Rectangle SearchSentenceR(string[] sentence,
OCRPage page, StringComparison comparisonType)
{
int countR = 1;
int numberWordOS = 0;

Rectangle recSentence = new Rectangle();

string wordSearch = sentence[numberWordOS];

foreach (OCRRow row in page.rows.Values)
{
foreach (OCRWord word in row.words)
{
if (numberWordOS == sentence.Length) return
recSentence;

wordSearch = sentence[numberWordOS];

if (word.Text.Equals(wordSearch, comparisonType))
{
numberWordOS++;

if (numberWordOS == 1)
{
recSentence.X = word.Left;
}

if (word.Top < recSentence.Y || recSentence.Y
== 0) recSentence.Y = word.Top;
if (word.Bottom > recSentence.Bottom)
recSentence.Height = word.Bottom - word.Top;

recSentence.Width = word.Right -
recSentence.X;

}
else
{
numberWordOS = 0;
}

}
countR++;
}

return recSentence;

}


public static int SearchWord(string wordSearch, OCRPage page,
StringComparison comparisonType)
{
int countR = 1;

foreach (OCRRow row in page.rows.Values)
{
foreach (OCRWord word in row.words)
{
if (word.Text.Equals(wordSearch, comparisonType))
return countR;
}
countR++;
}

return -1;
}


#region Metodi Helper

#endregion




[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
unsafe public struct ETEXT_DESC
{

/// INT16->short
public short count;

/// INT16->short
public short progress;

/// INT8->char
public byte more_to_come;

/// INT8->char
public byte ocr_alive;

/// INT8->char
public byte err_code;

/// CANCEL_FUNC
//[MarshalAs(UnmanagedType.FunctionPtr)]
//public CANCEL_FUNC cancel;
public IntPtr cancel;

/// void*
public System.IntPtr cancel_this;

/// clock_t->int
public int end_time;

/// EANYCODE_CHAR[1]

[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray,
SizeConst = 1, ArraySubType =
System.Runtime.InteropServices.UnmanagedType.Struct)]
public EANYCODE_CHAR[] text;
}

// [StructLayout(LayoutKind.Sequential, Size = 17)]

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
unsafe public struct EANYCODE_CHAR
{

/// UINT16->unsigned short
public ushort char_code;

/// INT16->short
public short left;

/// INT16->short
public short right;

/// INT16->short
public short top;

/// INT16->short
public short bottom;

/// INT16->short
public short font_index;

/// UINT8->unsigned char
public byte confidence;

/// UINT8->unsigned char
public byte point_size;

/// INT8->char
public byte blanks;

/// UINT8->unsigned char
public byte formatting;
}


}

public static class WrapperUnManaged
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);

[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule,
string procedureName);

[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}

#region Le classi per rappresentazione della pagina

/// <summary>
/// Classe che rappresenta una pagina
/// </summary>
public class OCRPage
{
public Dictionary<int, OCRRow> rows = new Dictionary<int,
OCRRow>();
internal List<Decimal> distancesRows = new List<decimal>();
private decimal p_distanceRows;
public decimal DistanceRows
{
get
{
return p_distanceRows;
}
set
{
p_distanceRows = value;
}
}
}

/// <summary>
/// Classe che rappresenta una riga di parole nella pagina
/// </summary>
public class OCRRow
{
public List<OCRWord> words = new List<OCRWord>();
}

/// <summary>
/// Classe che rappresenta una parola nella pagina
/// </summary>
public class OCRWord
{
private string p_text;
public string Text
{
get
{
return p_text;
}
set
{
p_text = value;
}
}

private int p_Left;
public int Left
{
get
{
return p_Left;
}
set
{
p_Left = value;
}
}

private int p_Right;
public int Right
{
get
{
return p_Right;
}
set
{
p_Right = value;
}
}

private int p_Top;
public int Top
{
get
{
return p_Top;
}
set
{
p_Top = value;
}
}

private int p_Bottom;
public int Bottom
{
get
{
return p_Bottom;
}
set
{
p_Bottom = value;
}
}

public OCRWord(string text)
{
p_text = text;
}
}

#endregion

}

unknowner

unread,
Apr 9, 2008, 10:45:09 AM4/9/08
to tesseract-ocr
Can you compile this to a DLL an put it up here for others using .NET?

Thanks
David

Francesco

unread,
Apr 11, 2008, 8:26:05 AM4/11/08
to tesseract-ocr
Hi David,
compile it using the following command line
csc /target:library /out:WaveRec.dll WaveRec.cs /unsafe
and add the library as a reference into your project.

Francesco

unknowner

unread,
Apr 11, 2008, 12:33:36 PM4/11/08
to tesseract-ocr
Hi Francesco,

compiling is not the problem, the problem is i use vb.net, not c#. I
don't want to install c# extra to only compile this one. Can you
please upload the dll for me :)

thanks
david

Lothar

unread,
May 18, 2008, 5:35:26 PM5/18/08
to tesseract-ocr
I compiled the c# dll from Francesco and used it the following
(simple) way with VB.NET:

Dim s As String = ""
Dim pic As Image
pic = Image.FromFile("c:\test.tif")

Dim page As Wave.OCR.Test.OCRPage = WaveRec.CreatePageWD(pic)

Dim i, j As Integer
For i = 0 To page.rows.Count - 1
For j = 0 To page.rows(i).words.Count - 1
'MsgBox(page.rows(i).words(j).Text)
s += page.rows(i).words(j).Text & " "
Next
s += vbCrLf
Next

MsgBox(s)

It only works fine with the sample files from tesseract
(eurotext.tif). But any other file brings no useable result. The files
are wioking fine with the tesseract exe. The picture files all have
300 dpi and have 1 BitPerPixel (black & white)

Has anyone any suggestion / hint for me?


Best Regards,

Lothar

joku

unread,
May 22, 2008, 3:20:25 PM5/22/08
to tesseract-ocr
I've got the same issue...
I even tried using EricD's ExtractDataWithExeLogic function and I get
the same results.

Ray Smith

unread,
May 29, 2008, 9:31:38 PM5/29/08
to tesser...@googlegroups.com
Is this the compressed tif issue? See the new FAQ page for how to install libtiff.
Ray.

george livingston

unread,
Apr 19, 2012, 5:53:59 AM4/19/12
to tesser...@googlegroups.com
Hi EricD,

Can you please share your code with me, really i need your tesseract code for my projects. Kindly do the needful.

Thanks in advance.

Regards,
George

george livingston

unread,
May 9, 2012, 5:05:44 AM5/9/12
to tesser...@googlegroups.com
HI,

After i add all your code, the project build successfully. The output always returns tilde (~) character for all the images. 

Please guide me in a correct way to get the correct output text.

Thanks in advance.

Code
>>>>>>>>>>>>>>>>>>>>>>>
using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Drawing; 
using System.Runtime.InteropServices; 
using System.Drawing.Imaging; 
using System.IO; 

[StructLayout(LayoutKind.Sequential)] 
public struct CharDescription                 /*single character */ 

    public UInt16 char_code;              /*character itself */ 
    public Int16 left;                    /*of char (-1) */ 
    public Int16 right;                   /*of char (-1) */ 
    public Int16 top;                     /*of char (-1) */ 
    public Int16 bottom;                  /*of char (-1) */ 
    public Int16 font_index;              /*what font (0) */ 
    public Byte confidence;              /*0=perfect, 100=reject 
(0/100) */ 
    public Byte point_size;              /*of char, 72=i inch, (10) */ 
    public SByte blanks;                   /*no of spaces before this 
char (1) */ 
    public Byte formatting;              /*char formatting (0) */ 

}                  /*single character */ 

[StructLayout(LayoutKind.Sequential)] 
public struct GeneralInfos     /*output header */ 
    public Int16 count;                   /*chars in this buffer(0) */ 
    public Int16 progress;                /*percent complete 
increasing (0-100) */ 
    public SByte more_to_come;             /*true if not last */ 
    public SByte ocr_alive;                /*ocr sets to 1, HP 0 */ 
    public SByte err_code;                 /*for errcode use */ 
    public Byte cancelSignature; 
    public IntPtr cancel;            /*returns true to cancel */ 
    public IntPtr cancel_this;             /*this or other data for 
cancel*/ 
    public Int32 end_time;              /*time to stop if not 0*/ 

/// <summary> 
/// Used for returning a managed structure contains OCR extraction results 
/// </summary> 
[StructLayout(LayoutKind.Sequential)] 
public class AnalysisResults 
    public GeneralInfos generalInfos=new GeneralInfos(); 
    public List<CharDescription> charactersFound = new 
List<CharDescription>(); 
    public string stringFound = ""; 

namespace tesseractOCR 
    /// <summary> 
    /// A wrapper that expose some fonctionnalies of TesseractOCR 
    /// </summary> 
    class WrapperTesseract 
    { 
        private static bool firstRun = true; 

        [DllImport("tessdll.dll", 
CallingConvention=CallingConvention.Cdecl)] 
        private unsafe static extern int 
TessDllBeginPageUpright(UInt32 xsize, UInt32 ysize, byte* buf, string 
lang); 

        [DllImport("tessdll.dll", CallingConvention = 
CallingConvention.Cdecl)] 
        private unsafe static extern int 
TessDllBeginPageUprightBPP(UInt32 xsize, UInt32 ysize, byte* buf, 
string lang, Byte bpp); 

        [DllImport("tessdll.dll", CallingConvention = 
CallingConvention.Cdecl)] 
        private static extern IntPtr TessDllRecognize_all_Words(); 

        [DllImport("tessdll.dll", CallingConvention = 
CallingConvention.Cdecl)] 
        private static extern void TessDllEndPage(); 

        [DllImport("tessdll.dll", CallingConvention = 
CallingConvention.Cdecl)] 
        private static extern void TessDllRelease(); 

        [DllImport("tessdll.dll", CallingConvention = 
CallingConvention.Cdecl, SetLastError=true)] 
        private static unsafe extern IntPtr 
ExtractDataWithExeLogic(UInt32 xsize, UInt32 ysize, byte* buf, string 
lang, Byte bpp); 


        /// <summary> 
        /// Converts the image to byte array without Strides data.. 
        /// </summary> 
        /// <param name="imageToConvert">The image to convert.</param> 
        /// <returns>A array of bytes</returns> 
        private static byte[] ConvertImageToByteArray(System.Drawing.Bitmap imageToConvert, Byte bpp) 
        { 
            //Create a BitmapData and Lock all pixels to be read 
            BitmapData bmpData = imageToConvert.LockBits(new Rectangle(0, 0, imageToConvert.Width, imageToConvert.Height), 
        /// </summary> 
        /// <param name="bmp">The image to analyse.</param> 
        /// <param name="language">The language to be used to extract text.</param> 
        /// <param name="bpp">The number of bits per pixel(bpp) of the image.</param> 
        /// <returns>The extracted string </returns> 
        public static String RecognizeCharsDll(System.Drawing.Bitmap 
bmp, string language) 
        { 

            if (firstRun) 
                firstRun = false; 
            else 
                TessDllEndPage(); 

            Byte bpp = 0; 
            AnalysisResults tmp = new AnalysisResults(); 
            IntPtr data = new IntPtr(); 


            switch (bmp.PixelFormat) 
            { 
                case PixelFormat.Format1bppIndexed: bpp = 1; break; 
                case PixelFormat.Format8bppIndexed: bpp = 8; break; 
                case PixelFormat.Format48bppRgb: bpp = 48; break; 
                case PixelFormat.Format32bppRgb: bpp = 32; break; 
                case PixelFormat.Format24bppRgb: bpp = 24; break; 
                default: throw new BadImageFormatException("Please use an image format of.... TODO");//TODO 

            }


            unsafe
            {
                byte[] imgArray = ConvertImageToByteArray(bmp, bpp);

                fixed (byte* ptr = &imgArray[0])
                {
                    try
                    {
                        data = (IntPtr)ptr;
                        int a = TessDllBeginPageUpright((uint)bmp.Width, (uint)bmp.Height, ptr, language);

                        data = TessDllRecognize_all_Words();
                        tmp = BuildAnalysisResults(data);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.ToString());
                    }

                }
            }


            //TODO: Character analysis... 


            return (BuildAnalysisResults(data)).stringFound; 
        } 

        /// <summary> 
        /// Take an image and extract any text found using the specified language with TesseractOCR EXE Logic. 
        /// </summary> 
        /// </summary> 
        /// <param name="data">The pointer to be converted.</param> 
        /// <returns>The string converted.</returns> 
        private static String PointerToString(IntPtr data) 
        { 
            String result = ""; 
            int i=0; 

            if (data != IntPtr.Zero) 
            { 
                char characterExtracted = (char)Marshal.ReadByte(data, 
i); 

                while (characterExtracted.ToString() != "\0") 
                { 
                    result += characterExtracted.ToString(); 
                    i++; 
                    characterExtracted = (char)Marshal.ReadByte(data, 
i); 
                } 

                //Unaffect the pointer 
                data = IntPtr.Zero; 
            } 

            return result; 



        } 
        /// <summary> 
        /// Build and return a AnalysisResults object that contains the results of the Tesseract 
        /// extraction. 
        /// </summary> 

>>>>>>>>>>>>>>>>>>>>>>>
Regards,
George 

ruwanthaka

unread,
May 27, 2012, 3:07:56 AM5/27/12
to tesser...@googlegroups.com
Hi EricD,

Can you please share your source code with me, hope this is related C# VS2010,
my target is develop a OCR application for language "Sinhala" which use in my country.
 i'm trying with general guides but unable to get success.
Thanks in advance.

Ruwanthaka
Reply all
Reply to author
Forward
0 new messages