HotPDF čini skenirane PDF stranice pretraživima s in-process RapidOCR-om kroz HPDFCreateRapidOCRDLLOCREngine, tvornicu dodanu u v2.774.0 koja učitava HotPDFRapidOCR.dll, drži ONNX detekcijske, klasifikacijske za kut i prepoznavne modele rezidentnima u memoriji i vraća IHPDFOCREngine. Taj engine predajete THotPDF.ApplyLoadedOCRTextLayeru, koji renderira svaku stranicu, izvodi CPU inferenciju bez Pythona ili dječjeg procesa i izvršava nevidljivi Unicode tekstualni sloj
Motivacija je trošak po stranici. RapidOCR procesni adapter isporučen ranije, HPDFCreateRapidOCREngine, pokreće Python workera za svaki poziv Recognize, i taj worker uvozi svoj runtime i učitava ONNX modele prije nego pročita jedan piksel. Na arhivu od 500 stranica taj porez pokretanja ponavlja se 500 puta, a raspoređivanje znači isporučiti Python okruženje uz Delphi izvršnu datoteku. Nativni DLL učitava modele jednom, kad stvorite engine, a raspoređivanje se smanji na DLL, njegove datoteke modela i rječnik znakova. Ono što zauzvrat odustajete jest sposobnost ubijanja zaglavljenog prepoznavatelja, i najveći dio inženjeringa u ovom adapteru jest živjeti s time pošteno
Kako učiniti skenirani PDF pretraživim s RapidOCR DLL-om?
Stvaranje pretraživog PDF-a s nativnim RapidOCR DLL-om traži jedan poziv tvornice i isti poziv ApplyLoadedOCRTextLayer koji svaki HotPDF OCR engine koristi. Tvornica živi u HPDFRapidOCRRecognition jedinici i validira rano: DLL i mapa modela moraju postojati, svaka datoteka modela i rječnika mora se razriješiti, ABI verzija mora biti 1, i svi potrebni izvozi moraju biti prisutni prije nego se bilo koji model inicijalizira. Pogreške konfiguracije dižu EArgumentException; model koji ne uspije učitati diže EInvalidOperation koji nosi dijagnostički tekst koji je DLL napisao
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Modeli se učitavaju ovdje, izvan bilo kojeg roka prepoznavanja.
// Relativna imena modela u THPDFRapidOCRDLLOptions.Default razrješuju se
// prema mapi modela.
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
// prazna lista stranica znači svaku stranicu; stranice koje već imaju tekst se preskaču
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 imenuje ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx i ppocr_keys_v1.txt, s jednom CPU dretvom, ograničenjem ulaza od 16.777.216 piksela i rokom prepoznavanja od 60.000 ms. Od v2.775.0 THPDFRapidOCRDLLOptions.ForLanguage umeće odgovarajući prepoznavni model i rječnik za tradicionalni kineski, ruski, japanski, arapski i druge profile; zašto se model i rječnik moraju mijenjati zajedno obrađeno je u RapidOCR višejezičnim modelima i CTC rječnicima u HotPDF-u. Engine se prijavljuje kao RapidOCR (native DLL) u Info.EngineName, što drži zapise nedvosmislenima uz vanjski Tesseract OCR procesni adapter i ugrađeni OCR engine s podudaranjem predložaka
Zašto C ABI govori samo int32_t i UTF-8 bajtove?
HotPDFRapidOCR.dll ABI koristi samo cijele brojeve fiksne širine, sirove pokazivače i eksplicitne bajtne duljine jer se Delphi, C++Builder i Free Pascal s MSVC-om ne dijele ničim izvan C pozivne konvencije. std::string, std::vector ili C++ iznimka imaju raspored i unwinding model koji pripadaju jednom kompajleru i jednoj runtime biblioteci. Pustite li bilo što od toga prijeći granicu, kvar je oštećeni stog ili blok heapa oslobođen pogrešnim alocatorom, a ne čista pogreška
ABI verzija 1 stoga slijedi kratak popis pravila. Svaki je izvoz cdecl i vraća int32_t status, gdje 1 znači uspjeh, a 0 neuspjeh. Svaka funkcija koja može pasti prima dijagnostički međuspremnik u vlasništvu pozivatelja i njegov kapacitet u bajtovima; DLL piše NUL-om završenu UTF-8 poruku skraćenu da stane, a adapter je dekodira s tvrdim terminatorom u posljednjem bajtu vlastitog međuspremnika od 4.096 bajtova. Svako tijelo izvoza umotano je u try s catch (const std::exception &) i catch (...), pa ONNX Runtime pogreška, OpenCV asercija ili nevažeći rječnik postaje status 0 plus tekst, nikad iznimka koja bježi u Pascal kod
| Export | Uloga | Kad ga adapter razrješuje |
|---|---|---|
HPDFRapidOCRAbiVersion | Vraća 1; svaka druga vrijednost se odbija | Prvi, prije svega ostalog |
HPDFRapidOCRCreate | Učitava detekcijske, neobavezne klasifikacijske i prepoznavne modele te rječnik | U tvornici |
HPDFRapidOCRRecognize | Izvodi jednu bitmapu i emitira jedan callback po tekstualnoj liniji | U tvornici |
HPDFRapidOCRDestroy | Oslobađa instancu modela | U tvornici |
HPDFRapidOCRSetReadingDirection | Neobavezni redoslijed redaka s desna na lijevo, dodan u v2.775.0 | Samo kad je postavljen RightToLeft |
Neobavezni se izvoz razrješuje lijeno namjerno: v2.774.0 DLL koji ga nema i dalje poslužuje zahtjeve s lijeva na desno. DLL se učitava s LoadLibraryEx s pretraživačkim zastavicama koje pokrivaju vlastitu mapu DLL-a plus zadane sigurne direktorije, pa se ONNX Runtime ili OpenCV ovisnosti postavljene uz HotPDFRapidOCR.dll nalaze bez diranja PATH-a. Putanje modela i rječnika putuju kao UTF-8, a DLL ih pretvara s MultiByteToWideChar u strogom načinu prije otvaranja datoteka kroz wide-character API-je, pa mapa modela pod kineskim ili ćiriličnim korisničkim imenom radi umjesto da se proširi bajt po bajt u besmisao
Jedno pravilo živi u buildu, a ne u zaglavlju. DLL statički povezuje ONNX Runtime i OpenCV, i zadana CMake konfiguracija koristi statički release CRT (/MT). Statičke biblioteke kompilirane protiv /MD pomiješane u /MT DLL proizvode u najboljem slučaju pogreške povezivanja, a u najgorem dva neovisna heapa, pa isporučene biblioteke moraju odgovarati načinu CRT-a koji DLL koristi
Što se događa između TBitmapa i tekstualne linije?
HotPDF predaje DLL-u neovisni top-down BGR snimak renderirane stranice, a DLL vraća jedan callback po prepoznatoj tekstualnoj liniji s posuđenim UTF-8 tekstom koji adapter mora kopirati prije povratka
Na Delphiju adapter dodjeljuje bitmapu stranice privatnom TBitmapu, forsira pf24bit, i čita retke s GetDIBits koristeći negativan biHeight, što daje top-down retke podložene na četverobajtno poravnanje; taj se stride prenosi eksplicitno. Na FPC-u čita kroz CreateIntfImage, jer LCL scanline upisi mogu ažurirati sirovu sliku bez osvježavanja GDI handlea. Pozivateljeva se bitmapa nikad ne mijenja, a pikselni proračun (MaxPixels, 16.777.216 po zadanim vrijednostima i podesivo do 67.108.864) i granica od 32.767 piksela po dimenziji provjeravaju se prije nego se dodijeli međuspremnik snimka
Unutar DLL-a snimak se podlaže s 50 bijelih piksela, tekstualna se područja detektiraju s maksimalnom stranom od 1.024 piksela, okviri se redaju u horizontalne retke, i svaki se izrezak po potrebi rotira klasifikatorom kuta prije prepoznavanja. Svaka tekstualna linija zatim prolazi kroz callback koji prima const char*, broj bajtova, cijelobrojni okvir u pikselima izvorne slike i srednju pouzdanost znakova. Tekstovni pokazivač valjan je samo tijekom callbacka, pa ga adapter odmah kopira, i strog je u tome što prihvaća:
- UTF-8 se dekodira s
MB_ERR_INVALID_CHARS; neispravan niz obara stranicu umjesto da proizvede znakove zamjene u pretraživom sloju - C0 i C1 kontrolni znakovi se odbijaju, a linije samo s razmacima se preskaču
- Okvir mora ležati unutar bitmape, a pouzdanost mora biti konačna vrijednost od 0 do 1
- Tekst se broji protiv
MaxTextCodeUnitszahtjeva s tvrdim stropom od 1.048.576 UTF-16 jedinica po pozivu, i znakovi dopunskih planova koštaju dvije jedinice - Svaka Pascal iznimka unutar callbacka hvata se tamo, sprema i pretvara u povratak 0, što natjerava DLL da stane i prijavi neuspjeh; spremljena poruka tada postaje dijagnostika
Dvije posljedice važne su za štelanje. Prvo, jedinica izlaza je linija, a ne riječ: svaka linija troši jedan MaxWords slot, Info.AcceptedWordCount i Info.DroppedWordCount broje linije, i isticanje pretraživanja prostire se preko okvira linije. Drugo, MinimumConfidence (0.5 po zadanim vrijednostima) uspoređuje se sa srednjom pouzdanošću znakova linije, pa linija s jednim nečitkim znakom među dvadeset čistih obično preživi. DLL ne isporučuje baseline, pa tekstualni sloj procjenjuje jedan iz okvira. Prazna stranica uspijeva s nula linija, i svaki neuspjeh čisti djelomične rezultate pa višestranično izvršenje ostaje sve-ili-ništa
Vlasništvo modela i thread safety
Svaki RapidOCR DLL engine posjeduje točno jednu instancu modela kroz svoj cijeli životni vijek, i pozivi Recognize na tom engineu serijaliziraju se critical sekcijom. Držanje IHPDFOCREngine sučelja ono je što modele drži toplima, pa je pravi obrazac za batch posao stvoriti engine jednom i ponovno ga koristiti kroz 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; // uspravni skenovi: nijedan klasifikacijski model se ne učitava
Models.Threads := 4; // 1..64, stezano na broj logičkih procesora
Models.TimeoutMilliseconds := 120000; // po Recognize pozivu, suradnički
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; // zadnja referenca oslobođena: modeli uništeni, pa se DLL iskrcava
Vrijednost Threads postavlja i intra-op i inter-op broj dretava svake ONNX sesije, i DLL je steže na broj aktivnih procesora. Dvije dretve koje dijele jedan engine ne izvode se paralelno; druga čeka lock. To čekanje nije slijepi EnterCriticalSection: adapter poziva TryEnterCriticalSection svakih 25 ms i između pokušaja provjerava token otkazivanja i rok, pa redom čekajući zahtjev i dalje može biti otkazan ili isteknuti. Ako trebate pravi paralelizam, stvorite jedan engine po workeru i prihvatite da svaki engine drži vlastitu kopiju modela u memoriji
Redoslijed rastavljanja fiksira destruktor enginea: HPDFRapidOCRDestroy najprije oslobađa instancu modela, pa FreeLibrary iskrcava DLL. Na nativnoj strani inicijalizacija modela podjednako je pažljiva; kad prepoznavni model padne nakon što su sesije detektora i klasifikatora već izgrađene, te sesije se oslobađaju prije nego se pogreška prijavi, i broj klasa rječnika provjerava se prema izlazu modela tijekom inicijalizacije, a ne na prvoj stranici
Zašto se nativni OCR poziv ne može ubiti usred inferencije?
Nativni se RapidOCR poziv ne može ubiti usred inferencije jer se izvodi na vašoj dretvi, unutar vašeg procesa, usred ONNX Runtime sesije koja ne prima prekid. Otkazivanje u HotPDF DLL adapteru stoga je suradničko: DLL poziva abort callback prije i poslije detekcije, poslije klasifikacije i poslije svake prepoznate linije, i staje na prvoj kontrolnoj točki gdje callback vrati 0. Jedan ONNX Run koji je počeo dovršit će se prvo
Alternative su gore od čekanja. TerminateThread ostavio bi CRT heap lock, ONNX Runtime thread pool i bilo koje OpenCV stanje u onom stanju u kojem su se slučajno našli, otrovavši ostatak procesa. FreeLibrary dok se poziv još izvodi iskrcava kod koji je na stogu. Nijedno se ne može učiniti sigurnim, pa adapter nikad ne pokušava s njima. Rok u TimeoutMilliseconds posljedično je suradnički rok, i istekli rok izlazi na vidjelo kao engine pogreška s dijagnostikom isteka vremena, dok otkazani token izlazi kao otlsCancelled:
// Token stvara pozivatelj i dijeli ga s UI dretvom,
// koja poziva Token.Cancel kad korisnik pritisne Stop
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// vraća se na sljedećoj granici faze ili linije; dokument nepromijenjen
Writeln('Cancelled');
otlsEngineError:
// uključuje istek suradničkog roka i nativnu dijagnostiku
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
To je srž razmjene između HotPDF procesnih adaptera i in-process DLL-a, i nijedna strana ne pobjeđuje u svakom redu:
- Trošak pokretanja: Tesseract i Python RapidOCR adapteri pokreću proces i učitavaju modele za svaku stranicu; DLL učitava modele jednom po engineu
- Zaustavljanje: dječji se proces može izravno terminirati, i Python worker izvodi se unutar kill-on-close Job Objecta pa cijelo njegovo procesno stablo ide s njim; DLL može stati samo na granicama faza i linija
- Sadržaj kvarova: rušenje u
tesseract.exeobara jednu stranicu; access violation unutar DLL-a obara vaš proces - Raspoređivanje: procesni adapteri trebaju instalirani program ili Python okruženje; DLL treba sebe, svoje modele i svoj rječnik, usklađene s bitnessom aplikacije
- Memorija: procesni adapteri oslobađaju sve kad dijete izađe; DLL engine drži svoje modele rezidentnima dok se zadnja referenca sučelja ne oslobodi
Za interaktivnu desktop aplikaciju koja OCR-a stranicu po stranicu obično pobjeđuje DLL-ova brzina odziva. Za server koji dobavlja nepouzdane skenove non-stop granica procesa vrijedi svoj trošak pokretanja
Gradnja i raspoređivanje HotPDFRapidOCR.dll
HotPDFRapidOCR.dll gradi se iz C++ izvora u Native/RapidOCR s MSVC-om, C++17, Windows SDK-om i CMake 3.20 ili kasnijim, koristeći pomoćnu skriptu koja prima direktorije nativnih mrežnih izvora, ONNX Runtimea i OpenCV-a plus platformu Win32 ili Win64. Gradite oba ako isporučujete oba, jer 32-bitna Delphi aplikacija ne može učitati 64-bitni DLL, a statičke biblioteke koje osigurate moraju odgovarati ciljnoj arhitekturi kao i načinu CRT-a
Strana modela ima vlastite granice kompatibilnosti. Detektor je DB text detector; prepoznavatelj prima CTC modele u NCHW rasporedu s fiksnom ulaznom visinom 32 ili 48, i koristi 48 za modele s dinamičkom visinom. Priloženi statički ONNX Runtime ne može učitati modele spremljene novijom IR verzijom, pa nedavni PP-OCRv5 izvozi padaju u inicijalizaciji s dijagnostikom umjesto da se učitaju djelomično. Rječnik mora biti UTF-8 bez BOM-a, točno u redoslijedu znakova modela, i njegov broj klasa mora odgovarati izlazu modela; CRLF završeci linija prihvaćaju se. Prepoznavanje je offline: DLL nikad ne preuzima model koji nedostaje
Brza referenca
- Tvornica:
HPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options])uHPDFRapidOCRRecognition, dostupna od v2.774.0 u Delphi, C++Builder i Windows FPC/Lazarus verzijama - Držite vraćeni
IHPDFOCREngineživim kroz stranice i dokumente; oslobađanje ga uništava modele i iskrcava DLL - Jedan engine izvodi jedno prepoznavanje istovremeno; stvorite nekoliko enginea za paralelne workere i izbudžetirajte memoriju za svaku kopiju modela
- Izlaz je jedan unos po tekstualnoj liniji sa srednjom pouzdanošću znakova, filtriran s
THPDFOCRTextLayerOptions.MinimumConfidence - Otkazivanje i
TimeoutMillisecondssuradnički su; ONNX izvođenje u tijeku uvijek se dovršava - Uskladite DLL bitness s aplikacijom, a način CRT-a statičkih ONNX Runtime i OpenCV biblioteka s DLL-om
- Birajte jezični profil po engineu s
THPDFRapidOCRDLLOptions.ForLanguage(v2.775.0); jedan engine ne detektira jezike sam od sebe
Nativni RapidOCR adapter, procesni OCR adapteri, renderer stranica koji ih hrani i pisac nevidljivog Unicode tekstualnog sloja svi se isporučuju zajedno u HotPDF-u, nativnoj VCL PDF komponenti za Delphi i C++Builder. Ako vaša aplikacija za snimanje ili arhiviranje dokumenata treba pretraživi izlaz bez Python runtimea na ciljnom stroju, HotPDF Delphi PDF komponenta daje cijeli pipeline, a za rasporediti ostaju samo DLL i njegovi modeli