HotPDF paleidžia Tesseract jūsų Delphi proceso viduje per HPDFCreateTesseractDLLOCREngine – faktoriją, pridėtą v2.772.0, kuri dinamiškai įkelia Tesseract 5 suderinamą DLL, valdo jos C API (TessBaseAPIInit2, TessBaseAPIRecognize, rezultatų iteratorių) ir grąžina IHPDFOCREngine. THotPDF.ApplyLoadedOCRTextLayer tą variklį naudoja, kad pridėtų nematomą, ieškomą Unicode teksto sluoksnį nuskenuotiems PDF puslapiams
Tas pats atpažintojas jau buvo pasiekiamas per išorinį tesseract.exe adapterį, rašantį BMP ir skaitantį TSV. Tas kelias veikia, bet kiekvienas puslapis moka už proceso paleidimą, laikiną bitmapo failą ir teksto formatą be bazinių linijų ir be kontrolės puslapių segmentavimui. DLL kvietimas pašalina visus tris. Kartu pašalina ir proceso sieną – vadinasi, Pascal jungtis sėdi tiesiai ant C struktūrų, C booleanų ir C priskirtų eilučių. Didžioji dalis to, ką verta žinoti apie šį adapterį, yra ten, kur ta jungtis gali tylioje suklysti
Kaip paleisti Tesseract procese iš Delphi su HotPDF?
Tesseract paleidimas procese su HotPDF užima vieną faktorijos kvietimą HPDFTesseractRecognition unito viduje ir tą patį ApplyLoadedOCRTextLayer kvietimą, kurį naudoja kiekvienas HotPDF OCR variklis. Faktorija tikrina anksti. DLL failas ir tessdata katalogas privalo egzistuoti, kalbos identifikatorius gali turėti tik ASCII raides, skaitmenis, _ ir +, kiekvienas modelis tokioje kombinacijoje kaip chi_sim+eng privalo turėti atitinkantį .traineddata failą, o visi 21 reikalingi eksportai privalo išspręstis, kol variklis grąžinamas. Konfigūracijos klaidos kelia EArgumentException; neįsikelianti DLL kelia EOSError su Windows klaidos kodu ir patarimu patikrinti architektūrą bei priklausomybes
uses
SysUtils, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Win64 programai reikia 64 bitų DLL; priklausomybių DLL keliauja šalia
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
// Tuščias puslapių sąrašas reiškia visus puslapius; puslapiai, turintys tekstą, praleidžiami
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 nustato PageSegMode į tpsAuto, EngineMode į temDefault, TimeoutMilliseconds į 60 000 ir MaxPixels į 16 777 216. Pikselių biudžetas svarbesnis, nei atrodo. US Letter puslapis ties numatytąja 300 DPI atvaizduojasi į 2 550 × 3 300 pikselių, apie 8,4 milijono – telpa. Tas pats puslapis ties 600 DPI yra 5 100 × 6 600, apie 33,7 milijono, ir adapteris jį atmeta dar prieš Tesseract pamatant pikselį. Kelkite MaxPixels (lubos – 67 108 864) arba palikite DPI kur yra; kiekviena kraštinė dar ribojama 32 767 pikseliais
DLL įkeliama su LoadLibraryEx, naudojant paieškos vėliavas pačiam DLL aplankui plius numatytosioms saugioms direktorijoms, tad paveikslėlių bibliotekos, nuo kurių priklauso Tesseract, gali gyventi šalia neliečiant PATH ar dabartinio katalogo. HotPDF nepūkuoja ir neparsiunčia jokio OCR runtime ar modelio; abu parūpinate patys
Kas keičiasi lyginant su tesseract.exe adapteriu?
DLL adapteris maino procesų izoliaciją į tursesnį išvedimą ir mažesnes puslapiui sąnaudas. Abu adapteriai įsijungia į tą patį teksto sluoksnio srautą, tad koordinačių susiejimas, pasitikėjimo filtravimas ir viskas-arba-nieko įrašymas identiški; skiriasi tai, kaip pikseliai įeina ir žodžiai išeina
| Aspektas | tesseract.exe adapteris | Tesseract DLL adapteris |
|---|---|---|
| Faktorija | HPDFCreateTesseractOCREngine | HPDFCreateTesseractDLLOCREngine |
| Pikseliai į | BMP failas privačiame laikinajame kataloge | 8 bitų pilkio skalės buferis atmintyje |
| Žodžiai iš | Žodžio lygio TSV, ribojama iki 64 MiB | Rezultatų iteratorius, UTF-8 žodžiui |
| Bazinės linijos | Neprieinamos | Perduodamos iš TessPageIteratorBaseline |
| Puslapių segmentavimas ir variklio režimas | Tik automatinis segmentavimas | THPDFTesseractPageSegMode, THPDFTesseractEngineMode |
| Terminas | Kietas: antrinis procesas nutraukiamas | Kooperatyvinis: Tesseract privalo pastebėti |
| Griūties ir atminties izoliacija | Atskiras procesas | Nėra, dalijasi jūsų adresų erdve |
Viena kaina nedingsta. Kiekvienas Recognize kvietimas sukuria savą API instanciją ir kviečia TessBaseAPIInit2, tad kalbos modeliai inicializuojami puslapiui, o ne kartą varikliui. Operacinės sistemos failų podėlis suminkština pakartotinį įkėlimą, bet didelėse daugiakalbėse modelių aibėse tai vis tiek dominuojanti fiksuota kaina puslapiui, ir ji skaitosi į atpažinimo terminą. RapidOCR DLL variklis procese pasirenka priešingą dizainą ir laiko savus ONNX modelius rezidentuotus visą variklio gyvenimą; ribos problemos (C ABI, pasiskolinti buferiai, nenutraukiamas natyvus darbas) – tos pačios šeimos
Kodėl Delphi negali nukopijuoti Tesseract monitoriaus struktūros?
Delphi negali saugiai atkartoti Tesseract pažangos monitoriaus, nes ETEXT_DESC turi nuo versijos priklausančių vidinių laukų, tad rankomis nukopijuotas įrašas atšaukimo callbacką ir terminą padeda netinkamuose poslinkiuose kai kuriuose dariniuose. Niekas negarsiai nesugriūva, kai tai atsitinka. Tesseract tiesiog skaito jūsų callbacko rodyklę iš lauko, kuriame dabar slypi kažkas kito, arba termino iš viso nemato
Todėl HotPDF monitorių traktuoja kaip nepermatomą rodyklę ir liečia tik per eksportuojamas funkcijas: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs ir TessMonitorDelete. Jeigu C API susiejate patys kitam tikslui, tas pats raštas galioja. Eskizas žemiau – jūsų pačių jungties kodas, ne HotPDF API, ir atkartoja deklaracijas, kurias HotPDF naudoja viduje
type
// C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
TTessMonitorCreate = function: Pointer; cdecl; // ETEXT_DESC*, niekada nedereferencuojama
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
// Sukasi Tesseract steku: skaitykite vėliavas ir laikrodį, niekada nekelkite išimties
Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
(GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;
// Naudojimas, su funkcijų rodyklėmis, išspręstomis per GetProcAddress:
// Monitor := MonitorCreate();
// try
// MonitorSetCancelThis(Monitor, @Job);
// MonitorSetCancelFunc(Monitor, ShouldCancel);
// MonitorSetDeadlineMSecs(Monitor, RemainingMs);
// RC := BaseAPIRecognize(API, Monitor);
// finally
// MonitorDelete(Monitor);
// end;
Du to eskizo detalės tyčiniai. Callbackas grąžina Boolean, kuris Delphi ir Free Pascal yra vienas baitas – sutampa su C bool TessCancelFunc viduje. Keturių baitų Windows BOOL arba Delphi LongBool atrodo sukeičiami ir nėra: kai viena pusė rašo vieną baitą, o kita skaito keturis, grąžinamojo registro aukštieji baitai yra tai, kas ten liko, ir false gali atkelti kaip true. Tas pats antraštės failas dar painioja, nes tokios funkcijos kaip TessPageIteratorBoundingBox grąžina int, kurį HotPDF deklaruoja kaip Integer. Skaitykite kiekvienos grąžinamos reikšmės C tipą, vietoj to, kad visai API priskirtumėte vieną konvenciją
Antra detalė – callbackas niekada nekelia išimties. Delphi išimtis, išsivyniojanti pro Tesseract C++ kadrus, yra neapibrėžta elgsena, tad HotPDF callbackas skaito tik atšaukimo tokeną ir monotonišką GetTickCount64 reikšmę. Adapteris rezultatą paverčia atšaukimo arba termino diagnostika, kai grįžta TessBaseAPIRecognize, ir atlieka tą patikrą nepriklausomai nuo natyvios grąžos kodo
Kurias natyvias rodykles valdo Delphi pusė?
HotPDF Tesseract DLL adapteris valdo tris natyvius objektus užklausai – API instanciją, monitorių ir rezultatų iteratorių – ir viską kitą pasiskolina. Kiekvienas Recognize kvietimas sukuria savą rinkinį ir jį atlaisvina finally bloke: TessResultIteratorDelete, tada TessMonitorDelete, tada TessBaseAPIDelete. Variklio sąsajos atleidimas iškrauna biblioteką
TessResultIteratorGetPageIteratorgrąžina pasiskolintą vaizdą į rezultatų iteratorių, o ne naują objektą. HotPDF jį naudojaTessPageIteratorBoundingBoxirTessPageIteratorBaselineir niekada neatlaisvina; atskirai jį ištrinus ta pati atmintis būtų atlaisvinta dukartTessResultIteratorGetUTF8Textgrąžina eilutę, priskirtą pačios DLL runtime. HotPDF ją nukopijuoja ir grąžina perTessDeleteTextfinallybloke; PascalFreeMematlaisvintų ją netinkamoje kravoje- Žodžių tekstas dekoduojamas su griežta UTF-8 patikra ir ilgio tikrinimu prieš konversiją. Žodžiai su valdymo simboliais, sugadintu UTF-8, langeliais už atvaizdo ribų, apverčiais stačiakampiais arba pasitikėjimu už 0–100 ribų žlugdo užklausą vietoj tyliojo lopymo
- Iš viso teksto užklausai ribojama iki 1 048 576 UTF-16 kodo vienetų, o žodžių skaičius privalo tilpti į
ApplyLoadedOCRTextLayeratiduotą užklausos biudžetą
Pasitikėjimas atkeliauja kaip 0–100 ir masteluoja į 0–1, tad THPDFOCRTextLayerOptions.MinimumConfidence kiekvienam varikliui reiškia tą patį. Kai Tesseract praneša bazinę liniją, abu galai perduodami; kitaip teksto sluoksnio srautas grįžta prie savo geometrinio įvertinimo – lygiai kaip daro TSV įvedimui
Kodėl patikrinti enum dar prieš jį pasiekiant DLL?
HotPDF žaliąjį PageSegMode ir EngineMode ordinalą nukopijuoja į Integer prieš intervalo patikrą, nes kompiliatorius gali manyti, kad enum kintamasis visada laiko deklaruotą reikšmę, ir sutraukti Ord(X) > Ord(High(T)) į konstantą false. Ordinalai ne puošmena: THPDFTesseractPageSegMode seka Tesseract puslapių segmentavimo numeraciją nuo 0 iki 13, THPDFTesseractEngineMode – variklio režimo numeraciją nuo 0 iki 3, ir abu keliauja į DLL kaip paprasti sveikieji. Nustatymų įrašas, sudėtas su FillChar, užpildytas iš srauto arba perduotas iš C++Builder su cast sveikuoju, gali nešti baitą, tokį kaip 200. Nukopijuoto ordinalo patvirtinimas tą paverčia EArgumentException faktorijos metu, o ne neapibrėžtu režimu natyviame kode. Faktorija dar atmeta tpsOSDOnly ir tpsAutoOnly, kurie neduoda jokių žodžių, ir reikalauja osd.traineddata tpsAutoOSD bei tpsSparseTextOSD atvejais
Ką atpažinimo terminas iš tikrųjų garantuoja?
Tesseract DLL terminas kooperatyvinis: HotPDF gali sustabdyti savą darbą ir paprašyti Tesseract sustoti, bet negali priversti natyvaus kodo grįžti. Laikrodis paleidžiamas, kai prasideda Recognize, tad bitmapo konversija ir modelių inicializacija vartoja tą patį biudžetą kaip atpažinimas. HotPDF tikrina praėjusį laiką ir atšaukimo tokeną pilkio skalės konversijos metu ir tarp žodžių, iteruojant rezultatus, o likusias milisekundes perduoda TessMonitorSetDeadlineMSecs prieš kviesdamas TessBaseAPIRecognize
Tarpas slypi natyviojo kvietimo viduje. Tesseract monitorius konsultuojamas žodžių atpažinimo metu, o ne TessBaseAPIInit2 ar puslapio išdėstymo analizės metu, tad lėtas modelio įkėlimas arba patologinis išdėstymas gali nubėgti pro terminą dar prieš pranešant termino pabaigą. Pikselių ir išvesties biudžetai taip pat neriboja pačios natyvios bibliotekos atminties vartojimo. Jeigu reikia darbuotojo, kurį galite nudurti, naudokite proceso adapterį; tai sąžiningas kompromisas, ne dingusi funkcija
Puslapių segmentavimas – vieta, kur DLL adapteris užsidirba savo duoną sunkiame įvedime. Formos, etiketės ir nuskenuotos lentelės su išmėtytais laukais dažnai atpažįstamos geriau su tpsSparseText nei su automatiniu segmentavimu, kuris bando surinkti stulpelius ir pastraipas, ten nesantis
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; // išmėtyti laukai, be stulpelių surinkimo
TessOptions.EngineMode := temLSTMOnly; // reikalauja LSTM modelių tessdata viduje
TessOptions.TimeoutMilliseconds := 20000; // apima modelių inicializaciją
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;
Terminas iškyla kaip otlsEngineError su diagnostika Tesseract DLL OCR timed out, o atšauktas tokenas – kaip otlsCancelled. Abiem atvejais ApplyLoadedOCRTextLayer yra atpažinęs kiekvieną pažymėtą puslapį, kol pradeda įrašymo transakciją, tad nesėkmė 40-ame puslapyje iš 50 palieka įkeltą dokumentą lygiai tokį, koks buvo. Pastebėkite, kad tpsSingleLine, tpsSingleBlock ir tpsSparseText keičia tik segmentavimą; nė vienas nenulenkina pakrypusios skenuotės
Free Pascal ir Lazarus: pasenę pikseliai ir dingusi kinų
Abi Tesseract faktorijos veikia Windows Free Pascal ir Lazarus Win32 bei Win64 dariniuose nuo v2.772.1, po dviejų FPC specifinių pataisų. Pirmiausia perstatykite Lazarus paketą tikslinei architektūrai; bendrasis pernešimas aprašytas HotPDF ant Free Pascal ir Lazarus Win64
Pirmoji pataisa liečia pikselius. LCL TBitmap, rašytas per scanline, gali atnaujinti žalią atvaizdą neatnaujinęs Windows bitmapo rankenos, tad GetDIBits ant tos rankenos grąžina senus pikselius. Simptomas buvo klaikus: tekstas, pieštas tiesiai ant bitmapo, atpažįstamas, o HotPDF PDF atvaizduoklio atvaizduotas puslapis duodavo tuščią žodžių sąrašą. FPC adapteris dabar skaito formatą žinantį snapshotą per CreateIntfImage, kuris gerbia žaliojo atvaizdo pikselių formatą ir eilučių tvarką. Delphi darinys GetDIBits kelią palieka ant privačios 24 bitų kopijos. Nė vienas darinys nekeičia kvietėjo bitmapo
Antroji pataisa priklauso tesseract.exe adapteriui. FPC TStringList saugo ANSI eilutes, tad dekoduoto UTF-8 TSV teksto priskyrimas Lines.Text tylioje numetdavo kiekvieną kinų ar papildomos plokštumos simbolį, kurio sistemos ANSI kodo puslapis negalėjo atvaizduoti. FPC kelias dabar laiko TSV kaip UTF-8 baitus, nuplauna BOM baitų lygyje ir kiekvieną žodį dekoduoja į UnicodeString atskirai. DLL adapteris šios problemos niekada neturėjo, nes kiekvieną žodį dekoduoja tiesiai iš iteratoriaus
Trumpa atmintinė
- Faktorija:
HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options])HPDFTesseractRecognitionunito viduje, pridėta v2.772.0, FPC palaikymas v2.772.1 - Numatytosios:
tpsAuto,temDefault, 60 000 ms, 16 777 216 pikselių; termino intervalas 1–3 600 000 ms, pikselių lubos 67 108 864 - Suderinkite DLL bitiškumą su programa ir padėkite priklausomybių DLL šalia Tesseract DLL
- Monitorių traktuokite kaip nepermatomą; niekada nekopijuokite
ETEXT_DESCį Pascal įrašą - Atšaukimo callbacką deklaruokite
cdeclsu vieno baitoBooleanrezultatu ir niekada neleiskite išimčiai iš jo pabėgti - Iteratoriaus tekstą atlaisvinkite su
TessDeleteText; niekada neatlaisvinkite puslapių iteratoriaus, gauto iš rezultatų iteratoriaus - Tikėkitės kooperatyvinio termino: modelių inicializacija ir išdėstymo analizė gali jį peržengti
- Naudokite tesseract.exe adapterį, kai reikia kieto nutraukimo ar griūties izoliacijos
Tesseract DLL adapteris, proceso adapteriai ir vidinis OCR variklis visi keliauja su HotPDF Delphi PDF komponentu, skirto Delphi, C++Builder ir Free Pascal; leidimams ir parsisiuntimams žiūrėkite HotPDF produkto puslapį