HotPDF dekodira rotirane QR simbole na učitanoj PDF stranici tako da uzorkovanu matricu modula normalizira kroz svih osam D4 orijentacija unutar samog dekodera. Vanjski retry s rotacijom koji radi kod linearnih simbologija za QR ne može raditi, a razumijevanje zašto vam ušteđuje dan potjere za dekoderom koji izgleda pokvaren a nije
Scenarij je sasvim običan. Skenirane otpremnice stižu kao PDF-ovi, svaka stranica nosi QR naljepnicu, a operater je hrpu listova ubacio u ladaicu bilo kojim smjerom koji je ona prihvatila. Neke su naljepnice uspravne, neke su okrenute za četvrtinu okreta, nekoliko ih je naopako. Pozovete barkod dekoder, pola stranica se razriješi, a druga polovica se vrati prazna bez ijedne greške
Zašto rotacija maske za skeniranje nikad ne popravi rotirani QR?
Jer je raspored QR finder uzoraka namjerno asimetričan, a rotacija cijele slike tu asimetriju čuva umjesto da je ukloni. QR Code stavlja tri finder kvadrata u gornji lijevi, gornji desni i donji lijevi kut, a donji desni kut ostavlja prazan (ISO/IEC 18004:2015 §6.3.3). Taj nedostajući kut je orijentacijski trag. Rotirajte bitmapu stranice za devedeset stupnjeva i praznina se jednostavno preseli u neki drugi kut. Ne postoji netrivijalna rotacija ravnine koja bi raspored s tri kuta preslikala natrag na samu sebe, pa će dekoder koji prihvaća samo kanonski raspored odbiti svaki pokušaj zaredom
To je bitno jer je očigledan fix pogrešan. Prirodan instinkt je objesiti retry izvana: renderirajte stranicu, predajte masku dekoderu, i ako to ne uspije, rotirajte masku i pokušajte ponovno na 90, 180 i 270 stupnjeva. Za Code 39 ta je politika točno prava, jer linearna simbologija ima start i stop uzorak koji skener nađe čim se crte vode horizontalno. Za QR to su četiri zajamčena neuspjeha nakon kojih slijedi izvještaj da ništa nije pronađeno
D4 grupa, primijenjena na matricu modula
Ispravno mjesto za normalizaciju je nakon uzorkovanja, na boolean rešetki modula a ne na pikselnoj maski. Kad dekoder riješi simbol u matricu tamnih i svijetlih modula veličine n puta n, može enumerirati dihedralnu grupu kvadrata: četiri rotacije puta dvije refleksije, ukupno osam kandidatskih orijentacija. Za svakog kandidata provjerava finder trokut, i prvi kandidat čija tri findera slijede na pozicije gore-lijevo, gore-desno i dolje-lijevo je prava orijentacija. Odatle postojeći pipeline teče nepromijenjen, jer format informacijski bitovi, zigzag smještaj podataka i Reed-Solomon korekcija svi pretpostavljaju kanonsku matricu i sad je dobivaju
Dvije osobine čine ovo jeftinim. Matrica je mala u usporedbi s renderiranom bitmapom, pa osam transpozicija košta daleko manje od osam renderiranja stranice. I matrica je čist boolean array koji gradi sampler, pa nijedan transform usput ne može unijeti vrijednosti koje nikad nisu uzorkovane
Detekcija verzije je pretraga djeljivosti, ne dijeljenje
Broj modula se ne može izvesti dijeljenjem uzorkovane širine s pretpostavljenom veličinom modula, i pogriješiti tu znači suptilan izvor neuspjeha dekodiranja na renderima visoke rezolucije. QR simbol verzije v je širok 4v + 17 modula, pa je verzija 1 široka 21 modul, a verzija 40 njih 177. Maska širine 126 piksela jednako je kompatibilna s verzijom 1 po šest piksela po modulu i s višim verzijama po manjim veličinama modula. Linearno dijeljenje odabere jedno od toga i obično je u krivu
Ono što radi je pretraga djeljivosti po kandidatskim verzijama. Idite od verzije 40 dolje do verzije 1, zadržite kandidate čiji broj modula deli uzorkovanu širinu bez ostatka i ostavlja najmanje tri piksela po modulu, i uzmite najmanju preživjelu verziju. Prag od tri piksela sprječava pretragu da prihvati apsurdno gusto čitanje grubog simbola, a pravilo najmanje verzije razrješuje preostalu dvosmislenost u korist čitanja koje bi skener stvarno proizveo
var
Pdf: THotPDF;
Options: THPDFBarcodeDecodeOptions;
Codes: THPDFDecodedBarcodes;
Info: THPDFBarcodeDecodeInfo;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('delivery-notes.pdf');
Options := THPDFBarcodeDecodeOptions.Default;
Options.DPI := 300;
Options.RotationPolicy := bdrpFallback;
Options.MinimumConfidence := 0.5;
Options.MaxResults := 16;
if Pdf.DecodeLoadedPageBarcodes(0, Options, Codes, Info) then
for I := 0 to High(Codes) do
if Codes[I].Symbology = bsyQRCode then
Writeln(Codes[I].Text, ' at ',
Format('%.0f', [Codes[I].OrientationDegrees]), ' degrees');
finally
Pdf.Free;
end;
end;
THPDFBarcodeDecodeOptions.Default vraća popunjen record a ne ispražnjen, što je bitno jer je DPI od nule ili limit rezultata od nule validno izgledajući način da ne dobijete ništa. RotationPolicy kontrolira samo vanjski retry: bdrpNone renderira jednom, bdrpFallback pokušava ostale orijentacije nakon neuspjelog prvog prolaza, a bdrpAll renderira svaku orijentaciju bezuvjetno. Budući da se QR normalizacija dešava unutar dekodera, QR stranice se razriješe u prvom pokušaju pod bilo kojom od tri politike. Politika je tu za linearne simbologije koje je stvarno trebaju
Kako dokazati da bitmap transform ne izmišlja piksele?
Izbrojite tintu na obje strane i zahtijevajte da se zbrojevi poklope. Rotacija je permutacija piksela, ništa više, pa broj ne-nultih ćelija u izlazu mora biti jednak broju u ulazu. Kad je rotacija maske u vanjskom retry putu javila 4800 postavljenih ćelija na ulazu i 7439 na izlazu, ta je jedna usporedba bila dovoljna da osudi transform bez čitanja ijedne linije njegove geometrije
Uzrok je bio svakidašnji i vrijedi ga ponijeti kao pravilo. Dinamički array dimenzioniran s SetLength nije zajamčeno da dođe nuliran kad je function result na putu koji runtime ne čisti, pa ćelije koje rotacija nikad ne zapiše nose bajtove koji su tamo bili prije. Neki od tih starih bajtova su ne-nulti, a ne-nulti znači tintu. Fix je jedna linija, FillChar(Result[0], N, 0) prije nego što krene petlja permutacije, a disciplina koju implicira je šira: svaka funkcija koja vraća masku ili bitmap buffer treba izričito očistiti svoj izlaz umjesto da se oslanja na semantiku alokacije
Ono što je defektu omogućilo da preživi tri izdanja je zanimljivije od samog defekta. Čim je QR preselio svoju obradu orijentacije u dekoder, QR je potpuno prestao koristiti vanjsku rotaciju maske, a jedini preostali potrošač tog code puta je bio Code 39. Dijeljena infrastruktura ovakve bugove krije cijelo vrijeme: pokrivenost jedne funkcije čini da put izgleda testirano dok funkcija koja stvarno ovisi o njemu nema vlastitih testova. Svaki put koji nova funkcija prestane koristiti treba test koji ga i dalje koristi
Čitanje rezultata natrag u koordinatama stranice
Svaka geometrijska vrijednost koju dekoder proizvede izražena je u koordinatnom okviru attempt bitmape, a pozivatelju treba u PDF user spaceu. Ta konverzija teče u dva stupnja: poništite četvrtinu okreta koju je retry primijenio, zatim poništite render transform koji je mapirao user space na bitmapu. Ono što stigne u THPDFDecodedBarcode je axis-aligned bounding box u user spaceu, s Left, Bottom, Right i Top po PDF konvenciji da Y raste prema gore, plus OrientationDegrees brojan suprotno od kazaljke
Pogriješite li smjer te druge konverzije, simptom je gadan: tekst se dekodira savršeno, ali box koji nacrtate za review overlay sleti na zrcalnu sliku prave pozicije. Tko god gradi review sučelje na vrhu dekodera treba se assertati protiv poznatog fixturea, sa simbolom namjerno smještenim blizu jednog kuta stranice da se okrenuta Y os vidi iz prve. Isti se način razmišljanja primjenjuje na svaku koordinatu koja prelazi render granicu, pa je renderiranje PDF stranice u bitmapu u Delphiju vrijedno razumijevanja prije nego što gradite na vrhu dekodera
Što ugrađeni dekoder hoće a što neće
Ugrađeni dekoder je ograničena implementacija bez dependencija, i pošten je oko svojih granica umjesto da se tiho pokvari. Prepoznaje Code 39 i QR, validira BCH-zaštićene format bitove i mask pattern prije nego objavi bilo koje podatke, i ne pokušava oporavak od grešaka na oštećenim simbolima. Ako je vaš ulaz fotografija zakrivljenog naljepnica pod neravnomjernim svjetlom, to je druga klasa problema i traži specijalizirani engine
// Zamijenite svoj engine: implementirajte IHPDFBarcodeDecoder i proslijedite
// ga overloadu koji zna za dekodere. HotPDF i dalje posjeduje render stranice,
// budžete, mapiranje koordinata i de-duplikaciju
if not Pdf.DecodeLoadedPageBarcodes(PageIndex, MyDecoder, Options,
Codes, Info) then
case Info.Status of
bdsBudgetExceeded:
Log('raise MaxPixels or lower DPI: ' + string(Info.Diagnostic));
bdsRenderError:
Log('page did not render: ' + string(Info.Diagnostic));
bdsDecoderError:
Log(string(Info.DecoderName) + ' failed: ' + string(Info.Diagnostic));
end;
THPDFBarcodeDecodeInfo je ono mjesto gdje produkcijski pipeline dolazi do izražaja. RotationAttemptCount i DecoderCallCount vam kažu je li vanjski retry uopće trčao, ReceivedResultCount naspram AcceptedResultCount razdvaja dekoder koji nije našao ništa od praga pouzdanosti koji je odbacio sve što je našao, a RenderedPixels s PeakWorkingBytes je ono što grafite kad batch posao krene u thrashing. Prazan result set plus bdsSucceeded znači da stranica stvarno nema čitljiv simbol, što je operativno drugačija činjenica od bdsBudgetExceeded
Budget polja zaslužuju promišljenu odluku a ne zadanu postavku. MaxPixels i MaxWorkingBytes postoje jer se DPI množi kvadratično: prelazak s 300 na 600 DPI na A4 stranicičetverostruči i cijenu rendera i vršnu alokaciju, a nepouzdani ulaz koji deklarira ogroman page box može scan posao pretvoriti u out-of-memory incident. Postavite limite na ono što vaš najgori legitimni dokument treba, pa pustite bdsBudgetExceeded da outliere usmjeri na sporiji, izolirani put
Ako vaši dokumenti miješaju mašinski čitljive naljepnice s tiskanim tekstom koji planirate indeksirati, barkod dekoder se prirodno upari s recognition engineom obrađenim u template-matching OCR-u unutar HotPDF-a, a generacijska strana iste priče je u crtanju barkodova u PDF s HotPDF-om. Oboje trči na istoj render i budget infrastrukturi, pa pipeline koji već postavlja pametne limite za jedno drugo dobiva gotovo besplatno
Tolerancija na rotaciju je jedna od onih mogućnosti koje su nevidljive kad rade i razjarujuće kad ne rade, a inženjerska lekcija se generalizira i preko QR-a: normalizirajte što bliže semantičkoj reprezentaciji, ne na pikselnom sloju gdje podaci još nose svaku slučajnost toga kako su uhvaćeni. HotPDF ovo isporučuje kao dio HotPDF Delphi PDF komponente, uz render, OCR i page-analysis dijelove koje isti intake pipelineovi obično trebaju