HotPDF obavlja kineski i višejezični OCR u Delphi-ju kroz svoj nativni RapidOCR DLL adapter: THPDFRapidOCRDLLOptions.ForLanguage mapira jezički tag poput 'zh-CN', 'zh-TW', 'ru' ili 'ar' na upareni model prepoznavanja i rečnik karaktera, a THotPDF.ApplyLoadedOCRTextLayer pretvara prepoznate linije u nevidljivi, pretraživi Unicode tekstualni sloj na skeniranim PDF stranicama
Naterati demo na latinici da radi je laki deo. Zanimljivi kvarovi počinju kad pređete na tradicionalni kineski ili ruski i izlaz se pretvori u samouvereno, uredno formirano bezumlje, ili kad svaka linija tiho izgubi svoj poslednji karakter, ili kad arapska stranica stigne sa svojim tekstualnim kutijama u pogrešnom redosledu. Ništa od toga ne podiže izuzetak samo od sebe. Jezički preseti dodati u HotPDF v2.775.0 postoje uglavnom da zatvore te praznine, i četiri zamke ispod vredi razumeti čak i ako nikada ne dirate nativni kod, jer svaka objašnjava simptom za koji biste inače potrošili dan u jurenju
Kako ForLanguage bira model i rečnik?
THPDFRapidOCRDLLOptions.ForLanguage razrešava tag na jedan od devet profila i vraća opcije koje pokazuju na <profile>/recognition.onnx i <profile>/dictionary.txt ispod vašeg direktorijuma modela, dok čuva deljeni detektor, opcionu klasifikator ugla i podrazumevane vrednosti niti, piksela i roka iz THPDFRapidOCRDLLOptions.Default. Metoda pretvara tag u mala slova, pretvara donje crte u crtice i odseče okolni prazan prostor, pa 'zh_TW', 'ZH-tw' i ' zh-tw ' svi doskoče na isti profil. Alijasi su eksplicitan spisak, a ne prefiksno poklapanje: 'zh-Hant-TW' se prima jer je izlistan, dok proizvoljna regionalna varijanta koja nije izlistana podiže EArgumentException pre nego što se bilo koji model učita
| Profil | Jezici | Primer tagova | Pribijen model |
|---|---|---|---|
ch | Pojednostavljeni kineski i engleski | zh, zh-CN, zh-Hans, chi_sim | PP-OCRv4 |
chinese_cht | Tradicionalni kineski | zh-TW, zh-HK, zh-Hant, chi_tra | PP-OCRv3 |
en | Engleski | en, en-US, en-GB, eng | PP-OCRv4 |
latin | Francuski, nemački, španski, portugalski, italijanski, holandski, turski | fr, de, es-419, pt-BR, tr | PP-OCRv3 |
japan | Japanski | ja, ja-JP, jpn | PP-OCRv4 |
korean | Korejski | ko, ko-KR, kor | PP-OCRv4 |
cyrillic | Ruski, ukrajinski, bugarski, beloruski | ru, ru-RU, uk, bg | PP-OCRv3 |
arabic | Arapski, persijski, urdu | ar, ar-SA, fa, ur | PP-OCRv4 |
devanagari | Hindi, marati, nepalski | hi, mr, ne | PP-OCRv4 |
Adapter sam nikada ništa ne preuzima. Fajlove obezbedite jednom priloženim pomoćnikom, na primer tools/Install-RapidOCRModels.ps1 -Destination C:/OCR/models -Language ch,chinese_cht,cyrillic (ili -Language All za svih devet profila), i pomoćnik postavlja deljeni detektor i klasifikator na korenska imena fajlova koja Default očekuje. Posle toga, sken na pojednostavljenom kineskom postaje pretraživ sa par linija. Instalacija engine-a je isto IHPDFOCREngine priključište opisano u članku o in-process RapidOCR DLL-u i njegovoj ABI granici, pa ovaj ostaje usredsređen na jezike
uses
SysUtils, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeChineseScanSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Models: THPDFRapidOCRDLLOptions;
Layer: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// ch/recognition.onnx + ch/dictionary.txt, deljeni detektor i klasifikator
Models := THPDFRapidOCRDLLOptions.ForLanguage('zh-CN');
Engine := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models', Models);
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
if Doc.LoadFromFile(SourceFile) < 1 then
raise Exception.Create('Cannot load ' + SourceFile);
Layer := 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, Layer, Info) then
raise Exception.Create(string(Info.Diagnostic));
Writeln(string(Info.EngineName), ': ', Info.AcceptedWordCount,
' lines, ', Info.UniqueScalarCount, ' distinct characters');
Doc.SaveLoadedDocument(TargetFile);
finally
Doc.Free;
end;
end;
Dva detalja u tom izlazu zaslužuju napomenu. Nativni cevovod vraća jedan rezultat po detektovanoj tekstualnoj liniji, ne po reči, pa AcceptedWordCount ovde broji linije, i MinimumConfidence se poredi sa srednjim poverenjem karaktera cele linije: linija koja prosečno daje 0.45 ispušta se kao jedinica. UniqueScalarCount prijavljuje koliko različitih Unicode skalara je tekstualni sloj morao mapirati u svoj font i ToUnicode tabelu, korisna provera zdravog razuma da je CJK tekst zaista stigao umesto šačice latiničnih fallbackova. Držite interfejs engine-a živim preko dokumenata, jer se inicijalizacija modela dešava u fabrici i to je skup korak
Zašto menjanje samo modela prepoznavanja proizvodi đubre?
CTC model prepoznavanja nikada ne ispisuje karaktere, samo indekse klasa, i rečnik je jedina stvar koja indeks 1.204 pretvara u znak. Zamenite ch/recognition.onnx sa cyrillic/recognition.onnx ali zadržite kineski rečnik, i model će rado ispisivati validne ćirilične indekse koje stari rečnik prevodi u nasumične Han karaktere. Rezultat izgleda kao tekst, prolazi UTF-8 validaciju, i pretraživ je za tačno ništa. Zato ForLanguage uvek postavlja RecognitionModel i CharacterDictionary zajedno, i zato ručno građene opcije nikada ne bi menjale jedno bez drugog
Očigledna sigurnosna provera, poređenje veličine rečnika sa širinom izlaza modela, potrebna je ali ne i dovoljna. Dva rečnika mogu imati isti broj unosa u drugom redosledu, i odstupanje od jednog u redosledu pomera svaki karakter za jedan code point. HotPDF zato proverava u dve faze kad fabrika inicijalizuje model. Prvo, broj izlaznih klasa mora biti jednak unosima rečnika plus dva. Drugo, kad ONNX fajl ugnezdjava character listu metapodataka, svaki unos rečnika poredi se sa njom po redu, i neslaganje pada inicijalizaciju sa EInvalidOperation i nativnom dijagnostikom umesto da kasnije proizvede uverljivo đubre
„Plus dva“ dolazi od rasporeda klasa. Klasa 0 je CTC prazan, klase 1 do N su linije rečnika u redosledu fajla, i završna klasa je razmak. Neki rečnici nose i svoj unos razmaka, i ta linija mora ostati tačno kakva jeste. Tu dobronamerni Trim čini pravu štetu: pretvara unos od jednog razmaka u prazan string i pomera ili lomi tabelu. Jedina bezbedna normalizacija je uklanjanje završnog znaka za povratak, pa rečnik sačuvan sa CRLF krajevima linija učitava se ispravno, dok UTF-8 BOM, prazna linija ili unos koji sadrži tab bivaju odbijeni. Skica ispod pokazuje raspored u Pascalu; to je objašnjavajući kod, ne HotPDF API
// Samo ilustracija: tabela klasa koju CTC prepoznavač očekuje
uses
SysUtils, IOUtils;
function BuildCTCClassTable(const FileName: string): TArray<string>;
var
Text, Entry: string;
Lines: TArray<string>;
I, Last: Integer;
begin
Text := TEncoding.UTF8.GetString(TFile.ReadAllBytes(FileName));
if (Text <> '') and (Text[1] = #$FEFF) then
raise EArgumentException.Create('Dictionary must be UTF-8 without a BOM');
Lines := Text.Split([#10]);
Last := High(Lines);
if (Last >= 0) and (Lines[Last] = '') then
Dec(Last); // novi red na kraju fajla
SetLength(Result, Last + 3);
Result[0] := ''; // klasa 0: CTC prazan
for I := 0 to Last do
begin
Entry := Lines[I];
if (Entry <> '') and (Entry[Length(Entry)] = #13) then
SetLength(Entry, Length(Entry) - 1); // CRLF: odbaci samo CR
if (Entry = '') or (Pos(#9, Entry) > 0) then
raise EArgumentException.Create('Invalid dictionary entry');
Result[I + 1] := Entry; // nikada Trim: ' ' je klasa
end;
Result[Last + 2] := ' '; // završna klasa: razmak
// Length(Result) mora biti jednak broju izlaznih klasa modela
end;
Šta pohlepno CTC dekodiranje zapravo radi?
Pohlepno CTC dekodiranje bira najbolje ocenjenu klasu na svakom vremenskom koraku, sruši uzastopne ponove u jedan karakter, i baca klasu prazan; prazan je ono što dozvoljava istinski udvostručenim slovima da prežive. Model prepoznavanja gleda tekstualnu liniju kao niz uskih vertikalnih isečaka, i za svaki isečak, ili vremenski korak, ispisuje verovatnoću za svaku klasu. Linija koja sadrži AA中 mogla bi proizvesti argmax sekvencu A A blank A 中 space. Srušavanje prva dva A koraka daje jedno A, prazan ga odvaja od sledećeg A, i rezultat je AA中 sa završnim razmakom netaknutim. Bez pravila praznog, book i bok bili bi nerazlučivi
Pošto je dekoder samo tucet linija, lako je pogrešiti granice, i kvarovi su tihi. Ako unutrašnja argmax petlja stane jednu klasu pre, klasa razmaka nikada ne može pobediti i svaka linija se vraća bez razmaka reči, što polomi pretragu fraza na engleskim i latiničnim stranicama. Ako spoljašnja petlja stane jedan vremenski korak pre, poslednji karakter svake linije nestane, što za kratku liniju može biti trećina teksta. A ako čuvarku ponova ne resetuje prazan, udvostručeni karakteri poput ll ili kineskih reduplikacija poput 谢谢 sruše se u jedan. HotPDF dekoder uključuje poslednju klasu i poslednji vremenski korak, čuva ponove razdvojene praznim, i dodatno odbija ocene koje nisu konačne ili padaju van 0 do 1, i bilo koji broj klasa koji se ne poklapa sa rečnikom. Evo iste logike kao Pascal ilustracija
// Samo ilustracija: pohlepno CTC dekodiranje sa ispravnim granicama.
// Scores drži Steps * Classes verovatnoća, jedan red po vremenskom koraku
function GreedyCTCDecode(const Scores: array of Single;
Steps, Classes: Integer; const Characters: array of string): string;
var
Step, C, Best, Previous: Integer;
BestScore: Single;
begin
if (Classes < 3) or (Length(Characters) <> Classes) or
(Length(Scores) <> Steps * Classes) then
raise EArgumentException.Create('Model output does not match the dictionary');
Result := '';
Previous := 0; // klasa 0 je CTC prazan
for Step := 0 to Steps - 1 do // uključite poslednji vremenski korak
begin
Best := 0;
BestScore := Scores[Step * Classes];
for C := 1 to Classes - 1 do // uključite poslednju klasu (razmak)
if Scores[Step * Classes + C] > BestScore then
begin
Best := C;
BestScore := Scores[Step * Classes + C];
end;
if (Best <> 0) and (Best <> Previous) then
Result := Result + Characters[Best];
Previous := Best; // prazan resetuje čuvarku ponova
end;
end;
Pohlepno dekodiranje nije najtačnija dostupna CTC strategija; beam pretraga sa jezičkim modelom može popraviti neke dvosmislene isečke. Za štampane dokumente na 300 DPI pohlepni rezultat obično je ono što model ima da ponudi, i dekoder nije mesto da se kompenziraju slabosti modela. Latinični PP-OCRv3 model na primer može pročitati ñ kao n čak i na čistom unosu. HotPDF to ne prekriva post-processing zamenama karaktera, jer zamenska tabela koja popravi španski polomi nešto drugo, i pogrešan karakter u pretraživom sloju gore je od poštenog promašaja
Kako HotPDF uređuje tekstualne linije, uključujući arapski s-desna-u-levo?
HotPDF sortira detektovane tekstualne kutije odozgo naniže, grupiše kutije u red kad se vertikalno preklapaju najmanje polovinom visine manje kutije, i uređuje svaki red s-leva-u-desno, ili s-desna-u-levo kad je RightToLeft uključen; karakteri unutar svake prepoznate linije nikada se ne obrću. Grupisanje je bitno jer detektor često cepa jednu vizuelnu liniju na više kutija, na primer oznaku i vrednost razdvojene širokim razmakom, i čisto sortiranje po gornjoj koordinati ispreplelo bi ih sa susednom linijom kad god se njihovi vrhovi razlikuju za piksel ili dva
Arapski preset postavlja RightToLeft := True, što govori DLL-u da uređuje kutije u svakom redu po njihovoj desnoj ivici, od desne margine ka unutrašnjosti. To je ceo efekat. Tekst koji model vraća za liniju već je u Unicode logičkom redosledu, redosledu u kojem arapski čitalac čita i kuca, i to je takođe redosled koji izdvajanje teksta iz PDF-a i pretraga očekuju. Mehaničko obrtanje stringa da bi „izgledao ispravno“ u debuggeru polomilo bi pretragu, kopiranje i lepljenje i čitače ekrana. Dvosmerni prikaz i oblikovanje znakova posao su pregledača
Jedan engine servira jedan jezički profil. Nema automatske detekcije pisma, pa dokument koji meša pisma treba jedan engine po profilu, primenjen na stranice koje ga koriste. Pošto ApplyLoadedOCRTextLayer prima eksplicitnu listu stranica i uveruje svaki poziv kao sopstvenu transakciju sve-ili-ništa, to je jednostavno
uses
SysUtils, HPDFDoc, HPDFRapidOCRRecognition;
function CreateRapidEngine(const Tag: string): IHPDFOCREngine;
var
Models: THPDFRapidOCRDLLOptions;
begin
// podiže EArgumentException za nepoznat tag, pre nego što se bilo koji model učita
Models := THPDFRapidOCRDLLOptions.ForLanguage(Tag);
Models.MaxPixels := 33554432; // prostora za A3 stranice na 300 DPI
Result := HPDFCreateRapidOCRDLLOCREngine(
'C:\OCR\Win64\HotPDFRapidOCR.dll', 'C:\OCR\models', Models);
end;
procedure OCRMixedArchive(Doc: THotPDF);
var
Chinese, Arabic: IHPDFOCREngine;
Layer: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
Chinese := CreateRapidEngine('zh-TW'); // chinese_cht profil
Arabic := CreateRapidEngine('ar-SA'); // arabic profil, RightToLeft = True
Layer := THPDFOCRTextLayerOptions.Default;
if not Doc.ApplyLoadedOCRTextLayer([0, 1, 2], Chinese, Layer, Info) then
raise Exception.Create(string(Info.Diagnostic));
if not Doc.ApplyLoadedOCRTextLayer([3], Arabic, Layer, Info) then
raise Exception.Create(string(Info.Diagnostic));
end;
Linija MaxPixels tamo je s razlogom. Opcije DLL-a podrazumevaju 16.777.216 piksela po zahtevu, što A4 i US Letter na 300 DPI pokriva sa udobnošću, ali A3 stranica na 300 DPI je oko 3508 sa 4961 piksela, otprilike 17,4 miliona, i zahtev se odbija kao preko budžeta. Povećajte MaxPixels (plafon je 67.108.864) ili spustite THPDFOCRTextLayerOptions.DPI za velike formate. Redosled s-desna-u-levo koristi opcion izvoz HPDFRapidOCRSetReadingDirection ABI verzije 1; adapter ga traži samo kad je RightToLeft postavljen, pa stariji DLL i dalje servira jezike s-levo-u-desno i pada pri stvaranju engine-a sa EArgumentException koja imenuje nedostajući izvoz za arapski
Zašto noviji OCR modeli ne uspevaju da se učitaju?
HotPDF RapidOCR DLL vezuje statički ONNX Runtime 1.14, koji ne može čitati modele sačuvane ONNX IR verzijom 10, i noviji izvozi poput PP-OCRv5 modela mogu tražiti noviji runtime od toga; takav model pada pri stvaranju engine-a sa nativnom dijagnostikom. To ograničenje je razlog što su jezički paketi pribijeni na konkretno uparene PP-OCRv3 i PP-OCRv4 prepoznavače i rečnike umesto na „najnovije“, i zašto tabela gore meša dve generacije: svaki pribijen par jedan je koji se učita i verifikuje pod tim runtime-om
Instaler sprovodi uparivanje. Svaki fajl u svom manifestu nosi SHA256 heš, postojeći fajl sa drugačijim hešom zaustavlja instalaciju umesto da se prepiše, i svako preuzimanje doskoči pod privremenim imenom i tek se pomera na mesto posle što se njegov heš poklopi. To štiti od tihe verzije problema rečnika: neko ručno spusti noviji recognition.onnx u folder profila, broj klasa slučajno se poklopi, i ništa ne padne dok kupac ne prijavi da pretraga ne nalazi reči koje očigledno vide. U vreme rada adapter ostaje oflajn i nikada ne dovlči nedostajući model. Prepoznavač takođe validira oblik modela pri učitavanju, primajući NCHW unos sa fiksnom visinom 32 ili 48 piksela ili dinamičkom visinom, koju izvodi na 48
Ako treba pismo koje nijedan od devet profila ne pokriva, i dalje možete usmeriti RecognitionModel i CharacterDictionary na sopstvene fajlove. Iste provere važe, što je poenta: nepoklopljen par pada pri inicijalizaciji, ne u arhivu vašeg kupca. Za stranice gde nijedan RapidOCR profil ne prija, Tesseract adapter za pretraživi PDF priključuje se na isti poziv ApplyLoadedOCRTextLayer, a za mašinski štampane ASCII obrasce ugrađeni OCR engine sa poklapanjem šablona ne trebaju modeli uopšte
Brzi pregled: višejezična RapidOCR proverna lista
- Stvarajte opcije sa
THPDFRapidOCRDLLOptions.ForLanguagei tretirajteEArgumentExceptionkao nepodržan tag, ne kao grešku u vreme rada - Menjajte
RecognitionModeliCharacterDictionaryzajedno, nikada jedno samo; jednaki brojevi klasa ne dokazuju jednak redosled karaktera - Držite rečnike kao UTF-8 bez BOM-a, nikada ne odsecajte unose, i očekujte da model ima N + 2 klase: prazan, N unosa, razmak
- Prilagođeni CTC dekoder mora pokriti poslednju klasu i poslednji vremenski korak i čuvati ponove razdvojene praznim
- Koristite jedan engine po jezičkom profilu i predajte eksplicitne liste stranica za dokumente mešanih pisama
RightToLeftmenja samo redosled kutija; prepoznati tekst ostaje u Unicode logičkom redosledu- Instalirajte modele sa
Install-RapidOCRModels.ps1da SHA256 pribijanja drže uparivanje modela i rečnika; postaviteUseAngleClassifier := Falseako ste instalirali sa-SkipClassifier - Podignite
MaxPixelsiznad podrazumevanih 16.777.216 pre pokretanja A3 ili većih stranica na 300 DPI
RapidOCR jezički preseti, nativni DLL adapter i cevovod OCR tekstualnog sloja deo su HotPDF Delphi PDF Component za Delphi, C++Builder i Windows FPC/Lazarus, počevši od v2.775.0 za višejezične profile