HotPDF muuttaa skannatut PDF-sivut etsiväksi PDF:ksi Tesseractilla HPDFCreateTesseractOCREnginein kautta — tehdasfunktio, joka käärii paikallisesti asennetun Tesseract-suoritettavan IHPDFOCREngineiksi. Välität kyseisen moottorin ApplyLoadedOCRTextLayerille, joka renderöi jokaisen sivun, ajaa Tesseractin kerran per sivu, jäsentää sen sanatason TSV-tuotoksen ja sitoo näkymättömän Unicode-tekstikerroksen kaikille pyydetyille sivuille yhdessä transaktiossa — tai ei yhdellekään
Syy sille, että tämä sovitin on olemassa, on laajuus. Sisäänrakennettu mallintunnistus-OCR-moottori on tahallaan kapea: koneella painettuja ASCII-kirjaimia ja numeroita, ei muuta. Korkattuja nimiä sisältävät laskut, kiinalaiset sopimukset ja monikieliset arkistot tarvitsevat oikean tunnistajan koulutetuilla kielimalleilla, ja Tesseract on ilmeinen ehdokas, koska se on komentoriviohjelma, jonka voit hankkia sovelluksesi viereen. Ulkoisen ohjelman kutsuminen asiakirjakirjastosta kuulostaa triviaalilta. Se ei ole, ja suurin osa sovittimen kiinnostavasta koodista koskee sitä, mitä tapahtuu, kun ohjelma käyttäytyy huonosti, jumittuu, perutaan tai perii asioita, joita sen ei koskaan pitäisi nähdä
Miten HotPDF ohjaa Tesseractia Delphi-sovelluksesta?
HotPDF ajaa Tesseractia piilotettuna lapsiprosessina per sivu, syöttää sille renderöidyn bittikartan ja lukee takaisin TSV-tiedoston, ja paljastaa tuloksen saman IHPDFOCREngine-sauman kautta, jota sisäänrakennettu moottori käyttää. Mikään alavirrassa ei muutu: koordinaattien kuvaus, kiertojen käsittely, Unicode-validointi, luotettavuussuodatus ja atominen sitoutuminen ovat tekstikerrosputki, jonka sinulla jo on. Tehdasfunktio asuu HPDFTesseractRecognition-yksikössä ja valido innokkaasti: suoritettavan on oltava olemassa, tessdata-hakemiston on oltava olemassa, aikakatkaisun on oltava välillä 1–3 600 000 millisekuntia, ja kielitunniste saa sisältää vain ASCII-kirjaimia, numeroita, _- ja +-merkkejä. Se viimeinen tarkistus on merkitsevä, koska kielimerkkijono päätyy komentoriville, ja eng+chi_sim on laillinen Tesseract-arvo, kun taas mikä tahansa lainausmerkkejä tai välilyöntejä sisältävä ei ole
uses
SysUtils, HPDFTypes, HPDFDoc, HPDFTesseractRecognition;
procedure MakeSearchable(const SourceFile, TargetFile: string;
Token: THPDFCancellationToken);
var
Doc: THotPDF;
Engine: IHPDFOCREngine;
Options: THPDFOCRTextLayerOptions;
Info: THPDFOCRTextLayerInfo;
begin
// nostaa EArgumentExceptionin puuttuvasta suoritettavasta, puuttuvasta tessdatasta,
// huonosta kielitunnisteesta tai alueen 1..3600000 ms ulkopuolisesta aikakatkaisusta
Engine := HPDFCreateTesseractOCREngine(
'C:\OCR\Tesseract\tesseract.exe',
'C:\OCR\Tesseract\tessdata',
'eng+chi_sim', // useita malleja yhdistettynä '+'-merkillä
120000); // rajoitus per sivu, oletus on 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;
// tyhjä sivuluettelo tarkoittaa kaikkia sivuja; tekstiä sisältävät sivut ohitetaan oletuksena
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;
Jokaisella sivulla Recognize luo yksityisen hakemiston väliaikaispolun alle nimeltä HotPDF-OCR-{GUID}, tallentaa renderöidyn bittikartan nimellä input.bmp ja käynnistää kutsun tesseract input.bmp output --tessdata-dir … -l … --dpi N --psm 3 -c tessedit_create_tsv=1, jossa jokainen polkuargumentti lainataan Windowsin komentorivin escape-säännöillä kenoviivoille ja upotetuille lainausmerkeille. --dpi-arvo on renderöinnin DPI arvosta THPDFOCRTextLayerOptions.DPI, joten Tesseractin ei koskaan tarvitse arvata resoluutiota kuvan metatiedoista, ja --psm 3 pyytää täysin automaattista sivun segmentointia. Moottori raportoi itsensä nimellä Tesseract (local CLI), joka päätyy Info.EngineNameiin. Tesseractia ja sen kielimalleja ei toimiteta HotPDF:n mukana; niiden asentaminen on sovelluksen tehtävä
Miksi TSV-jäsennin on niin tiukka?
HotPDF:n TSV-jäsennin kaataa koko sivun mihin tahansa epämuodostuneeseen riviin, koska osittain jäsennetty sanaluettelo tuottaa tekstikerroksen, joka eroaa hiljaa kuvasta. Tesseractin TSV-tuotoksella on kiinteä kahdentoista sarakkeen otsikkorivi, levelistä textiin, ja HotPDF vertaa ensimmäistä riviä täsmälleen kyseiseen otsikkoon valinnaisen tavujärjestymerkin poistamisen jälkeen. Jokaisen seuraavan rivin on jakauduttava täsmälleen kahteentoista kenttään, ja jako pysähtyy yhdenteistentoista sarkaimen jälkeen, niin että tunnistetun tekstin sisällä oleva sarkain jää sanan osaksi kolmannentoista sarakkeen synnyttyä sen sijaan. Vain tason 5 rivit ovat sanoja; tasot 1–4 kuvaavat sivuja, lohkoja, kappaleita ja rivejä, ja ne ohitetaan. Myös tason 5 rivit, joiden teksti on tyhjä tai puhdasta tyhjätilaa, ohitetaan, koska tyhjällä sanalla on laatikko mutta ei mitään paikannettavaa eikä etsittävää. Kaikki muu tarkistetaan kovakouraisesti: kokonaislukugeometria, luotettavuus, joka jäsennetään muuttumattomalla en-US-muodolla, jotta saksalainen lokaali ei lue arvoa 93.5 romuna, laatikko, joka sijaitsee kokonaan bittikartan sisällä, ja luotettavuus välillä 0–100. Yksittäinen epäonnistuminen nostaa poikkeuksen, moottori palauttaa Falsein, ja sanataulukko tyhjennetään. Regressiotestit sisältävät täsmälleen kyseisen tapauksen: yhden kelvollisen sanan jälkeen tulevan rikkinäisen rivin on tuotettava nolla sanaa, ei yhtään
// tiivistetty HPDFLocalTSVRecognitionin tason 5 silmukasta
if (Fields.Count <> 12) or not TryStrToInt(Fields[0], Level) then
raise EConvertError.Create('Invalid Local OCR TSV row');
if Level <> 5 then Continue; // sivu/lohko/kappale/rivi-rivit
WordText := Fields[11];
if Trim(WordText) = '' then Continue; // tyhjätilasanoilla ei ole sijaintia
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; // putki odottaa arvoa 0..1
Se viimeinen rivi vaikuttaa oletukseen, jota et ehkä odota. Tesseractin luotettavuus kulkee välillä 0–100, putki toimii välillä 0–1, ja THPDFOCRTextLayerOptions.MinimumConfidence olettaa 0,5:n, joten mikä tahansa Tesseract-sana alle 50:n lasketaan Info.DroppedWordCountiin eikä koskaan päädy sivulle. Siistissä 300 DPI:n skannauksessa se on järkevä lattia. Kohuisella faksilla se voi pudottaa yllättävän osan sivusta, ja oikea liike on katsoa pudotettujen määrää ennen kynnyksen laskemista, sillä matalan luotettavuuden sanat ovat täsmälleen niitä, jotka ovat todennäköisimmin väärin
Mitä Tesseract-lapsiprosessi perii?
Tesseract-lapsiprosessi perii täsmälleen kaksi kahvaa HotPDF:ltä: NUL-kahvan vakiosyötölle ja -tulosteelle sekä tiedostokahvan vakiovirheelle. Se täsmällisyys on koko pointti. CreateProcess arvolla bInheritHandles = True on tapa välittää vakiohandled lapselle, mutta yksinään se välittää jokaisen perittävän kahvan isäntäprosessissa, mukaanlukien tiedostot, putket ja tapahtumat, jotka sovelluksesi muu toisiinsa liittymätön koodi on avannut. Lapsi pitää sitten kyseiset objektit elossa poistumiseensa asti, joten tiedosto pysyy lukittuna tai putki ei koskaan näe loppuaan, sillä aikaa kun Tesseract raataa sivun läpi. HotPDF sulkee kyseisen kuilun laajennetulla käynnistystietueella: STARTUPINFOEX, attribuutiluettelo, joka kantaa PROC_THREAD_ATTRIBUTE_HANDLE_LISTin, ja EXTENDED_STARTUPINFO_PRESENT-luontilippu. Kahvaluettelon ollessa paikallaan bInheritHandlesin on silti oltava True, mutta vain luetellut kahvat ylittävät rajan. Sama eristämisaattelu ohjaa artikkelia PDF-kuvakoodekkien eristämisestä työprosesseissa, jossa lapsi on epäluotettavaa koodia; tässä lapsi on luotettava, mutta isäntä ei ole ainoa oma kahvataulunsa omistaja
// vakiot näytetään nimillä; lähdekoodi välittää niiden numeeriset arvot
// molemmat kahvat luodaan arvolla bInheritHandle = True
InheritedHandles[0] := NullHandle; // stdin ja stdout
InheritedHandles[1] := ErrorHandle; // stderr.txt yksityisessä hakemistossa
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, // vaaditaan kahvaluettelon vuoksi
CREATE_NO_WINDOW or EXTENDED_STARTUPINFO_PRESENT,
nil, PChar(DirectoryName), Startup.StartupInfo, ProcessInfo);
Miksi peruttu OCR-ajo voi näyttää moottorivirheeltä?
Peruttu OCR-ajo näyttää moottorivirheeltä, koska IHPDFOCREngine.Recognize palauttaa yhden totuusarvon, ja False tarkoittaa sekä sitä, että Tesseract epäonnistui, että käyttäjä painoi Peruuta. Sovitin pollaa peruutustokenia ja aikakatkaisua joka 25. millisekunti lapsen ajaessa, ja kun token lauhtuu, se nostaa poikkeuksen Recognizein sisällä, sieppaa oman poikkeuksensa, siivoaa ja palauttaa Falsein diagnoosin kanssa. Jos putki käsittelisi tuota moottorivirheenä, kutsuja näkisi otlsEngineErrorin työlle, jonka käyttäjä tarkoituksella pysäytti. ApplyLoadedOCRTextLayer tarkistaa siksi tokenin ensin aina, kun Recognize palauttaa Falsein, ja muuttaa tuloksen moottorivirheeksi vain, jos tokenia ei ollut asetettu. Tuo järjestys säilyttää monisivuisen lupauksen: tunnistus, validointi, budjettikirjanpito ja sisällön rakentaminen ajetaan jokaiselle pyydetylle sivulle ennen kuin graafitransaktio avautuu, joten peruutus sivulla 40 / 50 raportoi otlsCancelledin ja jättää asiakirjan, ensimmäiset 39 sivua mukaan lukien, koskemattomaksi. Osittain etsivää tiedostoa ei jää selitettäväksi myöhemmin, ja loput virheenkäsittelystä noudattaa samaa rajattua tyyliä:
- Aikakatkaisu on per
Recognize-kutsu, mitattuna sen alusta, joten oletus 60 000 ms koskee jokaista sivua koko asiakirjan sijaan - Lapsi, joka on yhä käynnissä aikakatkaisun tai peruutuksen hetkellä, lopetetaan, siltä odotetaan enintään 5 sekuntia, ja sen yksityinen hakemisto poistetaan
finally-lohkossa output.tsvin kattoluku on 64 MiB jastderr.txtin 1 MiB, tarkistettuna sekä lapsen ajaessa että sen poistuttua- Sanamäärä ja UTF-16-koodiyksiköt saavat kattoluvun per sivu jäljellä olevista
MaxWordsPerPage-,MaxTotalWords- jaMaxTextCodeUnits-budjeteista, ja niiden ylittäminen kaataa ajon sanaluettelon katkaisemisen sijaan - Vakiotuloste menee
NULiin, koska Tesseract kirjoittaaoutput.tsvin, kun taas vakiovirhe menee tiedostoon, jolloin nollasta poikkeava poistumiskoodi raportoidaan jopa 4 096 merkillä moottorin omasta valituksesta — yleensä nopein tapa oppia, että.traineddata-tiedosto puuttuu
Miten tunnistetut sanat muuttuvat näkymättömäksi tekstikerrokseksi
HotPDF kirjoittaa Tesseractin sanat näkymättömänä tekstinä käyttäen tekstirenderöintitilaa 3, eli ei-täytä-eikä-viivaa -tilaa, joka on määritelty standardissa ISO 32000-1 §9.3.6, joten sivu näyttää yhä skannatun kuvan, kun taas haku ja kopiointi toimivat tunnistetuilla sanoilla. Sisältöstream avaa BTin 3 Trllä, ja jokainen sana saa Tm-matriisin perusviivalleen, fonttikoon, joka johdetaan laatikon korkeudesta pikseleinä renderöinnin DPI:llä, ja Tz-vaakaskaalauksen, joka venyttää glyyfijuoksun mitatun laatikon leveyteen, mistä johtuu, että hakukorostus osuu kuvan sanaan sen leijumisen sijaan
Tesseractin TSV:ssä on laatikot mutta ei perusviivoja, joten sovitin raportoi jokaisen sanan ilman sitä ja putki arvioi perusviivan viidennekseen laatikon korkeudesta alareunan yläpuolella. Teksti itse kulkee jaetun upottamattoman Type0-fontin läpi Identity-H-koodauksella ja generoidulla ToUnicode-CMapilla, yksi CID kutakin erillistä Unicode-skaalaarua kohden koko ajon yli, ja juuri siksi kiina, korkattu latinalainen ja lisätaulun merkit selviävät kaikki kopioinnista ja hausta. Tuolla suunnittelulla on kaksi rajaa, jotka kannattaa sanoa heti: yksi ajo voi kantaa enintään 65 535 erillistä skaalaarua, ja upottamaton fontti ei täytä ISO 19005:n fonttiupotusvaatimusta, joten PDF/A-tuotos tarvitsee erikseen upotetun vaatimustenmukaisen fontin. Tuloksen tarkistaminen on yksinkertaista ja kannattaa automatisoida: tallenna, lataa uudelleen ja aja tavallinen ladatun asiakirjan tekstipolku artikkelista tekstin poimimisesta ladatusta PDF:stä Delphissä; jos sanat palaavat odotetuilta sivuilta, kerros on aitoa
RapidOCR ja muut moottorit samalla TSV-protokollalla
HotPDF käyttää samaa prosessiajajaa ja TSV-jäsentäjää uudelleen RapidOCR:lle funktiolla HPDFCreateRapidOCREngine(PythonExecutable, BridgeScript, ModelDirectory, TimeoutMilliseconds), joka on hyödyllisempi valinta yksinkertaistettujen kiinalaisten skannausten kohdalla. Komentorivi on identtinen, paitsi että siltaskriptin polku lisätään Python-suoritettavan jälkeen, ja kieli on lukittu arvoon chi_sim. HotPDF toimittaa sillan tiedostona tools/OCR/rapidocr_tsv.py; se odottaa rapidocr- ja onnxruntime-paketteja plus kolmea paikallista ONNX-mallia, poistaa automaattiset mallilataukset käytöstä ja kirjoittaa Tesseract-muotoista TSV:tä, joten Delphi-puoli ei tarvitse toista jäsentäjää. Moottorin nimi, joka raportoidaan Info.EngineNameissa, on RapidOCR (local ONNX). Tuo muoto vihjii yleiseen reseptiin: mikä tahansa tunnistaja, jonka voit kääriä pieneen skriptiin, joka hyväksyy Tesseract-tyylisen argumenttiluettelon ja tuottaa kahdentoista sarakkeen TSV:n, perii kahvaeristämisen, aikakatkaisun, peruutuksen, tuotosbudjetit ja kaikki-tai-ei mitään -sitoutumisen ilmaiseksi. Sovittimet ovat vain Windowsille, ajavat yhden sivun kerrallaan synkronisesti, eivätkä suorista tai esikäsitlele kuvaa rendererin tuottaman lisäksi, joten sisään menevä kuvanlaatu asettaa yhä katon sille, mikä tulee ulos
Tesseract- ja RapidOCR-sovittimet, näkymättömän tekstikerroksen kirjoittaja, niitä ruokkiva sivurenderoija ja tuloksen varmentava tekstin poiminta toimitetaan kaikki samassa natiivissa VCL-komponentissa Delphille ja C++Builderille. Jos lisäät OCR:ää asiakirjojen keruu- tai arkistointisovellukseen, HotPDF Delphi PDF -komponentti antaa sinulle putken, josta jää asennettavaksi vain itse OCR-moottori