HotPDF naredi skenirane strani PDF iskalne z in-process RapidOCR skozi HPDFCreateRapidOCRDLLOCREngine, tovarno, dodano v v2.774.0, ki naloži HotPDFRapidOCR.dll, drži modele ONNX za zaznavanje, klasifikacijo kota in prepoznavanje v pomnilniku ter vrne IHPDFOCREngine. Ta motor podate THotPDF.ApplyLoadedOCRTextLayer, ki izriše vsako stran, poganja CPU inferenco brez Pythona ali podrejenega procesa in zapiše nevidno plast besedila Unicode
Motivacija je cena na stran. Procesni adapter RapidOCR, poslan prej, HPDFCreateRapidOCREngine, zažene Python delavca za vsak klic Recognize, ta delavec pa uvozi svoje izvajalno okolje in naloži modele ONNX, preden prebere en sam piksel. Na arhivu s 500 stranmi se ta davek zagona ponovi 500-krat, namestitev pa pomeni pošiljanje okolja Python ob izvršljivi datoteki Delphi. Native DLL naloži modele enkrat, ko ustvarite motor, namestitev pa se skrči na DLL, njegove datoteke modelov in slovar znakov. Kar odrežete v zameno, je zmožnost ubiti zataknjenega prepoznavalnika, večina inženiringa tega adapterja pa je o tem, kako živeti s tem pošteno
Kako naredite skenirani PDF iskalen z DLL RapidOCR?
Ustvarjanje iskalnega PDF-ja z native DLL RapidOCR vzame en klic tovarne in isti klic ApplyLoadedOCRTextLayer, ki ga uporablja vsak OCR motor HotPDF. Tovarna živi v enoti HPDFRapidOCRRecognition in potrjuje nemudoma: DLL in mapa modelov morata obstajati, vsaka datoteka modela in slovarja se mora razrešiti, različica ABI mora biti 1, vsi zahtevani izvozi pa morajo biti prisotni, preden se kateri koli model inicializira. Napake nastavitve javijo EArgumentException; model, ki se ne naloži, javi EInvalidOperation z diagnostičnim besedilom, ki ga je zapisal DLL
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Modeli se naložijo tukaj, izven vsakega roka prepoznavanja.
// Relativna imena modelov v THPDFRapidOCRDLLOptions.Default se razrešijo
// glede na mapo modelov.
Engine := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models');
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,
' lines accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
THPDFRapidOCRDLLOptions.Default poimenuje ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx in ppocr_keys_v1.txt, z eno nitjo CPU, vhodno omejitvijo 16.777.216 pikslov in rokom prepoznavanja 60.000 ms. Od v2.775.0 THPDFRapidOCRDLLOptions.ForLanguage zamenja ujemajoč model prepoznavanja in slovar za tradicionalno kitajščino, ruščino, japonščino, arabščino in druge profile; zakaj se morata model in slovar spremeniti skupaj, je pokrito v večjezičnih modelih RapidOCR in slovarjih CTC v HotPDF. Motor se prijavi kot RapidOCR (native DLL) v Info.EngineName, kar ohranja dnevnike nedvoumne ob zunanjem procesnem adapterju OCR Tesseract in vgrajenem motorju OCR z ujemanjem predlog
Zakaj C ABI govori samo int32_t in bajte UTF-8?
ABI HotPDFRapidOCR.dll uporablja samo cela števila fiksne širine, surove kazalce in izrecne bajtne dolžine, ker se Delphi, C++Builder in Free Pascal z MSVC ne delijo ničesar prek C klicnega dogovora. std::string, std::vector ali izjema C++ ima postavitev in model razvijanja, ki pripadata enemu prevajalniku in eni izvajalni knjižnici. Pustite kateremu koli od njiju prestopiti mejo in odpoved je pokvarjen sklad ali blok kopu, sproščen s strani napačnega dodeljevalca, ne čista napaka
Različica ABI 1 zato sledi kratkemu seznamu pravil. Vsak izvoz je cdecl in vrne stanje int32_t, kjer 1 pomeni uspeh in 0 odpoved. Vsaka funkcija, ki lahko odpove, vzame diagnostični predpomnilnik v lasti klicalca in njegovo zmogljivost v bajtih; DLL zapiše sporočilo UTF-8, zaključeno z NUL, skrajšano, da gre noter, adapter pa ga dekodira s trdo zaključevalko v zadnjem bajtu svojega 4.096-bajtnega predpomnilnika. Telo vsakega izvoza je ovito v try z obema catch (const std::exception &) in catch (...), tako da napaka ONNX Runtime, trditev OpenCV ali neveljaven slovar postane stanje 0 plus besedilo — nikoli izjema, ki bi ušla v kodo Pascal
| Izvoz | Vloga | Kdaj ga adapter razreši |
|---|---|---|
HPDFRapidOCRAbiVersion | Vrne 1; vsaka druga vrednost je zavrnjena | Najprej, pred čimer koli drugim |
HPDFRapidOCRCreate | Naloži zaznavanje, neobvezen klasifikator, modele prepoznavanja in slovar | V tovarni |
HPDFRapidOCRRecognize | Poganja eno bitno sliko in izda enega callbacka na vrstico besedila | V tovarni |
HPDFRapidOCRDestroy | Sprosti instanco modelov | V tovarni |
HPDFRapidOCRSetReadingDirection | Neobvezen vrstni red vrstic od desne proti levi, dodan v v2.775.0 | Samo, kadar je nastavljen RightToLeft |
Neobvezni izvoz se namenoma razreši lenobno: DLL v2.774.0, ki mu manjka, še vedno postreže zahteve od leve proti desni. DLL se naloži z LoadLibraryEx z iskalnimi zastavicami, ki pokrijejo lastno mapo DLL plus privzete varne imenike, tako da so odvisnosti ONNX Runtime ali OpenCV, postavljene ob HotPDFRapidOCR.dll, najdene brez dotikanja PATH. Poti modelov in slovarjev potujejo kot UTF-8, DLL pa jih pretvori z MultiByteToWideChar v strogem načinu, preden odpre datoteke skozi API-je širokih znakov, tako da mapa modelov pod kitajskim ali ciriličnim uporabniškim imenom deluje, namesto da bi bila razširjena bajt za bajtom v nesmisel
Eno pravilo živi v izgradnji, ne v glavi. DLL statično poveže ONNX Runtime in OpenCV, privzeta nastavitev CMake pa uporablja statični release CRT (/MT). Statične knjižnice, prevedene proti /MD, pomešane v DLL /MT, dajejo napake povezovanja v najboljšem primeru in dva neodvisna kopa v najslabšem, zato morajo preskrbljene knjižnice ustrezati načinu CRT, ki ga DLL uporablja
Kaj se zgodi med TBitmap in vrstico besedila?
HotPDF izroči DLL-u neodvisen zgornje-navzdol posnetek BGR izrisane strani, DLL pa vrne enega callbacka na prepoznano vrstico besedila s posojenim besedilom UTF-8, ki ga mora adapter kopirati, preden se vrne
V Delphiju adapter dodeli bitno sliko strani zasebnemu TBitmap, vsili pf24bit in bere vrstice z GetDIBits z negativnim biHeight, kar da vrstice od zgoraj navzdol, oblazinjene na poravnavo štirih bajtov; ta korak je podan izrecno. Na FPC bere skozi CreateIntfImage, ker lahko zapisi scanline LCL posodobijo surovo sliko, brez osvežitve ročaja GDI. Bitna slika klicalca ni nikoli spremenjena, pikslovni proračun (MaxPixels, privzeto 16.777.216 in nastavljivo do 67.108.864) ter omejitev 32.767 pikslov na dimenzijo pa sta preverjena, preden se dodeli predpomnilnik posnetka
Znotraj DLL-ja je posnetek oblžinjen s 50 belimi piksli, besedilne regije so zaznane z največjo stranico 1.024 pikslov, okvirji so urejeni v vodoravne vrstice, vsak izrez pa je po potrebi zasukan s strani klasifikatorja kota, preden ga prepozna. Vsaka vrstica besedila nato gre skozi callback, ki prejme const char*, število bajtov, celoštevilčen okvir v pikslih izvirne slike in povprečno zaupanje znakov. Kazalec besedila je veljaven samo med callbackom, zato ga adapter takoj kopira in je strog glede tega, kaj sprejme:
- UTF-8 se dekodira z
MB_ERR_INVALID_CHARS; okvarjeno zaporedje razveljavi stran, namesto da bi izdelalo zamenjalne znake v iskalni plasti - Kontrolni znaki C0 in C1 so zavrnjeni, vrstice samo s presledki pa so preskočene
- Okvir mora ležati znotraj bitne slike in zaupanje mora biti končna vrednost od 0 do 1
- Besedilo se šteje proti
MaxTextCodeUnitszahteve s trdim stropom 1.048.576 enot UTF-16 na klic, znaki dopolnilne ravnine pa stanejo dve enoti - Katera koli izjema Pascal znotraj callbacka je ujeta tam, shranjena in spremenjena v vrnitev 0, kar naredi, da se DLL ustavi in poroča odpoved; shranjeno sporočilo potem postane diagnostika
Dve posledici sta pomembni za uglaševanje. Prva: enota izhoda je vrstica, ne beseda — vsaka vrstica porabi eno mesto MaxWords, Info.AcceptedWordCount in Info.DroppedWordCount štejeta vrstice, poudarjanje iskanja pa sega čez okvir vrstice. Druga: MinimumConfidence (privzeto 0,5) se primerja s povprečnim zaupanjem znakov vrstice, tako da vrstica z enim neberljivim znakom med dvajsetimi čistimi običajno preživi. DLL ne ponudi osnovne črte, zato jo cevovod plasti besedila oceni iz okvirja. Praza stran uspe z nič vrsticami, vsaka odpoved pa počisti delne rezultate, tako da večstranski zapis ostane vse-ali-nič
Lastnina modelov in varnost niti
Vsak motor DLL RapidOCR je lastnik točno ene instance modelov za svoje celotno življenje, klici Recognize na tem motorju pa so serializirani s kritičnim odsekom. Držanje vmesnika IHPDFOCREngine je tisto, kar ohranja modele tople, zato je pravi vzorec za paketno delo ustvariti motor enkrat in ga uporabljati čez dokumente
procedure OcrBatch(const Files: TStrings; const OutputDir: string);
var
Models: THPDFRapidOCRDLLOptions;
Engine: IHPDFOCREngine;
Doc: THotPDF;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
I: Integer;
begin
Models := THPDFRapidOCRDLLOptions.Default;
Models.UseAngleClassifier := False; // pokončni skeni: model klasifikatorja se ne naloži
Models.Threads := 4; // 1..64, stisnjeno na število logičnih procesorjev
Models.TimeoutMilliseconds := 120000; // na klic Recognize, sodelovalno
Engine := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models', Models);
Options := THPDFOCRTextLayerOptions.Default;
for I := 0 to Files.Count - 1 do
begin
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
if (Doc.LoadFromFile(Files[I]) > 0) and
Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
Doc.SaveLoadedDocument(IncludeTrailingPathDelimiter(OutputDir) +
ExtractFileName(Files[I]))
else
Writeln(Files[I], ': ', string(Info.Diagnostic));
finally
Doc.Free;
end;
end;
end; // zadnji sklic sproščen: modeli uničeni, nato se DLL raznaloži
Vrednost Threads nastavi število nitih intra-op in inter-op vsake seje ONNX, DLL pa jo stisne na število aktivnih procesorjev. Dve niti, ki si delita enega motorja, ne tečeta vzporedno; druga čaka na zaklep. To čakanje ni slep EnterCriticalSection: adapter kliče TryEnterCriticalSection vsakih 25 ms in preverja žeton preklica ter rok med poskusi, tako da lahko čakajoča zahteva še vedno prekliče ali poteče. Če potrebujete pravo vzporednost, ustvarite enega motorja na delavca in sprejmite, da vsak motor drži svojo kopijo modelov v pomnilniku
Razstavljanje je fiksirano z destruktorjem motorja: HPDFRapidOCRDestroy najprej sprosti instanco modelov, nato FreeLibrary raznaloži DLL. Na native strani je inicializacija modelov enako skrbna; kadar model prepoznavanja odpove, potem ko sta bili seji zaznavalnika in klasifikatorja že zgrajeni, sta ti seji sproščeni, preden je napaka prijavljena, število razredov slovarja pa se preveri proti izhodu modela med inicializacijo, ne na prvi strani
Zakaj native klica OCR ni mogoče ubiti sredi inference?
Native klica RapidOCR ni mogoče ubiti sredi inference, ker teče na vaši niti, znotraj vašega procesa, sredi seje ONNX Runtime, ki ne sprejema prekinitve. Preklic v DLL adapterju HotPDF je zato sodelovalen: DLL kliče abort callback pred in po zaznavanju, po klasifikaciji in po vsaki prepoznani vrstici ter se ustavi na prvem kontrolni točki, kjer callback vrne 0. Posamezen Run ONNX, ki se je že začel, se najprej konča
Alternative so slabše od čakanja. TerminateThread bi pustil zaklep kopa CRT, bazen nitih ONNX Runtime in katero koli stanje OpenCV v kakršnem koli stanju, v katerem se je znašla, in zastrupil preostanek procesa. FreeLibrary med izvajanjem klica raznaloži kodo, ki je na skladu. Nobenega ni mogoče narediti varnega, zato adapter ju nikoli ne poskusi. Rok v TimeoutMilliseconds je posledično sodelovalni rok, potekli rok se pokaže kot napaka motorja z diagnostiko o času, preklican žeton pa kot otlsCancelled:
// Žeton ustvari klicalec in ga deli z UI nitjo,
// ki pokliče Token.Cancel, kadar uporabnik pritisne Stop
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// vrnjeno na naslednji meji faze ali vrstice; dokument nespremenjen
Writeln('Cancelled');
otlsEngineError:
// vključuje potek sodelovalnega roka in native diagnostiko
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
To je osrednje trgovanje med procesnimi adapterji HotPDF in DLL-jem v procesu, in nobena stran ne zmaga v vsaki vrstici:
- Cena zagona: adapterja Tesseract in Python RapidOCR zaženeta proces in naložita modele za vsako stran; DLL naloži modele enkrat na motor
- Ustavljanje: podrejeni proces je mogoče ubiti izrecno in Python delavec teče znotraj Job Object kill-on-close, tako da gre z njim celo njegovo drevo procesov; DLL pa se lahko ustavi samo na mejah faz in vrstic
- Vsebovanje napak: sesutje v
tesseract.exepokvari eno stran; kršitev dostopa znotraj DLL-ja potegne dol vaš proces - Namestitev: procesni adapterji potrebujejo nameščen program ali okolje Python; DLL potrebuje sebe, svoje modele in svoj slovar, ujemajoče z bitnostjo aplikacije
- Pomnilnik: procesni adapterji sprostijo vse, ko podrejeni proces izide; motor DLL drži svoje modele v pomnilniku, dokler se ne sprosti zadnji sklic vmesnika
Za interaktivno namizno aplikacijo, ki OCR-a stran naenkrat, običajno zmaga odzivnost DLL-ja. Za strežnik, ki prebada nezaupljive skene non-stop, je procesna meja vredna svoje cene zagona
Izgradnja in namestitev HotPDFRapidOCR.dll
HotPDFRapidOCR.dll se gradi iz virov C++ v Native/RapidOCR z MSVC, C++17, Windows SDK in CMake 3.20 ali novejšim, s pomožnim skriptom, ki vzame imenike izvornih omrežnih virov, ONNX Runtime in OpenCV ter platformo Win32 ali Win64. Zgradite oba, če pošiljate oba, ker 32-bitna aplikacija Delphi ne more naložiti 64-bitnega DLL-ja, statične knjižnice, ki jih preskrbite, pa morajo ustrezati ciljni arhitekturi kot tudi načinu CRT
Stran modelov ima svoje meje skladnosti. Zaznavalec je besedilni zaznavalec DB; prepoznavalec sprejema modele CTC v postavitvi NCHW s fiksno vhodno višino 32 ali 48 in uporablja 48 za modele z dinamično višino. Vgrajeni statični ONNX Runtime ne more naložiti modelov, shranjenih z novejšo različico IR, tako da nedavni izvozi PP-OCRv5 ne uspejo inicializacije z diagnostiko, namesto da bi se naložili delno. Slovar mora biti UTF-8 brez BOM, v točno vrstnem redu znakov modela, njegovo število razredov pa se mora ujemati z izhodom modela; koncaji vrstic CRLF so sprejeti. Prepoznavanje je offline: DLL nikoli ne prenese manjkajočega modela
Hiter pregled
- Tovarna:
HPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options])vHPDFRapidOCRRecognition, na voljo od v2.774.0 v Delphi, C++Builder in izgradnjah Windows FPC/Lazarus - Obdržite vrnjeni
IHPDFOCREngineživ čez strani in dokumente; njegovo sproščanje uniči modele in raznaloži DLL - En motor poganja eno prepoznavanje naenkrat; ustvarite več motorjev za vzporedne delavce in načrtujte pomnilnik za vsako kopijo modelov
- Izhod je en vnos na vrstico besedila s povprečnim zaupanjem znakov, filtriran s
THPDFOCRTextLayerOptions.MinimumConfidence - Preklic in
TimeoutMillisecondssta sodelovalna; tekoči Run ONNX se vedno zaključi - Ujemite bitnost DLL-ja z aplikacijo in način CRT statičnih knjižnic ONNX Runtime in OpenCV z DLL-jem
- Izberite jezikovni profil na motor s
THPDFRapidOCRDLLOptions.ForLanguage(v2.775.0); en motor sam ne zaznava jezikov
Native adapter RapidOCR, procesni adapterji OCR, izrisovalnik strani, ki ju napaja, in pisec nevidne plasti besedila Unicode vsi prihajajo skupaj v HotPDF, izvorno komponento PDF VCL za Delphi in C++Builder. Če vaša aplikacija za zajem ali arhiviranje dokumentov potrebuje iskalni izhod brez izvajalnega okolja Python na ciljnem računalniku, komponenta HotPDF Delphi PDF ponudi celoten cevovod, za namestitev pa ostane samo DLL in njegovi modeli