Tehnični članak

HotPDF OCR z DLL Tesseract: klicanje C API iz Delphija

HotPDF poganja Tesseract znotraj vašega procesa Delphi skozi HPDFCreateTesseractDLLOCREngine, tovarno, dodano v v2.772.0, ki dinamično naloži DLL, skladen s Tesseract 5, poganja njegov C API (TessBaseAPIInit2, TessBaseAPIRecognize, iterator rezultatov) in vrne IHPDFOCREngine. THotPDF.ApplyLoadedOCRTextLayer uporabi ta motor, da skeniranim stranem PDF doda nevidno, iskalno plast besedila Unicode

Isti prepoznavalec je bil že dosegljiv skozi zunanji adapter tesseract.exe, ki zapiše BMP in razčleni TSV. Ta pot dela, vsaka stran pa plača zagon procesa, začasno bitno-slikovno datoteko in besedilni format brez osnovnih črt in brez nadzora nad segmentacijo strani. Klicanje DLL-ja odstrani vse tri. Odstrani tudi procesno steno, kar pomeni, da vezava Pascal leži neposredno na strukturi C, predznaken C in nizih, dodeljenih s strani C. Večina tega, kar je vredno vedeti o tem adapterju, so mesta, kjer lahko ta vezava tiho zgreši

Kako poganjate Tesseract v procesu iz Delphija s HotPDF?

Poganjanje Tesseract v procesu s HotPDF vzame en klic tovarne v enoti HPDFTesseractRecognition in isti klic ApplyLoadedOCRTextLayer, ki ga uporablja vsak OCR motor HotPDF. Tovarna potrjuje nemudoma. Datoteka DLL in imenik tessdata morata obstajati, jezikovni identifikator sme vsebovati samo črke ASCII, številke, _ in +, vsak model v kombinaciji, kot je chi_sim+eng, mora imeti ujemajočo datoteko .traineddata, vsi 21 zahtevanih izvozov pa se morajo razrešiti, preden je motor vrnjen. Napake nastavitve javijo EArgumentException; DLL, ki se ne naloži, javi EOSError s kodo napake Windows in namigom, da preverite arhitekturo in odvisnosti

uses
  SysUtils, HPDFDoc, HPDFTesseractRecognition;

procedure MakeSearchable(const SourceFile, TargetFile: string);
var
  Doc: THotPDF;
  Engine: IHPDFOCREngine;
  Options: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  // Aplikacija Win64 potrebuje 64-bitni DLL; odvisnostne DLL-je postavite ob njega
  Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
    'C:\OCR\tessdata', 'chi_sim+eng');   // THPDFTesseractOptions.Default
  Doc := THotPDF.Create(nil);
  try
    Doc.AutoLaunch := False;
    if Doc.LoadFromFile(SourceFile) < 1 then
      raise Exception.Create('Cannot load ' + SourceFile);
    Options := THPDFOCRTextLayerOptions.Default;   // 300 DPI, MinimumConfidence 0.5
    // Prazen seznam strani pomeni vsako stran; strani z že obstoječim besedilom se preskočijo
    if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
      raise Exception.Create(string(Info.Diagnostic));
    Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
      ' words accepted, ', Info.DroppedWordCount, ' dropped');
    Doc.SaveLoadedDocument(TargetFile);
  finally
    Doc.Free;
  end;
end;

THPDFTesseractOptions.Default nastavi PageSegMode na tpsAuto, EngineMode na temDefault, TimeoutMilliseconds na 60.000 in MaxPixels na 16.777.216. Pikslovni proračun šteje več, kot izgleda. Stran US Letter pri privzetih 300 DPI se izriše v 2.550 × 3.300 pikslov, približno 8,4 milijona, kar gre noter. Ista stran pri 600 DPI je 5.100 × 6.600, približno 33,7 milijona, adapter pa jo zavrne, preden Tesseract vidi piksel. Dvignite MaxPixels (strop je 67.108.864) ali pustite DPI, kjer je; vsaka stranica je tudi stisnjena na 32.767 pikslov

DLL se naloži z LoadLibraryEx z iskalnimi zastavicami za lastno mapo DLL plus privzete varne imenike, tako da lahko slikovne knjižnice, od katerih je odvisen Tesseract, živijo ob njem, brez dotikanja PATH ali trenutnega imenika. HotPDF ne priloži ne prenese nobenega izvajalnega okolja ali modela OCR; oboje preskrbite sami

