Tehnički članak

HotPDF Tesseract DLL OCR: C API iz Delphi-ja

HotPDF pokreće Tesseract unutar vašeg Delphi procesa kroz HPDFCreateTesseractDLLOCREngine, fabriku dodatu u v2.772.0 koja dinamički učitava DLL kompatibilan sa Tesseract 5, vozi njegov C API (TessBaseAPIInit2, TessBaseAPIRecognize, iterator rezultata) i vraća IHPDFOCREngine. THotPDF.ApplyLoadedOCRTextLayer koristi taj engine da doda nevidljivi, pretraživi Unicode tekstualni sloj skeniranim PDF stranicama

Isti prepoznavač već je bio dostižan kroz eksterni tesseract.exe adapter koji piše BMP i parsira TSV. Ta putanja radi, ali svaka stranica plaća pokretanje procesa, privremeni bitmap fajl i tekstualni format bez baznih linija i bez kontrole nad segmentacijom stranica. Pozivanje DLL-a uklanja sve tri. Uklanja i procesni zid, što znači da Pascal vezivanje sedi direktno na C strukturama, C boolean-ima i C-alociranim stringovima. Većina onoga što vredi znati o ovom adapteru je gde to vezivanje može tiho poći naopako

Kako pokrenuti Tesseract in-process iz Delphi-ja sa HotPDF-om?

Pokretanje Tesseract-a in-process sa HotPDF-om traži jedan poziv fabrike u HPDFTesseractRecognition jedinici i isti poziv ApplyLoadedOCRTextLayer koji svaki HotPDF OCR engine koristi. Fabrika validira rano. DLL fajl i tessdata direktorijum moraju postojati, jezički identifikator sme sadržati samo ASCII slova, cifre, _ i +, svaki model u kombinaciji poput chi_sim+eng mora imati odgovarajući .traineddata fajl, i svih 21 potrebnih izvoza mora se razrešiti pre nego što se engine vrati. Greške konfiguracije podižu EArgumentException; DLL koji ne uspe da se učita podiže EOSError sa Windows kodom greške i nagoveštajem da proverite arhitekturu i zavisnosti

uses
  SysUtils, HPDFDoc, HPDFTesseractRecognition;

procedure MakeSearchable(const SourceFile, TargetFile: string);
var
  Doc: THotPDF;
  Engine: IHPDFOCREngine;
  Options: THPDFOCRTextLayerOptions;
  Info: THPDFOCRTextLayerInfo;
begin
  // Win64 aplikacija traži 64-bitni DLL; DLL-ovi zavisnosti idu pored njega
  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
    // Prazna lista stranica znači svaku stranicu; stranice koje već imaju tekst preskaču se
    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 postavlja PageSegMode na tpsAuto, EngineMode na temDefault, TimeoutMilliseconds na 60.000 i MaxPixels na 16.777.216. Piksel budžet je važniji nego što izgleda. US Letter stranica na podrazumevanih 300 DPI renderuje se u 2.550 × 3.300 piksela, oko 8,4 miliona, što staje. Ista stranica na 600 DPI je 5.100 × 6.600, oko 33,7 miliona, i adapter je odbija pre nego što Tesseract vidi piksel. Podignite MaxPixels (plafon je 67.108.864) ili ostavite DPI gde jeste; svaka strana takođe je ograničena na 32.767 piksela

DLL se učitava sa LoadLibraryEx koristeći zastavice pretrage za sopstveni folder DLL-a plus podrazumevane bezbedne direktorijume, pa slikovne biblioteke kojima Tesseract zavisi mogu živeti pored njega bez diranja PATH ili trenutnog direktorijuma. HotPDF ne zbija ni ne preuzima nikakav OCR runtime ni model; vi obezbeđujete oba

Šta se menja u poređenju sa tesseract.exe adapterom?

DLL adapter trguje izolacijom procesa za bogatiji izlaz i manji trošak po stranici. Oba adaptera priključuju se na isti cevovod tekstualnog sloja, pa su mapiranje koordinata, filtriranje poverenja i uveravanje sve-ili-ništa identični; razlikuje se kako pikseli ulaze i reči izlaze

Aspekttesseract.exe adapterTesseract DLL adapter
FabrikaHPDFCreateTesseractOCREngineHPDFCreateTesseractDLLOCREngine
Pikseli ulazeBMP fajl u privatnom privremenom direktorijumu8-bitni grayscale bafer u memoriji
Reči izlazeTSV nivoa reči, ograničen na 64 MiBIterator rezultata, UTF-8 po reči
Bazne linijeNedostupneProsleđene iz TessPageIteratorBaseline
Segmentacija stranica i režim engine-aSamo automatska segmentacijaTHPDFTesseractPageSegMode, THPDFTesseractEngineMode
RokTvrdo: podproces se prekidaKooperativno: Tesseract mora primetiti
Izolacija pada i memorijeOdvojen procesNema, deli vaš adresni prostor

