Tehnični članak

Dekodiranje vrtenih kod QR na straneh PDF s HotPDF

HotPDF dekodira vrten QR simbole v naloženi strani PDF z normalizacijo vzorčene matrice modulov skozi vseh osem orientacij D4 znotraj samega dekoderja. Zunanji poskus z vrtenjem, ki deluje za linearne simbologije, za QR ne more delovati, razumevanje zakaj pa vam prihrani dan lova na dekoder, ki izgleda pokvarjen, a ni

Scenarij je povsem vsakdanji. Skenirane dobavnice prispejo kot PDF-ji, vsaka stran nosi nalepko QR, operater skenerja pa je podal kup listov v katero koli smer, ki jo je pladenj sprejel. Nekatere nalepke so pokončne, nekatere so za četrtinko zgrešene, nekaj jih je na glavo. Pokličete dekoder črtne kode, polovica strani se razreši, druga polovica pa se vrne prazna, brez nobene napake

Zakaj vrtenje maske skeniranja nikoli ne popravi vrtenega QR?

Ker je razporeditev vzorca iskalnika QR namerno asimetrična, vrtenje cele slike pa to asimetrijo ohrani, namesto da bi jo odstranilo. QR Code postavi tri kvadrate iskalnika v kote zgoraj-levo, zgoraj-desno in spodaj-levo, kot spodaj-desno pa pusti praznega (ISO/IEC 18004:2015 §6.3.3). Ta manjkajoči kot je namig orientacije. Bitno preslikavo strani zavrtite za devetdeset stopinj in vrzel se preprosto preseli v drug kot. Ne obstaja netrivialno vrtenje ravnine, ki bi razporeditev s tremi koti preslikalo nazaj nase, zato bo dekoder, ki sprejema le kanonično razporeditev, vsak poskus po vrsti zavrl

To šteje, ker je očitno popravilo napačno. Naravni instinkt je obesiti poskus na zunanjost: izrišite stran, masko podajte dekoderju in če to spodleti, masko zavrtite in poskusite znova za 90, 180 in 270 stopinj. Za Code 39 je ta politika točno prava, ker ima linearna simbologija vzorca začetka in ustavitve, ki jih skener najde, takrat ko črte tečejo vodoravno. Za QR so to štirje zagotovljeni spodleti, ki jim sledi poročilo o nič najdenega

Grupa D4, uporabljena na matrici modulov

Pravo mesto za normalizacijo je po vzorčenju, na boolean mreži modulov in ne na maski pikslov. Ko dekoder simbol razreši v matrico temnih in svetlih modulov velikosti n-krat-n, lahko našteje diedrsko grupo kvadrata: štiri vrtenja krat dva zrcaljenja, skupaj osem kandidatskih orientacij. Za vsakega kandidata preveri trikotnik iskalnikov in prvi kandidat, ki njegovi trije iskalniki pristanejo na mestih zgoraj-levo, zgoraj-desno in spodaj-levo, je prava orientacija. Od tam naprej obstoječi cevovod teče nespremenjen, ker biti informacije o formatu, cikcak postavitev podatkov in popravilo Reed-Solomon vsi privzemajo kanonično matrico in zdaj jo dobijo

Štirje izrisi iste matrice modulov QR v HotPDF pod vrtenji grupe D4 pri 0, 90, 180 in 270 stopinjah, ki prikazujejo tri vzorce iskalnikov, kako selijo kote, medtem ko se prazen kot premika z njimi, tako da le kanonična orientacija dekoderju ponudi iskalnike na zgoraj-levo, zgoraj-desno in spodaj-levo
Vrtenje maske pikslov ne more odstraniti asimetrije iskalnikov QR, zato HotPDF našteje orientacije D4 na vzorčeni matrici modulov in obdrži prvega kandidata, ki njegovi iskalniki pristanejo zgoraj-levo, zgoraj-desno in spodaj-levo

Dve lastnosti naredita to poceni. Matrica je majhna v primerjavi z izrisano bitno preslikavo, tako da osem transpozicij stane veliko manj kot osem izrisov strani. Matrica je čist boolean seznam, zgrajen s strani vzorčevalnika, zato nobena transformacija na poti ne more uvesti vrednosti, ki nikoli niso bile vzorčene

Zaznavanje verzije je iskanje deljivosti, ne deljenje

Števila modulov ni mogoče izpeljati z deljenjem vzorčene širine s privzetimi velikostjo modula, napaka tu pa je subtilen vir spodletelih dekodiranj na izrisih visoke ločljivosti. Simbol QR verzije v je 4v + 17 modulov čez, tako da je verzija 1 21 modulov in verzija 40 177. Maska, široka 126 pikslov, je enakovredno skladna z verzijo 1 pri šestih pikslih na modul in z več višjimi verzijami pri manjših velikostih modulov. Linearno deljenje izbere enega od njih in je običajno napačno

