HotPDF gjør skannede PDF-sider søkbare med in-process RapidOCR gjennom HPDFCreateRapidOCRDLLOCREngine, en fabrikk lagt til i v2.774.0 som laster HotPDFRapidOCR.dll, holder ONNX-deteksjons-, vinkelklassifiserings- og gjenkjenningsmodellene residente i minnet, og returnerer en IHPDFOCREngine. Du sender den motoren til THotPDF.ApplyLoadedOCRTextLayer, som rendrer hver side, kjører CPU-inferens uten Python eller en barne-prosess, og committer et usynlig Unicode-tekstlag
Motivasjonen er kostnad per side. RapidOCR-prosessadapteren som kom tidligere, HPDFCreateRapidOCREngine, starter en Python-worker for hvert Recognize-kall, og den workeren importerer sin runtime og laster sine ONNX-modeller før den leser én eneste piksel. På et 500-siders arkiv gjentar den oppstartsskatten seg 500 ganger, og deployering betyr å skippe et Python-miljø ved siden av en Delphi-kjørbar fil. Den native DLL-en laster modellene én gang, når du oppretter motoren, og deployeringen krymper til DLL-en, dens modellfiler og en tegnordbok. Det du gir opp til gjengjeld, er evnen til å drepe en fastlåst gjenkjenner, og mesteparten av ingeniørarbeidet i denne adapteren handler om å leve med det ærlig
Hvordan gjør du en skannet PDF søkbar med RapidOCR DLL-en?
Å lage en søkbar PDF med den native RapidOCR DLL-en tar ett fabrikk-kall og det samme ApplyLoadedOCRTextLayer-kallet enhver HotPDF OCR-motor bruker. Fabrikken bor i HPDFRapidOCRRecognition-uniten og validerer ivrig: DLL-en og modellkatalogen må finnes, hver modell- og ordbokfil må løses, ABI-versjonen må være 1, og alle påkrevde eksporter må være til stede før noen modell initialiseres. Konfigurasjonsfeil reiser EArgumentException; en modell som feiler å laste reiser EInvalidOperation med diagnostikkteksten DLL-en skrev
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Modeller lastes her, utenfor enhver gjenkjenningsfrist.
// Relative modellnavn i THPDFRapidOCRDLLOptions.Default løses
// mot modellkatalogen.
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
// en tom sideliste betyr hver side; sider som allerede har tekst hoppes over
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 navngir ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx og ppocr_keys_v1.txt, med én CPU-tråd, en inngrense på 16 777 216 piksler og en gjenkjenningsfrist på 60 000 ms. Siden v2.775.0 bytter THPDFRapidOCRDLLOptions.ForLanguage inn en matchende gjenkjenningsmodell og ordbok for tradisjonell kinesisk, russisk, japansk, arabisk og andre profiler; hvorfor modellen og ordboken må endres sammen, er dekket i RapidOCR flerspråklige modeller og CTC-ordbøker i HotPDF. Motoren rapporterer seg selv som RapidOCR (native DLL) i Info.EngineName, noe som holder logger entydige ved siden av den eksterne Tesseract OCR-prosessadapteren og den innebygde mal-sammenlignings-OCR-motoren
Hvorfor snakker C ABI-en bare int32_t og UTF-8-byter?
HotPDFRapidOCR.dll ABI-en bruker bare fastbreddde heltall, rå pekere og eksplisitte bytelengder, for Delphi, C++Builder og Free Pascal deler ingenting med MSVC utover C-kallekonvensjonen. En std::string, en std::vector eller et C++-unntak har et layout og en unwinding-modell som tilhører én kompilator og ett runtime-bibliotek. La noen av dem krysse grensen, og feilen er en korrupt stakk eller en heap-blokk frigjort av feil allokator, ikke en ren feil
ABI versjon 1 følger derfor en kort liste med regler. Hver eksport er cdecl og returnerer en int32_t-status, der 1 betyr suksess og 0 betyr feil. Hver funksjon som kan feile, tar en kalleid diagnostikkbuffer og dens kapasitet i byte; DLL-en skriver en NUL-terminert UTF-8-melding avkortet til å passe, og adapteren dekoder den med en hard terminator i siste byte av sin egen 4 096-bytes buffer. Hvert eksport-legeme er pakket inn i try med både catch (const std::exception &) og catch (...), så en ONNX Runtime-feil, en OpenCV-assertion eller en ugyldig ordbok blir status 0 pluss tekst, aldri et unntak som rømmer inn i Pascal-kode
| Eksport | Rolle | Når adapteren løser den |
|---|---|---|
HPDFRapidOCRAbiVersion | Returnerer 1; enhver annen verdi avvises | Først, før noe annet |
HPDFRapidOCRCreate | Laster deteksjons-, valgfrie klassifiserings- og gjenkjenningsmodeller og ordboken | I fabrikken |
HPDFRapidOCRRecognize | Kjører én bitmap og sender ett callback per tekstlinje | I fabrikken |
HPDFRapidOCRDestroy | Frigjør modellinstansen | I fabrikken |
HPDFRapidOCRSetReadingDirection | Valgfri høyre-til-venstre-rekkefølge, lagt til i v2.775.0 | Bare når RightToLeft er satt |
Den valgfrie eksporten løses dovent med vilje: en v2.774.0-DLL som mangler den, serverer fortsatt venstre-til-høyre-forespørsler. DLL-en lastes med LoadLibraryEx med søkeflagg som dekker DLL-ens egen mappe pluss standard sikre kataloger, så ONNX Runtime- eller OpenCV-avhengigheter plassert ved siden av HotPDFRapidOCR.dll finnes uten å røre PATH. Modell- og ordbokstier reiser som UTF-8 og DLL-en konverterer dem med MultiByteToWideChar i streng modus før den åpner filer gjennom wide-character API-er, så en modellkatalog under et kinesisk eller kyrillisk brukernavn virker i stedet for å bli utvidet byte for byte til vrøvl
Én regel bor i bygget i stedet for i headeren. DLL-en lenker ONNX Runtime og OpenCV statisk, og standard CMake-konfigurasjonen bruker den statiske release CRT-en (/MT). Statiske biblioteker kompilert mot /MD blandet inn i en /MT-DLL gir link-feil i beste fall og to uavhengige heaps i verste fall, så de foreslåtte bibliotekene må matche den CRT-moden DLL-en bruker
Hva skjer mellom en TBitmap og en tekstlinje?
HotPDF gir DLL-en et uavhengig top-down BGR-øyebliksbilde av den renderte siden, og DLL-en gir tilbake ett callback per gjenkjent tekstlinje med lånt UTF-8-tekst som adapteren må kopiere før den returnerer
På Delphi tilordner adapteren sidebitmapen til en privat TBitmap, tvinger pf24bit, og leser rader med GetDIBits ved bruk av en negativ biHeight, som gir top-down-rader polstret til fire-byte-justering; den strien sendes eksplisitt. På FPC leser den gjennom CreateIntfImage, for LCL scanline-skrivinger kan oppdatere det rå bildet uten å oppfriske GDI-håndtaket. Kallerens bitmap endres aldri, og pikselbudsjettet (MaxPixels, 16 777 216 som standard og konfigurerbart opptil 67 108 864) og grensen på 32 767 piksler per dimensjon sjekkes før øyebliksbilde-bufferen allokeres
Inne i DLL-en polstres øyebliksbildet med 50 hvite piksler, tekstregioner detekteres med en maksimal side på 1 024 piksler, bokser ordnes i horisontale rader, og hver beskjæring roteres valgfritt av vinkelklassifisereren før gjenkjenning. Hver tekstlinje går så gjennom et callback som mottar en const char*, et byteantall, en heltallsboks i originalbilde-piksler, og gjennomsnittlig tegnkonfidens. Tekstpekeren er gyldig bare under callback-en, så adapteren kopierer den umiddelbart, og den er streng med hva den aksepterer:
- UTF-8 dekodes med
MB_ERR_INVALID_CHARS; en misdannet sekvens feiler siden i stedet for å produsere erstatningstegn i et søkbart lag - C0- og C1-kontrolltegn avvises, og linjer med bare mellomrom hoppes over
- Boksen må ligge inne i bitmapen, og konfidensen må være en endelig verdi fra 0 til 1
- Tekst telles mot forespørselens
MaxTextCodeUnitsmed et hardt tak på 1 048 576 UTF-16-enheter per kall, og supplementary-plane-tegn koster to enheter - Ethvert Pascal-unntak inne i callback-en fanges der, lagres, og gjøres om til en 0-retur, som får DLL-en til å stoppe og rapportere feil; den lagrede meldingen blir da diagnostikken
To konsekvenser betyr noe for tuning. For det første er utgangsenheten en linje, ikke et ord: hver linje forbruker én MaxWords-plass, Info.AcceptedWordCount og Info.DroppedWordCount teller linjer, og søkehøydelys spenner over linjeboksen. For det andre sammenlignes MinimumConfidence (0,5 som standard) mot linjens gjennomsnittlige tegnkonfidens, så en linje med ett uleselig tegn blant tjue rene overlever som regel. DLL-en leverer ingen baseline, så tekstlag-pipelinen anslår én fra boksen. En tom side lykkes med null linjer, og enhver feil tømmer delresultater så multi-side-committen forblir alt-eller-ingenting
Modelleierskap og trådsikkerhet
Hver RapidOCR DLL-motor eier nøyaktig én modellinstans i hele sin levetid, og kall til Recognize på den motoren serialiseres av en critical section. Å holde IHPDFOCREngine-grensesnittet er det som holder modellene varme, så det riktige mønsteret for batch-arbeid er å opprette motoren én gang og gjenbruke den på tvers av dokumenter
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; // skann uten rotasjon: ingen klassifikator-modell lastes
Models.Threads := 4; // 1..64, taket på logisk prosessortall
Models.TimeoutMilliseconds := 120000; // per Recognize-kall, kooperativ
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; // siste referanse sluppet: modeller ødelagt, så DLL-en losses
Threads-verdien setter både intra-op- og inter-op-trådantallet til hver ONNX-økt, og DLL-en klemmer den til antallet aktive prosessorer. To tråder som deler én motor, kjører ikke parallelt; den andre venter på låsen. Den ventingen er ikke en blind EnterCriticalSection: adapteren kaller TryEnterCriticalSection hvert 25 ms og sjekker cancellation-token og fristen mellom forsøk, så en køet forespørsel kan fortsatt kanselleres eller få tidsavbrudd. Trenger du ekte parallellitet, opprett én motor per worker og aksepter at hver motor holder sin egen kopi av modellene i minnet
Nedrivningsrekkefølgen er fastsatt av motorens destruktor: HPDFRapidOCRDestroy frigjør modellinstansen først, så FreeLibrary lossar DLL-en. På nativesiden er modellinitialisering like nøye; når gjenkjenningsmodellen feiler etter at detektor- og klassifikatorøktene allerede var bygget, slippes de øktene før feilen rapporteres, og ordbokens klasseantall sjekkes mot modellutgangen under initialisering i stedet for på første side
Hvorfor kan ikke et native OCR-kall drepes midt i inferens?
Et native RapidOCR-kall kan ikke drepes midt i inferens fordi det kjører på tråden din, inne i prosessen din, midt i en ONNX Runtime-økt som ikke aksepterer avbrytelse. Kansellering i HotPDFs DLL-adapter er derfor kooperativ: DLL-en kaller en abort-callback før og etter deteksjon, etter klassifisering, og etter hver gjenkjent linje, og stopper ved det første sjekkpunktet der callback-en returnerer 0. En enkelt ONNX Run som har startet, fullfører først
Alternativene er verre enn å vente. TerminateThread ville etterlate CRT heap-låsen, ONNX Runtime sin tråd-pool og enhver OpenCV-tilstand i den tilstanden de tilfeldigvis var i, og forgifte resten av prosessen. FreeLibrary mens et kall fortsatt kjører, lossar kode som ligger på stakken. Ingen av delene kan gjøres trygge, så adapteren forsøker dem aldri. Fristen i TimeoutMilliseconds er følgelig en kooperativ frist, og en utløpt frist viser seg som en motorfeil med en timed out-diagnostikk, mens en kansellert token viser seg som otlsCancelled:
// Token opprettes av kalleren og deles med UI-tråden,
// som kaller Token.Cancel når brukeren trykker Stopp
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// returnert ved neste stage- eller linjegrense; dokument uendret
Writeln('Cancelled');
otlsEngineError:
// inkluderer et kooperativt fristutløp og native diagnostikk
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
Dette er kjerneavveiningen mellom HotPDFs prosessadaptere og in-process DLL-en, og ingen av sidene vinner på hver rad:
- Oppstartskostnad: Tesseract- og Python RapidOCR-adapterne starter en prosess og laster modeller for hver side; DLL-en laster modeller én gang per motor
- Å stoppe: en barne-prosess kan termineres outright, og Python-workeren kjører inne i en kill-on-close Job Object så hele prosesstreet følger med; DLL-en kan bare stoppe ved stage- og linjegrenser
- Feilinnkapsling: et krasj i
tesseract.exefeiler én side; en access violation inne i DLL-en tar prosessen din ned - Deployering: prosessadaptere trenger et installert program eller et Python-miljø; DLL-en trenger seg selv, sine modeller og sin ordbok, matchet til applikasjonens bitthet
- Minne: prosessadaptere slipper alt når barnet avslutter; en DLL-motor holder modellene sine residente til den siste grensesnittreferansen slippes
For en interaktiv desktop-applikasjon som OCR-er en side om gangen, vinner DLL-ens responsivitet som regel. For en server som tar imot utruelige skann døgnet rundt, er prosessgrensen verdt oppstartskostnaden sin
Bygge og deployere HotPDFRapidOCR.dll
HotPDFRapidOCR.dll bygges fra C++-kildene i Native/RapidOCR med MSVC, C++17, en Windows SDK og CMake 3.20 eller senere, ved bruk av et hjelperskript som tar de native nettverkskildene, ONNX Runtime- og OpenCV-katalogene pluss en Win32- eller Win64-plattform. Bygg begge hvis du skiper begge, for en 32-bits Delphi-applikasjon kan ikke laste en 64-bits DLL, og de statiske bibliotekene du foreslår, må matche målarkitekturen så vel som CRT-moden
Modellsiden har sine egne kompatibilitetsgrenser. Detektoren er en DB text-detektor; gjenkjenneren godtar CTC-modeller i NCHW-layout med en fast inngangshøyde på 32 eller 48, og bruker 48 for modeller med dynamisk høyde. Den medfølgende statiske ONNX Runtime-en kan ikke laste modeller lagret med en nyere IR-versjon, så ferske PP-OCRv5-eksporter feiler initialiseringen med en diagnostikk i stedet for å laste delvis. Ordboken må være UTF-8 uten BOM, i nøyaktig modellens tegnrekkefølge, og dens klasseantall må matche modellutgangen; CRLF-linjeslutt godtas. Gjenkjenning er offline: DLL-en laster aldri ned en manglende modell
Hurtigreferanse
- Fabrikk:
HPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options])iHPDFRapidOCRRecognition, tilgjengelig siden v2.774.0 i Delphi-, C++Builder- og Windows FPC/Lazarus-bygg - Hold den returnerte
IHPDFOCREnginei live på tvers av sider og dokumenter; å slippe den ødelegger modellene og lossar DLL-en - Én motor kjører én gjenkjenning om gangen; opprett flere motorer for parallelle workere og budsjetter minne for hver modellkopi
- Utgangen er én oppføring per tekstlinje med gjennomsnittlig tegnkonfidens, filtrert av
THPDFOCRTextLayerOptions.MinimumConfidence - Kansellering og
TimeoutMillisecondser kooperative; en pågående ONNX-kjøring fullføres alltid - Match DLL-bittheten til applikasjonen og CRT-moden til de statiske ONNX Runtime- og OpenCV-bibliotekene til DLL-en
- Velg en språkprofil per motor med
THPDFRapidOCRDLLOptions.ForLanguage(v2.775.0); én motor detekterer ikke språk på egen hånd
Den native RapidOCR-adapteren, de prosessbaserte OCR-adapterne, siderendereren som mater dem, og den usynlige Unicode-tekstlag-skriveren skiper alle sammen i HotPDF, en native VCL PDF-komponent for Delphi og C++Builder. Trenger applikasjonen din for dokumentfangst eller arkivering søkbar utgang uten et Python-miljø på målmaskinen, gir HotPDF Delphi PDF-komponenten hele pipelinen med bare DLL-en og dens modeller igjen å deployere