Jedna cena ne nestaje. Svaki poziv Recognize stvara sopstvenu API instancu i zove TessBaseAPIInit2, pa se jezički modeli inicijalizuju po stranici, a ne jednom po engine-u. Fajl keš operativnog sistema omekšava ponovno učitavanje, ali na velikim višejezičnim skupovima modela i dalje je dominantan fiksni trošak po stranici, i računa se u rok prepoznavanja. In-process RapidOCR DLL engine uzima suprotan dizajn i drži svoje ONNX modele rezidentne za vek engine-a; problemi granice (C ABI, pozajmljeni baferi, neprekidiva nativna dela) ista su porodica

Zašto Delphi ne može kopirati Tesseract monitor strukturu?

Delphi ne može bezbedno ogledati Tesseract monitor napretka jer ETEXT_DESC sadrži verziji-zavisna interna polja, pa ručno kopiran zapis stavlja cancel callback i rok na pogrešne ofsete na nekim verzijama. Ništa ne pada glasno kad se to desi. Tesseract jednostavno čita vaš callback pokazivač iz polja koje sada drži nešto drugo, ili nikada ne vidi rok

HotPDF zato tretira monitor kao neproziran pokazivač i dodiruje ga samo kroz izvezene funkcije: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs i TessMonitorDelete. Ako sami vezujete C API za drugu svrhu, isti obrazac važi. Skica ispod je vaš sopstveni kod vezivanja, ne HotPDF API, i ogleda deklaracije koje HotPDF interno koristi

