HotPDF púšťa Tesseract vo vnútri vášho Delphi procesu cez HPDFCreateTesseractDLLOCREngine, factory pridanú vo v2.772.0, ktorá dynamicky načíta DLL kompatibilné s Tesseractom 5, ovláda jeho C API (TessBaseAPIInit2, TessBaseAPIRecognize, result iterator) a vráti IHPDFOCREngine. THotPDF.ApplyLoadedOCRTextLayer použije ten engine na pridanie neviditeľnej, prehľadávateľnej Unicode textovej vrstvy na naskenované PDF strany
Ten istý recognizer bol už dostupný cez externý adaptér tesseract.exe, ktorý zapisuje BMP a parsuje TSV. Tá cesta funguje, ale každá strana platí za štart procesu, dočasný bitmapový súbor a textový formát bez baseline a bez kontroly nad segmentáciou strany. Volanie DLL odstráni všetky tri. Odstráni aj múr procesu, čo znamená, že Pascal binding sedí priamo na C štruktúrach, C booleovských hodnotách a C-alokovaných reťazcoch. Väčšina z toho, čo sa o tomto adaptéri oplatí vedieť, je miesto, kde taký binding môže potichu pokaziť
Ako pustíte Tesseract in-process z Delphi s HotPDF?
Pustenie Tesseractu in-process s HotPDF je jedno factory volanie v jednotke HPDFTesseractRecognition a to isté volanie ApplyLoadedOCRTextLayer, ktoré používa každý OCR engine HotPDF. Factory validuje záhorlivé. Súbor DLL aj adresár tessdata musia existovať, identifikátor jazyka smie obsahovať len ASCII písmená, číslice, _ a +, každý model v kombinácii ako chi_sim+eng musí mať zodpovedajúci súbor .traineddata a všetkých 21 povinných exportov sa musí vyriešiť skôr, než sa engine vráti. Chyby konfigurácie vyhodia EArgumentException; DLL, ktorý sa nepodarí načítať, vyhodí EOSError s Windows chybovým kódom a tipom skontrolovať architektúru a závislosti
uses
SysUtils, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Win64 aplikácia potrebuje 64-bitové DLL; závislostné DLL sedia vedľa
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
// Prázdny zoznam strán znamená každá strana; strany, ktoré už text majú, sa preskočia
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 nastaví PageSegMode na tpsAuto, EngineMode na temDefault, TimeoutMilliseconds na 60 000 a MaxPixels na 16 777 216. Pixelový rozpočet má väčší význam, než vyzerá. Strana US Letter pri predvolených 300 DPI sa renderuje na 2 550 × 3 300 pixelov, zhruba 8,4 milióna, čo sa zmestí. Tá istá strana pri 600 DPI je 5 100 × 6 600, zhruba 33,7 milióna, a adaptér ju odmietne skôr, než Tesseract uvidí pixel. Zvýšte MaxPixels (strop je 67 108 864) alebo nechajte DPI tam, kde je; každá strana je navyše kapovaná na 32 767 pixelov
DLL sa načítava cez LoadLibraryEx s vyhľadávacími príznakmi pre vlastný priečinok DLL plus predvolené bezpečné adresáre, takže obrazové knižnice, na ktorých Tesseract závisí, môžu bývať vedľa neho bez siahania na PATH alebo aktuálny adresár. HotPDF nebali ani nestahuje žiadny OCR runtime ani modely; oboje provisionsujete sami
Čo sa mení v porovnaní s adaptérom tesseract.exe?
Adaptér DLL mení izoláciu procesu za bohatší výstup a nižšiu réžiu na stranu. Oba adaptéry sa zapájajú do tej istej text-layer pipeline, takže mapovanie súradníc, filtrovanie podľa confidence aj all-or-nothing commit sú identické; líši sa to, ako pixely idú dnu a slová von
| Hľadisko | Adaptér tesseract.exe | Adaptér Tesseract DLL |
|---|---|---|
| Factory | HPDFCreateTesseractOCREngine | HPDFCreateTesseractDLLOCREngine |
| Pixely dnu | Súbor BMP v súkromnom dočasnom adresári | 8-bitový grayscale buffer v pamäti |
| Slová von | Word-level TSV, kapovaný na 64 MiB | Result iterator, UTF-8 na slovo |
| Baseliny | Nedostupné | Prepásnuté z TessPageIteratorBaseline |
| Segmentácia strany a režim enginu | Len automatická segmentácia | THPDFTesseractPageSegMode, THPDFTesseractEngineMode |
| Timeout | Tvrdý: child proces sa ukončí | Kooperatívny: Tesseract si to musí všimnúť |
| Izolácia pádov a pamäti | Samostatný proces | Žiadna, zdieľa váš adresný priestor |
Jedna cena nezmizne. Každé volanie Recognize vytvára vlastnú API inštanciu a volá TessBaseAPIInit2, takže jazykové modely sa inicializujú na stranu, nie raz na engine. Súborová cache operačného systému zmierni opätovné načítanie, ale na veľkých viacjazyčných sadách modelov je to aj tak dominantná fixná cena na stranu a počíta sa do termínu rozpoznávania. In-process engine RapidOCR DLL volí opačný dizajn a drží svoje ONNX modely rezidentné po celý život enginu; hraničné problémy (C ABI, požičané buffre, neprerušiteľná natívna práca) sú tá istá rodina
Prečo nemôže Delphi skopírovať štruktúru monitora Tesseractu?
Delphi nedokáže bezpečne zrkadliť progress monitor Tesseractu, lebo ETEXT_DESC obsahuje verziou podmienené interné polia, takže ručne okopírovaný záznam položí cancel callback aj termín na nesprávne ofsety na niektorých zostaveniach. Nič z toho nespadne hlasno. Tesseract jednoducho číta váš callback pointer z poľa, v ktorom teraz sedí niečo iné, alebo termín nikdy nevidí vôbec
HotPDF preto berie monitor ako opaque pointer a siaha naň len cez exportované funkcie: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs a TessMonitorDelete. Ak si C API bindujete sami na iný účel, platí ten istý vzor. Náčrt nižšie je váš vlastný binding kód, nie API HotPDF, a zrkadlí deklarácie, ktoré HotPDF používa interne
type
// C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
TTessMonitorCreate = function: Pointer; cdecl; // ETEXT_DESC*, nikdy nedereferencované
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
// Beží na stacku Tesseractu: čítajte príznaky a hodiny, nikdy nevyhadzujte
Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
(GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;
// Použitie, pričom ukazovatele funkcií sa preložia cez GetProcAddress:
// Monitor := MonitorCreate();
// try
// MonitorSetCancelThis(Monitor, @Job);
// MonitorSetCancelFunc(Monitor, ShouldCancel);
// MonitorSetDeadlineMSecs(Monitor, RemainingMs);
// RC := BaseAPIRecognize(API, Monitor);
// finally
// MonitorDelete(Monitor);
// end.
Dva detaily v tom náčrte sú zámerné. Callback vracia Boolean, čo je jeden bajt v Delphi aj Free Pascal, sediaci s C bool v TessCancelFunc. Štvorbajtové Windows BOOL alebo Delphi LongBool vyzerá zameniteľne a nie je: keď jedna strana zapíše jediný bajt a druhá číta štyri, horné bajty návratového registru sú čokoľvek, čo tam zostalo, a false sa dokáže dostať ako true. Tá istá hlavička veci komplikuje ďalej, lebo funkcie ako TessPageIteratorBoundingBox vracajú int, ktoré HotPDF deklaruje ako Integer. Čítajte C typ každej návratovej hodnoty namiesto predpokladu jednej konvencie pre celé API
Druhý detail je, že callback nikdy nevyhadzuje. Delphi výnimka rozvíjajúca sa cez C++ rámy Tesseractu je undefined behavior, takže callback HotPDF číta len cancellation token a monotónnu hodnotu GetTickCount64. Adaptér zmení výsledok na diagnostiku zrušenia alebo termínu po tom, čo sa TessBaseAPIRecognize vráti, a tú kontrolu vykoná bez ohľadu na natívny návratový kód
Ktoré natívne pointery vlastní strana Delphi?
Adaptér Tesseract DLL v HotPDF vlastní tri natívne objekty na požiadavku, inštanciu API, monitor aj result iterator a všetko ostatné si požičiava. Každé volanie Recognize vytvára vlastnú sadu a uvoľní ju v bloku finally: TessResultIteratorDelete, potom TessMonitorDelete, potom TessBaseAPIDelete. Uvoľnenie rozhrania enginu unloaduje knižnicu
TessResultIteratorGetPageIteratorvracia požičaný pohľad do result iteratoru, nie nový objekt. HotPDF ho používa preTessPageIteratorBoundingBoxaTessPageIteratorBaselinea nikdy ho neuvolňuje; samostatné zmazanie by uvoľnilo tú istú pamäť dvakrátTessResultIteratorGetUTF8Textvracia reťazec alokovaný vlastným runtime DLL. HotPDF ho skopíruje a podá späť cezTessDeleteTextv blokufinally; PascalFreeMemby ho uvoľnil na nesprávnom heap- Text slova sa dekóduje s prísnou UTF-8 validáciou a kontrolou dĺžky skôr, než sa prevedie. Slová s riadiacimi znakmi, deformovaným UTF-8, boxmi mimo obrazu, prevrátenými obdĺžnikmi alebo confidence mimo 0–100 prepasujú požiadavku namiesto tichej opravy
- Celý text na požiadavku je kapovaný na 1 048 576 UTF-16 code unitov a počet slov sa musí zmestiť do rozpočtu požiadavky odovzdaného z
ApplyLoadedOCRTextLayer
Confidence prichádza ako 0–100 a škáluje sa na 0–1, takže THPDFOCRTextLayerOptions.MinimumConfidence znamená to isté pre každý engine. Keď Tesseract hlási baseline, prepasú sa oba koncové body; inak text-layer pipeline prepadne na svoj geometrický odhad, presne ako pri TSV vstupe
Prečo validovať enum skôr, než sa dostane do DLL?
HotPDF kopíruje holý ordinál PageSegMode a EngineMode do Integer skôr, než kontroluje rozsah, lebo kompilátor môže predpokladať, že enum premenná vždy drží deklarovanú hodnotu, a zloží Ord(X) > Ord(High(T)) na konštantné false. Ordinály nie sú dekorácia: THPDFTesseractPageSegMode nasleduje číslovanie segmentácie strán Tesseractu od 0 do 13, THPDFTesseractEngineMode nasleduje číslovanie režimov enginu od 0 do 3 a obe idú do DLL ako holé celé čísla. Options záznam postavený cez FillChar, naplnený zo streamu alebo podaný z C++Builder s pretypovaným celým číslom dokáže niesť bajt ako 200. Validácia skopírovaného ordinálu zmení to na EArgumentException vo chvíli factory namiesto nedefinovaného režimu vo vnútri natívneho kódu. Factory navyše odmieta tpsOSDOnly a tpsAutoOnly, ktoré nevyrobia žiadne slová, a vyžaduje osd.traineddata pre tpsAutoOSD a tpsSparseTextOSD
Čo termín rozpoznávania naozaj garantuje?
Termín Tesseract DLL je kooperatívny: HotPDF dokáže zastaviť vlastnú prácu a poprosiť Tesseract, aby zastavil, ale nedokáže prinútiť natívny kód sa vrátiť. Hodiny bežia od začiatku Recognize, takže konverzia bitmapy aj inicializácia modelov konzumujú ten istý rozpočet ako rozpoznávanie. HotPDF kontroluje uplynulý čas a cancellation token počas grayscale konverzie aj medzi slovami pri iterovaní výsledkov a podá zostávajúce milisekundy do TessMonitorSetDeadlineMSecs skôr, než zavolá TessBaseAPIRecognize
Medzera je vo vnútri natívneho volania. Monitor Tesseractu sa konzultuje počas rozpoznávania slov, nie počas TessBaseAPIInit2 ani analýzy rozloženia strany, takže pomalé načítanie modelu alebo patologické rozloženie dokáže bežať za termínom skôr, než sa timeout nahlási. Pixelové aj výstupné rozpočty tiež nekapujú vlastnú spotrebu pamäte natívnej knižnice. Ak potrebujete workera, ktorého dokážete zabiť, použite procesový adaptér; to je poctivý kompromis, nie chýbajúca funkcia
Segmentácia strany je miesto, kde adaptér DLL zarobí na ťažkom vstupe. Formuláre, nálepky a naskenované tabuľky s roztrúsenými poľami sa často rozpoznajú lepšie s tpsSparseText než s automatickou segmentáciou, ktorá sa snaží poskladať stĺpce a odstavce, ktoré tam nie sú
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; // roztrúsené polia, žiadne poskladanie stĺpcov
TessOptions.EngineMode := temLSTMOnly; // potrebuje LSTM modely v tessdata
TessOptions.TimeoutMilliseconds := 20000; // vrátane inicializácie 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;
Termín sa ukáže ako otlsEngineError s diagnostikou Tesseract DLL OCR timed out, zatiaľ čo zrušený token sa ukáže ako otlsCancelled. V oboch prípadoch ApplyLoadedOCRTextLayer rozpoznal každú vybranú stranu skôr, než začne transakciu commitu, takže zlyhanie na strane 40 z 50 nechá načítaný dokument presne taký, aký bol. Všimnite si, že tpsSingleLine, tpsSingleBlock a tpsSparseText menia len segmentáciu; ani jeden nenarovná naklonený sken
Free Pascal a Lazarus: zastaralé pixely a stratená čínština
Obe Tesseract factory fungujú vo Windows Free Pascal a Lazarus zostaveniach Win32 aj Win64 od v2.772.1, po dvoch opravách špecifických pre FPC. Najprv zostavte Lazarus balík pre cieľovú architektúru; všeobecný port pokrýva HotPDF na Free Pascal a Lazarus Win64
Prvá oprava sa týka pixelov. LCL TBitmap zapisovaný cez scanlines dokáže aktualizovať holý obraz bez obnovenia Windows bitmap handle, takže GetDIBits na tom handle vráti staré pixely. Príznak bol mätúci: text nakreslený priamo na bitmapu sa rozpoznal, zatiaľ čo strana vyrenderovaná PDF rendererom HotPDF vyprodukovať prázdny zoznam slov. Vo FPC teraz adaptér číta formát-aware snapshot cez CreateIntfImage, ktorý rešpektuje pixelový formát a poradie riadkov holého obrazu. Zostavenie Delphi drží cestu GetDIBits na súkromnej 24-bitovej kópii. Ani jedno zostavenie nemodifikuje bitmapu volajúceho
Druhá oprava patrí adaptéru tesseract.exe. TStringList vo FPC ukladá ANSI reťazce, takže priradenie dekódovaného UTF-8 TSV textu do Lines.Text potichu zahodilo každý čínsky alebo doplnkovorojový znak, ktorý systémová ANSI kódová strana nedokázala zastúpiť. Cesta FPC teraz drží TSV ako UTF-8 bajty, odstrihne BOM na úrovni bajtov a dekóduje každé slovo do UnicodeString osobitne. Adaptér DLL tento problém nikdy nemal, lebo dekóduje každé slovo priamo z iteratoru
Rýchla referencia
- Factory:
HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options])vHPDFTesseractRecognition, pridaná vo v2.772.0, podpora FPC vo v2.772.1 - Predvolené:
tpsAuto,temDefault, 60 000 ms, 16 777 216 pixelov; rozsah termínu 1–3 600 000 ms, pixelový strop 67 108 864 - Slaďte bitness DLL s aplikáciou a položte závislostné DLL vedľa DLL Tesseractu
- Berte monitor ako opaque; nikdy nekopírujte
ETEXT_DESCdo Pascal záznamu - Deklarujte cancel callback
cdecls jednobajtovým výsledkomBooleana nikdy nenechajte cezeň utiecť výnimku - Uvoľňujte text iteratora cez
TessDeleteText; nikdy neuvolňujte page iterator získaný z result iteratoru - Očakávajte, že termín je kooperatívny: inicializácia modelov aj analýza rozloženia ho dokážu prebehnúť
- Používajte adaptér tesseract.exe, keď potrebujete tvrdé ukončenie alebo izoláciu pádov
Adaptér Tesseract DLL, procesové adaptéry aj vstavaný OCR engine dodáva HotPDF Delphi PDF component pre Delphi, C++Builder a Free Pascal; pozrite produktovú stránku HotPDF pre edície a stiahnutia