HotPDF, Tesseract'ı Delphi sürecinizin içinde HPDFCreateTesseractDLLOCREngine üzerinden koşturur; bu, v2.772.0'da eklenen ve Tesseract 5 uyumlu bir DLL'i dinamik yükleyen, C API'sini (TessBaseAPIInit2, TessBaseAPIRecognize, result iterator) süren ve bir IHPDFOCREngine döndüren fabrikadır. THotPDF.ApplyLoadedOCRTextLayer o motoru, taranmış PDF sayfalarına görünmez, aranabilir bir Unicode metin katmanı eklemek için kullanır
Aynı tanıyıcıya BMP yazıp TSV ayrıştıran harici tesseract.exe adaptörü üzerinden de zaten ulaşılıyordu. O yol çalışır; ama her sayfa bir süreç başlatmanın, geçici bir bitmap dosyasının ve taban çizgisi olmayan, sayfa segmentasyonu üzerinde hiçbir kontrol vermeyen bir metin formatının bedelini öder. DLL'i çağırmak üçünü de kaldırır. Aynı zamanda süreç duvarını da kaldırır; yani bir Pascal bağlaması doğrudan C yapıtlarının, C Boolean'larının ve C ayrılmış string'lerin üstüne oturur. Bu adaptör hakkında bilinmeye değer olanın çoğu, o bağlamanın sessizce nerede yanlış gidebileceğidir
HotPDF ile Tesseract'ı Delphi'den süreç içinde nasıl koşturursunuz?
HotPDF ile Tesseract'ı süreç içinde koşturmak, HPDFTesseractRecognition unitinde tek bir fabrika çağrısı ve her HotPDF OCR motorunun kullandığı aynı ApplyLoadedOCRTextLayer çağrısıdır. Fabrika istekli doğrular. DLL dosyası ile tessdata dizini var olmalı, dil tanımlayıcısı yalnızca ASCII harfleri, rakamları, _ ve +'ı içerebilir, chi_sim+eng gibi bir birleşimdeki her modelin eşleşen bir .traineddata dosyası olmalı ve motor döndürülmeden önce 21 zorunlu exportun hepsi çözülmelidir. Yapılandırma hataları EArgumentException fırlatır; yüklenemeyen bir DLL, Windows hata koduyla ve mimari ile bağımlılıkları denetleme ipucuyla EOSError fırlatır
uses
SysUtils, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Bir Win64 uygulaması 64 bitlik DLL ister; bağımlılık DLL'leri yanına gider
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
// Boş sayfa listesi her sayfa demektir; metni hâlihazırda olan sayfalar atlanır
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, PageSegMode'u tpsAuto'ya, EngineMode'u temDefault'a, TimeoutMilliseconds'i 60.000'e ve MaxPixels'i 16.777.216'ya set eder. Pixel bütçesi göründüğünden daha önemlidir. Varsayılan 300 DPI'da bir US Letter sayfası 2.550 × 3.300, yani kabaca 8,4 milyona render olur; bu sığar. Aynı sayfa 600 DPI'da 5.100 × 6.600, kabaca 33,7 milyondur ve adaptör, Tesseract tek bir pixel görmeden onu reddeder. MaxPixels'i yükseltin (tavan 67.108.864) ya da DPI'yı olduğu yerde tutun; her kenar ayrıca 32.767 pixel'de tavanlanır
DLL, DLL'in kendi klasörünü artı varsayılan güvenli dizinleri kapsayan arama bayraklarıyla LoadLibraryEx ile yüklenir; böylece Tesseract'ın bağımlı olduğu image kütüphaneleri PATH'e ya da geçerli dizine dokunmadan yanında durabilir. HotPDF hiçbir OCR çalışma zamanını ya da modelini paketlemez ya da indirmez; ikisini de siz temin edersiniz
tesseract.exe adaptörüyle karşılaştırıldığında ne değişir?
DLL adaptörü, süreç izolasyonunu daha zengin çıktı ve daha düşük sayfa-başı ek yük için takas eder. İki adaptör de aynı metin katmanı hattına takılır; dolayısıyla koordinat eşleme, güven filtreleme ve hep-ya-da-hiç işleme birebir aynıdır. Değişen şey, pixel'lerin nasıl girdiği ile kelimelerin nasıl çıktığıdır
| Yön | tesseract.exe adaptörü | Tesseract DLL adaptörü |
|---|---|---|
| Fabrika | HPDFCreateTesseractOCREngine | HPDFCreateTesseractDLLOCREngine |
| Pixeller | Özel geçici dizinde BMP dosyası | Bellekte 8 bitlik gri tonlama tamponu |
| Kelimeler | Kelime düzeyli TSV, 64 MiB'de tavanlı | Result iterator, kelime başına UTF-8 |
| Taban çizgileri | Kullanılamaz | TessPageIteratorBaseline'dan geçirilir |
| Sayfa segmentasyonu ve motor modu | Yalnızca otomatik segmentasyon | THPDFTesseractPageSegMode, THPDFTesseractEngineMode |
| Zaman aşımı | Sert: çocuk süreç öldürülür | İşbirlikçi: Tesseract fark etmelidir |
| Çökme ve bellek izolasyonu | Ayrı süreç | Yok, adres uzayınızı paylaşır |
Tek bir maliyet kaybolmaz. Her Recognize çağrısı kendi API instance'ını kurar ve TessBaseAPIInit2 çağırır; dolayısıyla dil modelleri motor başına bir kez değil, sayfa başına başlatılır. İşletim sisteminin dosya cache'i yeniden yüklemeyi yumuşatır ama büyük çok dilli model setlerinde yine de sayfa başına baskın sabit maliyettir ve tanıma son tesliminden düşer. Süreç içi RapidOCR DLL motoru ters tasarımı seçer ve ONNX modellerini motorun ömrü boyunca yerleşik tutar; sınır sorunları (C ABI, ödünç tamponlar, kesintisiz native iş) aynı ailedendir
Delphi Tesseract monitor yapısını neden kopyalayamaz?
Delphi, Tesseract ilerleme monitorunu güvenle aynalayamaz, çünkü ETEXT_DESC sürüme bağlı dâhilî alanlar taşır; elle kopyalanmış bir record, cancel callback'ini ile son teslimi bazı derlemelerde yanlış ofsetlere koyar. O olduğunda hiçbir şey gürültüyle düşmez. Tesseract ya callback işaretçinizi artık başka bir şey tutan bir alandan okur ya da son teslimi hiç görmez
HotPDF bu yüzden monitoru opak bir işaretçi sayar ve ona yalnızca export edilmiş fonksiyonlar üzerinden dokunur: TessMonitorCreate, TessMonitorSetCancelThis, TessMonitorSetCancelFunc, TessMonitorSetDeadlineMSecs ve TessMonitorDelete. C API'sini başka bir amaçla kendiniz bağlıyorsanız aynı örüntü geçerlidir. Aşağıdaki taslak, kendi bağlama kodunuzdur — HotPDF API'si değil — ve HotPDF'in dâhilde kullandığı bildirimleri aynalar
type
// C: typedef bool (*TessCancelFunc)(void *cancel_this, int words);
TTessCancelFunc = function(CancelThis: Pointer; Words: Integer): Boolean; cdecl;
TTessMonitorCreate = function: Pointer; cdecl; // ETEXT_DESC*, asla dereference edilmez
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
// Tesseract'ın stack'inde koşar: bayrakları ve saati okuyun, asla fırlatmayın
Result := (CancelThis = nil) or POCRJob(CancelThis)^.CancelRequested or
(GetTickCount64 >= POCRJob(CancelThis)^.DeadlineTick);
end;
// Kullanım; fonksiyon işaretçileri GetProcAddress ile çözülür:
// Monitor := MonitorCreate();
// try
// MonitorSetCancelThis(Monitor, @Job);
// MonitorSetCancelFunc(Monitor, ShouldCancel);
// MonitorSetDeadlineMSecs(Monitor, RemainingMs);
// RC := BaseAPIRecognize(API, Monitor);
// finally
// MonitorDelete(Monitor);
// end;
O taslaktaki iki ayrıntı kasıtlıdır. Callback Boolean döndürür; bu, Delphi'de de Free Pascal'da da bir bayttır ve TessCancelFunc'teki C bool'una uyar. Dört baytlık Windows BOOL'u ya da Delphi LongBool'u birbirinin yerine geçiyormuş gibi görünür ve geçmez: bir taraf tek bayt yazarken öteki dört bayt okuduğunda dönüş register'ının üst baytları orada kalmış olan her neyse odur ve bir false, true olarak gelebilir. Aynı header işi daha da zorlaştırır; TessPageIteratorBoundingBox gibi fonksiyonlar HotPDF'in Integer olarak bildirdiği bir int döndürür. Bütün API için tek bir kural varsaymak yerine her dönüş değerinin C tipini okuyun
İkinci ayrıntı, callback'in asla fırlatmamasıdır. Delphi istisnasının Tesseract'ın C++ çerçeveleri boyunca açılması tanımsız davranıştır; HotPDF'in callback'i bu yüzden yalnızca cancellation token'ı ve monotonik bir GetTickCount64 değeri okur. Adaptör sonucu, TessBaseAPIRecognize döndükten sonra bir iptal ya da zaman aşımı teşhisine çevirir ve o denetimi native dönüş kodundan bağımsız yapar
Delphi tarafı hangi native işaretçilere sahiptir?
HotPDF Tesseract DLL adaptörü istek başına üç native objecte sahiptir — API instance'ı, monitor ve result iterator — ve geri kalan her şeyi ödünç alır. Her Recognize çağrısı kendi setini kurar ve bir finally bloğunda bırakır: önce TessResultIteratorDelete, sonra TessMonitorDelete, sonra TessBaseAPIDelete. Motor arayüzünü bırakmak kütüphaneyi boşaltır
TessResultIteratorGetPageIterator, result iterator'ın içine ödünç bir görünüm döndürür, yeni bir object değil. HotPDF onuTessPageIteratorBoundingBoxileTessPageIteratorBaselineiçin kullanır ve asla free etmez; onu ayrıca silmek aynı belleği iki kez free etmek olurduTessResultIteratorGetUTF8Text, DLL'in kendi çalışma zamanı tarafından ayrılmış bir string döndürür. HotPDF onu kopyalar ve birfinallybloğundaTessDeleteTextile geri verir; PascalFreeMem'i yanlış heap'te bırakırdı- Kelime metni katı UTF-8 doğrulamasıyla decode edilir ve dönüşümden önce uzunluğu denetlenir. Kontrol karakteri taşıyan, bozuk UTF-8'li, image dışına çıkan kutulu, ters çevrilmiş dikdörtgenli ya da güveni 0-100 dışında olan kelimeler sessizce yamalanmak yerine isteği düşürür
- İstek başına toplam metin 1.048.576 UTF-16 code unit'te tavanlıdır ve kelime sayısı,
ApplyLoadedOCRTextLayer'ın aşağı aktardığı istek bütçesine sığmak zorundadır
Güven 0-100 olarak gelir ve 0-1'e ölçeklenir; dolayısıyla THPDFOCRTextLayerOptions.MinimumConfidence her motor için aynı anlama gelir. Tesseract bir taban çizgisi bildirdiğinde her iki uç geçirilir; bildirmediğinde metin katmanı hattı, TSV girdisinde yaptığı gibi geometrik tahminine döner
Bir enum, DLL'e ulaşmadan önce neden doğrulanır?
HotPDF, PageSegMode ile EngineMode'un ham ordinal'ini aralık denetiminden önce bir Integer'a kopyalar; çünkü derleyici, bir enum değişkeninin daima bildirilmiş bir değer tuttuğunu varsayıp Ord(X) > Ord(High(T))'yi sabit bir false'a katlayabilir. Ordinaller süs değildir: THPDFTesseractPageSegMode Tesseract'ın sayfa segmentasyonu numaralandırmasını 0'dan 13'e izler, THPDFTesseractEngineMode motor modu numaralandırmasını 0'dan 3'e izler ve ikisi de DLL'e sade integer olarak gider. FillChar ile kurulmuş, bir stream'den doldurulmuş ya da C++Builder'dan cast edilmiş bir integer'la geçirilmiş bir options record, 200 gibi bir bayt taşıyabilir. Kopyalanan ordinali doğrulamak bunu native kodun içinde tanımsız bir mod yerine fabrika zamanında bir EArgumentException'ye çevirir. Fabrika ayrıca kelime üretmeyen tpsOSDOnly ile tpsAutoOnly'yi reddeder ve tpsAutoOSD ile tpsSparseTextOSD için osd.traineddata ister
Tanıma zaman aşımı aslında neyi garanti eder?
Tesseract DLL zaman aşımı işbirlikçidir: HotPDF kendi işini durdurup Tesseract'a da durmasını söyleyebilir ama native kodu dönmeye zorlayamaz. Saat, Recognize başladığında işler; dolayısıyla bitmap dönüşümü ile model başlatma, tanımayla aynı bütçeyi tüketir. HotPDF geçen süreyi ile cancellation token'ı gri tonlama dönüşümü sırasında ve sonuçları gezerken kelimeler arasında denetler ve TessBaseAPIRecognize'i çağırmadan önce kalan milisaniyeleri TessMonitorSetDeadlineMSecs'e geçirir
Boşluk native çağrının içindedir. Tesseract'ın monitoruna kelime tanıma sırasında başvurulur; TessBaseAPIInit2 ya da sayfa düzeni analizi sırasında değil. Dolayısıyla yavaş bir model yüklemesi ya da patolojik bir düzen, zaman aşımı bildirilmeden son teslimi aşabilir. Pixel ve çıktı bütçeleri de native kütüphanenin kendi bellek kullanımını tavanlamaz. Öldürebileceğiniz bir işçi istiyorsanız süreç adaptörünü kullanın; dürüst takas budur, eksik bir özellik değil
Sayfa segmentasyonu, DLL adaptörünün zorlu girdide parladığı yerdir. Dağınık alanlı formlar, etiketler ve taranmış tablolar, olmayan sütunları ve paragrafları kurmaya çalışan otomatik segmentasyondan çok tpsSparseText ile daha iyi tanınabilir
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; // dağınık alanlar, sütun kurma yok
TessOptions.EngineMode := temLSTMOnly; // tessdata'da LSTM modelleri ister
TessOptions.TimeoutMilliseconds := 20000; // model başlatmasını içerir
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;
Bir zaman aşımı, Tesseract DLL OCR timed out teşhisiyle otlsEngineError olarak yüzeye çıkar; iptal edilen token ise otlsCancelled olarak. Her iki durumda da ApplyLoadedOCRTextLayer, işleme işlemini başlatmadan önce seçilen her sayfayı tanımış olur; dolayısıyla 50 sayfanın 40'ındaki bir başarısızlık yüklenen belgeyi tam olarak olduğu gibi bırakır. tpsSingleLine, tpsSingleBlock ve tpsSparseText'in yalnızca segmentasyonu değiştirdiğini unutmayın; hiçbiri eğik bir taramayı düzleştirmez
Free Pascal ve Lazarus: bayat pixel'ler ve kaybolan Çince
İki Tesseract fabrikası da, iki FPC'ye özgü düzeltmenin ardından, Windows Free Pascal ile Lazarus Win32 ve Win64 derlemelerinde v2.772.1'den beri çalışır. Önce Lazarus paketini hedef mimari için yeniden derleyin; genel port Free Pascal ve Lazarus Win64'te HotPDFde kapsanır
İlk düzeltme pixel'lerle ilgilidir. Scanline'lar üzerinden yazılan bir LCL TBitmap, Windows bitmap handle'ını tazelemeden ham image'ini güncelleyebilir; o handle üzerindeki GetDIBits böylece eski pixel'leri döndürür. Belirti şaşırtıcıydı: doğrudan bir bitmap üzerine çizilen metin tanınıyordu, HotPDF'in PDF renderer'ının render ettiği bir sayfa ise boş bir kelime listesi üretiyordu. FPC'de adaptör artık ham image'in pixel formatını ve satır sırasını gözeten, CreateIntfImage üzerinden format-farkındalıklı bir anlık görüntü okur. Delphi derlemesi, özel bir 24 bitlik kopyada GetDIBits yolunu korur. İki derleme de çağıranın bitmap'ini değiştirmez
İkinci düzeltme tesseract.exe adaptörüne aittir. FPC'nin TStringList'i ANSI string saklar; dolayısıyla decode edilmiş UTF-8 TSV metnini Lines.Text'e atamak, sistem ANSI kod sayfasının temsil edemediği her Çince ya da yardımcı düzlem karakterini sessizce düşürüyordu. FPC yolu artık TSV'yi UTF-8 baytları olarak tutar, BOM'u bayt düzeyinde soyar ve her kelimeyi tek tek UnicodeString'e decode eder. DLL adaptöründe bu sorun hiç olmadı; çünkü her kelimeyi doğrudan iterator'dan decode eder
Hızlı başvuru
- Fabrika:
HPDFTesseractRecognition'daHPDFCreateTesseractDLLOCREngine(LibraryPath, TessDataDirectory, Language[, Options]), v2.772.0'da eklendi, FPC desteği v2.772.1'de - Varsayılanlar:
tpsAuto,temDefault, 60.000 ms, 16.777.216 pixel; zaman aşımı aralığı 1-3.600.000 ms, pixel tavanı 67.108.864 - DLL bitliğini uygulamayla eşleştirin ve bağımlılık DLL'lerini Tesseract DLL'inin yanına koyun
- Monitoru opak sayın;
ETEXT_DESC'i asla bir Pascal record'una kopyalamayın - Cancel callback'ini tek baytlık
Booleansonuçlacdeclbildirin ve ondan bir istisna asla kaçmasın - Iterator metnini
TessDeleteTextile free edin; result iterator'dan alınan page iterator'ı asla free etmeyin - Son teslimin işbirlikçi olmasını bekleyin: model başlatma ile düzen analizi onu aşabilir
- Sert sonlandırma ya da çökme izolasyonu gerektiğinde tesseract.exe adaptörünü kullanın
Tesseract DLL adaptörü, süreç adaptörleri ve yerleşik OCR motoru, Delphi, C++Builder ve Free Pascal için HotPDF Delphi PDF bileşeniyle gelir; sürümler ile indirmeler için HotPDF ürün sayfasına bakın