HotPDF, taranmış PDF sayfalarını Tesseract ile aranabilir PDF'e çevirir; HPDFCreateTesseractOCREngine, yerel olarak kurulmuş bir Tesseract çalıştırılabilirini IHPDFOCREngine olarak saran bir fabrikadır. O motoru ApplyLoadedOCRTextLayere geçirirsiniz; bu, her sayfayı render eder, sayfa başına bir kez Tesseract çalıştırır, kelime düzeyli TSV çıktısını ayrıştırır ve istenen tüm sayfalar için görünmez bir Unicode metin katmanını tek transactionda işler, ya da hiçbirini işlemez
Bu adaptörün var olma sebebi kapsamdır. Yerleşik şablon eşleştirmeli OCR motoru bilinçli olarak dardır: makinede basılmış ASCII harfleri ve rakamları, başka hiçbir şey. Aksanlı adlı faturalar, Çince sözleşmeler ve çok dilli arşivler, eğitilmiş dil modelleriyle gerçek bir tanıyıcı ister ve Tesseract bariz adaydır, çünkü uygulamanızın yanına kurabileceğiniz bir komut satırı programıdır. Bir belge kütüphanesinden harici bir program çağırmak basit gibi görünür. Değildir; adaptördeki ilginç kodun çoğu, programın ters davrandığı, takıldığı, iptal edildiği ya da asla görmemesi gereken şeyleri miras aldığı anda olanlarla ilgilidir
HotPDF bir Delphi uygulamasından Tesseract'ı nasıl sürer?
HotPDF, Tesseract'ı sayfa başına gizli bir alt süreç olarak çalıştırır; ona render edilmiş bir bitmap verir, bir TSV dosyası geri okur ve sonucu, yerleşik motorun kullandığı aynı IHPDFOCREngine dikişinden ortaya koyar. Sonraki hiçbir şey değişmez: koordinat eşleme, rotasyon işleme, Unicode doğrulama, güven filtreleme ve atomik işlem, zaten sahip olduğunuz metin katmanı hattıdır. Fabrika HPDFTesseractRecognition biriminde yaşar ve istekli doğrular: çalıştırılabilir var olmalı, tessdata dizini var olmalı, zaman aşımı 1 ile 3.600.000 milisaniye arasında olmalı ve dil tanımlayıcısı yalnızca ASCII harfleri, rakamları, _ ve + içerebilir. Son kontrol önemlidir, çünkü dil dizisi bir komut satırına düşer; eng+chi_sim meşru bir Tesseract değeridir, tırnak ya da boşluk taşıyan her şey değildir
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string;
Token: THPDFCancellationToken);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// eksik çalıştırılabilir, eksik tessdata, kötü bir dil tanımlayıcısı ya da
// 1..3600000 ms dışında bir zaman aşımı için EArgumentException fırlatır
Engine := HPDFCreateTesseractOCREngine(
'C:\OCR\Tesseract\tesseract.exe',
'C:\OCR\Tesseract\tessdata',
'eng+chi_sim', // '+' ile birleştirilmiş birkaç model
120000); // sayfa başına sınır, varsayılan 60000
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
Options.CancellationToken := Token;
// boş sayfa listesi tüm sayfalar demektir; metinli sayfalar varsayılan atlanır
if Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
begin
Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
' words accepted, ', Info.DroppedWordCount, ' dropped');
Doc.SaveLoadedDocument(TargetFile);
end
else
case Info.Status of
otlsCancelled: Writeln('Cancelled, document unchanged');
otlsEngineError: Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded: Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
finally
Doc.Free;
end;
end;
Her sayfa için Recognize, geçici yol altında HotPDF-OCR-{GUID} adında özel bir dizin oluşturur, render edilmiş bitmapi input.bmp olarak kaydeder ve tesseract input.bmp output --tessdata-dir … -l … --dpi N --psm 3 -c tessedit_create_tsv=1 başlatır; her yol argümanı, ters bölü ve gömülü tırnaklar için Windows komut satırı kaçırma kurallarıyla tırnaklanır. --dpi değeri THPDFOCRTextLayerOptions.DPIden gelen render DPIsidir, böylece Tesseract çözünürlüğü görüntü üst verisinden asla tahmin etmek zorunda kalmaz; --psm 3 tümüyle otomatik sayfa bölümleme ister. Motor kendini Tesseract (local CLI) olarak bildirir; Info.EngineNamee düşen budur. Tesseract ve dil modelleri HotPDF ile paketlenmez; onları kurmak uygulamanın işidir
TSV parserı neden bu kadar katı?
HotPDF'in TSV parserı herhangi bir bozuk satırda tüm sayfayı batırır, çünkü kısmen ayrıştırılmış bir kelime listesi görüntüyle sessizce çelişen bir metin katmanı üretir. Tesseract'ın TSV çıktısının sabit bir on iki sütunlu başlığı vardır, levelden texte; HotPDF ilk satırı, isteğe bağlı bir bayt sırası işaretini soyduktan sonra o tam başlıkla karşılaştırır. Sonraki her satır tam olarak on iki alana bölünmeli ve bölünme on birinci sekmeden sonra durmalı, böylece tanınan metnin içindeki bir sekme on üçüncü bir sütun kurmak yerine kelimenin parçası kalır. Yalnızca seviye 5 satırları kelimedir; 1 ile 4 arası seviyeler sayfaları, blokları, paragrafları ve satırları anlatır ve atlanırlar. Metni boş ya da saf boşluk olan seviye 5 satırları da atlanır, çünkü boş bir kelimenin kutusu vardır ama konumlanacak ya da aranacak bir şeyi yoktur. Geri kalan her şey sertçe sınanır: tamsayı geometri, değişmez en-US biçimiyle ayrıştırılmış bir güven — Alman bir yerel ayar 93.5i çöp olarak okumasın — bitmapin tümüyle içinde duran bir kutu ve 0 ile 100 arasında bir güven. Tek bir başarısızlık istisna fırlatır, motor False döndürür ve kelime dizisi temizlenir. Regresyon testleri tam olarak o vakayı içerir: geçerli bir kelimeyi bozuk bir satırın izlemesi sıfır kelime vermeli, bir değil
// HPDFLocalTSVRecognition içindeki seviye-5 döngüsünden derlenmiş
if (Fields.Count <> 12) or not TryStrToInt(Fields[0], Level) then
raise EConvertError.Create('Invalid Local OCR TSV row');
if Level <> 5 then Continue; // sayfa/blok/paragraf/satır satırları
WordText := Fields[11];
if Trim(WordText) = '' then Continue; // boşluk kelimelerin konumu yoktur
if not TryStrToInt(Fields[6], X) or not TryStrToInt(Fields[7], Y) or
not TryStrToInt(Fields[8], W) or not TryStrToInt(Fields[9], H) or
not TryStrToFloat(Fields[10], Confidence, Settings) then
raise EConvertError.Create('Invalid Local OCR word geometry');
if (X < 0) or (Y < 0) or (W <= 0) or (H <= 0) or
(Int64(X) + W > Request.Bitmap.Width) or
(Int64(Y) + H > Request.Bitmap.Height) or
not ((Confidence >= 0) and (Confidence <= 100)) then
raise EConvertError.Create('Local OCR word is outside the image');
Words[Count].Confidence := Confidence / 100; // hat 0..1 bekler
O son satır, beklemediğiniz bir varsayılanla etkileşir. Tesseract güveni 0 ile 100 arasında koşar, hat 0 ile 1 arasında çalışır ve THPDFOCRTextLayerOptions.MinimumConfidence varsayılanı 0,5'tir; 50nin altındaki her Tesseract kelimesi Info.DroppedWordCountte sayılır ve sayfaya asla ulaşmaz. Temiz bir 300 DPI taramada bu makul bir tabandır. Gürültülü bir faksta sayfanın şaşırtıcı bir payını düşürebilir ve doğru hamle, eşiği düşürmeden önce düşen sayıya bakmaktır; çünkü düşük güvenli kelimeler, tam olarak yanlış olma ihtimali en yüksek olanlardır
Tesseract alt süreci neyi miras alır?
Tesseract alt süreci HotPDF'ten tam olarak iki tutamaç miras alır: standart girdi ve çıktı için bir NUL tutamacı ve standart hata için bir dosya tutamacı. O hassasiyet konunun ta kendisidir. bInheritHandles = True ile CreateProcess, standart tutamaçları bir çocuğa geçme yoludur ama tek başına ana süreçteki her kalıtsal tutamacı geçirir; uygulamanızdaki alakasız kodun açtığı dosyalar, pipe'lar ve olaylar dahil. Alt süreç sonra o nesneleri çıkana dek canlı tutar; Tesseract bir sayfayı öğütürken bir dosya kilitli kalır ya da bir pipe ucunu hiç görmez. HotPDF o boşluğu genişletilmiş başlangıç kaydıyla kapatır: STARTUPINFOEX, PROC_THREAD_ATTRIBUTE_HANDLE_LIST taşıyan bir öznitelik listesi ve EXTENDED_STARTUPINFO_PRESENT oluşturma bayrağı. Tutamaç listesi yerindeyken bInheritHandles yine de True olmak zorundadır ama sınırı yalnızca listelenen tutamaçlar geçer. Aynı kapatma düşüncesi, çocuğun güvenilmeyen kod olduğu işçi süreçlerinde PDF görüntü codec'lerini izole etmeki de sürer; burada çocuk güvenilirdir ama ana, kendi tutamaç tablosunun tek sahibi değildir
// sabitler adlarıyla gösteriliyor; kaynak sayısal değerlerini geçirir
// iki tutamaç da bInheritHandle = True ile oluşturulur
InheritedHandles[0] := NullHandle; // stdin ve stdout
InheritedHandles[1] := ErrorHandle; // özel dizinde stderr.txt
InitializeProcThreadAttributeList(Startup.AttributeList, 1, 0, AttributeBytes);
UpdateProcThreadAttribute(Startup.AttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
@InheritedHandles[0], SizeOf(InheritedHandles), nil, nil);
CreateProcess(PChar(Executable), PChar(Command), nil, nil,
True, // tutamaç listesi bunu gerektirir
CREATE_NO_WINDOW or EXTENDED_STARTUPINFO_PRESENT,
nil, PChar(DirectoryName), Startup.StartupInfo, ProcessInfo);
İptal edilmiş bir OCR çalıştırması neden motor hatası gibi görünebilir?
İptal edilmiş bir OCR çalıştırması motor hatası gibi görünür, çünkü IHPDFOCREngine.Recognize tek bir Boolean döndürür ve False, hem "Tesseract başarısız oldu" hem de "kullanıcı Cancel'a bastı" demektir. Adaptör, alt süreç çalışırken iptal tokenini ve zaman aşımını her 25 milisaniyede yoklar; token tetiklendiğinde Recognize içinde istisna fırlatır, kendi istisnasını yakalar, temizlik yapar ve bir tanıyla False döndürür. Hat bunu motor hatası saysaydı çağıran, kullanıcının bilinçli durdurduğu bir iş için otlsEngineError görürdü. ApplyLoadedOCRTextLayer bu yüzden Recognize False döndürdüğünde önce tokeni kontrol eder ve sonucu yalnızca token set edilmemişse motor hatasına çevirir. O sıralama çok sayfalı sözleşmeyi korur: tanıma, doğrulama, bütçe muhasebesi ve içerik kurulumu, graf transactionı açılmadan önce istenen her sayfa için çalışır; 50 sayfalık işte 40. sayfadaki bir iptal otlsCancelled bildirir ve belgeyi, ilk 39 sayfa dahil, dokunulmamış bırakır. Sonradan açıklanacak kısmen aranabilir bir dosya yoktur ve başarısızlık işlemenin gerisi aynı sınırlı tarzı izler:
- Zaman aşımı
Recognizeçağrısı başınadır, başlangıcından itibaren ölçülür; varsayılan 60.000 ms tüm belgeye değil her sayfaya uygulanır - Zaman aşımında ya da iptalde hâlâ koşan bir alt süreç sonlandırılır, en çok 5 saniye beklenir ve özel dizini bir
finallybloğunda silinir output.tsv64 MiB ilestderr.txt1 MiB ile sınırlıdır; alt süreç çalışırken de çıktıktan sonra da kontrol edilir- Kelime sayısı ve UTF-16 kod birimleri, kalan
MaxWordsPerPage,MaxTotalWordsveMaxTextCodeUnitsbütçeleriyle sayfa başına sınırlandırılır; aşmak kelime listesini kırpmak yerine çalıştırmayı batırır - Standart çıktı
NULe gider, çünkü Tesseractoutput.tsvyi yazar; standart hata ise bir dosyaya gider, böylece sıfır olmayan çıkış kodu motorun kendi şikâyetinden en çok 4.096 karakterle bildirilir — genelde bir.traineddatadosyasının eksik olduğunu öğrenmenin en hızlı yolu
Tanınan kelimeler görünmez bir metin katmanına nasıl dönüşür?
HotPDF, Tesseract kelimelerini metin render modu 3 ile — ISO 32000-1 §9.3.6'da tanımlanan ne doldurma ne çizme modu — görünmez metin olarak yazar; sayfa taranmış görüntüyü göstermeye devam ederken arama ve kopyalama tanınan kelimeler üzerinde çalışır. İçerik akışı BTyi 3 Tr ile açar ve her kelime baselineında bir Tm matrisi, render DPIsında piksel cinsinden kutu yüksekliğinden türetilmiş bir font boyutu ve glyph dizisini ölçülen kutu genişliğine geren bir Tz yatay ölçeği alır; arama vurgusunun görüntüdeki kelimenin üzerinde, onun üzerinde sürüklenmek yerine oturmasının sebebi budur
Tesseract'ın TSVsinde kutular var ama baseline yoktur; adaptör her kelimeyi baselinesız bildirir ve hat baselineı, alt kenarın üzerinde kutu yüksekliğinin beşte biri olarak kestirir. Metnin kendisi, Identity-H kodlamalı ve üretilmiş bir ToUnicode CMap taşıyan paylaşılan gömülü olmayan bir Type0 fonttan geçer; tüm çalıştırma boyunca her ayrı Unicode skaler için bir CID — Çincenin, aksanlı Latinin ve ek düzlem karakterlerinin kopyalama ve aramadan sağ çıkmasının yolu budur. O tasarımın baştan söylenmeye değer iki sınırı vardır: bir çalıştırma en çok 65.535 ayrı skaler taşıyabilir ve gömülü olmayan font, ISO 19005'in font gömme gereksinimini karşılamaz; PDF/A çıktısı ayrıca gömülmüş uyumlu bir font ister. Sonucu kontrol etmek basittir ve otomatikleştirmeye değer: kaydedin, yeniden yükleyin ve Delphi'de yüklü bir PDF'ten metin çıkarmak yazısındaki sıradan yüklü belge metin yolunu çalıştırın; kelimeler beklenen sayfalardan dönerse katman gerçektir
Aynı TSV protokolünde RapidOCR ve diğer motorlar
HotPDF, RapidOCR için aynı süreç çalıştırıcıyı ve TSV parserı HPDFCreateRapidOCREngine(PythonExecutable, BridgeScript, ModelDirectory, TimeoutMilliseconds) üzerinden yeniden kullanır; Basit Çince taramalar için daha işlevsel tercih budur. Komut satırı özdeştir; yalnızca köprü script yolu Python çalıştırılabilirinden sonra yerleştirilir ve dil chi_sime sabitlenir. HotPDF köprüyü tools/OCR/rapidocr_tsv.py olarak dağıtır; rapidocr ile onnxruntime paketlerini ve üç yerel ONNX modelini bekler, otomatik model indirmelerini kapatır ve Tesseract biçimli TSV yazar, böylece Delphi tarafı ikinci bir parsera ihtiyaç duymaz. Info.EngineNamede bildirilen motor adı RapidOCR (local ONNX)dir. O biçim genel tarifi önerir: Tesseract tarzı argüman listesini kabul eden ve on iki sütunlu TSV çıkaran küçük bir scriptle sardığınız her tanıyıcı, tutamaç izolasyonunu, zaman aşımını, iptali, çıktı bütçelerini ve hep-ya-da-hiç işlemi bedavaya devralır. Adaptörler yalnızca Windows'tur, bir anda tek sayfayı senkron çalıştırır ve renderın ürettiğinin ötesinde görüntüyü düzleştirmez ya da ön işlemez; giren görüntü kalitesi çıkanın tavanını hâlâ belirler
Tesseract ve RapidOCR adaptörleri, görünmez metin katmanı yazıcısı, onları besleyen sayfa renderı ve sonucu doğrulayan metin çıkarımı, Delphi ve C++Builder için aynı yerli VCL componentiyle gelir. Belge yakalama ya da arşivleme uygulamasına OCR ekliyorsanız HotPDF Delphi PDF component, yalnızca OCR motorunun kurulumunun kaldığı hattı size verir