Delphi wrapper for tessdll

4,951 views
Skip to first unread message

davith

unread,
Dec 29, 2007, 6:32:21 AM12/29/07
to tesseract-ocr
Hi all,

I'm trying to use tessdll from delphi but I can't get true results.

This is my code:

procedure TForm1.Button1Click(Sender: TObject);
type
CANCEL_FUNC = function (cancel_this: Pointer; words: Integer):
Boolean;
PCANCEL_FUNC = ^CANCEL_FUNC;

EANYCODE_CHAR = packed record
char_code : Word;
left : SmallInt;
right : SmallInt;
top : SmallInt;
bottom : SmallInt;
font_index : SmallInt;
confidence : Byte;
point_size : Byte;
blanks : Shortint;
formatting : Byte;
end;

ETEXT_STRUCT = packed record
count : SmallInt;
progress : SmallInt;
more_to_come : Shortint;
ocr_alive : Shortint;
err_code : Shortint;
cancel : PCANCEL_FUNC;
cancel_this : Pointer;
end_time : Longint;
text : array [0..0] of EANYCODE_CHAR;
end;
PETEXT_STRUCT = ^ETEXT_STRUCT;

TBeginPageFunc = function (xsize: Integer; ysize: Integer; var buf:
Byte;
const lang: PChar): integer; stdcall;
TProcessWords = function: PETEXT_STRUCT; stdcall;
var
ms : TMemoryStream;
arb : TByteArray;

Handle : THandle;
DllBeginPage : TBeginPageFunc;

DllProcessWOrds : TProcessWords;

ts : ETEXT_STRUCT;
pts : PETEXT_STRUCT;
po : PByteArray;

i : Integer;
begin
Image1.Picture.LoadFromFile('kk.bmp');

ms := TMemoryStream.Create;
try
Image1.Picture.Bitmap.SaveToStream(ms);
Image1.Picture.Bitmap.PixelFormat := pf24bit;
po := Image1.Picture.Bitmap.ScanLine[Image1.Picture.Bitmap.Height
- 1];
//ms.Read(po^, 0);

Handle := LoadLibrary('tessdll.dll');
if Handle <> 0 then
begin
@DllBeginPage := GetProcAddress(Handle,
'TessDllBeginPageUpright');
if @DllBeginPage <> nil then
begin
i := DllBeginPage(Image1.Picture.Bitmap.Width,
Image1.Picture.Bitmap.Height, po^[0],
{pbytearray(po)[0],} PChar('eng'));

@DllProcessWOrds := GetProcAddress(Handle,
'TessDllRecognize_all_Words');
if @DllProcessWOrds <> nil then
begin
pts := DllProcessWOrds;

ShowMessage(IntToStr(pts^.count));
end;
end;

FreeLibrary(Handle);
end;


Where is wrong? Someone use tessdll from Delphi?

Best regards from Spain, Thanks

Scan...@gmail.com

unread,
Dec 29, 2007, 8:52:58 AM12/29/07
to tesseract-ocr
Yes. There are people out there using the tess dll interface in delhi.

There are two problems with your code. It appears you are using a 24
bit image but the dll function for one bit images. You need to use the
function with BPP on the end of it specify the number of bits you are
using.

The other problem is currently the DLL interface has no parameter for
rowBytes. So if you have a different number of bytes in a row that the
width *3 (24bits), width (8 bits), or width/8, you will need to pass
the rowBytes/3 (24 bits), rowBytes, or rowBytes*8 as the width
parameter.


We should add another function in the next release of tess to allow
the pass of a rowBytes field.

davith

unread,
Dec 30, 2007, 10:20:19 AM12/30/07
to tesseract-ocr
Yes, you true...

but my question is for definitions of:

- TBeginPageFunc = function (xsize: Integer; ysize: Integer; var buf:
Byte; const lang: PChar; bpp: byte): integer; stdcall; , it's OK? buf
parameter is Byte type?
- po := Image1.Picture.Bitmap.ScanLine[Image1.Picture.Bitmap.Height -
1]; , this pointer is OK to pass into TBeginPageFunc?
- text : array [0..0] of EANYCODE_CHAR; , in c code this array has
only 1 element? then where I can read all words?

