HotPDF izvodi kineski i višejezični OCR u Delphiju kroz svoj nativni RapidOCR DLL adapter: THPDFRapidOCRDLLOptions.ForLanguage mapira jezičnu oznaku poput 'zh-CN', 'zh-TW', 'ru' ili 'ar' na upareni prepoznavni model i rječnik znakova, a THotPDF.ApplyLoadedOCRTextLayer pretvara prepoznate linije u nevidljivi, pretraživi Unicode tekstualni sloj na skeniranim PDF stranicama
Natjerati demo na latinici da radi lakši je dio. Zanimljivi kvarovi počinju kad pređete na tradicionalni kineski ili ruski pa izlaz postane samouvjerena, dobro oblikovana besmislica, ili kad svaka linija tiho izgubi svoj zadnji znak, ili kad arapska stranica dođe natrag s okvirima teksta u pogrešnom redoslijedu. Nijedno od toga samo po sebi ne diže iznimku. Jezični preseti dodani u HotPDF v2.775.0 postoje uglavnom da zatvore te praznine, a četiri podvale dolje vrijedi razumjeti i ako nikad ne dirate nativni kod, jer svaka objašnjava simptom za kojim biste inače proveli dan u lovu
Kako ForLanguage bira model i rječnik?
THPDFRapidOCRDLLOptions.ForLanguage razrješuje oznaku na jedan od devet profila i vraća opcije koje pokazuju na <profile>/recognition.onnx i <profile>/dictionary.txt ispod vaše mape modela, dok zadržava dijeljeni detektor, neobavezni klasifikator kuta te zadane vrijednosti dretava, piksela i roka iz THPDFRapidOCRDLLOptions.Default. Metoda pretvara oznaku u mala slova, podvlake u crtice i reže okolne razmake, pa 'zh_TW', 'ZH-tw' i ' zh-tw ' svi doskoče na isti profil. Aliasi su eksplicitni popis, a ne prefiksno poklapanje: 'zh-Hant-TW' prihvaćen je jer je izlistan, dok proizvoljna regionalna varijanta koja nije izlistana diže EArgumentException prije nego se bilo koji model učita
| Profil | Jezici | Primjeri oznaka | Pribiti 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, njemački, španjolski, portugalski, talijanski, nizozemski, 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, bjeloruski | ru, ru-RU, uk, bg | PP-OCRv3 |
arabic | Arapski, perzijski, urdu | ar, ar-SA, fa, ur | PP-OCRv4 |
devanagari | Hindi, marati, nepalski | hi, mr, ne | PP-OCRv4 |
Adapter sam nikad ništa ne preuzima. Datoteke osigurate jednom s priloženim pomoćnikom, na primjer tools/Install-RapidOCRModels.ps1 -Destination C:/OCR/models -Language ch,chinese_cht,cyrillic (ili -Language All za svih devet profila), i pomoćnik postavlja dijeljeni detektor i klasifikator na korijenska imena datoteka koja Default očekuje. Nakon toga pojednostavljeni kineski sken postaje pretraživ s par linija. Instalacija enginea isti je IHPDFOCREngine spoj opisan u članku o in-process RapidOCR DLL-u i njegovoj ABI granici, pa ovaj ostaje usredotoč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, dijeljeni 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 se preskaču
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 ispisu zaslužuju napomenu. Nativni pipeline vraća jedan rezultat po detektiranoj tekstualnoj liniji, a ne po riječi, pa AcceptedWordCount ovdje broji linije, i MinimumConfidence uspoređuje se sa srednjom pouzdanošću znakova cijele linije: linija koja u prosjeku ima 0.45 ispada kao cjelina. UniqueScalarCount javlja koliko različitih Unicode skalara tekstualni sloj morao mapirati u svoj font i ToUnicode tablicu, koristan sanity check da je CJK tekst doista stigao umjesto šačice latiničnih fallbackova. Držite sučelje enginea živim kroz dokumente, jer se inicijalizacija modela događa u tvornici i to je skupi korak
Zašto mijenjanje samo prepoznavnog modela proizvodi smeće?
CTC prepoznavni model nikad ne ispisuje znakove, samo indekse klasa, i rječnik je jedina stvar koja indeks 1.204 pretvara u glif. Zamijenite li ch/recognition.onnx s cyrillic/recognition.onnx ali zadržite kineski rječnik, model će rado ispisivati valjane ćirilične indekse koje stari rječnik prevodi u slučajne Han znakove. Rezultat izgleda kao tekst, prolazi UTF-8 validaciju, i pretraživ je točno za ništa. Zato ForLanguage uvijek postavlja RecognitionModel i CharacterDictionary zajedno, i zato ručno građene opcije nikad ne smiju mijenjati jedno bez drugoga
Očita sigurnosna provjera, usporedba veličine rječnika sa širinom izlaza modela, potrebna je ali nije dostatna. Dva rječnika mogu imati isti broj unosa u drugom redoslijedu, i off-by-one u redoslijedu pomjera svaki znak za jednu kodnu točku. HotPDF stoga provjerava u dva stupnja kad tvornica inicijalizira model. Prvo, broj izlaznih klasa mora odgovarati unosima rječnika plus dva. Drugo, kad ONNX datoteka ugrađuje character popis metapodataka, svaki se unos rječnika uspoređuje s njim po redu, i nepoklapanje obara inicijalizaciju s EInvalidOperation i nativnom dijagnostikom umjesto da kasnije proizvede vjerojatno smeće
„Plus dva" dolazi iz rasporeda klasa. Klasa 0 jest CTC blank, klase 1 do N retci su rječnika u redoslijedu datoteke, i završna klasa jest razmak. Neki rječnici nose i vlastiti unos razmaka, i taj se redak mora zadržati točno kakav jest. Tu dobronamjerni Trim čini pravu štetu: pretvara unos od jednog razmaka u prazan string i pomjera ili lomi tablicu. Jedina sigurna normalizacija jest uklanjanje završnog carriage returna, pa se rječnik spremljen s CRLF završecima linija učita ispravno, dok se UTF-8 byte order mark, prazan redak ili unos koji sadrži tab odbijaju. Skica dolje pokazuje raspored u Pascalu; to je objašnjavajući kod, a ne HotPDF API
// Samo ilustracija: tablica klasa koju CTC prepoznavatelj 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 redak na kraju datoteke
SetLength(Result, Last + 3);
Result[0] := ''; // klasa 0: CTC blank
for I := 0 to Last do
begin
Entry := Lines[I];
if (Entry <> '') and (Entry[Length(Entry)] = #13) then
SetLength(Entry, Length(Entry) - 1); // CRLF: ispusti samo CR
if (Entry = '') or (Pos(#9, Entry) > 0) then
raise EArgumentException.Create('Invalid dictionary entry');
Result[I + 1] := Entry; // nikad Trim: ' ' je klasa
end;
Result[Last + 2] := ' '; // završna klasa: razmak
// Length(Result) mora odgovarati broju izlaznih klasa modela
end;
Što pohlepno CTC dekodiranje zapravo radi?
Pohlepno CTC dekodiranje bira najviše bodovanu klasu u svakom vremenskom koraku, urušava uzastopne ponavke u jedan znak i ispušta blank klasu; blank je ono što dozvoljava da doista udvostručena slova prežive. Prepoznavni model gleda tekstualnu liniju kao niz uskih okomitih rezova, i za svaki rez, odnosno vremenski korak, ispisuje vjerojatnost za svaku klasu. Linija koja sadrži AA中 mogla bi proizvesti argmax niz A A blank A 中 space. Urušavanje prva dva A koraka daje jedno A, blank ga odvaja od sljedećeg A, i rezultat je AA中 sa završnim razmakom netaknutim. Bez blank pravila book i bok bili bi nerazlučivi
Budući da je dekoder desetak linija, lako je granice pogriješiti, i kvarovi su tihi. Ako se unutarnja argmax petlja zaustavi jednu klasu prerano, klasa razmaka nikad ne može pobijediti i svaka linija dolazi natrag bez razmaknica riječi, što razbilo frazno pretraživanje na engleskim i latiničnim stranicama. Ako se vanjska petlja zaustavi jedan vremenski korak prerano, zadnji znak svake linije nestane, što za kratku liniju može biti trećina teksta. A ako čuvar ponavke ne resetira blank, udvostručeni znakovi poput ll ili kineske reduplikacije poput 谢谢 uruše se u jedan. HotPDF dekoder uključuje zadnju klasu i zadnji vremenski korak, zadržava ponavke odvojene blankom, i dodatno odbija bodove koji nisu konačni ili padaju izvan 0 do 1, i bilo koji broj klasa koji se ne poklapa s rječnikom. Evo iste logike kao Pascal ilustracija
// Samo ilustracija: pohlepno CTC dekodiranje s ispravnim granicama.
// Scores drži Steps * Classes vjerojatnosti, jedan redak 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 blank
for Step := 0 to Steps - 1 do // uključi zadnji vremenski korak
begin
Best := 0;
BestScore := Scores[Step * Classes];
for C := 1 to Classes - 1 do // uključi zadnju 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; // blank resetira čuvara ponavke
end;
end;
Pohlepno dekodiranje nije najtočnija dostupna CTC strategija; beam search s jezičnim modelom može popraviti neke dvosmislene rezove. Za tiskane dokumente na 300 DPI pohlepni rezultat obično je ono što model ima za ponuditi, i dekoder nije mjesto za nadoknadu slabosti modela. Latinični PP-OCRv3 model, na primjer, može pročitati ñ kao n i na čistom ulazu. HotPDF to ne prekriva postobradnim zamjenama znakova, jer tablica zamjena koja popravi španjolski nešto drugo slomi, i pogrešan znak u pretraživom sloju gori je od poštenog promašaja
Kako HotPDF reda tekstualne linije, uključujući arapski s desna na lijevo?
HotPDF sortira detektirane tekstualne okvire od vrha prema dnu, grupira okvire u redak kad se okomito preklapaju za barem polovicu visine manjeg okvira, i reda svaki redak s lijeva na desno, ili s desna na lijevo kad je RightToLeft omogućen; znakovi unutar svake prepoznate linije nikad se ne okreću. Grupiranje je važno jer detektor često razdvaja jednu vizualnu liniju u nekoliko okvira, na primjer oznaku i vrijednost odvojene širokim razmakom, i čisto sortiranje po gornjoj koordinati ispreplelo bi ih sa susjednom linijom kad god im se vrhovi razlikuju za piksel ili dva
Arapski preset postavlja RightToLeft := True, što DLL-u govori da reda okvire u svakom retku po njihovu desnom rubu, od desnog ruba prema unutra. To je cijeli učinak. Tekst koji model vraća za liniju već je u Unicode logičkom redoslijedu, onom u kojem arapski čitatelj čita i tipka, i to je i redoslijed koji PDF izdvajanje teksta i pretraživanje očekuju. Mehaničko okretanje stringa da „izgleda ispravno" u debuggeru slomilo bi pretraživanje, kopiranje i lijepljenje i čitače ekrana. Dvosmjerni prikaz i oblikovanje glifova posao su preglednika
Jedan engine poslužuje jedan jezični profil. Nema automatske detekcije pisma, pa dokument koji miješa pisma treba jedan engine po profilu, primijenjen na stranice koje ga koriste. Budući da ApplyLoadedOCRTextLayer prima eksplicitnu listu stranica i izvršava svaki poziv kao vlastitu transakciju sve-ili-ništa, to je jednostavno
uses
SysUtils, HPDFDoc, HPDFRapidOCRRecognition;
function CreateRapidEngine(const Tag: string): IHPDFOCREngine;
var
Models: THPDFRapidOCRDLLOptions;
begin
// diže EArgumentException za nepoznatu oznaku, prije učitavanja bilo kojeg modela
Models := THPDFRapidOCRDLLOptions.ForLanguage(Tag);
Models.MaxPixels := 33554432; // prostor 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'); // arapski 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 zadano su 16.777.216 piksela po zahtjevu, što A4 i US Letter na 300 DPI pokriva udobno, ali A3 stranica na 300 DPI oko je 3508 puta 4961 piksela, otprilike 17,4 milijuna, i zahtjev se odbija kao preko proračuna. Podignite MaxPixels (strop je 67.108.864) ili spustite THPDFOCRTextLayerOptions.DPI za velike formate. Redoslijed s desna na lijevo koristi neobavezni izvoz HPDFRapidOCRSetReadingDirection ABI verzije 1; adapter ga traži samo kad je RightToLeft postavljen, pa stariji DLL i dalje poslužuje jezike s lijeva na desno i pada pri stvaranju enginea s EArgumentException koji imenuje nedostajući izvoz za arapski
Zašto se noviji OCR modeli ne uspijevaju učitati?
HotPDF RapidOCR DLL povezuje statički ONNX Runtime 1.14, koji ne može čitati modele spremljene ONNX IR verzijom 10, a noviji izvozi poput PP-OCRv5 modela mogu tražiti noviji runtime od toga; takav model pada pri stvaranju enginea s nativnom dijagnostikom. To ograničenje razlog je što su jezični paketi pribiti na specifične PP-OCRv3 i PP-OCRv4 parove prepoznavatelja i rječnika umjesto na „najnovije", i što tablica gore miješa dvije generacije: svaki pribiti par onaj je koji se učita i potvrdi pod tim runtimeom
Instalater provodi uparivanje. Svaka datoteka u manifestu nosi SHA256 hash, postojeća datoteka s drugačijim hashom zaustavlja instalaciju umjesto da se prepiše, i svako se preuzimanje doskoči pod privremenim imenom i tek pomjera na mjesto nakon što se njegov hash poklopi. To štiti od tihe verzije problema rječnika: netko ručno ubaci noviji recognition.onnx u mapu profila, broj klasa slučajno se poklopi, i ništa ne padne dok kupac ne prijavi da pretraživanje ne nalazi riječi koje očito vidi. U vrijeme izvođenja adapter ostaje offline i nikad ne dohvaća model koji nedostaje. Prepoznavatelj također validira oblik modela pri učitavanju, prihvativši NCHW ulaz s fiksnom visinom 32 ili 48 piksela ili dinamičkom visinom, koju izvodi na 48
Ako trebate pismo koje nijedan od devet profila ne pokriva, i dalje možete usmjeriti RecognitionModel i CharacterDictionary na vlastite datoteke. Iste se provjere primjenjuju, i to je poanta: neuparen par pada pri inicijalizaciji, a ne u arhivu vašeg kupca. Za stranice gdje nijedan RapidOCR profil ne odgovara, Tesseract adapter za pretraživi PDF priključuje se na isti poziv ApplyLoadedOCRTextLayer, a za strojno tiskane ASCII obrasce ugrađeni OCR engine s podudaranjem predložaka ne treba nijedan model
Brza referenca: višejezična RapidOCR kontrolna lista
- Stvarajte opcije s
THPDFRapidOCRDLLOptions.ForLanguagei tretirajteEArgumentExceptionkao nepodržanu oznaku, a ne runtime kvar - Mijenjajte
RecognitionModeliCharacterDictionaryzajedno, nikad jedno; jednaki brojevi klasa ne dokazuju jednak redoslijed znakova - Držite rječnike kao UTF-8 bez BOM-a, nikad ne režite unose, i očekujte da model ima N + 2 klase: blank, N unosa, razmak
- Prilagođeni CTC dekoder mora pokrivati zadnju klasu i zadnji vremenski korak i zadržavati ponavke odvojene blankom
- Koristite jedan engine po jezičnom profilu i prenosite eksplicitne liste stranica za dokumente s mješovitim pismima
RightToLeftmijenja samo redoslijed okvira; prepoznati tekst ostaje u Unicode logičkom redoslijedu- Instalirajte modele s
Install-RapidOCRModels.ps1da SHA256 pribitja drže uparivanje modela i rječnika; postaviteUseAngleClassifier := Falseako ste instalirali s-SkipClassifier - Podignite
MaxPixelsiznad zadanih 16.777.216 prije izvođenja A3 ili većih stranica na 300 DPI
RapidOCR jezični preseti, nativni DLL adapter i OCR pipeline tekstualnog sloja dio su HotPDF Delphi PDF komponente za Delphi, C++Builder i Windows FPC/Lazarus, počevši od v2.775.0 za višejezične profile