PDFium Component dodaje pretraživ tekstualni sloj skeniranim PDF stranicama iz Delphija kroz ApplyOcrSearchLayer. Renderuje svaku izabranu stranicu, predaje piksele OCR provajderu koji sami dostavite, i upisuje prepoznate reči nazad kao nevidljive tekstualne objekte pozicionirane preko reči na skenu. Originalna slika stranice se nikada ne dekodira, ponovo kodira ili zamenjuje, tako da je vizuelni rezultat bajt po bajt ista stranica od koje ste krenuli
Motor za prepoznavanje namerno nije deo biblioteke. PDFium izlaže renderovanje stranice, mapiranje koordinata, učitavanje fontova, kreiranje tekstualnih objekata i nevidljive režime renderovanja, ali ne sadrži OCR motor, a pretvaranje da sadrži značilo bi paketovanje nečijeg proizvoda za prepoznavanje u PDF komponentu. Umesto toga, prepoznavanje živi iza interfejsa IPdfOcrProvider: biblioteka prosleđuje piksele fiksnog rasporeda, sa poreklom u gornjem levom uglu, u BGRA formatu, a provajder vraća Unicode tekst, vrednosti pouzdanosti i četvorouglove reči
Šta je tačno pretraživ tekstualni sloj?
Skeniran PDF je slika dokumenta. Sadržaj stranice je jedna velika slika, i nema ničega za biranje, pretragu, kopiranje ili indeksiranje. Pretraživ tekstualni sloj dodaje prave tekstualne objekte preko te slike sa režimom renderovanja postavljenim na nevidljiv, tako da pregledači ne crtaju ništa, ali selekcija, pretraga i izvlačenje pronalaze reči tačno tamo gde se pojavljuju
Pozicioniranje je cela igra. Ako nevidljiv tekst sedi nekoliko tačaka van mesta, isticanja selekcije padaju pored reči umesto na njih, a kopiranje pasusa proizvodi tekst u pogrešnom redosledu. Zato geometrija mora doći iz istih transformacija koje PDFium koristi za renderovanje stranice, a ne iz proporcionalne procene
Implementacija provajdera
Ugovor provajdera je jedna metoda. Prima zapis slike stranice koji nosi dimenzije, korak, DPI, format piksela i same bajtove piksela, plus token otkazivanja, i vraća reči ili poruku greške:
uses
PDFium;
type
TMyOcrProvider = class(TInterfacedObject, IPdfOcrProvider)
public
function RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
end;
function TMyOcrProvider.RecognizePage(const Image: TPdfOcrImage;
const CancellationToken: IPdfCancellationToken;
out Words: TPdfOcrWords; out ErrorMessage: string): Boolean;
var
I: Integer;
begin
// Image.Pixels nosi redove sa poreklom u gornjem levom uglu, u BGRA
// formatu, dužine Image.Stride bajtova. Predajte ih vašem motoru,
// zatim popunite po jedan unos za svaku prepoznatu reč
SetLength(Words, RecognisedCount);
for I := 0 to RecognisedCount - 1 do
begin
Words[I].Text := EngineWordText(I);
Words[I].Confidence := EngineWordConfidence(I); // 0..1
Words[I].Quad := TPdfOcrQuad.FromRectangle(
EngineLeft(I), EngineTop(I), EngineRight(I), EngineBottom(I));
end;
ErrorMessage := '';
Result := True;
end;
Četvorouglovi umesto pravougaonika, jer sken retko stoji pravougaono prema stranici. Reč na blago rotiranoj stranici zauzima paralelogram, a TPdfOcrQuad nosi četiri ugaone tačke tako da iskošene i rotirane reči zadrže tačan region selekcije. Motori koji prijavljuju samo pravougaonike poravnate sa osama mogu koristiti FromRectangle, koji gradi degenerisan četvorougao
Zašto pozicije reči ne mogu biti proporcionalno skalirane?
Primamljivo je pretvoriti koordinatu piksela u koordinatu stranice deljenjem sa širinom renderovanja i množenjem sa širinom stranice. To radi samo za stranice bez rotacije, sa CropBox identičnim MediaBox-u, i poreklom na nuli, a mnogo skeniranih dokumenata ne ispunjava barem jedan od tih uslova
PDFium Component mapira svaki od četiri ugla četvorougla pojedinačno kroz FPDF_DeviceToPage, istu mapu koju je renderer koristio za proizvodnju piksela, tako da se unosi /Rotate i pomerene granice isecanja obrađuju konstrukcijom. Afina matrica za tekstualni objekat se zatim gradi iz tri od mapiranih tačaka, donjeg levog, donjeg desnog i gornjeg levog ugla, što je tačno dovoljno da izrazi poziciju, skaliranje, rotaciju i smicanje
Sam tekstualni objekat se kreira sa jediničnom veličinom fonta tako da se njegove stvarne granice fonta mogu izmeriti, a izmerene granice objekta se zatim mapiraju na ciljni četvorougao. Određivanje veličine pogodenom tačkastom veličinom i nadanjem da se poklapa sa skeniranom rečju bi lutalo sa svakom zamenom fonta; merenje unapred čini uklapanje nezavisnim od toga koji font sloj koristi
Pokretanje preko dokumenta
Zapis opcija kontroliše rezoluciju, filtriranje i svaki budžet. Filtriranje pouzdanosti je bitnije nego što izgleda: reči sa niskom pouzdanošću trajno zagađuju rezultate pretrage, a za razliku od pogrešnog renderovanja, niko to ne primeti dok pretraga ne vrati besmislice:
var
Pdf: TPdf;
Options: TPdfOcrOptions;
Report: TPdfOcrReport;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'scanned-contract.pdf';
Pdf.LoadDocument;
Options := TPdfOcrOptions.Default;
Options.Dpi := 300; // rezolucija prepoznavanja
Options.MinConfidence := 0.60; // odbaci nesigurne reči
Options.SkipPagesWithText := True; // ostavi rođeno-digitalne stranice na miru
Options.ContinueOnError := True; // jedna loša stranica ne sme zaustaviti posao
Options.MaxPixelsPerPage := 40 * 1000 * 1000;
if Pdf.ApplyOcrSearchLayer(TMyOcrProvider.Create, Options, Report) then
Pdf.SaveAs('scanned-contract-searchable.pdf');
for I := 0 to High(Report.Pages) do
if Report.Pages[I].Status = popsFailed then
Writeln(Format('page %d failed: %s',
[Report.Pages[I].PageNumber, Report.Pages[I].ErrorMessage]));
Writeln(Format('%d word(s) inserted, %d rejected, %d page(s) skipped',
[Report.InsertedWordCount, Report.RejectedWordCount,
Report.SkippedPageCount]));
finally
Pdf.Free;
end;
end;
SkipPagesWithText zaslužuje naglasak u mešovitim arhivama. PDF koji već nosi pravi tekst, bilo rođen digitalno ili prethodno obrađen, dobija drugi tekstualni sloj ako pokrenete OCR preko njega naslepo, a duplikat čini da izvlačenje vraća svaku reč dvaput. Status po stranici popsSkippedExistingText vam govori tačno koje su stranice ostavljene na miru
Budžeti, otkazivanje i zadržavanje otkaza
Svaka veličina koju neprijateljski ili tek ogroman dokument može naduvati ima plafon: piksele po stranici i ukupno, reči po stranici i ukupno, i karaktere po reči. Sve se proveravaju pre nego što se stranica upiše, ne posle, a procena piksela se izračunava iz dimenzija stranice i DPI pre nego što se ijedna bitmapa dodeli. Podizanje DPI sa 150 na 300 učetvorostručuje memoriju po stranici, pa je plafon po stranici parametar koji treba prvi podesiti kada serijski posao počne da otkazuje na velikim formatima
Token otkazivanja se provlači kroz celu putanju: progresivno renderovanje, poziv provajdera i petlju umetanja po reči. To znači da korisnik koji otkaže tokom prepoznavanja fajla od 400 stranica stane unutar jedne stranice umesto na kraju dokumenta, a isti obrazac tokena koji se koristi drugde u komponenti, opisan u otkazivom progresivnom renderovanju, primenjuje se ovde nepromenjen
Zadržavanje otkaza je po stranici. Biblioteka prikuplja ručke objekata koje je umetnula na stranicu i poziva FPDFPage_GenerateContent jednom, nakon što su sve reči postavljene. Ako bilo šta ne uspe usput, bilo greška provajdera ili problem fonta, objekti umetnuti na tu stranicu se uklanjaju unazad, a sadržaj stranice se ponovo generiše, tako da neuspela stranica vrati svoje originalno stanje umesto da zadrži polovinu tekstualnog sloja. Petlja dokumenta zatim nastavlja ili se zaustavlja prema ContinueOnError, a aktivna stranica se uvek vraća
Provera da li je slika zaista ostala netaknuta
Najjača dostupna provera je i najjednostavnija: renderujte stranicu pre i posle primene sloja na istoj veličini i uporedite bitmape. Trebalo bi da su identične bajt po bajt, jer nevidljiv tekst ne crta ništa, a tok slike nikada nije dekodiran. Bilo kakva razlika znači da je nešto drugo osim tekstualnog sloja izmenilo stranicu
Nakon toga, proverite stranu teksta izvlačenjem iz obrađenog fajla i potvrđivanjem da pozicije reči padaju na sken. Putanja izvlačenja je ista ona opisana u izvlačenju teksta iz PDF dokumenata, a za brzu vizuelnu proveru poravnanja, renderovanje stranica u slike kao u konverziji PDF stranica u JPEG vam omogućava da preklopite okvire reči preko skena
OCR slojevi, renderovanje, izvlačenje i uređivanje svi se izvode nad istim objektom dokumenta u Delphiju, C++Builderu i Lazarusu; kompletna API površina je opisana na stranici PDFium Component za Delphi