Kaj se spremeni v primerjavi z adapterjem tesseract.exe?

DLL adapter odstopi izolacijo procesov za bogatejši izhod in nižje stroške na stran. Oba adapterja se priključita na isti cevovod plasti besedila, tako da sta preslikava koordinat, filtriranje zaupanja in zapis vse-ali-nič identični; razlikuje se, kako piksli gredo noter in besede ven

VidAdapter tesseract.exeAdapter DLL Tesseract
TovarnaHPDFCreateTesseractOCREngineHPDFCreateTesseractDLLOCREngine
Piksli noterDatoteka BMP v zasebnem začasnem imenikuPredpomnilnik 8-bitnih sivin v pomnilniku
Besede venTSV na ravni besed, omejen na 64 MiBIterator rezultatov, UTF-8 na besedo
Osnovne črteNi na voljoPodane naprej iz TessPageIteratorBaseline
Segmentacija strani in način motorjaSamo samodejna segmentacijaTHPDFTesseractPageSegMode, THPDFTesseractEngineMode
ZakasnitevTrda: podrejeni proces je ubitSodelovalna: Tesseract ga mora opaziti
Izolacija sesutja in pomnilnikaLočen procesBrez, si deli vaš naslovni prostor

Ena cena ne izgine. Vsak klic Recognize ustvari svojo instanco API in pokliče TessBaseAPIInit2, tako da se jezikovni modeli inicializirajo na stran, namesto enkrat na motor. Datotečni predpomnilnik operacijskega sistema zmehča ponovno nalaganje, pri velikih večjezičnih naborih modelov pa je še vedno prevladujoča fiksna cena na stran in šteje proti roku prepoznavanja. Motor DLL RapidOCR v procesu vzame obratno zasnovo in drži svoje modele ONNX v pomnilniku za življenje motorja; težave na meji (C ABI, posojeni predpomnilniki, neprekinljivo native delo) pa so ista družina

Zakaj Delphi ne more kopirati strukture monitorja Tesseract?

Delphi ne more varno zrcaliti monitorja napredka Tesseract, ker ETEXT_DESC vsebuje različico-odvisna notranja polja, tako da ročno prepisan zapis postavi callback preklica in rok na napačne odmike na nekaterih izgradnjah. Nič ne odpove glasno, ko se to zgodi. Tesseract preprosto prebere vaš kazalec callback iz polja, ki zdaj drži kaj drugega, ali pa rok sploh nikoli ne vidi

HotPDF zato monitor obravnava kot neprozoren kazalec in se ga dotika samo skozi izvožene funkcije: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs in TessMonitorDelete. Če C API vežete sami za drug namen, velja isti vzorec. Skica spodaj je vaša lastna koda vezave, ne API HotPDF, in zrcali deklaracije, ki jih HotPDF uporablja interno

HotPDF ravnanje z monitorjem DLL Tesseract: kopiranje zapisa ETEXT_DESC, odvisnega od različice, postavi callback preklica in rok na napačne odmike ter odpove tiho, HotPDF pa monitor obravnava kot neprozornega, poganja TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc in TessMonitorSetDeadlineMSecs ter obdrži callback cdecl brez izjem
Neprozoren kazalec plus pet izvozov je celoten dogovor; callback ostaja enobajtni Boolean, ki bere samo zastavico in uro
type
  // C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
  TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
  TTessMonitorCreate = function: Pointer; cdecl;   // ETEXT_DESC*, nikoli dereferenciran
  TTessMonitorDelete = procedure(Monitor: Pointer); cdecl;
  TTessMonitorSetCancelFunc = procedure(Monitor: Pointer; Func: TTessCancelFunc); cdecl;
  TTessMonitorSetCancelThis = procedure(Monitor, CancelThis: Pointer); cdecl;
  TTessMonitorSetDeadlineMSecs = procedure(Monitor: Pointer; MSecs: Integer); cdecl;
  TTessBaseAPIRecognize = function(Handle, Monitor: Pointer): Integer; cdecl;

  TOCRJob = record
    CancelRequested: Boolean;
    DeadlineTick: UInt64;
  end;
  POCRJob = ^TOCRJob;