Deluje iskanje deljivosti čez kandidatske verzije. Pojdite od verzije 40 navzdol do verzije 1, obdržite kandidate, katerih število modulov deli vzorčeno širino brez ostanka in pušča vsaj tri piksle na modul, ter vzemite najmanjšo preživelo verzijo. Tla treh pikslov so tisto, kar iskanju prepreči sprejeti absurdno gosto branje grobega simbola, pravilo najmanjše verzije pa razreši preostalo dvoumnost v prid branju, ki bi ga dejansko dal skener

Pohod zaznavanja verzije HotPDF za simbol QR na maski, vzorčeni na 126 pikslov, ki preizkusi vsako kandidatsko število modulov 4v plus 17 od verzije 40 navzdol do verzije 1 za deljivost brez ostanka in tla modula treh pikslov, preden zmaga najmanjša preživela verzija
Število modulov QR pride iz iskanja deljivosti čez kandidatske verzije, ne iz deljenja širine maske s privzetimi velikostjo modula, najmanjša preživela verzija pa razreši dvoumnost
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 vrne zapolnjen zapis in ne izničenega, kar šteje, ker je DPI nič ali omejitev rezultatov nič videti veljaven način, da ne dobite nič nazaj. RotationPolicy nadzoruje le zunanji poskus: bdrpNone izriše enkrat, bdrpFallback poskusi druge orientacije po spodletelem prvem prehodu, bdrpAll pa izriše vsako orientacijo brezpogojno. Ker se normalizacija QR zgodi znotraj dekoderja, se strani QR razrešijo ob prvem poskusu pod katero koli od treh politik. Politika je tam za linearne simbologije, ki jo dejansko potrebujejo

Kako dokažete, da bitna preslikava ne izmišljuje pikslov?

Preštejte črnilo na obeh straneh in zahtevajte, da se vsoti ujemata. Vrtenje je permutacija pikslov in nič več, tako da mora število neničelnih celic v izhodu biti enako številu v vnosu. Ko je vrtenje maske na poti zunanjega poskusa sporočilo 4800 nastavljenih celic na vstopu in 7439 na izstopu, je bila ta ena sama primerjava dovolj, da se transformacija obsodi, brez branja ene same vrstice njene geometrije

Vzrok je bil vsakdanj in vreden odnesti kot pravilo. Dinamičnemu seznamu, velikosti z SetLength, ni jamstvo, da pride izničen, ko je rezultat funkcije, ki potuje po poti, ki je izvajalno okolje ne počisti, celice, ki jih vrtenje nikoli ne zapiše, pa nosijo kateri koli bajte, ki so bili tam prej. Nekateri od teh zastarelih bajtov so neničelni, neničelno pa pomeni črnilo. Popravilo je ena vrstica, FillChar(Result[0], N, 0), preden zažene zanka permutacije, disciplina, ki jo implicira, pa je širša: vsaka funkcija, ki vrača masko ali medpomnilnik bitne preslikave, naj svoj izhod izrecno počisti, namesto da bi se zanašala na semantiko dodelitve

Tisto, kar je pomanjkljivost pustilo preživeti tri izdaje, je zanimivejše od pomanjkljivosti. Ko je QR preselil svojo obravnavo orientacije v dekoder, je QR prenehal popolnoma izvajati zunanje vrtenje maske in edini preostali potrošnik te kodne poti je bil Code 39. Deljena infrastruktura skriva takšne hrošče nenehno: pokritost iz ene zmožnosti naredi pot videti testirano, medtem ko zmožnost, ki dejansko visi od nje, nima nobene svoje. Vsaka pot, ki jo nova zmožnost preneha uporabljati, potrebuje test, ki jo še uporablja

Branje rezultatov nazaj v koordinatah strani

Vsaka geometrijska vrednost, ki jo dekoder proizvede, je izražena v koordinatnem okvirju poskusne bitne preslikave, klicatelj pa jo potrebuje v uporabniškem prostoru PDF. Pretvorba teče v dveh stopnjah: razveljavite četrtinski obrat, ki ga je nanesel poskus, nato razveljavite izrisno transformacijo, ki je uporabniški prostor preslikala na bitno preslikavo. Kar pride v THPDFDecodedBarcode, je omejitvena škatla, poravnana z osmimi, v uporabniškem prostoru, z Left, Bottom, Right in Top, ki sledijo konvenciji PDF, da Y narašča navzgor, plus OrientationDegrees v nasprotni smeri urinega kazalca

