HotPDF tekee skannatuista PDF-sivuista haettavia in-process RapidOCR:lla funktion HPDFCreateRapidOCRDLLOCREngine kautta, versiossa v2.774.0 lisätty tehdas, joka lataa HotPDFRapidOCR.dllin, pitää ONNX-detektointi-, kulmaluokittelu- ja tunnistusmallit muistissa ja palauttaa IHPDFOCREnginen. Annat kyseisen moottorin funktiolle THotPDF.ApplyLoadedOCRTextLayer, joka renderöi jokaisen sivun, ajaa CPU-inferenssin ilman Pythonia tai lapsiprosessia ja vahvistaa näkymättömän Unicode-tekstitason
Perustelu on kustannus sivua kohden. Aiemmin toimitettu RapidOCR-prosessisovitin, HPDFCreateRapidOCREngine, käynnistää Python-työläisen jokaiselle Recognize-kutsulle, ja kyseinen työläinen tuo ajonympäristönsä ja lataa ONNX-mallinsa ennen kuin lukee yhtään pikseliä. 500 sivun arkistossa kyseinen käynnistysvero toistuu 500 kertaa, ja käyttöönotto tarkoittaa Python-ympäristön toimittamista Delphi-suoritettavan viereen. Natiivi DLL lataa mallit kerran, kun luot moottorin, ja käyttöönotto kutistuu DLL:ään, sen mallitiedostoihin ja merkistösanakirjaan. Se, mistä luoput vastineeksi, on mahdollisuus tappaa jumiutunut tunnistin, ja suurin osa tämän sovittimen suunnittelusta on siitä selviämistä rehellisesti
Miten skannatusta PDF:stä tehdään haettava RapidOCR-DLL:llä?
Haettavan PDF:n luominen natiivilla RapidOCR-DLL:llä vie yhden tehdaskutsun ja saman ApplyLoadedOCRTextLayer-kutsun, jota jokainen HotPDF-OCR-moottori käyttää. Tehdas asuu yksikössä HPDFRapidOCRRecognition ja validoi innokkaasti: DLL:n ja mallikansion on oltava olemassa, jokaisen mallin ja sanakirjatiedoston on ratkeava, ABI-version on oltava 1, ja kaikkien vaadittujen vientien on oltava läsnä ennen kuin yksikään malli alustetaan. Määritysvirheet nostavat EArgumentExceptionn; latautumista epäonnistuva malli nostaa EInvalidOperationin, joka kantaa DLL:n kirjoittaman diagnostiikkatekstin
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFRapidOCRRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// Mallit latautuvat tässä, minkään tunnistustakarajan ulkopuolella.
// THPDFRapidOCRDLLOptions.Defaultin suhteelliset mallinimet ratkeavat
// mallikansiota vasten.
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
// tyhjä sivuluettelo tarkoittaa jokaista sivua; sivut, joilla on jo tekstiä, ohitetaan
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 nimeää tiedostot ch_PP-OCRv3_det_infer.onnx, ch_PP-OCRv3_rec_infer.onnx, ch_ppocr_mobile_v2.0_cls_infer.onnx ja ppocr_keys_v1.txt, yhdellä CPU-säikeellä, 16 777 216 pikselin syöttörajalla ja 60 000 ms tunnistustakarajalla. Versiosta v2.775.0 alkaen THPDFRapidOCRDLLOptions.ForLanguage vaihtaa paikalleen vastaavan tunnistusmallin ja sanakirjan perinteiselle kiinalaiselle, venäjälle, japanille, arabiaksi ja muille profiileille; miksi mallin ja sanakirjan on vaihduttava yhdessä, käsitellään artikkelissa RapidOCR-monikieliset mallit ja CTC-sanakirjat HotPDF:ssä. Moottori ilmoittaa itsensä muodossa RapidOCR (native DLL) kentässä Info.EngineName, mikä pitää lokit yksiselitteisinä artikkelin ulkoisen Tesseract-OCR-prosessisovittimen ja artikkelin sisäänrakennetun mallivertailu-OCR-moottorin vieressä
Miksi C ABI puhuu vain int32_t:tä ja UTF-8-tavuja?
HotPDFRapidOCR.dllin ABI käyttää vain kiinteän levyisiä kokonaislukuja, raakoja osoittimia ja eksplisiittisiä tavumääriä, koska Delphi, C++Builder ja Free Pascal eivät jaa mitään MSVC:n kanssa C-kutsukäytännön ulkopuolelta. std::stringillä, std::vectorilla tai C++-poikkeuksella on asettelu ja purkumalli, jotka kuuluvat yhdelle kääntäjälle ja yhdelle ajokirjastolle. Anna minkä tahansa niistä ylittää rajan, ja vika on korruptoitunut pino tai väärän allokaattorin vapauttama heap-lohko, ei siisti virhe
ABI-versio 1 noudattaa siksi lyhyttä sääntöluetteloa. Jokainen vienti on cdecl ja palauttaa int32_t-tilan, jossa 1 merkitsee onnistumista ja 0 epäonnistumista. Jokainen epäonnistua voiva funktio ottaa kutsujan omistaman diagnostiikkapuskurin ja sen kapasiteetin tavuina; DLL kirjoittaa NUL-päätteisen UTF-8-viestin, katkaistuna mahtumaan, ja sovitin dekoodaa sen kovalla päätemerkillä oman 4 096-tavuisen puskurinsa viimeisessä tavussa. Jokaisen vientirungon ympärillä on try sekä catch (const std::exception &) että catch (...), joten ONNX Runtime -virhe, OpenCV-assertion tai epäkelpo sanakirja muuttuu tilaksi 0 plus teksti, ei koskaan Pascal-koodiin pakenevaksi poikkeukseksi
| Vienti | Rooli | Milloin sovitin ratkaisee sen |
|---|---|---|
HPDFRapidOCRAbiVersion | Palauttaa arvon 1; mikä tahansa muu arvo hylätään | Ensin, ennen kaikkea muuta |
HPDFRapidOCRCreate | Lataa detektointi-, valinnaisen luokittelun ja tunnistusmallit sekä sanakirjan | Tehtaassa |
HPDFRapidOCRRecognize | Ajaa yhden bittikartan ja emittoi yhden callbackin tekstiriviä kohden | Tehtaassa |
HPDFRapidOCRDestroy | Vapauttaa malli-instanssin | Tehtaassa |
HPDFRapidOCRSetReadingDirection | Valinnainen oikealta vasemmalle -rivijärjestys, lisätty versiossa v2.775.0 | Vain kun RightToLeft on asetettu |
Valinnainen vienti ratkaistaan laiskasti tahallaan: v2.774.0-DLL, jolta se puuttuu, palvelee yhä vasemmalta oikealle -pyyntöjä. DLL ladataan funktiolla LoadLibraryEx, jonka hakuflagit kattavat DLL:n oman kansion plus oletusturvalliset hakemistot, joten HotPDFRapidOCR.dllin viereen sijoitetut ONNX Runtime- tai OpenCV-riippuvuudet löytyvät koskematta muuttujaan PATH. Malli- ja sanakirjapolut kulkevat UTF-8:nä, ja DLL muuntaa ne funktiolla MultiByteToWideChar tiukassa tilassa ennen tiedostojen avaamista leveämerkkisten API:jen kautta, joten kiinalaisen tai kyrillisen käyttäjänimen alla oleva mallikansio toimii sen sijaan, että levennettäisiin tavu tavulta hölynpölyksi
Yksi sääntö elää koostuuversiossa, ei otsikkotiedostossa. DLL linkittää ONNX Runtime:n ja OpenCV:n staattisesti, ja oletusarvoinen CMake-määritys käyttää staattista release-CRT:tä (/MT). /MDä vasten käännetyt staattiset kirjastot, sekoitettuna /MT-DLL:ään, tuottavat parhaimmillaan linkitysvirheitä ja pahimmillaan kaksi itsenäistä heapia, joten hankittujen kirjastojen on vastattava sitä CRT-tilaa, jota DLL käyttää
Mitä tapahtuu TBitmapin ja tekstirivin välillä?
HotPDF ojentaa DLL:lle itsenäisen ylhäältä alas -suuntautuisen BGR-snapshotin renderöidystä sivusta, ja DLL ojentaa takaisin yhden callbackin kutakin tunnistettua tekstiriviä kohden lainatulla UTF-8-tekstillä, jonka sovittimen on kopioitava ennen paluuta
Delphissä sovitin sijoittaa sivubittikartan yksityiseen TBitmapiin, pakottaa muodon pf24bit ja lukee rivit funktiolla GetDIBits negatiivisella biHeightillä, mikä antaa ylhäältä alas -rivit, täytettyinä neljän tavun tasaukseen; kyseinen stride annetaan eksplisiittisesti. FPC:llä se lukee funktion CreateIntfImage kautta, koska LCL-scanline-kirjoitukset voivat päivittää raakakuvaa päivittämättä GDI-kahvaa. Kutsujan bittikarttaa ei koskaan muokata, ja pikselibudjetti (MaxPixels, oletus 16 777 216 ja säädettävissä arvoon 67 108 864 asti) sekä 32 767 pikselin raja per dimensio tarkistetaan ennen snapshot-puskurin varaamista
DLL:n sisällä snapshot täytetään 50 valkoisella pikselillä, tekstialueet detektoidaan 1 024 pikselin maksimisivulla, laatikot järjestetään vaakariveiksi, ja jokainen rajaus kiertyy tarvittaessa kulmaluokittelijan toimesta ennen tunnistusta. Jokainen tekstirivi kulkee sitten callbackin läpi, joka vastaanottaa osoittimen const char*, tavumäärän, kokonaislukulaatikon alkuperäisen kuvan pikseleinä ja keskimääräisen merkkiluottamusarvon. Tekstiosoitin on kelvollinen vain callbackin aikana, joten sovitin kopioi sen heti, ja se on tiukka siinä, mitä hyväksyy:
- UTF-8 dekoodataan lipulla
MB_ERR_INVALID_CHARS; epäkelpo jakso kaataa sivun korvausmerkkien tuottamisen sijaan haettavaan tasoon - C0- ja C1-ohjausmerkit hylätään, ja pelkkiä välilyöntejä sisältävät rivit ohitetaan
- Laatikon on oltava bittikartan sisällä, ja luottamusarvon on oltava äärellinen arvo väliltä 0–1
- Tekstiä lasketaan pyynnön
MaxTextCodeUnitsia vasten kovalla 1 048 576 UTF-16-yksikön katolla per kutsu, ja täydentävän tason merkit maksavat kaksi yksikköä - Mikä tahansa Pascal-poikkeus callbackin sisällä napataan sinne, talletetaan ja muutetaan paluuarvoksi 0, mikä saa DLL:n pysähtymään ja raportoimaan epäonnistumisen; talletettu viesti muuttuu sitten diagnostiikaksi
Kaksi seurausta merkitsee viritykselle. Ensinnäkin tulosteen yksikkö on rivi, ei sana: jokainen rivi kuluttaa yhden MaxWords-paikan, kentät Info.AcceptedWordCount ja Info.DroppedWordCount laskevat rivejä, ja hakukorostus ulottuu rivilaatikon yli. Toiseksi MinimumConfidence (oletus 0.5) verrataan rivin keskimääräiseen merkkiluottamusarvoon, joten rivi, jossa on yksi lukukelvoton merkki kahdenkymmenen puhtaan joukossa, selviää yleensä. DLL ei toimita perusviivaa, joten tekstitasoputki estimoi yhden laatikosta. Tyhjä sivu onnistuu nollalla rivillä, ja mikä tahansa epäonnistuminen tyhjentää osittaiset tulokset, joten monisivuinen vahvistus pysyy kaikki-tai-ei mitään -periaatteella
Mallien omistajuus ja säieturvallisuus
Jokainen RapidOCR-DLL-moottori omistaa täsmälleen yhden malli-instanssin koko elinaikansa, ja kyseisen moottorin Recognize-kutsut sarjoitetaan kriittisellä osiolla. Rajapinnan IHPDFOCREngine pitäminen hallussa pitää mallit lämpiminä, joten oikea kaava erätyölle on luoda moottori kerran ja käyttää sitä asiakirjojen yli
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; // pystysuorat skannaukset: luokittelijamallia ei ladata
Models.Threads := 4; // 1..64, katkaistu loogisten prosessorien määrään
Models.TimeoutMilliseconds := 120000; // per Recognize-kutsu, yhteistoiminnallinen
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; // viimeinen viittaus vapautettu: mallit tuhotaan, sitten DLL puretaan
Arvo Threads asettaa sekä jokaisen ONNX-istunnon intra-op- että inter-op-säiemäärät, ja DLL katkaisee sen aktiivisten prosessorien määrään. Kaksi säiettä, jotka jakavat yhden moottorin, eivät aja rinnakkain; toinen odottaa lukkoa. Kyseinen odotus ei ole sokea EnterCriticalSection: sovitin kutsuu funktiota TryEnterCriticalSection 25 ms välein ja tarkistaa peruutustokenin ja takarajan yritysten välillä, joten jonossa olevan pyynnön voi yhä peruuttaa tai se voi aikakatkeaa. Jos tarvitset aitoa rinnakkaisuutta, luo yksi moottori työläistä kohden ja hyväksy, että jokainen moottori pitää omat mallikopionsa muistissa
Purkujärjestys on kiinteä moottorin tuhoajassa: HPDFRapidOCRDestroy vapauttaa malli-instanssin ensin, sitten FreeLibrary purkaa DLL:n latauksen. Natiivilla puolella mallin alustus on yhtä huolellinen; kun tunnistusmalli epäonnistuu sen jälkeen, kun detektorin ja luokittelijan istunnot oli jo rakennettu, kyseiset istunnot vapautetaan ennen virheen raportoimista, ja sanakirjan luokkamäärä tarkistetaan mallin tulostusta vasten alustuksen yhteydessä ensimmäisen sivun sijaan
Miksi natiivia OCR-kutsua ei voi tappaa inferenssin keskellä?
Natiivia RapidOCR-kutsua ei voi tappaa inferenssin keskellä, koska se ajaa omalla säikeelläsi, prosessisi sisällä, ONNX Runtime -istunnon keskellä, joka ei hyväksy keskeytystä. Peruuttaminen HotPDF:n DLL-sovittimessa on siksi yhteistoiminnallista: DLL kutsuu abortti-callbackia ennen ja jälkeen detektoinnin, luokittelun jälkeen ja jokaisen tunnistetun rivin jälkeen, ja pysähtyy ensimmäisessä tarkistuspisteessä, jossa callback palauttaa arvon 0. Yksi aloitettu ONNX Run päättyy ensin
Vaihtoehdot ovat huonompia kuin odottaminen. TerminateThread jättäisi CRT-heaplukon, ONNX Runtimen säiepuolan ja minkä tahansa OpenCV-tilan siihen kuntoon, jossa ne sattuivat olemaan, myrkyttäen prosessin loput. FreeLibrary kutsun ollessa yhä kesken purkaa koodia, joka on pinossa. Kumpaakaan ei voi tehdä turvalliseksi, joten sovitin ei koskaan yritä niitä. Takaraja funktiossa TimeoutMilliseconds on siten yhteistoiminnallinen takaraja, ja vanhentunut takaraja ilmestyy moottorivirheenä aikakatkaisudiagnostiikalla, kun taas peruutettu tokeni ilmestyy arvona otlsCancelled:
// Tokenin luo kutsuja ja se on jaettu käyttöliittymäsäikeen kanssa,
// joka kutsuu Token.Cancelia, kun käyttäjä painaa Stop-painiketta
Options := THPDFOCRTextLayerOptions.Default;
Options.CancellationToken := Token;
if not Doc.ApplyLoadedOCRTextLayer([], Engine, Options, Info) then
case Info.Status of
otlsCancelled:
// palautetaan seuraavassa vaiheessa tai rivirajassa; asiakirja muuttumaton
Writeln('Cancelled');
otlsEngineError:
// sisältää yhteistoiminnallisen takarajan umpeutumisen ja natiivit diagnostiikat
Writeln('Engine: ', string(Info.Diagnostic));
otlsBudgetExceeded:
Writeln('Budget: ', string(Info.Diagnostic));
else
Writeln(string(Info.Diagnostic));
end;
Kyse on ydinkompromissi HotPDF:n prosessisovittimien ja in-process-DLL:n välillä, eikä kumpikaan puoli voita jokaista riviä:
- Käynnistyskustannus: Tesseract- ja Python RapidOCR -sovittimet käynnistävät prosessin ja lataavat mallit jokaiselle sivulle; DLL lataa mallit kerran moottoria kohden
- Pysäyttäminen: lapsiprosessin voi lopettaa suoraan, ja Python-työläinen ajaa kill-on-close Job Objectin sisällä, joten sen koko prosessipuu lähtee mukaan; DLL voi pysähtyä vain vaihe- ja rivirajoissa
- Vikojen eristys: kaatuminen tiedostossa
tesseract.exeepäonnistaa yhden sivun; access violation DLL:n sisällä vie prosessisi alas - Käyttöönotto: prosessisovittimet tarvitsevat asennetun ohjelman tai Python-ympäristön; DLL tarvitsee itsensä, mallinsa ja sanakirjansa, sovitettuna sovelluksen bittisyyteen
- Muisti: prosessisovittimet vapauttavat kaiken, kun lapsi poistuu; DLL-moottori pitää mallinsa muistissa, kunnes viimeinen rajapintaviittaus vapautetaan
Interaktiiviselle työpöytäsovellukselle, joka OCR-aa sivun kerrallaan, DLL:n responsiivisuus voittaa yleensä. Palvelimelle, joka nielee luottamattomia skannauksia ympäri vuorokauden, prosessiraja on maksamisen arvoinen käynnistyskustannuksensa takia
HotPDFRapidOCR.dllin koostaminen ja käyttöönotto
HotPDFRapidOCR.dll koostetaan C++-lähteistä kohteessa Native/RapidOCR MSVC:llä, C++17:llä, Windows SDK:lla ja CMake 3.20:llä tai uudemmalla, käyttäen apuskriptiä, joka ottaa natiivin verkkolähteet sekä ONNX Runtime- ja OpenCV-hakemistot plus Win32- tai Win64-alustan. Koosta molemmat, jos toimitat molemmat, koska 32-bittinen Delphi-sovellus ei voi ladata 64-bittistä DLL:ää, ja hankkimiesi staattisten kirjastojen on vastattava kohdearkkitehtuuria samoin kuin CRT-tilaa
Mallipuolella on omat yhteensopivuusrajansa. Detektori on DB-tekstidetektori; tunnistin hyväksyy CTC-mallit NCHW-asettelussa kiinteällä 32:n tai 48:n syöttökorkeudella ja käyttää arvoa 48 malleille, joilla on dynaaminen korkeus. Mukana toimitettu staattinen ONNX Runtime ei voi ladata uudemmalla IR-versiolla tallennettuja malleja, joten tuoreet PP-OCRv5-viennit epäonnistuvat alustuksessa diagnostiikalla osittaisen lataamisen sijaan. Sanakirjan on oltava UTF-8:aa ilman BOM:ia, täsmälleen mallin merkkijärjestyksessä, ja sen luokkamäärän on vastattava mallin tulostusta; CRLF-rivinvaihdot hyväksytään. Tunnistus on offline-toimintoa: DLL ei koskaan lataa puuttuvaa mallia
Pikamuistio
- Tehdas:
HPDFCreateRapidOCRDLLOCREngine(LibraryPath, ModelDirectory[, Options])yksikössäHPDFRapidOCRRecognition, saatavilla versiosta v2.774.0 alkaen Delphi-, C++Builder- ja Windows FPC/Lazarus -koosteissa - Pidä palautettu
IHPDFOCREngineelossa sivujen ja asiakirjojen yli; sen vapauttaminen tuhoaa mallit ja purkaa DLL:n latauksen - Yksi moottori ajaa yhden tunnistuksen kerrallaan; luo useita moottoreita rinnakkaisille työläisille ja budjetoi muisti kullekin mallikopiolle
- Tuotos on yksi merkintä tekstiriviä kohden keskimääräisellä merkkiluottamusarvolla, suodatettuna arvolla
THPDFOCRTextLayerOptions.MinimumConfidence - Peruuttaminen ja
TimeoutMillisecondsovat yhteistoiminnallisia; kesken oleva ONNX-ajo päättyy aina loppuun - Sovita DLL:n bittisyys sovellukseen sekä staattisten ONNX Runtime- ja OpenCV-kirjastojen CRT-tila DLL:ään
- Valitse kieliprofiili moottoria kohden funktiolla
THPDFRapidOCRDLLOptions.ForLanguage(v2.775.0); yksi moottori ei itse tunnista kieliä
Natiivi RapidOCR-sovitin, prosessipohjaiset OCR-sovittimet, niitä ruokkiva sivurenderöijä ja näkymätön Unicode-tekstitason kirjoittaja toimitetaan yhdessä HotPDF:ssä, natiivina VCL-PDF-komponenttina Delphille ja C++Builderille. Jos asiakirjojen kaappaus- tai arkistointisovelluksesi tarvitsee haettavaa tuotosta ilman Python-ajonympäristöä kohdekoneella, artikkeli HotPDF Delphi PDF component tarjoaa koko putken, jolloin käyttöönotettavaksi jää vain DLL ja sen mallit