function ShouldCancel(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
begin
  // Teče na skladu Tesseract: branje zastavic in ure, nikoli izjeme
  Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
    (GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;

// Uporaba, s kazalci funkcij, razrešenimi z GetProcAddress:
//   Monitor := MonitorCreate();
//   try
//     MonitorSetCancelThis(Monitor, @Job);
//     MonitorSetCancelFunc(Monitor, ShouldCancel);
//     MonitorSetDeadlineMSecs(Monitor, RemainingMs);
//     RC := BaseAPIRecognize(API, Monitor);
//   finally
//     MonitorDelete(Monitor);
//   end;

Dve podrobnosti te skice sta namerni. Callback vrne Boolean, ki je en bajt tako v Delphiju kot v Free Pascalu, kar se ujema s C bool v TessCancelFunc. Štiribajtni Windows BOOL ali Delphi LongBool je videti zamenljiv in ni: kadar ena stran zapiše en bajt in druga bere štiri, so zgornji bajti vrnilnega registra karkoli je tam ostalo, in false lahko prispe kot true. Isti glavni stvari dodatno zaplete, ker funkcije, kot je TessPageIteratorBoundingBox, vrnejo int, ki ga HotPDF deklarira kot Integer. Preberite C tip vsake vrnjene vrednosti, namesto da bi predpostavili en dogovor za celoten API

Druga podrobnost je, da callback nikoli ne javi izjeme. Izjema Delphi, ki se razvije skozi okvirje C++ Tesseract, je nedoločeno obnašanje, zato callback HotPDF bere samo žeton preklica in monotono vrednost GetTickCount64. Adapter pretvori rezultat v diagnostiko preklica ali zakasnitve, potem ko se TessBaseAPIRecognize vrne, in to preverjanje opravi ne glede na native vrnilno kodo

Katere native kazalce je lastnik strani Delphi?

Adapter DLL Tesseract HotPDF je lastnik treh native objektov na zahtevo — instance API, monitorja in iteratorja rezultatov — vse ostalo pa posoja. Vsak klic Recognize ustvari svojo množico in jo sprosti v bloku finally: TessResultIteratorDelete, nato TessMonitorDelete, nato TessBaseAPIDelete. Sprostitev vmesnika motorja raznaloži knjižnico

HotPDF lastnina objektov DLL Tesseract na klic Recognize: iterator rezultatov, monitor in instanca API so v lasti in sproščeni v tem vrstnem redu znotraj finally, iterator strani iz TessResultIteratorGetPageIterator je posojen pogled, ki ga nikoli ne smete sprostiti, nizi GetUTF8Text pa so kopirani in vrnjeni skozi TessDeleteText
Trije objekti v lasti, vse ostalo posojeno: sprostite v fiksnem vrstnem redu, nikoli dvakrat ne sprostite iteratorja strani in nikoli ne mešajte dodeljevalcev
  • TessResultIteratorGetPageIterator vrne posojen pogled v iterator rezultatov, ne novega objekta. HotPDF ga uporablja za TessPageIteratorBoundingBox in TessPageIteratorBaseline ter ga nikoli ne sprosti; njegovo ločeno brisanje bi isti pomnilnik sprostilo dvakrat
  • TessResultIteratorGetUTF8Text vrne niz, dodeljen s strani lastnega izvajalnega okolja DLL. HotPDF ga skopira in vrne skozi TessDeleteText v bloku finally; Pascal FreeMem bi ga sprostil na napačnem kupu
  • Besedilo besed se dekodira s strogim potrjevanjem UTF-8 in preverjanjem dolžine, preden se pretvori. Besede s kontrolnimi znaki, okvarjenim UTF-8, okvirji izven slike, obrnjenimi pravokotniki ali zaupanjem izven 0-100 ne uspejo zahteve, namesto da bi bile tiho zakrpane
  • Skupno besedilo na zahtevo je omejeno na 1.048.576 kodnih enot UTF-16, število besed pa mora soditi v proračun zahteve, izročen od ApplyLoadedOCRTextLayer

Zaupanje prispe kot 0-100 in se skalira na 0-1, tako da THPDFOCRTextLayerOptions.MinimumConfidence pomeni isto stvar za vsak motor. Ko Tesseract poroča osnovno črto, sta oba konca podana naprej; sicer cevovod plasti besedila pade nazaj na svojo geometrijsko oceno, točno kakor pri vhodu TSV

Zakaj potrjevati enum, preden doseže DLL?

HotPDF skopira surovi ordinal PageSegMode in EngineMode v Integer, preden preveri obseg, ker lahko prevajalnik predpostavi, da enum spremenljivka vedno drži deklarirano vrednost, in zloži Ord(X) > Ord(High(T)) v konstantni false. Ordinali niso okras: THPDFTesseractPageSegMode sledi številčenju segmentacije strani Tesseract od 0 do 13, THPDFTesseractEngineMode številčenju načina motorja od 0 do 3, oba pa greta v DLL kot gola cela števila. Zapis opcij, zgrajen s FillChar, zapolnjen iz toka ali podan iz C++Builderja z ulitim celim številom, lahko nosi bajt, kot je 200. Potrjevanje skopiranega ordinala to spremeni v EArgumentException ob času tovarne, namesto v nedefiniran način znotraj native kode. Tovarna tudi zavrne tpsOSDOnly in tpsAutoOnly, ki ne izdelajo nobene besede, ter zahteva osd.traineddata za tpsAutoOSD in tpsSparseTextOSD

Kaj dejansko jamči zakasnitev prepoznavanja?

Zakasnitev DLL Tesseract je sodelovalna: HotPDF lahko ustavi svoje lastno delo in prosi Tesseract, naj se ustavi, a ne more prisiliti native kode, da se vrne. Ura se začne, ko se Recognize začne, tako da pretvorba bitne slike in inicializacija modelov porabita isti proračun kot prepoznavanje. HotPDF preverja pretečen čas in žeton preklica med pretvorbo v sivine in med besedami, medtem ko prehaja rezultate, ter podaja preostale milisekunde TessMonitorSetDeadlineMSecs, preden pokliče TessBaseAPIRecognize

Vrzel je znotraj native klica. Monitor Tesseract je obravnavan med prepoznavanjem besed, ne med TessBaseAPIInit2 ali analizo postavitve strani, tako da lahko počasno nalaganje modelov ali patološka postavitev teče čez rok, preden je zakasnitev prijavljena. Pikslovni in izhodni proračuni tudi ne omejijo lastne rabe pomnilnika native knjižnice. Če potrebujete delavca, ki ga lahko ubijete, uporabite procesni adapter; to je pošteno trgovanje, ne manjkajoča zmožnost

HotPDF anatomija sodelovalne zakasnitve DLL Tesseract: ura se začne, ko se Recognize začne, in pokriva pretvorbo v sivine, TessBaseAPIInit2 in analizo postavitve, monitor pa je obravnavan samo med prepoznavanjem besed, tako da lahko nalaganja modelov in postavitev pretečeta, preden HotPDF poroča otlsEngineError ali otlsCancelled
Rok tukaj je zahteva, ne jamstvo: inicializacija in analiza postavitve lahko tečeta dolgo, delavec, ki ga res lahko ubijete, pa potrebuje procesni adapter

Segmentacija strani je mesto, kjer DLL adapter zasluži svoj kruh na težkem vhodu. Obrazci, oznake in skenirane tabele z raztresenimi polji se pogosto bolje prepoznajo s tpsSparseText kot s samodejno segmentacijo, ki poskuša sestaviti stolpce in odstavke, ki jih ni

procedure OCRFormPages(Doc: THotPDF; const Pages: array of Integer);
var
  Engine: IHPDFOCREngine;
  TessOptions: THPDFTesseractOptions;
  LayerOptions: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  TessOptions := THPDFTesseractOptions.Default;
  TessOptions.PageSegMode := tpsSparseText;  // raztresena polja, brez sestavljanja stolpcev
  TessOptions.EngineMode := temLSTMOnly;     // potrebuje modele LSTM v tessdata
  TessOptions.TimeoutMilliseconds := 20000;  // vključuje inicializacijo modelov
  Engine := HPDFCreateTesseractDLLOCREngine('C:\OCR\Win64\libtesseract-5.dll',
    'C:\OCR\tessdata', 'eng+deu', TessOptions);

  LayerOptions := THPDFOCRTextLayerOptions.Default;
  LayerOptions.MinimumConfidence := 0.6;
  if not Doc.ApplyLoadedOCRTextLayer(Pages, Engine, LayerOptions, Info) then
    case Info.Status of
      otlsCancelled:
        Writeln('OCR cancelled, document unchanged');
      otlsEngineError:
        Writeln('Tesseract failed or timed out: ', string(Info.Diagnostic));
    else
      Writeln(string(Info.Diagnostic));
    end;
end;

Zakasnitev pride na plan kot otlsEngineError z diagnostiko Tesseract DLL OCR timed out, preklican žeton pa kot otlsCancelled. V obeh primerih je ApplyLoadedOCRTextLayer prepoznala vsako izbrano stran, preden začne transakcijo zapisa, tako da odpoved na strani 40 od 50 pusti naloženi dokument točno takšnega, kot je bil. Vedmite, da tpsSingleLine, tpsSingleBlock in tpsSparseText spremenijo samo segmentacijo; nobeden od njiju ne poravna poševnega skena

Free Pascal in Lazarus: zastareli piksli in izgubljena kitajščina

Obe tovarni Tesseract delujeta v Windows Free Pascal in izgradnjah Lazarus Win32 ter Win64 od v2.772.1, po dveh popravkih, specifičnih za FPC. Najprej znova zgradite paket Lazarus za ciljno arhitekturo; splošni prenos je pokrit v HotPDF na Free Pascal in Lazarus Win64

Prvi popravek zadeva piksle. LCL TBitmap, zapisan skozi scanline, lahko posodobi svojo surovo sliko, brez osvežitve ročaja bitne slike Windows, tako da GetDIBits na tem ročaju vrne stare piksle. Simptom je bil zmeden: besedilo, narisano neposredno na bitno sliko, je bilo prepoznano, stran, izrisana s strani izrisovalnika PDF HotPDF, pa je izdelala prazen seznam besed. Na FPC adapter zdaj bere posnetek, ki upošteva obliko, skozi CreateIntfImage, ki spoštuje pikslovni format in vrstni red vrstic surove slike. Izgradnja Delphi obdrži pot GetDIBits na zasebni 24-bitni kopiji. Nobena izgradnja ne spremeni bitne slike klicalca

Drugi popravek pripada adapterju tesseract.exe. TStringList FPC shranjuje nize ANSI, tako da je dodelitev dekodiranega besedila TSV UTF-8 k Lines.Text tiho izpustila vsak kitajski znak ali znak dopolnilne ravnine, ki ga sistemska kodna stran ANSI ni mogla predstaviti. Pot FPC zdaj obdrži TSV kot bajte UTF-8, odstrani BOM na ravni bajtov in dekodira vsako besedo v UnicodeString posebej. DLL adapter tega problema ni nikoli imel, ker vsako besedo dekodira neposredno iz iteratorja

Hiter pregled

  • Tovarna: HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options]) v HPDFTesseractRecognition, dodana v v2.772.0, podpora FPC v v2.772.1
  • Privzete vrednosti: tpsAuto, temDefault, 60.000 ms, 16.777.216 pikslov; obseg zakasnitve 1-3.600.000 ms, pikslovni strop 67.108.864
  • Ujemite bitnost DLL-ja z aplikacijo in postavite odvisnostne DLL-je ob DLL Tesseract
  • Monitor obravnavajte kot neprozornega; nikoli ne kopirajte ETEXT_DESC v zapis Pascal
  • Deklarirajte callback preklica kot cdecl z enobajtnim rezultatom Boolean in nikoli ne pustite, da bi iz njega ušla izjema
  • Besedilo iteratorja sprostite z TessDeleteText; nikoli ne sprostite iteratorja strani, pridobljenega iz iteratorja rezultatov
  • Pričakujte, da je rok sodelovalen: inicializacija modelov in analiza postavitve ga lahko pretečeta
  • Uporabite adapter tesseract.exe, kadar potrebujete trdo prekinitev ali izolacijo sesutij

Adapter DLL Tesseract, procesni adapterji in vgrajeni motor OCR vsi prihajajo s komponento HotPDF Delphi PDF za Delphi, C++Builder in Free Pascal; poglejte stran izdelka HotPDF za izdaje in prenose