HotPDF Tesseract DLL rukovanje monitorom: kopiranje verziji-zavisnog ETEXT_DESC zapisa stavlja cancel callback i rok na pogrešne ofsete i pada tiho, dok HotPDF tretira monitor kao neproziran, vozi TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc i TessMonitorSetDeadlineMSecs, i drži cdecl callback bez izuzetaka
neproziran pokazivač plus pet izvoza je ceo ugovor; callback ostaje Boolean od jednog bajta koji čita samo zastavicu i sat
type
  // C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
  TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
  TTessMonitorCreate = function: Pointer; cdecl;   // ETEXT_DESC*, nikada ne dereferenciran
  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
  // Radi na Tesseract steku: čitaj zastavice i sat, nikada ne podiži
  Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
    (GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;

// Upotreba, sa pokazivačima funkcija razrešenim kroz GetProcAddress:
//   Monitor := MonitorCreate();
//   try
//     MonitorSetCancelThis(Monitor, @Job);
//     MonitorSetCancelFunc(Monitor, ShouldCancel);
//     MonitorSetDeadlineMSecs(Monitor, RemainingMs);
//     RC := BaseAPIRecognize(API, Monitor);
//   finally
//     MonitorDelete(Monitor);
//   end;

Dva detalja u toj skici namerni su. Callback vraća Boolean, što je jedan bajt i u Delphi-ju i u Free Pascalu, poklapajući se sa C bool u TessCancelFunc. Četvorobajtni Windows BOOL ili Delphi LongBool izgleda zamenljivo i nije: kad jedna strana piše jedan bajt a druga čita četiri, gornji bajtovi povratnog registra su ono što je tamo ostalo, i false može stići kao true. Isti header dalje komplikuje stvari, jer funkcije poput TessPageIteratorBoundingBox vraćaju int, koji HotPDF deklariše kao Integer. Čitajte C tip svake povratne vrednosti umesto da pretpostavite jednu konvenciju za ceo API

Drugi detalj je da callback nikada ne podiže. Delphi izuzetak koji se odmotava kroz Tesseract C++ okvire nedefinisano je ponašanje, pa HotPDF callback čita samo token otkazivanja i monotonsku GetTickCount64 vrednost. Adapter pretvara rezultat u dijagnostiku otkazivanja ili roka posle što se TessBaseAPIRecognize vrati, i izvodi tu proveru bez obzira na nativni povratni kod

Koje nativne pokazivače Delphi strana poseduje?

HotPDF Tesseract DLL adapter poseduje tri nativna objekta po zahtevu, API instancu, monitor i iterator rezultata, a pozajmljuje sve ostalo. Svaki poziv Recognize stvara svoj skup i otpušta ga u finally bloku: TessResultIteratorDelete, pa TessMonitorDelete, pa TessBaseAPIDelete. Otpuštanje interfejsa engine-a istovara biblioteku

HotPDF Tesseract DLL vlasništvo objekata po Recognize pozivu: iterator rezultata, monitor i API instanca poseduju se i oslobađaju tim redom unutar finally, iterator stranica iz TessResultIteratorGetPageIterator pozajmljeni je pogled koji nikada ne sme biti oslobođen, i GetUTF8Text stringovi kopiraju se i vraćaju kroz TessDeleteText
tri objekta u vlasništvu, sve ostalo pozajmljeno: oslobađajte fiksnim redom, nikada ne oslobađajte dva puta iterator stranica, i nikada ne mešajte alokatore
  • TessResultIteratorGetPageIterator vraća pozajmljen pogled u iterator rezultata, ne novi objekat. HotPDF ga koristi za TessPageIteratorBoundingBox i TessPageIteratorBaseline i nikada ga ne oslobađa; brisanje odvojeno oslobodilo bi istu memoriju dvaput
  • TessResultIteratorGetUTF8Text vraća string alociran sopstvenim runtime-om DLL-a. HotPDF ga kopira i vraća kroz TessDeleteText u finally bloku; Pascal FreeMem oslobodio bi ga na pogrešnom hipu
  • Tekst reči dekodira se sa strogom UTF-8 validacijom i proverom dužine pre konverzije. Reči sa kontrolnim karakterima, deformisanim UTF-8, kutijama van slike, obrnutim pravougaonicima ili poverenjem van 0–100 padaju zahtev umesto da se tiho zakrpe
  • Ukupan tekst po zahtevu ograničen je na 1.048.576 UTF-16 code jedinica, i broj reči mora stati u budžet zahteva predat s naniže od ApplyLoadedOCRTextLayer

Poverenje stiže kao 0–100 i skalira se na 0–1, pa THPDFOCRTextLayerOptions.MinimumConfidence znači isto za svaki engine. Kad Tesseract prijavi baznu liniju, oba kraja se prosleđuju; inače se cevovod tekstualnog sloja vraća na svoju geometrijsku procenu, tačno kao za TSV unos

Zašto validirati enum pre nego što stigne do DLL-a?

HotPDF kopira sirovi ordinal od PageSegMode i EngineMode u Integer pre provere opsega, jer kompajler može pretpostaviti da enum promenljiva uvek drži deklarisanu vrednost i skupiti Ord(X) > Ord(High(T)) u konstantu false. Ordinali nisu dekoracija: THPDFTesseractPageSegMode prati Tesseract numeraciju segmentacije stranica od 0 do 13, THPDFTesseractEngineMode prati numeraciju režima engine-a od 0 do 3, i oba idu u DLL kao obični celobrojni. Zapis opcija građen sa FillChar, popunjen iz toka ili predat iz C++Builder-a sa pretvorenim celim brojem može nositi bajt poput 200. Validiranje kopiranog ordinala pretvara to u EArgumentException u vreme fabrike umesto nedefinisanog režima unutar nativnog koda. Fabrika takođe odbija tpsOSDOnly i tpsAutoOnly, koji ne proizvode reči, i traži osd.traineddata za tpsAutoOSD i tpsSparseTextOSD

Šta rok prepoznavanja zapravo garantuje?

Rok Tesseract DLL-a je kooperativan: HotPDF može zaustaviti svoj rad i zamoliti Tesseract da stane, ali ne može naterrati nativni kod da se vrati. Sat kreće kad Recognize počne, pa konverzija bitmape i inicijalizacija modela troše isti budžet kao prepoznavanje. HotPDF proverava proteklo vreme i token otkazivanja tokom grayscale konverzije i između reči dok iterira rezultate, i predaje preostale milisekunde TessMonitorSetDeadlineMSecs pre poziva TessBaseAPIRecognize

Praznina je unutar nativnog poziva. Tesseract monitor pita se tokom prepoznavanja reči, ne tokom TessBaseAPIInit2 ili analize rasporeda stranice, pa sporo učitavanje modela ili patološki raspored može pretrčati rok pre nego što se istek prijavi. Piksel i izlazni budžeti takođe ne ograničavaju sopstvenu upotrebu memorije nativne biblioteke. Ako treba radnik koga možete ubiti, koristite procesni adapter; to je pošteno prilagođavanje, ne nedostajuća funkcija

HotPDF Tesseract DLL anatomija kooperativnog roka: sat kreće kad Recognize počne i pokriva grayscale konverziju, TessBaseAPIInit2 i analizu rasporeda, ali se monitor pita samo tokom prepoznavanja reči, pa učitavanja modela i raspored mogu pretrčati pre nego što HotPDF prijavi otlsEngineError ili otlsCancelled
rok ovde je zahtev, ne garancija: init i analiza rasporeda mogu dugo trajati, i radnik koga zaista možete ubiti traži procesni adapter

Segmentacija stranica je gde DLL adapter zarađuje svoj hljeb na teškom unosu. Obrasci, etikete i skenirane tabele sa rasutim poljima često se prepoznaju bolje sa tpsSparseText nego sa automatskom segmentacijom, koja pokušava sklopiti kolone i pasuse kojih nema

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;  // rasuta polja, bez sklapanja kolona
  TessOptions.EngineMode := temLSTMOnly;     // traži LSTM modele u tessdata
  TessOptions.TimeoutMilliseconds := 20000;  // uključuje inicijalizaciju modela
  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;

Istek pokazuje se kao otlsEngineError sa dijagnostikom Tesseract DLL OCR timed out, dok otkazani token pokazuje se kao otlsCancelled. U oba slučaja ApplyLoadedOCRTextLayer prepoznao je svaku izabranu stranicu pre nego što počne transakciju uveravanja, pa neuspeh na stranici 40 od 50 ostavlja učitani dokument tačno kakav je bio. Zapamtite da tpsSingleLine, tpsSingleBlock i tpsSparseText menjaju samo segmentaciju; nijedan ne ispravi iskrivljen sken

Free Pascal i Lazarus: zastareli pikseli i izgubljeni kineski

Oba Tesseract fabriku rade u Windows Free Pascal i Lazarus Win32 i Win64 verzijama od v2.772.1, posle dve FPC-specifične popravke. Prvo ponovo izgradite Lazarus paket za ciljnu arhitekturu; opšti port pokriven je u HotPDF na Free Pascal i Lazarus Win64

Prva popravka se tiče piksela. LCL TBitmap pisan kroz scanline može ažurirati svoju sirovu sliku bez osvežavanja Windows bitmap handle-a, pa GetDIBits na tom handle-u vraća stare piksele. Simptom je bio zbunjujući: tekst nacrtan direktno na bitmapu prepoznavao se, dok je stranica renderovana HotPDF PDF renderer-om davala praznu listu reči. Na FPC-u adapter sada čita snapshot svestan formata kroz CreateIntfImage, koji poštuje piksel format i redosled redova sirove slike. Delphi verzija zadržava GetDIBits putanju na privatnoj 24-bitnoj kopiji. Nijedna verzija ne menja bitmapu pozivaoca

Druga popravka pripada tesseract.exe adapteru. FPC TStringList čuva ANSI stringove, pa je dodela dekodiranog UTF-8 TSV teksta Lines.Text tiho bacala svaki kineski ili dopunsko-ravanski karakter koji sistemski ANSI kod stranice nije mogao predstaviti. FPC putanja sada čuva TSV kao UTF-8 bajtove, skida BOM na nivou bajtova i dekodira svaku reč u UnicodeString pojedinačno. DLL adapter nikada nije imao ovaj problem jer dekodira svaku reč direktno iz iteratora

Brzi pregled

  • Fabrika: HPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options]) u HPDFTesseractRecognition, dodata u v2.772.0, FPC podrška u v2.772.1
  • Podrazumevane vrednosti: tpsAuto, temDefault, 60.000 ms, 16.777.216 piksela; opseg roka 1–3.600.000 ms, piksel plafon 67.108.864
  • Uskladite bitnost DLL-a sa aplikacijom i postavite DLL-ove zavisnosti pored Tesseract DLL-a
  • Tretirajte monitor kao neproziran; nikada ne kopirajte ETEXT_DESC u Pascal zapis
  • Deklarišite cancel callback kao cdecl sa Boolean rezultatom od jednog bajta, i nikada ne pustite izuzetak da pobegne iz njega
  • Oslobađajte tekst iteratora sa TessDeleteText; nikada ne oslobađajte iterator stranica dobijen iz iteratora rezultata
  • Očekujte da je rok kooperativan: inicijalizacija modela i analiza rasporeda mogu ga pretrčati
  • Koristite tesseract.exe adapter kad treba tvrdo prekidanje ili izolacija padova

Tesseract DLL adapter, procesni adapteri i ugrađeni OCR engine svi se isporučuju sa HotPDF Delphi PDF komponentom za Delphi, C++Builder i Free Pascal; pogledajte stranicu proizvoda HotPDF za izdanja i preuzimanja