HotPDF, v2.774.0'da eklenen HPDFCreateRapidOCRDLLOCREngine fabrikasıyla taranmış PDF sayfalarını süreç içi RapidOCR ile aranabilir kılar: fabrika HotPDFRapidOCR.dll'i yükler, ONNX detection, angle classification ve recognition modellerini bellekte yerleşik tutar ve bir IHPDFOCREngine döndürür. O motoru THotPDF.ApplyLoadedOCRTextLayer'a geçirirsiniz; bu da her sayfayı render eder, Python olmadan, alt süreç olmadan CPU inference koşturur ve görünmez bir Unicode metin katmanını işler
Motivasyon, sayfa başına maliyettir. Daha önce çıkan RapidOCR süreç adaptörü HPDFCreateRapidOCREngine, her Recognize çağrısı için bir Python işçisi başlatır ve o işçi tek bir pixel okumadan önce çalışma zamanını içe aktarıp ONNX modellerini yükler. 500 sayfalık bir arşivde o açılış vergisi 500 kez tekrarlanır ve dağıtım, bir Delphi çalıştırılabilirinin yanına bir Python ortamı koymak demektir. Native DLL modelleri, motoru kurduğunuzda bir kez yükler ve dağıtım DLL'e, model dosyalarına ve bir karakter sözlüğüne küçülür. Karşılığında vazgeçtiğiniz şey, takılan bir tanıyıcıyı öldürebilmektir; bu adaptördeki mühendisliğin çoğu da o gerçekle dürüstçe yaşamakla ilgilidir
RapidOCR DLL'iyle taranmış bir PDF nasıl aranabilir yapılır?
Native RapidOCR DLL'iyle aranabilir bir PDF kurmak bir fabrika çağrısı ve her HotPDF OCR motorunun kullandığı aynı ApplyLoadedOCRTextLayer çağrısıdır. Fabrika HPDFRapidOCRRecognition unitinde yaşar ve istekli doğrular: DLL ile model dizini var olmalı, her model ve sözlük dosyası çözülmeli, ABI sürümü 1 olmalı ve herhangi bir model başlatılmadan önce bütün zorunlu export'lar bulunmalıdır. Yapılandırma hataları EArgumentException fırlatır; yüklenemeyen bir model, DLL'in yazdığı teşhis metnini taşıyan EInvalidOperation fırlatır
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Modeller burada yüklenir, tanıma son tesliminin dışında.
// THPDFRapidOCRDLLOptions.Default içindeki göreli model adları model
// dizinine göre çözülür.
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
// 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,
' lines accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
THPDFRapidOCRDLLOptions.Default, tek CPU thread'i, 16.777.216 pixellik bir girdi sınırı ve 60.000 ms'lik bir tanıma son teslimiyle ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx ve ppocr_keys_v1.txt adlarını verir. v2.775.0'dan beri THPDFRapidOCRDLLOptions.ForLanguage, Geleneksel Çince, Rusça, Japonca, Arapça ve başka profiller için eşleşen bir recognition modeliyle sözlüğü takar; model ile sözlüğün neden birlikte değişmesi gerektiği HotPDF'te RapidOCR çok dilli modeller ve CTC sözlüklerinde kapsanır. Motor kendini Info.EngineName'de RapidOCR (native DLL) olarak bildirir; böylece loglar, harici Tesseract OCR süreç adaptörünün ve yerleşik şablon eşleştirmeli OCR motorunun yanında belirsizliğini korur
C ABI'si neden yalnızca int32_t ve UTF-8 baytları konuşur?
HotPDFRapidOCR.dll ABI'si yalnızca sabit genişlikteki integer'ları, ham işaretçileri ve açık bayt uzunluklarını kullanır; çünkü Delphi, C++Builder ve Free Pascal, C çağırma kuralının ötesinde MSVC ile hiçbir şey paylaşmaz. Bir std::string, bir std::vector ya da bir C++ istisnası, bir derleyiciye ve bir çalışma zamanı kütüphanesine ait bir düzen ve bir unwinding modeli taşır. Herhangi birinin sınırı geçmesine izin verin, başarısızlık ya bozuk bir stack ya yanlış ayırıcı tarafından free edilen bir heap bloğu olur; temiz bir hata değil
ABI sürüm 1 bu yüzden kısa bir kural listesini izler. Her export cdecl'dir ve bir int32_t durumu döndürür; 1 başarı, 0 başarısızlık demektir. Başarısız olabilen her fonksiyon, çağıranın malı bir teşhis tamponu ve onun bayt cinsinden kapasitesini alır; DLL, sığacak biçimde kırpılmış NUL ile biten bir UTF-8 mesaj yazar ve adaptör, kendi 4.096 baytlık tamponunun son baytındaki sert bir sonlandırıcıyla onu decode eder. Her export gövdesi hem catch (const std::exception &) hem catch (...) içeren bir try'a sarılır; böylece bir ONNX Runtime hatası, bir OpenCV assertion'ı ya da geçersiz bir sözlük, 0 durumu artı metne dönüşür ve Pascal koduna kaçan bir istisna asla olmaz
| Export | Rol | Adaptör ne zaman çözer |
|---|---|---|
HPDFRapidOCRAbiVersion | 1 döndürür; başka her değer reddedilir | Önce, her şeyden evvel |
HPDFRapidOCRCreate | Detection, isteğe bağlı classification, recognition modellerini ve sözlüğü yükler | Fabrikada |
HPDFRapidOCRRecognize | Tek bir bitmap koşturur ve metin satırı başına bir callback üretir | Fabrikada |
HPDFRapidOCRDestroy | Model instance'ını free eder | Fabrikada |
HPDFRapidOCRSetReadingDirection | İsteğe bağlı sağdan sola satır sırası, v2.775.0'da eklendi | Yalnızca RightToLeft setliyken |
İsteğe bağlı export kasıtlı olarak tembel çözülür: onu taşımayan bir v2.774.0 DLL'i soldan sağa istekleri sunmaya devam eder. DLL, kendi klasörünü artı varsayılan güvenli dizinleri kapsayan arama bayraklarıyla LoadLibraryEx ile yüklenir; böylece HotPDFRapidOCR.dll'in yanına konan ONNX Runtime ya da OpenCV bağımlılıkları PATH'e dokunmadan bulunur. Model ve sözlük yolları UTF-8 olarak gider ve DLL, dosyaları geniş karakterli API'lerle açmadan önce onları katı modda MultiByteToWideChar ile çevirir; böylece Çince ya da Kiril bir kullanıcı adının altındaki model dizini çalışır, bayt bayt genişletilip anlamsızlaşmaz
Bir kural header'da değil, derlemede yaşar. DLL, ONNX Runtime ile OpenCV'yi statik bağlar ve varsayılan CMake yapılandırması statik release CRT'yi (/MT) kullanır. /MD'ye karşı derlenmiş statik kütüphanelerin bir /MT DLL'ine karışması en iyi ihtimalle link hataları, en kötü ihtimalle iki bağımsız heap üretir; dolayısıyla temin edilen kütüphaneler, DLL'in hangi CRT modunu kullandığına uymak zorundadır
Bir TBitmap ile bir metin satırı arasında ne olur?
HotPDF, DLL'e render edilen sayfanın bağımsız bir üstten alta BGR anlık görüntüsünü verir; DLL ise tanınan her metin satırı için bir callback ile geri döner ve adaptörün dönmelerinden önce kopyalamak zorunda olduğu ödünç UTF-8 metin taşır
Delphi'de adaptör, sayfa bitmap'ini özel bir TBitmap'e atar, pf24bit'i zorlar ve negatif bir biHeight ile GetDIBits üzerinden satırları okur; bu, dört bayta hizalanmış üstten alta satırlar verir ve o stride açıkça geçirilir. FPC'de CreateIntfImage üzerinden okur; çünkü LCL scanline yazımları GDI handle'ını tazelemeden ham image'i güncelleyebilir. Çağıranın bitmap'i asla değiştirilmez ve pixel bütçesi (MaxPixels, varsayılan 16.777.216, 67.108.864'e kadar yapılandırılabilir) ile boyut başına 32.767 pixellik sınır, anlık görüntü tamponu ayrılmadan önce denetlenir
DLL'in içinde anlık görüntü 50 beyaz pixel ile pad edilir, metin bölgeleri 1.024 pixellik azami kenarla saptanır, kutular yatay satırlara dizilir ve her crop, tanımadan önce isteğe bağlı olarak açı sınıflandırıcısıyla döndürülür. Sonra her metin satırı, bir const char*, bir bayt sayısı, özgün image pixel'lerinde bir integer kutu ve ortalama karakter güveni alan bir callback'ten geçer. Metin işaretçisi yalnızca callback sırasında geçerlidir; adaptör onu hemen kopyalar ve kabul ettiklerine karşı katıdır:
- UTF-8,
MB_ERR_INVALID_CHARSile decode edilir; bozuk bir dizi, aranabilir bir katmanda değiştirme karakterleri üretmek yerine sayfayı düşürür - C0 ile C1 kontrol karakterleri reddedilir ve yalnızca boşluktan oluşan satırlar atlanır
- Kutu bitmap'in içinde bulunmalı ve güven, 0 ile 1 arasında sonlu bir değer olmalıdır
- Metin, çağrı başına 1.048.576 UTF-16 unit'lik sert bir tavanla isteğin
MaxTextCodeUnitsine karşı sayılır ve yardımcı düzlem karakterleri iki unit'e mal olur - Callback içindeki herhangi bir Pascal istisnası orada yakalanır, saklanır ve 0 döndürüşüne çevrilir; bu da DLL'i durdurup başarısızlık bildirtir ve saklanan mesaj teşhis olur
Ayar yaparken iki sonuç önemlidir. Birincisi, çıktının birimi satırdır, kelime değil: her satır bir MaxWords yuvası tüketir, Info.AcceptedWordCount ile Info.DroppedWordCount satır sayar ve arama vurgusu satır kutusuna yayılır. İkincisi, MinimumConfidence (varsayılan 0.5), satırın ortalama karakter güveniyle karşılaştırılır; yani yirmi temiz karakterin arasında tek okunamayan karakter taşıyan bir satır genelde hayatta kalır. DLL taban çizgisi sağlamaz, dolayısıyla metin katmanı hattı onu kutudan tahmin eder. Boş bir sayfa sıfır satırla başarılı olur ve herhangi bir başarısızlık kısmi sonuçları temizler; böylece çok sayfalı işleme hep-ya-da-hiç kalır
Model sahipliği ve thread safety
Her RapidOCR DLL motoru bütün ömrü boyunca tam olarak bir model instance'ına sahiptir ve o motor üzerindeki Recognize çağrıları bir critical section ile serileştirilir. IHPDFOCREngine arayüzünü tutmak, modelleri sıcak tutan şeydir; dolayısıyla toplu iş için doğru örüntü motoru bir kez kurmak ve belgeler boyunca yeniden kullanmaktır
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; // dik taramalar: sınıflandırıcı modeli yüklenmez
Models.Threads := 4; // 1..64, mantıksal işlemci sayısına kırpılır
Models.TimeoutMilliseconds := 120000; // Recognize çağrısı başına, işbirlikçi
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; // son referans bırakıldı: modeller yok edilir, sonra DLL boşaltılır
Threads değeri her ONNX oturumunun hem intra-op hem inter-op thread sayılarını set eder ve DLL onu etkin işlemci sayısına kırpatır. Tek bir motoru paylaşan iki thread paralel koşmaz; ikincisi kilidi bekler. O bekleyiş kör bir EnterCriticalSection değildir: adaptör her 25 ms'de bir TryEnterCriticalSection çağırır ve denemeler arasında cancellation token'ı ile son teslimi denetler; böylece kuyrukta bekleyen bir istek hâlâ iptal edilebilir ya da zaman aşımına uğrayabilir. Gerçek paralellik istiyorsanız işçi başına bir motor kurun ve her motorun modellerin bellekte kendi kopyasını tuttuğunu kabul edin
Yıkım sırası motor yıkıcısı tarafından sabitlenmiştir: önce HPDFRapidOCRDestroy model instance'ını free eder, sonra FreeLibrary DLL'i boşaltır. Native tarafta model başlatma aynı ölçüde özenlidir; detector ile sınıflandırıcı oturumları hâlihazırda kurulmuşken recognition modeli başarısız olursa oturumlar hata bildirilmeden önce bırakılır ve sözlük sınıf sayısı, ilk sayfada değil başlatma sırasında model çıktısıyla karşılaştırılır
Native bir OCR çağrısı inference ortasında neden öldürülemez?
Native bir RapidOCR çağrısı inference ortasında öldürülemez, çünkü sizin thread'inizde, sizin sürecinizin içinde, kesintiye kabul etmeyen bir ONNX Runtime oturumunun ortasında koşar. HotPDF'in DLL adaptöründe bu yüzden iptal işbirlikçidir: DLL, detection öncesi ve sonrası, classification sonrası ve tanınan her satır sonrası bir abort callback çağırır ve callback'in 0 döndürdüğü ilk kontrol noktasında durur. Başlamış tek bir ONNX Run önce bitmesini tamamlar
Alternatifler beklemekten kötüdür. TerminateThread, CRT heap kilidini, ONNX Runtime'ın thread havuzunu ve herhangi bir OpenCV durumunu tam da bulundukları koşulda bırakır ve sürecin geri kalanını zehirler. Çağrı hâlâ yürürken FreeLibrary, stack üzerindeki kodu boşaltır. İkisi de güvenli hâle getirilemez; dolayısıyla adaptör hiçbir zaman denemez. TimeoutMilliseconds'teki son teslim bu yüzden işbirlikçi bir son teslimdir; dolan son teslim, zaman aşımı teşhisli bir motor hatası olarak yüzeye çıkar, iptal edilen token ise otlsCancelled olarak:
// Token çağıran tarafından kurulur ve UI thread'iyle paylaşılır;
// kullanıcı Durdur'a bastığında Token.Cancel çağırır
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// bir sonraki aşama ya da satır sınırında döndürülür; belge değişmez
Writeln('Cancelled');
otlsEngineError:
// işbirlikçi son teslim dolmasını ve native teşhisleri içerir
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
Bu, HotPDF'in süreç adaptörleri ile süreç içi DLL arasındaki temel takastır ve hiçbir taraf her satırda kazanmaz:
- Açılış maliyeti: Tesseract ile Python RapidOCR adaptörleri her sayfa için bir süreç başlatır ve modelleri yükler; DLL modelleri motor başına bir kez yükler
- Durdurma: bir çocuk süreç dobra öldürülebilir ve Python işçisi, kapanışta-ölen bir Job Object içinde koştuğu için bütün süreç ağacı onunla gider; DLL yalnızca aşama ve satır sınırlarında durabilir
- Hata kuşatması:
tesseract.exeiçindeki bir çökme tek bir sayfayı düşürür; DLL'in içindeki bir access violation sürecinizi götürür - Dağıtım: süreç adaptörleri kurulmuş bir program ya da Python ortamı ister; DLL kendini, modellerini ve sözlüğünü ister, uygulamanın bitliğine uymak koşuluyla
- Bellek: süreç adaptörleri çocuk çıktığında her şeyi bırakır; bir DLL motoru, son arayüz referansı bırakılana dek modellerini yerleşik tutar
Bir sayfayı bir kez OCR eden etkileşimli bir masaüstü uygulaması için DLL'in duyarlılığı genelde kazanır. Gündüz gece güvenilmeyen taramaları yutan bir sunucu için süreç sınırı, açılış maliyetine değer
HotPDFRapidOCR.dll'i derlemek ve dağıtmak
HotPDFRapidOCR.dll, Native/RapidOCR altındaki C++ kaynaklarından MSVC, C++17, bir Windows SDK ve CMake 3.20 ya da sonrasıyla, native ağ kaynaklarını, ONNX Runtime ile OpenCV dizinlerini artı bir Win32 ya da Win64 platformunu alan bir yardımcı script kullanılarak derlenir. İkisini de dağıtıyorsanız ikisini de derleyin; çünkü 32 bitlik bir Delphi uygulaması 64 bitlik bir DLL yükleyemez ve temin ettiğiniz statik kütüphaneler, hedef mimariye olduğu gibi CRT moduna da uymak zorundadır
Model tarafının kendi uyumluluk sınırları vardır. Detector bir DB text detector'dur; recognizer, sabit 32 ya da 48 girdi yüksekliğiyle NCHW düzeninde CTC modelleri kabul eder ve dinamik yükseklikli modeller için 48 kullanır. Paketlenen statik ONNX Runtime, daha yeni bir IR sürümüyle kaydedilmiş modelleri yükleyemez; dolayısıyla son PP-OCRv5 export'ları kısmen yüklenmek yerine bir teşhisle başlatmayı düşürür. Sözlük, tam olarak modelin karakter sırasında, BOM'suz UTF-8 olmak zorundadır ve sınıf sayısı model çıktısına uymak zorundadır; CRLF satır sonları kabul edilir. Tanıma çevrimdışıdır: DLL eksik bir modeli asla indirmeye kalkmaz
Hızlı başvuru
- Fabrika:
HPDFRapidOCRRecognition'daHPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options]), Delphi, C++Builder ve Windows FPC/Lazarus derlemelerinde v2.774.0'dan beri mevcut - Döndürülen
IHPDFOCREngine'i sayfalar ve belgeler boyunca canlı tutun; onu bırakmak modelleri yok eder ve DLL'i boşaltır - Tek bir motor bir anda tek bir tanıma koşturur; paralel işçiler için birkaç motor kurun ve her model kopyası için bellek bütçesi ayırın
- Çıktı, ortalama karakter güveniyle metin satırı başına bir girdidir ve
THPDFOCRTextLayerOptions.MinimumConfidenceile filtrelenir - İptal ile
TimeoutMillisecondsişbirlikçidir; sürmekte olan bir ONNX koşusu daima tamamlanır - DLL bitliğini uygulamayla, statik ONNX Runtime ve OpenCV kütüphanelerinin CRT modunu DLL'le eşleştirin
- Motor başına dil profilini
THPDFRapidOCRDLLOptions.ForLanguageile seçin (v2.775.0); tek bir motor kendi başına dil saptamaz
Native RapidOCR adaptörü, süreç tabanlı OCR adaptörleri, onları besleyen page renderer ve görünmez Unicode metin katmanı yazıcısı birlikte, Delphi ve C++Builder için native bir VCL PDF bileşeni olan HotPDF ile gelir. Belge yakalama ya da arşivleme uygulamanız, hedef makinede Python çalışma zamanı olmadan aranabilir çıktı istiyorsa HotPDF Delphi PDF component, dağıtılacak yalnızca DLL ile modelleri kalarak bütün hattı sağlar