Now, in pts variable (pts := DllProcessWords) I receive wrong data...

Maybe I should upload delphi code and bmp, I'll just prepare it...

Scan...@gmail.com

unread,
Dec 30, 2007, 11:15:09 PM12/30/07
to tesseract-ocr
The buf is a pointer to bytes;
The buf you would pass is always the begining of the image buffer.
Take a look at the dll example of how to parse the array. It has a max
of 32000 elements.

Jan Oosting

unread,
Dec 31, 2007, 3:31:33 AM12/31/07
to tesser...@googlegroups.com
I created a couple of units for Delphi that use the tessdll. The first
one basically translates the tessdll.h file, the second unit defines a
class that can take a bitmap, and OCR it.
There is no proper documentation (yet), so you'll have to have a look
at the source to be able to use. The comments show some of the issues
that had to be overcome in order to use tessdll from Delphi

Jan
==TESSDLL.PAS======================================================

unit TESSDLL;
{**************************************************************************}
{ }
{ This C DLL header file first (automatic) conversion generated by: }
{ HeadConv 4.0 (c) 2000 by Bob Swart (aka Dr.Bob - www.drbob42.com) }
{ Final Delphi-Jedi (Darth) command-line units edition }
{ }
{ Generated Date: 02-08-2007 }
{ Generated Time: 11:50:26 }
{ }
{**************************************************************************}
interface
uses
Windows;
{//////////////////////////////////////////////////////////////////////// }
{/// File: tessdll.h }
{/// Description: Windows dll interface for Tesseract. }
{/// Author: Glen Wernersbach }
{/// Created: Tue May 15 10:30:01 PDT 2007 }
{/// }
{/// (C) Copyright 2007, Jetsoftdev. }
{/// Licensed under the Apache License, Version 2.0 (the "License"); }
{/// you may not use this file except in compliance with the License. }
{/// You may obtain a copy of the License at }
{/// http://www.apache.org/licenses/LICENSE-2.0 }
{/// Unless required by applicable law or agreed to in writing, software }
{/// distributed under the License is distributed on an "AS IS" BASIS, }
{/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{/// See the License for the specific language governing permissions and }
{/// limitations under the License. }
{/// }
{ Delphi translation: J. Oosting
{//////////////////////////////////////////////////////////////////////// }

type

TEANYCODE_CHAR = packed record //*single character */
// It should be noted that the format for char_code for version 2.0
and beyond is UTF8
// which means that ASCII characters will come out as one structure
but other characters
// will be returned in two or more instances of this structure with a
single byte of the
// UTF8 code in each, but each will have the same bounding box.
// Programs which want to handle languagues with different characters
sets will need to
// handle extended characters appropriately, but *all* code needs to
be prepared to
// receive UTF8 coded characters for characters such as bullet and fancy quotes.
char_code:word; //*character itself */
left:SmallInt; //*of char (-1) */
right:smallInt; //*of char (-1) */
top:smallInt; //*of char (-1) */
bottom:smallint; //*of char (-1) */
font_index:smallInt; //*what font (0) */
confidence:byte; //*0=perfect, 100=reject (0/100) */
point_size:byte; //*of char, 72=i inch, (10) */
blanks:shortint; //*no of spaces before this char (1) */
formatting:byte; //*char formatting (0) */
end;
PEANYCODE_CHAR = ^TEANYCODE_CHAR;

{/**********************************************************************
* ETEXT_DESC
* Description of the output of the OCR engine.
* This structure is used as both a progress monitor and the final
* output header, since it needs to be a valid progress monitor while
* the OCR engine is storing its output to shared memory.
* During progress, all the buffer info is -1.
* Progress starts at 0 and increases to 100 during OCR. No other constraint.
* Every progress callback, the OCR engine must set ocr_alive to 1.
* The HP side will set ocr_alive to 0. Repeated failure to reset
* to 1 indicates that the OCR engine is dead.
* If the cancel function is not null then it is called with the number of
* user words found. If it returns true then operation is cancelled.
**********************************************************************/
typedef bool (*CANCEL_FUNC)(void* cancel_this, int words);}

TETEXT_DESC = record //*output header */
count:smallInt; //*chars in this buffer(0) */
progress:smallInt; //*percent complete increasing (0-100) */
more_to_come:shortInt; //*true if not last */
ocr_alive:shortInt; //*ocr sets to 1, HP 0 */
err_code:shortInt; //*for errcode use */
cancel:pointer; //*returns true to cancel */
cancel_this:pointer; //*this or other data for cancel*/
end_time:longint; //*time to stop if not 0*/
text: array[0..31999] of TEANYCODE_CHAR; //*character data */
end ;
PETEXT_DESC = ^TETEXT_DESC;

{///The functions below provide a c wrapper to a global recognize class object }

{///xsize should be the width of line in bytes times 8 }
{///ysize is the height }
{///pass through a buffer of bytes for a 1 bit per pixel bitmap }
{///BeginPage assumes the first memory address is the bottom of the
image (MS DIB format) }
{///BeginPageUpright assumes the first memory address is the top of
the image (TIFF format) }
{///lang is the code of the language for which the data will be loaded. }
{///(Codes follow ISO 639-2.) If it is NULL, english (eng) will be loaded. }
var
TessDllBeginPage: function(xsize: cardinal;
ysize: cardinal;
buf: pointer): LongInt ;cdecl;

var
TessDllBeginPageLang: function(xsize: cardinal;
ysize: cardinal;
buf: Pointer;
const lang: PChar): LongInt ; cdecl;
var
TessDllBeginPageUpright: function(xsize: cardinal;
ysize: cardinal;
buf: Pointer;
const lang: PChar): LongInt ; cdecl;
{///Added in version 2.0 to allow users to specify bytes per pixel to do }
{///1 for binary biptmap }
{///8 for gray }
{///24 bit for color RGB }
var
TessDllBeginPageBPP: function(xsize: cardinal;
ysize: cardinal;
buf: pointer;
bpp: byte): LongInt ; cdecl;

var
TessDllBeginPageLangBPP: function(xsize: cardinal;
ysize: cardinal;
buf: pointer;
const lang: PChar;
bpp: byte): LongInt ; cdecl;
var
TessDllBeginPageUprightBPP: function(xsize: cardinal;
ysize: cardinal;
buf: pointer;
const lang: PChar;
bpp: byte): LongInt ; cdecl;

var
TessDllEndPage: function:longint; cdecl;

{///This allows you to extract one word or section from the bitmap or }
{///the whole page }
{///To extract the whole page just enter zeros for left, right, top, bottom }
{///Note: getting one word at time is not yet optimized for speed. }
{///limit of 32000 character can be returned }
{///see ocrclass.h for a decription of the ETEXT_DESC file }
var
TessDllRecognize_a_Block: function(left: cardinal;
right: cardinal;
top: cardinal;
bottom: cardinal): PETEXT_DESC; cdecl;
var
TessDllRecognize_all_Words: function: PETEXT_DESC cdecl;

{///This will release any memory associated with the recognize class object }
var
TessDllRelease: function :longint cdecl;

var
TessDLLLoaded: Boolean = False;

implementation

var
SaveExit: pointer;
DLLHandle: THandle;
ErrorMode: Integer;

procedure NewExit; far;
begin
TessDllRelease;
ExitProc := SaveExit;
FreeLibrary(DLLHandle)
end {NewExit};

procedure LoadTessDLL;
begin
if TessDLLLoaded then Exit;
ErrorMode := SetErrorMode($8000{SEM_NoOpenFileErrorBox});
DLLHandle := LoadLibrary('TESSDLL.DLL');
if DLLHandle >= 32 then
begin
TessDLLLoaded := True;
SaveExit := ExitProc;
ExitProc := @NewExit;
// @TessDllAPI := GetProcAddress(DLLHandle,'TessDllAPI');
// Assert(@TessDllAPI <> nil);

@TessDllBeginPage := GetProcAddress(DLLHandle,'TessDllBeginPage');
Assert(@TessDllBeginPage <> nil);

@TessDllBeginPageLang := GetProcAddress(DLLHandle,'TessDllBeginPageLang');
Assert(@TessDllBeginPageLang <> nil);

@TessDllBeginPageUpright :=
GetProcAddress(DLLHandle,'TessDllBeginPageUpright');
Assert(@TessDllBeginPageUpright <> nil);

@TessDllBeginPageBPP := GetProcAddress(DLLHandle,'TessDllBeginPageBPP');
Assert(@TessDllBeginPageBPP <> nil);

@TessDllBeginPageLangBPP :=
GetProcAddress(DLLHandle,'TessDllBeginPageLangBPP');
Assert(@TessDllBeginPageLangBPP <> nil);

@TessDllBeginPageUprightBPP :=
GetProcAddress(DLLHandle,'TessDllBeginPageUprightBPP');
Assert(@TessDllBeginPageUprightBPP <> nil);

@TessDllEndPage := GetProcAddress(DLLHandle,'TessDllEndPage');
Assert(@TessDllEndPage <> nil);

@TessDllRecognize_a_Block :=
GetProcAddress(DLLHandle,'TessDllRecognize_a_Block');
Assert(@TessDllRecognize_a_Block <> nil);

@TessDllRecognize_all_Words :=
GetProcAddress(DLLHandle,'TessDllRecognize_all_Words');
Assert(@TessDllRecognize_all_Words <> nil);

@TessDllRelease := GetProcAddress(DLLHandle,'TessDllRelease');
Assert(@TessDllRelease <> nil);
end
else
begin
TessDLLLoaded := False;
{ Error: TESSDLL.DLL could not be loaded !! }
end;
SetErrorMode(ErrorMode)
end {LoadDLL};

begin
LoadTessDLL;
end.


================================================
===TesseractOCR.pas ===============================
unit TesseractOCR;
{(C) Copyright 2007, J. Oosting
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. }
interface
uses Windows, SysUtils,Classes,Graphics,TESSDLL;

type EOCRDLL = class (SysUtils.Exception);

type
TRecognizer = class
private
FBitmap:TBitmap;
FLanguage: array[0..15] of char;
function ConvertEText_Desc2String(EText_Desc:PETEXT_DESC):string;
procedure SetLanguage(const Value: string);
procedure PrepareOCR;
procedure ReleaseOCR;
function GetLanguage: string;
published
constructor Create(aBitmap:TBitmap);
destructor Destroy;override;
function GetAllWords:string;
function GetABlock(block:TRect):string;
property Language:string read GetLanguage write SetLanguage;
End;

procedure GetOCRLanguages(langs:TStrings);
function OCRDLLLoaded:boolean;

implementation
uses Forms;

procedure GetOCRLanguages(langs: TStrings);
var
sr:TSearchRec;
res:integer;
dotpos:integer;
begin
langs.Clear;
// look in <appdir>tessdata for language files
res:=FindFirst(ExtractFilePath(Application.Exename)+'tessdata\*.inttemp',faAnyFile,sr);
try
while res=0 do
begin
dotpos:=Pos('.',sr.Name);
langs.Add(copy(sr.name,1,dotpos-1));
res:=FindNext(sr);
end;
finally
FindClose(sr);
end;
end;

function OCRDLLLoaded:boolean;
begin
result:=TessDLLLoaded;
end;
{ TRecognizer }

function TRecognizer.ConvertEText_Desc2String(EText_Desc: PETEXT_DESC): string;
var
i,b:integer;
ch:TEANYCODE_CHAR;
begin
result:='';
for I := 0 to EText_Desc^.Count - 1 do
begin
ch:=EText_Desc^.text[i];
for b:= 0 to ch.blanks - 1 do
result:=result+' ';
result:=result+chr(ch.char_code);
if (ch.formatting and 64) = 64 then // end of line
result:=result+chr(13)+chr(10);
if (ch.formatting and 128) = 128 then // end of paragraph
result:=result+chr(13)+chr(10)+chr(13)+chr(10);
end;
end;

constructor TRecognizer.Create(aBitmap: TBitmap);
const padding=32;
begin
if not OCRDLLLoaded then
raise EOCRDLL.Create('Tesseract DLL not loaded');
// make a copy so bitmap conversions will not change the original picture
fbitmap:=TBitmap.Create;
fBitmap.Assign(aBitmap);
FLanguage:='eng';
end;

destructor TRecognizer.Destroy;
begin
FBitmap.Free;
inherited;
end;

function TRecognizer.GetABlock(block: TRect): string;
var
RecognizedText:PETEXT_DESC;
begin
PrepareOCR;
if Integer(FBitmap.ScanLine[0])>Integer(FBitmap.ScanLine[1]) then
begin
Block.Top:=FBitmap.Height-Block.Top-1;
Block.Bottom:=FBitmap.Height-Block.Bottom-1;
end;
RecognizedText:=TessDllRecognize_a_Block(block.Left,block.Right,block.Top,block.Bottom);
result:=ConvertEText_Desc2String(RecognizedText);
ReleaseOCR;
end;

function TRecognizer.GetAllWords: string;
begin
result:=GetABlock(Rect(0,0,fBitmap.width-1,FBitmap.Height-1));
end;

function TRecognizer.GetLanguage: string;
begin
GetLanguage:=FLanguage;
end;

procedure TRecognizer.PrepareOCR;
var
bpp:integer;
BytesPerLine:integer;
VirtualWidth:integer;
begin
// make sure bitmap is DIB, will hopefully convert some types of
bitmaps to recognizable pixelformats
FBitmap.HandleType:=bmDIB;
// convert non-supported bitmap formats and determine bpp
case FBitmap.PixelFormat of
pfDevice: begin
FBitmap.PixelFormat:=pf24bit;
bpp:=24;
end;
pf1bit: bpp:=1;
pf4bit: begin
FBitmap.PixelFormat:=pf8bit;
bpp:=8;
end;
pf8bit: bpp:=8;
pf15bit: begin
FBitmap.PixelFormat:=pf24bit;
bpp:=24;
end;
pf16bit: begin
FBitmap.PixelFormat:=pf24bit;
bpp:=24;
end;
pf24bit: bpp:=24;
pf32bit: begin
FBitmap.PixelFormat:=pf24bit;
bpp:=24;
end;
else // pfCustom
raise EInvalidGraphic.Create('Graphics format not recognized for OCR');
end;
// handle different types of bitmaps
// Bitmaps in Delphi are 4-byte aligned per line, images in
Tesseract can be 1-byte aligned
// make sure that tesseract thinks lines are 4-byte aligned
BytesPerLine:=Integer(FBitmap.ScanLine[0])-Integer(FBitmap.ScanLine[1]);
case bpp of
1:VirtualWidth:=BytesPerLine*8;
8:VirtualWidth:=BytesPerLine;
else // 24:
fBitmap.Width:=4*((FBitmap.Width+3) div 4);
VirtualWidth:=FBitmap.Width;
end;
if BytesPerLine>0 then // usually Windows DIB
TessDllBeginPageLangBPP(VirtualWidth,FBitmap.Height,FBitmap.ScanLine[FBitmap.Height-1],FLanguage,bpp)
else // typical TIFF
TessDllBeginPageUprightBPP(VirtualWidth,FBitmap.Height,FBitmap.ScanLine[0],FLanguage,bpp);
end;

procedure TRecognizer.ReleaseOCR;
begin
TessDllEndPage;
end;

procedure TRecognizer.SetLanguage(const Value: string);
begin
StrPCopy(FLanguage, Value);
end;

end.

davith

unread,
Jan 2, 2008, 7:46:11 AM1/2/08
to tesseract-ocr
Hi Jan,

thanks.

My error was in the calling dll function with stdcall vs cdecl.

I have done a descendant class from TBitmap that give functions for
recognize AllWords or Block --> gth.Graphic.Bitmap.Tess.zip in the
groups files.

Thanks so much

buyi wen

unread,
Sep 17, 2015, 11:13:57 PM9/17/15
to tesseract-ocr
if you like tesseract ocr, you may like this free online ocr tool using tesseract ocr 3.02

Reply all
Reply to author
Forward
0 new messages