HotPDF opravlja kitajski in večjezični OCR v Delphiju skozi svoj native adapter DLL RapidOCR: THPDFRapidOCRDLLOptions.ForLanguage preslika jezikovno oznako, kot je 'zh-CN', 'zh-TW', 'ru' ali 'ar', na ujemajoč model prepoznavanja in slovar znakov, THotPDF.ApplyLoadedOCRTextLayer pa spremeni prepoznane vrstice v nevidno, iskalno plast besedila Unicode na skeniranih straneh PDF
Delujoča demonstracija v latinski pisavi je lažji del. Zanimive odpovedi se začnejo, kadar preklopite na tradicionalno kitajščino ali ruščino in izhod se spremeni v samozavestno, dobro oblikovano nesmisel, kadar vsaka vrstica tiho izgubi svoj zadnji znak, ali kadar se stran v arabščini vrne s svojimi besedilnimi okvirji v napačnem vrstnem redu. Nobeno od teh ne javi izjeme same od sebe. Jezikovne prednastavitve, dodane v HotPDF v2.775.0, obstajajo večinoma, da zaprejo te vrzeli, štiri pasti spodaj pa so vredne razumevanja, tudi če se nikoli ne dotaknete native kode, ker vsaka pojasnjuje simptom, za katerim bi sicer lahko dan lovili
Kako ForLanguage izbere model in slovar?
THPDFRapidOCRDLLOptions.ForLanguage razreši oznako na enega od devetih profilov in vrne opcije, ki kažejo na <profile>/recognition.onnx in <profile>/dictionary.txt pod vašo mapo modelov, hkrati pa obdrži deljenega zaznavalca, neobveznega klasifikatorja kota ter privzete vrednosti niti, pikslov in roka od THPDFRapidOCRDLLOptions.Default. Metoda pretvori oznako v male črke, podčrtaje spremeni v pomišljaje in obreže obdajajoče presledke, tako da 'zh_TW', 'ZH-tw' in ' zh-tw ' vsi pristanejo na istem profilu. Vzdevki so izrecen seznam, ne preskus predpone: 'zh-Hant-TW' je sprejet, ker je našteto, poljubna regionalna različica, ki ni našteta, pa javi EArgumentException, preden se naloži kateri koli model
| Profil | Jeziki | Primeri oznak | Pripet model |
|---|---|---|---|
ch | Poenostavljena kitajščina in angleščina | zh, zh-CN, zh-Hans, chi_sim | PP-OCRv4 |
chinese_cht | Tradicionalna kitajščina | zh-TW, zh-HK, zh-Hant, chi_tra | PP-OCRv3 |
en | Angleščina | en, en-US, en-GB, eng | PP-OCRv4 |
latin | Francoščina, nemščina, španščina, portugalščina, italijanščina, nizozemščina, turščina | fr, de, es-419, pt-BR, tr | PP-OCRv3 |
japan | Japonščina | ja, ja-JP, jpn | PP-OCRv4 |
korean | Korejščina | ko, ko-KR, kor | PP-OCRv4 |
cyrillic | Ruščina, ukrajinščina, bolgarščina, beloruščina | ru, ru-RU, uk, bg | PP-OCRv3 |
arabic | Arabščina, perzijščina, urdujščina | ar, ar-SA, fa, ur | PP-OCRv4 |
devanagari | Hindijščina, maratščina, nepalščina | hi, mr, ne | PP-OCRv4 |
Adapter sam nikoli ne prenese ničesar. Datoteke preskrbite enkrat s priloženim pomočnikom, na primer tools/Install-RapidOCRModels.ps1 -Destination C:/OCR/models -Language ch,chinese_cht,cyrillic (ali -Language All za vseh devet profilov), pomočnik pa postavi deljenega zaznavalca in klasifikator na korenska imena datotek, ki jih pričakuje Default. Po tem postane kitajski sken v poenostavljeni pisavi iskalen s nekaj vrsticami. Omrežje motorja je isti šiv IHPDFOCREngine, opisan v članku o DLL RapidOCR v procesu in njegovi meji ABI, zato ta ostane osredotoč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, deljen zaznavalec in 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
// prazen seznam strani pomeni vsako stran; strani z že obstoječim besedilom se preskočijo
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;
Dve podrobnosti tistega izhoda zaslužita opombo. Native cevovod vrne en rezultat na zaznano vrstico besedila, ne na besedo, tako da AcceptedWordCount tu šteje vrstice, MinimumConfidence pa se primerja s povprečnim zaupanjem znakov cele vrstice: vrstica s povprečjem 0,45 se izpusti kot enota. UniqueScalarCount poroča, koliko različnih skalarjev Unicode je morala plast besedila preslikati v svojo pisavo in tabelo ToUnicode — koristen preizkus zdrave pameti, da je besedilo CJK dejansko prišlo, namesto peščice latinskih rezerv. Obdržite vmesnik motorja živ čez dokumente, ker se inicializacija modelov zgodi v tovarni in je drag korak
Zakaj zamenjava samo modela prepoznavanja izdela smeti?
Model prepoznavanja CTC nikoli ne izda znakov, samo razredne indekse, slovar pa je edina stvar, ki iz indeksa 1.204 naredi glif. Zamenjajte ch/recognition.onnx za cyrillic/recognition.onnx, obdržite pa kitajski slovar, in model bo z veseljem izdal veljavne cirilične indekse, ki jih stari slovar prevede v naključne znake Han. Rezultat izgleda kot besedilo, gre skozi potrjevanje UTF-8 in je iskalen za točno nič. Zato ForLanguage vedno nastavi RecognitionModel in CharacterDictionary skupaj in zakaj ročno zgrajene opcije nikoli ne smejo spremeniti enega brez drugega
Očitni varnostni preizkus, primerjava velikosti slovarja s širino izhoda modela, je nujen, a ni zadosten. Dva slovarja lahko imata isto število vnosov v drugačnem vrstnem redu in odmik za ena v vrstnem redu premakne vsak znak za eno kodno točko. HotPDF zato preverja v dveh stopnjah, kadar tovarna inicializira model. Najprej mora število izhodnih razredov biti enako vnosom slovarja plus dva. Drugič, kadar datoteka ONNX vgradi seznam metapodatkov character, se vsak vnos slovarja primerja z njim po vrstnem redu, neujemanje pa ne uspe inicializacije z EInvalidOperation in native diagnostiko, namesto da bi kasneje izdelalo verjetne smeti
"Plus dva" pride iz postavitve razredov. Razred 0 je CTC blank, razredi 1 do N so vrstice slovarja v vrstnem redu datoteke, zadnji razred pa je presledek. Nekateri slovarji nosijo tudi svoj vnos presledka in ta vrstica mora ostati točno takšna, kot je. Tu dobrohoten Trim naredi pravo škodo: spremeni vnos enega samega presledka v prazen niz in premakne ali pokvari tabelo. Edina varna normalizacija je odstranitev končnega znaka za povratkom na začetek vrstice, tako da se slovar, shranjen s končavami vrstic CRLF, naloži pravilno, oznaka vrstnega reda bajtov UTF-8, prazna vrstica ali vnos, ki vsebuje tabulator, pa so zavrnjeni. Skica spodaj prikaže postavitev v Pascalu; to je razlagalna koda, ne API HotPDF
// Samo ilustracija: razredna tabela, ki jo pričakuje CTC prepoznavalec
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); // nova vrstica na koncu datoteke
SetLength(Result, Last + 3);
Result[0] := ''; // razred 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: izpusti samo CR
if (Entry = '') or (Pos(#9, Entry) > 0) then
raise EArgumentException.Create('Invalid dictionary entry');
Result[I + 1] := Entry; // nikoli Trim: ' ' je razred
end;
Result[Last + 2] := ' '; // zadnji razred: presledek
// Length(Result) mora biti enak številu izhodnih razredov modela
end;
Kaj dejansko dela pohlepno dekodiranje CTC?
Pohlepno dekodiranje CTC izbere najvišje ocenjeni razred pri vsakem časovnem koraku, skrči zaporedne ponovitve v en znak in izpusti razred blank; blank je tisto, kar omogoča, da resnično podvojeni črki preživita. Model prepoznavanja gleda vrstico besedila kot zaporedje ozkih navpičnih rezin in za vsako rezino, oziroma časovni korak, izda verjetnost za vsak razred. Vrstica, ki vsebuje AA中, bi lahko izdelala zaporedje argmax A A blank A 中 space. Skrčitev prvih dveh korakov A da enega A, blank ga loči od naslednjega A, rezultat pa je AA中 s končnim presledkom, ki ostane nedotaknjen. Brez pravila blank bi bila book in bok nerazločljiva
Ker je dekodirnik samo ducat vrstic, je zlahka zgrešiti meje, odpovedi pa so tihe. Če se notranja zanka argmax ustavi en razred prezgodaj, razred presledka nikoli ne more zmagati in vsaka vrstica pride nazaj brez presledkov med besedami, kar razbije iskanje po frazah na straneh v angleščini in latinici. Če se zunanja zanka ustavi en časovni korak prezgodaj, izgine zadnji znak vsake vrstice — za kratko vrstico lahko to je tretjina besedila. In če varovala ponovitev ne ponastavi blank, se podvojeni znaki, kot je ll, ali kitajske podvajanja, kot je 谢谢, skrčijo v enega. Dekodirnik HotPDF vključuje zadnji razred in zadnji časovni korak, ohranja ponovitve, ločene z blank, poleg tega pa zavrne ocene, ki niso končne ali padejo izven 0 do 1, ter vsako število razredov, ki se ne ujema s slovarjem. Tukaj je ista logika kot Pascal ilustracija
// Samo ilustracija: pohlepno dekodiranje CTC s pravimi mejami.
// Scores drži Steps * Classes verjetnosti, ena vrstica na časovni korak
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; // razred 0 je CTC blank
for Step := 0 to Steps - 1 do // vključi zadnji časovni korak
begin
Best := 0;
BestScore := Scores[Step * Classes];
for C := 1 to Classes - 1 do // vključi zadnji razred (presledek)
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 ponastavi varovalo ponovitev
end;
end;
Pohlepno dekodiranje ni najbolj natančna razpoložljiva strategija CTC; iskanje žarka z jezikovnim modelom lahko popravi nekaj dvoumnih rezin. Za natisnjene dokumente pri 300 DPI je pohlepni rezultat običajno to, kar model ponuja, dekodirnik pa ni mesto za odpravljanje šibkosti modela. Latinski model PP-OCRv3 lahko na primer prebere ñ kot n, tudi na čistem vhodu. HotPDF tega ne prekrije s popravljanjem znakov po obdelavi, ker tabela zamenjav, ki popravi španščino, pokvari nekaj drugega, napačen znak v iskalni plasti pa je slabše od poštene zgrešitve
Kako HotPDF ureja vrstice besedila, vključno z arabščino od desne proti levi?
HotPDF razvrsti zaznane besedilne okvirje od zgoraj navzdol, združi okvirje v vrstico, kadar se navpično prekrivajo za vsaj polovico višine manjšega okvirja, in uredi vsako vrstico od leve proti desni ali od desne proti levi, kadar je omogočen RightToLeft; znaki znotraj vsake prepoznane vrstice niso nikoli obrnjeni. Združevanje šteje, ker zaznavalec pogosto razbije eno vidno vrstico v več okvirjev — na primer oznako in vrednost, ločena s široko vrzeljo — in čisto razvrščanje po zgornji koordinati bi jih prepletlo s sosednjo vrstico, kadar koli se njuna zgornja meja razlikuje za piksel ali dva
Prednastavitev arabščine nastavi RightToLeft := True, kar pove DLL-ju, da uredi okvirje v vsaki vrstici po njihovem desnem robu, od desnega roba navznoter. To je celoten učinek. Besedilo, ki ga model vrne za vrstico, je že v logičnem vrstnem redu Unicode — vrstnem redu, v katerem ga bralec arabščine bere in tipka — in to je tudi vrstni red, ki ga pričakujeta izvlečenje in iskanje besedila PDF. Mehanično obračanje niza, da bi "izgledal prav" v razhroščevalniku, bi pokvarilo iskanje, kopiranje in lepljenje ter bralnike zaslona. Dvostranski prikaz in oblikovanje glifov sta delo pregledovalnika
En motor streže enemu jezikovnemu profilu. Samodejnega zaznavanja pisave ni, zato dokument, ki meša pisave, potrebuje en motor na profil, uporabljen na straneh, ki ga uporabljajo. Ker ApplyLoadedOCRTextLayer vzame izrecen seznam strani in vsak klic zapiše kot svojo lastno transakcijo vse-ali-nič, je to enostavno
uses
SysUtils, HPDFDoc, HPDFRapidOCRRecognition;
function CreateRapidEngine(const Tag: string): IHPDFOCREngine;
var
Models: THPDFRapidOCRDLLOptions;
begin
// javi EArgumentException za neznan tag, preden se naloži kateri koli model
Models := THPDFRapidOCRDLLOptions.ForLanguage(Tag);
Models.MaxPixels := 33554432; // prostor za strani A3 pri 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'); // profil chinese_cht
Arabic := CreateRapidEngine('ar-SA'); // profil arabic, 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;
Vrstica MaxPixels je tam z razlogom. Opcije DLL se privzamejo na 16.777.216 pikslov na zahtevo, kar A4 in US Letter pri 300 DPI udobno pokrije, stran A3 pri 300 DPI pa je približno 3508 krat 4961 pikslov, torej približno 17,4 milijona, in zahteva je zavrnjena kot čez proračun. Dvignite MaxPixels (strop je 67.108.864) ali znižajte THPDFOCRTextLayerOptions.DPI za velike formate. Urejanje od desne proti levi uporablja neobvezni izvoz HPDFRapidOCRSetReadingDirection različice ABI 1; adapter ga zahteva samo, kadar je nastavljen RightToLeft, tako da starejši DLL še naprej postreže jezike od leve proti desni in odpove pri ustvarjanju motorja z EArgumentException, ki poimenuje manjkajoči izvoz za arabščino
Zakaj se novejši modeli OCR ne naložijo?
DLL RapidOCR HotPDF poveže statični ONNX Runtime 1.14, ki ne more brati modelov, shranjenih z različico ONNX IR 10, novejši izvozi, kot so modeli PP-OCRv5, pa lahko zahtevajo novejše izvajalno okolje od tega; takšen model odpove pri ustvarjanju motorja z native diagnostiko. Ta omejitev je razlog, da so jezikovni paketi pripeti na določene pare prepoznavalnika in slovarja PP-OCRv3 ter PP-OCRv4, namesto na "najnovejše", in zakaj tabela zgoraj meša obe generaciji: vsak pripet par je takšen, ki se pod tem izvajalnim okoljem naloži in potrdi
Namestitveni program uveljavi parjenje. Vsaka datoteka v svojem manifestu nosi hash SHA256, obstoječa datoteka z drugačnim hashom ustavi namestitev, namesto da bi bila prepisana, vsak prenos pa pristane pod začasnim imenom in se premakne na svoje mesto šele, ko se njegov hash ujema. To ščiti pred tiho različico problema slovarja: nekdo ročno spusti novejši recognition.onnx v mapo profila, število razredov po naključju se ujema in nič ne odpove, dokler stranka ne poroča, da iskanje ne najde besed, ki jih vidno vidi. Ob izvajanju adapter ostane offline in nikoli ne prenese manjkajočega modela. Prepoznavalec tudi potrjuje obliko modela ob nalaganju, sprejema vhod NCHW s fiksno višino 32 ali 48 pikslov ali dinamično višino, ki jo poganja pri 48
Če potrebujete pisavo, ki je noben od devetih profilov ne pokriva, lahko še vedno usmerite RecognitionModel in CharacterDictionary na svoje lastne datoteke. Isti preizkusi veljajo — to je točka: neujemajoč par odpove pri inicializaciji, ne v arhivu vaše stranke. Za strani, kjer se ne ujema noben profil RapidOCR, se adapter Tesseract za iskalni PDF priključi na isti klic ApplyLoadedOCRTextLayer, za strojno natisnjene obrazce ASCII pa vgrajeni motor OCR z ujemanjem predlog ne potrebuje modelov sploh
Hiter pregled: kontrolni seznam večjezičnega RapidOCR
- Ustvarite opcije s
THPDFRapidOCRDLLOptions.ForLanguageinEArgumentExceptionobravnavajte kot nepodprto oznako, ne kot odpoved izvajanja - Spremenite
RecognitionModelinCharacterDictionaryskupaj, nikoli enega samega; enaka števila razredov ne dokazujejo enakega vrstnega reda znakov - Slovarje hranite kot UTF-8 brez BOM, vnosov nikoli ne obrezujte in pričakujte, da ima model N + 2 razredov: blank, N vnosov, presledek
- Lasten dekodirnik CTC mora pokriti zadnji razred in zadnji časovni korak ter ohraniti ponovitve, ločene z blank
- Uporabite en motor na jezikovni profil in podajte izrecne sezname strani za dokumente z mešanimi pisavami
RightToLeftspremeni samo vrstni red okvirjev; prepoznano besedilo ostane v logičnem vrstnem redu Unicode- Namestite modele z
Install-RapidOCRModels.ps1, tako da pripetja SHA256 držita parjenje modela in slovarja; nastaviteUseAngleClassifier := False, če ste namestili z-SkipClassifier - Dvignite
MaxPixelsnad privzeti 16.777.216, preden poganjate strani A3 ali večje pri 300 DPI
Jezikovne prednastavitve RapidOCR, native adapter DLL in cevovod plasti besedila OCR so del komponente HotPDF Delphi PDF za Delphi, C++Builder in Windows FPC/Lazarus, od v2.775.0 za večjezične profile