HotPDF dekoder roterte QR-symboler i en innlastet PDF-side ved å normalisere den samplede modulmatrisen gjennom alle åtte D4-orienteringer inne i selve dekoderen. Den ytre rotasjons-retry-en som fungerer for lineære symbologier, kan ikke fungere for QR, og å forstå hvorfor sparer deg for en dag med å jakte på en dekoder som ser ødelagt ut, men ikke er det
Scenarioet er ordinært nok. Skannede følgesedler ankommer som PDF-er, hver side bærer et QR-merke, og skanneoperatøren matet inn en stabel ark i den retningen skuffen aksepterte. Noen merker står oppreist, noen er et kvartals sving unna, noen få er opp-ned. Du kaller strekkodedekoderen, halvparten av sidene løser seg, og den andre halvparten kommer tilbake tomme uten noen feil i det hele tatt
Hvorfor fikser aldri rotasjon av skannemasken en rotert QR?
Fordi et QR-finnermønsteroppsett er bevisst asymmetrisk, og en rotasjon av hele bildet bevarer asymmetrien i stedet for å fjerne den. QR Code plasserer tre finnerkvadrater ved hjørnet oppe til venstre, oppe til høyre og nede til venstre, og lar hjørnet nede til høyre stå tomt (ISO/IEC 18004:2015 §6.3.3). Det manglende hjørnet er orienteringssignalet. Roter side-bitmapen nitti grader, og gapet flytter bare til et annet hjørne. Det finnes ingen ikke-triviell rotasjon av planet som mapper et trehjørnesoppsett tilbake på seg selv, så en dekoder som bare aksepterer den kanoniske ordningen, vil avvise hvert forsøk etter tur
Dette betyr noe fordi den åpenbare fiksen er den gale. Den naturlige instinkten er å henge retry-en på utsiden: render siden, gi masken til dekoderen, og hvis det feiler, roter masken og prøv igjen for 90, 180 og 270 grader. For Code 39 er den politikken nøyaktig riktig, for en lineær symbologi har et start- og stoppmønster skanneren kan finne så snart strekene løper horisontalt. For QR er det fire garanterte feiler etterfulgt av en rapport om ingenting funnet
D4-gruppen, anvendt på modulmatrisen
Det riktige stedet for normaliseringen er etter sampling, på den boolske modulmatrisen snarere enn på pikselmasken. Når dekoderen har løst symbolet opp i en n ganger n matrise av mørke og lyse moduler, kan den enumerere kvadratets dihedrale gruppe: fire rotasjoner ganger to refleksjoner, åtte kandidatorienteringer totalt. For hver kandidat sjekker den finner-trekanten, og den første kandidaten hvis tre finnere lander i posisjonene oppe til venstre, oppe til høyre og nede til venstre, er den sanne orienteringen. Derfra kjører den eksisterende rørledningen uendret, fordi formatinformasjonsbitene, zigzag-dataplasseringen og Reed-Solomon-korreksjonen alle antar en kanonisk matrise og får nå én
To egenskaper gjør dette billig. Matrisen er liten sammenlignet med det renderte bitmapet, så åtte transponeringer koster langt mindre enn åtte siderendringer. Og matrisen er en ren boolsk array bygget av sampleren, så ingen transform underveis kan introdusere verdier som aldri ble samplet
Versjonsdeteksjon er et delbarhetssøk, ikke en divisjon
Modulantallet kan ikke utledes ved å dele den samplede bredden på en antatt modulstørrelse, og å ta feil her er en subtil kilde til dekodefeiler på høyoppløselige rendringer. Et QR-symbol av versjon v er 4v + 17 moduler på tvers, så versjon 1 er 21 moduler og versjon 40 er 177. En maske som måler 126 piksler bred, er like forenlig med versjon 1 ved seks piksler per modul og med flere høyere versjoner ved mindre modulstørrelser. Lineær divisjon velger en av dem og tar vanligvis feil
Det som fungerer, er et delbarhetssøk over kandidatversjonene. Gå fra versjon 40 ned til versjon 1, behold kandidatene hvis modulantall deler den samplede bredden jevnt og etterlater minst tre piksler per modul, og ta den minste overlevende versjonen. Gulvet på tre piksler er det som stopper søket fra å akseptere en absurd tett avlesning av et grovt symbol, og minste-versjon-regelen løser den gjenværende tvetydigheten til fordel for avlesningen en skanner faktisk ville produsert
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 gir tilbake en fylt post snarere enn en nullstilt, noe som betyr noe fordi en DPI på null eller et resultattak på null er en plausibel måte å få ingenting tilbake på. RotationPolicy styrer bare den ytre retry-en: bdrpNone rendrer én gang, bdrpFallback prøver de andre orienteringene på nytt etter et mislykket første gjennomløp, og bdrpAll rendrer hver orientering betingelsesløst. Fordi QR-normalisering skjer inne i dekoderen, løser QR-sider seg på første forsøk under alle de tre politikkene. Politikken er der for de lineære symbologiene som faktisk trenger den
Hvordan beviser du at en bitmap-transform ikke finner på piksler?
Tell blekket på begge sider og krev at summene stemmer. En rotasjon er en permutasjon av piksler, ingenting mer, så antallet ikke-null celler i utdata må være lik antallet i inndata. Da en maske-rotasjon i den ytre retry-veien rapporterte 4800 satte celler inn og 7439 ut, var den eneste sammenligningen nok til å dømme transformen uten å lese en eneste linje av geometrien dens
Årsaken var hverdagslig og verdt å ta med seg som en regel. En dynamisk array størrelsesordnet med SetLength er ikke garantert å ankomme nullstilt når den er et funksjonsresultat som reiser en vei kjøretiden ikke rydder, og celler rotasjonen aldri skriver, bærer da de bytene som var der før. Noen av de foreldede bytene er ikke-null, og ikke-null betyr blekk. Fiksen er én linje, FillChar(Result[0], N, 0) før permutasjonssløyfen kjører, og disiplinen den impliserer er bredere: enhver funksjon som returnerer en maske- eller bitmap-buffer bør nullstille utdataen sin eksplisitt i stedet for å stole på allokeringsemantikk
Det som fikk defekten til å overleve tre utgivelser, er mer interessant enn selve defekten. Da QR flyttet orienteringshåndteringen sin inn i dekoderen, sluttet QR å bruke den ytre maskerotasjonen helt, og den eneste gjenværende konsumenten av den kodeveien var Code 39. Delt infrastruktur skjuler feil som dette hele tiden: dekning fra én funksjon får en vei til å virke testet mens funksjonen som faktisk avhenger av den, ikke har noen egen. Hver vei en ny funksjon slutter å bruke, trenger en test som fortsatt bruker den
Å lese resultatene tilbake i sidekoordinater
Hver geometrisk verdi dekoderen produserer, er uttrykt i koordinatrammen til forsøks-bitmapet, og kalleren trenger den i PDFs brukerrom. Den konverteringen kjører i to etapper: angre kvartssvingen retry-en utførte, og angre så rendringstransformen som mappet brukerrommet over på bitmapet. Det som ankommer THPDFDecodedBarcode er en aksejustert avgrensningsboks i brukerrommet, med Left, Bottom, Right og Top etter PDF-konvensjonen om at Y vokser oppover, pluss en OrientationDegrees regnet mot klokken
Ta feil retning på den andre konverteringen, og symptomet er ekkelt: tekst dekoder perfekt, men boksen du tegner for et gjennomgangslag lander på speilbildet av riktig posisjon. Alle som bygger et gjennomgangsgrensesnitt oppå dekoderen, bør teste mot et kjent fikstur, med et symbol plassert bevisst nær et sidehjørne, slik at en snudd Y-akse er synlig ved et blikk. Samme resonnement gjelder alle koordinater som krysser rendringsgrensen, noe som er grunnen til at rendring av en PDF-side til en bitmap i Delphi er verdt å forstå før du bygger oppå dekoderen
Hva den innebygde dekoderen vil og ikke vil gjøre
Den innebygde dekoderen er en avgrenset, avhengighetsfri implementasjon, og den er ærlig om grensene sine i stedet for å degradere i stillhet. Den gjenkjenner Code 39 og QR, validerer de BCH-beskyttede formatbitene og massemønsteret før den publiserer noen data, og den forsøker ikke feilgjenoppretting på skadede symboler. Hvis inndataene dine er et fotografi av en bøyd etikett under ujevnt lys, er det en annen problemklasse som vil ha en spesialisert motor
// Bytt inn din egen motor: implementer IHPDFBarcodeDecoder og gi den
// til overload-en som tar imot dekodere. HotPDF eier fortsatt
// siderendring, budsjetter, koordinatmapping og deduplisering
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 er der en produksjonsrørledning tjener sine penger. RotationAttemptCount og DecoderCallCount forteller deg om den ytre retry-en kjørte i det hele tatt, ReceivedResultCount mot AcceptedResultCount skiller en dekoder som fant ingenting fra en konfidens terskel som avviste alt den fant, og RenderedPixels med PeakWorkingBytes er det du grafer når en jobb begynner å thrashe. Et tomt resultatsett pluss bdsSucceeded betyr at siden virkelig ikke har noe lesbart symbol, noe som er et annet operasjonelt faktum enn bdsBudgetExceeded
Budsjettfeltene fortjener en bevisst beslutning snarere enn en standardverdi. MaxPixels og MaxWorkingBytes finnes fordi DPI multipliserer kvadratisk: å gå fra 300 til 600 DPI på en A4-side firedobler både rendringskostnaden og toppallokeringen, og en utiltrodd inndata som deklarerer en enorm sideboks, kan gjøre en skannejobb om til en out-of-memory-hendelse. Sett takene til det verste legitime dokumentet ditt trenger, og la bdsBudgetExceeded rute avvikerne til en saktere, isolert vei
Hvis dokumentene dine blander maskinlesbare merker med trykt tekst du planlegger å indeksere, pares strekkodedekoderen naturlig med gjenkjenningsmotoren dekket i malmatchings-OCR inne i HotPDF, og genereringssiden av samme historie er i å tegne strekkoder inn i en PDF med HotPDF. Begge kjører på samme rendrings- og budsjettinfrastruktur, så en rørledning som allerede setter fornuftige grenser for den ene, får den andre nesten gratis
Rotasjonstoleranse er en av de funksjonene som er usynlig når den fungerer og irriterende når den ikke gjør det, og ingeniørlærdommen generaliserer utover QR: normaliser så nær den semantiske representasjonen du kan komme, ikke på piksellaget der dataene fortsatt bærer hver tilfeldighet av hvordan de ble fanget. HotPDF leverer dette som del av HotPDF Delphi PDF-komponent, ved siden av rendrings-, OCR- og sideanalyse-brikkene de samme inntaksrørledningene vanligvis trenger