2022-05-19

tesseract ocr 5.0 Api调用,delphi源码实现--识别率超高速度快


笔者过去使用tesseract-ocr 4.0,一直被识别速度慢和识别率底的问题困扰。最近更新使用了64位的tesseract5.0 dll后识别速度大幅提升,以下是调用DLL的源码和程序说明,供大家参考。

一:下载tesseract DLL和中文字库

方式1,到tesseract官网下载dll和字库,tesseract官网提供源码和编译好的DLL,建议直接使用编译好的DLL,方便省时。以下是64位DLL安装包下载地址:https://github.com/UB-Mannheim/tesseract/wiki要下载64位版本,笔者测试后发现32位识别率没有64位高。


中文字库下载地址:https://github.com/tesseract-ocr/tessdata 下载后的中文简体字库chi_sim.traineddata和 chi_sim_vert.traineddata放到安装包的tessdata文件夹下。

方式2,直接下载笔者提供的完整64位DLL和中文字库及Delphi调用源码,地址如下: https://www.gaya-soft.cn/download/


二:调用dll实例

此delphi源码是由国外的开源项目TTesseractOCR4(https://github.com/r1me/TTesseractOCR4)基础上完善的,原来只支持Tesseract4.0版本,笔者修改了部分源码使之能适应5.0版本。源码中tesseractocr.capi.pas单元是定义DLL接口的,是最主要的部分。testMain.pas单元的实现了4个最主要的函数,TessBaseAPICreate是得到一个API接口,TessBaseAPIInit2加载字库,TessBaseAPISetImage2是加载一个图像,TessBaseAPIRecognize是文字识别。源码下载地址:  https://www.gaya-soft.cn/download/以下是界面设计: 


代码实现如下:

unit testMain;


interface


uses

    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Imaging.jpeg, DateUtils,

    tesseractocr.capi, tesseractocr.leptonica;


type

    TForm1 = class(TForm)

        Panel1: TPanel;

        Panel2: TPanel;

        Memo1: TMemo;

        Splitter1: TSplitter;

        ComboBox1: TComboBox;

        Label1: TLabel;

        Image1: TImage;

        Label2: TLabel;

        edtImage: TEdit;

        btSelectImg: TButton;

        btRecognize: TButton;

        OpenDialog1: TOpenDialog;

        btInitialize: TButton;

        procedure btSelectImgClick(Sender: TObject);

        procedure btRecognizeClick(Sender: TObject);

        procedure FormCreate(Sender: TObject);

        procedure FormDestroy(Sender: TObject);

        procedure btInitializeClick(Sender: TObject);

    private

        FModulePath: string;

        PApi: TessBaseAPI;

        FUtf8Text: string;

        FHOcrText: string;

        FInitOver: Boolean;


        function RootPath(): string;

        function PUTF8CharToString(Char: PUTF8Char; DeleteText: Boolean = True): string;


        function TessInitialize(DataPath, Language: string; Mode: TessOcrEngineMode = OEM_DEFAULT): Boolean;

        function SetVariable(Key: string; Value: string): Boolean;

        function SetImage(FileName: string): PPix; overload;

        function SetImage(Stream: TMemoryStream): PPix; overload;

        procedure RecognizeAsText();

    public

        { Public declarations }

    end;


var

    Form1: TForm1;

    _CancelRecognize: Boolean;


implementation


{$R *.dfm}


procedure TForm1.FormCreate(Sender: TObject);

var

    Buf: array [0 .. MAX_PATH] of Char;

    S: string;

begin

    SetString(S, Buf, GetModuleFileName(HInstance, Buf, SizeOf(Buf)));

    FModulePath := ExtractFilePath(S);


    if (hTesseractLib = 0) then

        raise Exception.Create('Tesseract library is not loaded');

    //

    _CancelRecognize := False;

//得到一个API接口

    PApi := TessBaseAPICreate();

    FInitOver := False;

end;


procedure TForm1.FormDestroy(Sender: TObject);

begin

    if Assigned(PApi) then begin

        TessBaseAPIEnd(PApi);

        TessBaseAPIDelete(PApi);

    end;

end;


function TForm1.RootPath(): string;

var

    I: Integer;

begin

    Result := FModulePath;

    for I := Length(Result) - 1 downto 1 do begin

        if Result[I] = '\' then begin

            Result := Copy(Result, 1, I);

            Break;

        end;

    end;

end;


procedure TForm1.btSelectImgClick(Sender: TObject);

begin

    OpenDialog1.InitialDir := RootPath() + 'example\';

    OpenDialog1.Filter := 'Image File|*.JPG;*.PNG;*.GIF;*.BMP;*.JPEG;';

    if OpenDialog1.Execute() then begin

        edtImage.Text := OpenDialog1.FileName;

        Image1.Picture.LoadFromFile(edtImage.Text);

    end;

end;


function TForm1.PUTF8CharToString(Char: PUTF8Char; DeleteText: Boolean = True): string;

var

    UtfStr: UTF8String;

    X: Integer;

begin

    Result := '';


    if Assigned(Char) then begin

        X := Length(Char);

        SetString(UtfStr, Char, X);

        if DeleteText and (X > 0) then

            TessDeleteText(Char);

        Result := string(UtfStr);

    end;

end;


//初始化识别字库

function TForm1.TessInitialize(DataPath, Language: string; Mode: TessOcrEngineMode = OEM_DEFAULT): Boolean;

begin

    SetCurrentDirectory(PChar(FModulePath));

    //

    if Assigned(PApi) then

        Result := TessBaseAPIInit2(PApi, PUTF8Char(UTF8Encode(DataPath)), PUTF8Char(UTF8Encode(Language)), Mode) = 0

    else

        Result := False;

end;


function CancelCallback(cancel_this: Pointer; words: Integer): Boolean; cdecl;

begin

    Result := _CancelRecognize;

end;


function ProgressCallback(Progress: Integer; Left, Right, Top, Bottom: Integer): Boolean; cdecl;

begin

    Result := False;

end;


//设置识别参数

function TForm1.SetVariable(Key: string; Value: string): Boolean;

begin

    Result := TessBaseAPISetVariable(PApi, PUTF8Char(UTF8Encode(Key)), PUTF8Char(UTF8Encode(Value)));

end;


//识别图片中的文字

procedure TForm1.RecognizeAsText();

var

    FMonitor: ETEXT_DESC;

begin

    FillChar(FMonitor, SizeOf(FMonitor), #0);

    FMonitor.cancel := @CancelCallback;

    FMonitor.progress_callback := @ProgressCallback;

    FUtf8Text := '';

    FHOcrText := '';

    //

    if TessBaseAPIRecognize(PApi, FMonitor) <> 0 then

        Exit;


//识别文字

    FUtf8Text := PUTF8CharToString(TessBaseAPIGetUTF8Text(PApi));

    FUtf8Text := StringReplace(FUtf8Text, #10, #13#10, [rfReplaceAll]);


//识别结果和位置信息

    FHOcrText := PUTF8CharToString(TessBaseAPIGetHOCRText(PApi, 0));

    FHOcrText := StringReplace(FHOcrText, #10, #13#10, [rfReplaceAll]);

end;


//加载要识别的图片文件

function TForm1.SetImage(FileName: string): PPix;

begin

    Result := pixRead(PUTF8Char(UTF8Encode(FileName)));

    if Assigned(Result) then

        TessBaseAPISetImage2(PApi, Result)

    else

        Result := nil;

end;


//加载要识别的内存

function TForm1.SetImage(Stream: TMemoryStream): PPix;

begin

    Stream.Position := 0;

    Result := pixReadMem(Stream.Memory, Stream.Size);

    if Assigned(Result) then

        TessBaseAPISetImage2(PApi, Result)

    else

        Result := nil;

end;


procedure TForm1.btInitializeClick(Sender: TObject);

var

    Language: string;

begin

    case ComboBox1.ItemIndex of

        3, 2:

            Language := 'eng';

        1:

            Language := 'chi_sim';

    else

        Language := 'chi_sim+eng';

    end;

//加载需要很长时间,最好只执行一次

    TessInitialize('tessdata' + PathDelim, Language);

    FInitOver := True;

Memo1.Text := '字库加载完成';

end;


procedure TForm1.btRecognizeClick(Sender: TObject);

var

    Stream: TMemoryStream;

    Bitmap: TBitmap;

    ImagePix: PPix;

    T1: TDateTime;

    F: Int64;

begin

    if not FInitOver then

        btInitializeClick(Sender);

    //

    T1 := Now();

    //

    Stream := TMemoryStream.Create();

    try

        Bitmap := TBitmap.Create();

        try

            Bitmap.Assign(Image1.Picture.Graphic);

//最好把图像转化为标准bmp格式,tesseract加载图片不好用,经常内存错误

            Bitmap.SaveToStream(Stream);

        finally

            Bitmap.Free();

        end;

//设置内存图片,也可以直接加载内存文件

        ImagePix := SetImage(Stream);

    finally

        Stream.Free();

    end;

    //

    if Assigned(ImagePix) then begin

        try

//数字的情况,设置识别文字白名单

            if ComboBox1.ItemIndex = 3 then

                SetVariable('tessedit_char_whitelist', '0123456789');

//开始识别

            RecognizeAsText();

//毫秒数

            F := MilliSecondsBetween(Now, T1);

            //

            Memo1.Text := FUtf8Text + #13#10 + //

              '-------------------------------HOCRText-------------------------------' + #13#10 + //

              FHOcrText + #13#10 + Format('%d', [F]);

        finally

            pixDestroy(ImagePix);

        end;

    end;

end;

end.


//初始化识别字库

function TForm1.TessInitialize(DataPath, Language: string; Mode: TessOcrEngineMode = OEM_DEFAULT): Boolean;

begin

    SetCurrentDirectory(PChar(FModulePath));

    //

    if Assigned(PApi) then

        Result := TessBaseAPIInit2(PApi, PUTF8Char(UTF8Encode(DataPath)), PUTF8Char(UTF8Encode(Language)), Mode) = 0

    else

        Result := False;

end;


function CancelCallback(cancel_this: Pointer; words: Integer): Boolean; cdecl;

begin

    Result := _CancelRecognize;

end;


function ProgressCallback(Progress: Integer; Left, Right, Top, Bottom: Integer): Boolean; cdecl;

begin

    Result := False;

end;


//设置识别参数

function TForm1.SetVariable(Key: string; Value: string): Boolean;

begin

    Result := TessBaseAPISetVariable(PApi, PUTF8Char(UTF8Encode(Key)), PUTF8Char(UTF8Encode(Value)));

end;


//识别图片中的文字

procedure TForm1.RecognizeAsText();

var

    FMonitor: ETEXT_DESC;

begin

    FillChar(FMonitor, SizeOf(FMonitor), #0);

    FMonitor.cancel := @CancelCallback;

    FMonitor.progress_callback := @ProgressCallback;

    FUtf8Text := '';

    FHOcrText := '';

    //

    if TessBaseAPIRecognize(PApi, FMonitor) <> 0 then

        Exit;


//识别文字

    FUtf8Text := PUTF8CharToString(TessBaseAPIGetUTF8Text(PApi));

    FUtf8Text := StringReplace(FUtf8Text, #10, #13#10, [rfReplaceAll]);


//识别结果和位置信息

    FHOcrText := PUTF8CharToString(TessBaseAPIGetHOCRText(PApi, 0));

    FHOcrText := StringReplace(FHOcrText, #10, #13#10, [rfReplaceAll]);

end;


//加载要识别的图片文件

function TForm1.SetImage(FileName: string): PPix;

begin

    Result := pixRead(PUTF8Char(UTF8Encode(FileName)));

    if Assigned(Result) then

        TessBaseAPISetImage2(PApi, Result)

    else

        Result := nil;

end;


//加载要识别的内存

function TForm1.SetImage(Stream: TMemoryStream): PPix;

begin

    Stream.Position := 0;

    Result := pixReadMem(Stream.Memory, Stream.Size);

    if Assigned(Result) then

        TessBaseAPISetImage2(PApi, Result)

    else

        Result := nil;

end;


procedure TForm1.btInitializeClick(Sender: TObject);

var

    Language: string;

begin

    case ComboBox1.ItemIndex of

        3, 2:

            Language := 'eng';

        1:

            Language := 'chi_sim';

    else

        Language := 'chi_sim+eng';

    end;

//加载需要很长时间,最好只执行一次

    TessInitialize('tessdata' + PathDelim, Language);

    FInitOver := True;

Memo1.Text := '字库加载完成';

end;


procedure TForm1.btRecognizeClick(Sender: TObject);

var

    Stream: TMemoryStream;

    Bitmap: TBitmap;

    ImagePix: PPix;

    T1: TDateTime;

    F: Int64;

begin

    if not FInitOver then

        btInitializeClick(Sender);

    //

    T1 := Now();

    //

    Stream := TMemoryStream.Create();

    try

        Bitmap := TBitmap.Create();

        try

            Bitmap.Assign(Image1.Picture.Graphic);

//最好把图像转化为标准bmp格式,tesseract加载图片不好用,经常内存错误

            Bitmap.SaveToStream(Stream);

        finally

            Bitmap.Free();

        end;

//设置内存图片,也可以直接加载内存文件

        ImagePix := SetImage(Stream);

    finally

        Stream.Free();

    end;

    //

    if Assigned(ImagePix) then begin

        try

//数字的情况,设置识别文字白名单

            if ComboBox1.ItemIndex = 3 then

                SetVariable('tessedit_char_whitelist', '0123456789');

//开始识别

            RecognizeAsText();

//毫秒数

            F := MilliSecondsBetween(Now, T1);

            //

            Memo1.Text := FUtf8Text + #13#10 + //

              '-------------------------------HOCRText-------------------------------' + #13#10 + //

              FHOcrText + #13#10 + Format('%d', [F]);

        finally

            pixDestroy(ImagePix);

        end;

    end;

end;


end.


      代码实现中注意以下几点:

1:加载字库需要时间较长,代码实现最好调用一次。

2:Tesseract API接口加载图片文件错误比较多,最好是程序自己实现图片转化为Bitmap图像,直接加载到内存为好。

3:图片适当放大,识别效果更好。

4:如果只识别某些字符,比如数字,先调用SetVariable函数加载字符白名单,效果很好。

5:识别结果可以返回文字位置数据,可以更加这些数据定位文字和图片关系。

三:DLL文件及字库

Tesseract API主要DLL文件为liblept-5.dll 和 libtesseract-5.dll,其他的是大量的加载图片使用的DLL,tessdata为字库,如下图:


四:测试效果


一张A4图片的识别时间1.5-3秒左右,中文识别率在96%以上。

笔者还提供PDF图片识别,区域模板识别,请关注。

--------------------------------------------------------------------------------------------------------------

(文中代码来源:北京盖亚软件有限公司,详见 http://www.gaya-soft.cn/ )

***** 敬请关注,欢迎交流 *****

--------------------------------------------------------------------------------------------------------------

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容