Cevovod črtnih kod HotPDF od izrisane bitne preslikave strani prek vzorčenja v boolean matrico modulov, normalizacije D4, zaznavanja verzije z deljivostjo in dekodiranja Reed-Solomon, nato dvostopenjska pretvorba koordinat, ki razveljavi četrtinski obrat poskusa in izrisno transformacijo, preden THPDFDecodedBarcode objavi Left, Bottom, Right, Top in OrientationDegrees v uporabniškem prostoru
Normalizacija QR znotraj dekoderja omogoči, da se strani razrešijo ob prvem poskusu, dvostopenjska pretvorba koordinat pa rezultate poskusne bitne preslikave spremeni v škatle uporabniškega prostora, poravnane z osmimi

Zmešajte smer te druge pretvorbe in simptom je grd: besedilo se dekodira popolnoma, a škatla, ki jo narišete za pregledno plast, pristane na zrcalni sliki pravega položaja. Kdorkoli gradi pregledni vmesnik na dekoderju, naj trditev postavi proti znani napravi, s simbolom, namerno postavljenim blizu enega od kotov strani, da je obrnjen os Y viden na prvi pogled. Isto razmišljanje velja za vsako koordinato, ki prečka izrisno mejo, zato je vredno razumeti izris strani PDF v bitno preslikavo v Delphi, preden gradite na dekoderju

Kaj bo in česar ne bo vgrajeni dekoder

Vgrajeni dekoder je omejena implementacija brez odvisnosti in je iskren glede svojih mej, namesto da bi se tiho zapadel. Prepozna Code 39 in QR, validira BCH zaščitene bite formata in vzorec maske, preden objavi kakršne koli podatke, in ne poskuša obnoviti napak na poškodovanih simbolih. Če je vaš vhod fotografija ukrivljene nalepke pod neenakomerno svetlobo, je to drugačen razred težav in želi specializiran motor

// Zamenjajte svoj motor: implementirajte IHPDFBarcodeDecoder in ga podajte
// preobremenitvi, ki zna dekoder. HotPDF še vedno lasti izris strani,
// proračune, preslikavo koordinat in odstranjevanje dvojnikov
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 tisto, pri čemer produkcijski cevovod zasluži svoje. RotationAttemptCount in DecoderCallCount vam povesta, ali je zunanji poskus sploh tekel, ReceivedResultCount proti AcceptedResultCount loči dekoder, ki ni našel nič, od praga zaupanja, ki je zavrnil vse, kar je našel, RenderedPixels z PeakWorkingBytes pa je tisto, kar grafično prikažete, ko se paketno opravilo začne dušiti samo. Prazen nabor rezultatov plus bdsSucceeded pomeni, da stran res nima berljivega simbola, kar je druga operativno dejstvo od bdsBudgetExceeded

Polja proračuna si zaslužijo premišljeno odločitev in ne privzetka. MaxPixels in MaxWorkingBytes obstajata, ker DPI množi kvadratno: prehod s 300 na 600 DPI na strani A4 štirikratno poveča tako ceno izrisa kot vrh dodelitve, nezaupljiv vhod, ki razglasi ogromno škatlo strani, pa lahko skeniransko opravilo spremeni v incident zmanjkane pomnilnine. Omejitve nastavite na to, kar potrebuje vaš najslabši legitimni dokument, nato pa naj bdsBudgetExceeded osamelce usmeri na počasnejšo, izolirano pot

Če vaši dokumenti mešajo strojno berljive nalepke z natisnjenim besedilom, ki ga nameravate indeksirati, se dekoder črtnih kod naravno poveže z prepoznavalnim motorjem, obravnavanim v OCR z iskanjem predlog znotraj HotPDF, generacijska stran iste zgodbe pa je v risanju črtnih kod v PDF s HotPDF. Oba tečeta na isti infrastrukturi izrisa in proračuna, tako da cevovod, ki že postavi zdrave meje za enega, drugega dobi skoraj zastonj

Toleranca vrtenja je ena od tistih zmožnosti, ki je nevidna, ko deluje, in razjarjujoča, ko ne, inženirski pouk pa se posplošuje čez QR: normalizirajte tako blizu semantični predstavitvi, kot lahko pridete, in ne na plasti pikslov, kjer podatki še nosijo vsako nesrečo tega, kako so bili zajeti. HotPDF to pošilja kot del HotPDF Delphi PDF component, ob delih izrisa, OCR in analize strani, ki jih isti sprejemni cevovodi običajno potrebujejo