Odborný článok

Dekódovanie otočených QR kódov v stranách PDF s HotPDF

HotPDF dekóduje otočené QR symboly v načítanej strane PDF tak, že normalizuje vzorkovanú modulovú maticu cez všetkých ôsmich D4 orientácií priamo vo vnútri dekodéra. Vonkajší rotačný retry, ktorý funguje pre lineárne symbológie, nemôže fungovať pre QR a pochopiť prečo vám ušetrí deň lovenia dekodéra, ktorý vyzerá pokazený, ale nie je

Scenár je úplne obyčajný. Naskenované dodacie listy prichádzajú ako PDF, každá strana nesie QR etiketu a operátor skenera nasypal stoh listov do zásobníka tak, ako sa mu to zmestilo. Niektoré etikety sú vzpriamené, niektoré sú o štvrtinu otočené a pár je hore nohami. Zavoláte barcode dekodér, polovica strán sa vyrieši a druhá polovica sa vráti prázdna bez jedinej chyby

Prečo rotácia scan masky nikdy neopraví otočené QR?

Pretože rozloženie QR finder patternov je zámerne asymetrické a rotácia celého obrazu túto asymetriu zachováva namiesto toho, aby ju odstránila. QR Code umiestňuje tri finder štvorce do rohov hore vľavo, hore vpravo a dole vľavo a roh dole vpravo necháva prázdny (ISO/IEC 18004:2015 §6.3.3). Ten chýbajúci roh je orientácií nápoveda. Otočte bitmapu strany o deväťdesiat stupňov a medzera sa jednoducho presunie do iného rohu. Neexistuje netriviálna rotácia roviny, ktorá by namapovala trojrohové rozloženie späť na seba, takže dekodér akceptujúci len kanonické usporiadanie zamietne každý pokus postupne

Na tom záleží preto, že zjavná oprava je zlá. Prirodzený inštinkt je zavesiť retry navonok: vyrenderujte stranu, podajte masku dekodéru a ak to zlyhá, otočte masku a skúste znova pri 90, 180 a 270 stupňoch. Pre Code 39 je táto politika úplne správna, lebo lineárna symbológia má start a stop pattern, ktorý skener nájde, akonáhle bežia čiary vodorovne. Pre QR sú to štyri garantované zlyhania nasledované správou nič nenájdené

Grupa D4 aplikovaná na modulovú maticu

Správne miesto pre normalizáciu je po vzorkovaní, na booleovskej modulovej mriežke, nie na pixelovej maske. Keď dekodér vyrieši symbol do matice n krát n tmavých a svetlých modulov, môže vymenovať dihedrálnu grupu štvorca: štyri rotácie krát dva odrazy, celkovo osem kandidátnych orientácií. Pre každého kandidáta skontroluje finder trojuholník a prvý kandidát, ktorej tri findery dopadnú na pozície hore vľavo, hore vpravo a dole vľavo, je skutočná orientácia. Odtiaľ beží existujúca pipeline nezmenene, pretože bity formátových informácií, zigzag umiestnenie dát aj Reed-Solomon korekcia všetky predpokladajú kanonickú maticu a teraz ju dostanú

Štyri vykreslenia tej istej QR modulovej matice HotPDF pod rotáciami grupy D4 pri 0, 90, 180 a 270 stupňoch, ukazujúce tri finder patterny putujúce po rohoch, kým prázdny roh sa s nimi presúva, takže len kanonická orientácia predkladá dekodéru findery hore vľavo, hore vpravo a dole vľavo
Rotácia pixelovej masky nedokáže odstrániť QR finder asymetriu, takže HotPDF vymenúva D4 orientácie na vzorkovanej modulovej matici a drží prvého kandidáta, ktorej findery dopadnú hore vľavo, hore vpravo a dole vľavo

Dve vlastnosti to robia lacným. Matica je malá v porovnaní s vyrenderovanou bitmapou, takže osem transpozícií stoja výrazne menej než osem renderovaní strany. A matica je čisté booleovské pole postavené vzorkovačom, takže žiadna transformácia po ceste nemôže vniesť hodnoty, ktoré nikdy neboli vzorkované

Detekcia verzie je hľadanie deliteľnosti, nie delenie

Počet modulov nemožno odvodiť delením vzorkovanej šírky predpokladanou veľkosťou modulu a pomýliť sa tu je jemný zdroj dekódovacích zlyhaní na vysoce rozlíšených renderoch. QR symbol verzie v je široký 4v + 17 modulov, takže verzia 1 je 21 modulov a verzia 40 je 177. Maska široká 126 pixelov je rovnako konzistentná s verziou 1 pri šiestich pixeloch na modul ako s niekoľkými vyššími verziami pri menších veľkostiach modulov. Lineárne delenie jednu vyberie a zvyčajne vyberie zle

Funguje hľadanie deliteľnosti nad kandidátnymi verziami. Prejdite od verzie 40 smerom dole po verziu 1, ponechajte kandidátov, ktorých počet modulov delí vzorkovanú šírku rovnomerne a necháva aspoň tri pixely na modul, a vezmite najmenšiu preživšiu verziu. Trojpíxelsové minimum je to, čo bráni hľadaniu akceptovať absurdne husté čítanie hrubého symbolu a pravidlo najmenšej verzie rozrieši zostávajúcu nejednoznačnosť v prospech čítania, ktoré by skener reálne vyprodukoval

Prechod detekcie verzie HotPDF pre QR symbol na vzorkovanej maske širokej 126 pixelov, testujúci každý kandidátny počet modulov 4v plus 17 od verzie 40 smerom do po verziu 1 na rovnomernú deliteľnosť a trojpíxelsové minimum modulu, kým nevyhria najmenšia preživšia verzia
Počet QR modulov pochádza z hľadania deliteľnosti nad kandidátnymi verziami, nie z delenia šírky masky predpokladanou veľkosťou modulu a najmenšia preživšia verzia rozrieši nejednoznačnosť
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 vracia naplnený záznam, nie vynulovaný, čo záleží, lebo DPI nula alebo strop výsledkov nula je vyzerajúco platný spôsob, ako nedostanú nič. RotationPolicy riadi len vonkajší retry: bdrpNone renderuje raz, bdrpFallback skúša ostatné orientácie po neúspešnom prvom prechode a bdrpAll renderuje každú orientáciu bezpodmienečne. Keďže QR normalizácia prebieha vo vnútri dekodéra, QR strany sa vyriešia na prvý pokus pri ktorejkoľvek z troch politík. Politika je tam pre lineárne symbológie, ktoré ju naozaj potrebujú

Ako dokážete, že bitmapová transformácia nevynašla pixely?

Spočítajte atrament na oboch stranách a vyžadujte, aby si sady sedeli. Rotácia je permutácia pixelov, nič viac, takže počet nenulových buniek vo výstupe sa musí rovnať počtu na vstupe. Keď rotácia masky vo vonkajšej retry ceste hlásila 4800 nastavených buniek na vstupe a 7439 na výstupe, toto jediné porovnanie stačilo na usvedčenie transformácie bez prečítania jediného riadka jej geometrie

Príčina bola prozaická a stojí za odnesenie ako pravidlo. Dynamické pole dimensionované cez SetLength nie je garantované, že príde vynulované, keď je výsledkom funkcie putujúcej cestou, ktorú behové prostredie nečistí, a bunky, ktoré rotácia nikdy nezapíše, nesú potom bajty, ktoré tam boli predtým. Niektoré z týchto zastaraných bajtov sú nenulové a nenulové znamená atrament. Oprava je jeden riadok, FillChar(Result[0], N, 0) pred spustením permutačného cyklu a disciplína, ktorú naznačuje, je širšia: každá funkcia vrátiaca masku alebo bitmapový buffer by mala výstup explicitne vyčistiť namiesto spoliehania sa na sémantiku alokácie

To, čo umožnilo defektu prežiť tri vydania, je zaujímavejšie než defekt sám. Keď QR presunulo svoje spracovanie orientácie do dekodéra, QR úplne prestalo používať vonkajšiu rotáciu masky a jediným zostávajúcim konzumentom tej kódovej cesty bol Code 39. Zdieľaná infraštruktúra skrýva chyby ako táto neustále: pokrytie z jednej funkcie robí cestu vyzerajúco testovanou, kým funkcia, ktorá na nej reálne závisí, nemá vlastné žiadne. Každá cesta, ktorú nová funkcia prestane používať, potrebuje test, ktorý ju stále používa

Čítanie výsledkov späť v súradniciach strany

Každá geometrická hodnota, ktorú dekodér produkuje, je vyjadrená v súradnicovom rámci pokusnej bitmapy a volajúci ju potrebuje v PDF user space. Táto konverzia beží v dvoch etapách: najprv sa vráti štvrtina otáčky, ktorú aplikoval retry, potom sa vráti render transformácia, ktorá namapovala user space na bitmapu. To, čo prichádza v THPDFDecodedBarcode, je bounding box zarovnaný na osi v user space, s Left, Bottom, Right a Top podľa PDF konvencie, že Y rastie nahor, plus OrientationDegrees proti smeru hodinových ručičiek

Barcode pipeline HotPDF od vyrenderovanej bitmapy strany cez vzorkovanie do booleovskej modulovej matice, D4 normalizáciu, deliteľnostnú detekciu verzie a Reed-Solomon dekódovanie, potom dvojetapová súradnicová konverzia, ktorá vráti štvrtinu otáčky retry aj render transformáciu skôr, než THPDFDecodedBarcode zverejní Left, Bottom, Right, Top a OrientationDegrees v user space
QR normalizácia vo vnútri dekodéra umožňuje stránam vyriešiť sa na prvý pokus, kým dvojetapová súradnicová konverzia mení výsledky pokusnej bitmapy na boxy v user space zarovnané na osi

Pomýliť sa na smere tej druhej konverzie a príznak je nepríjemný: text sa dekóduje perfektne, ale box, ktorý nakreslíte pre revíznu vrstvu, dopadne na zrkadlový obraz správnej pozície. Každý, kto stavia revízne rozhranie nad dekodérom, by mal tvrdiť proti známemu fixtúrnemu prípadu, so symbolom umiestneným zámerne blízko jedného rohu strany, aby prevrátená os Y bola viditeľná na prvý pohľad. Rovnaké uvažovanie platí pre každú súradnicu prekračujúcu renderovaciu hranicu, a práve preto stojí za to pochopiť článok vykreslenie PDF strany do bitmapy v Delphi, skôr než začnete stavať nad dekodérom

Čo zabudovaný dekodér urobí a čo nie

Zabudovaný dekodér je ohraničená implementácia bez závislostí a je voči svojim limitom poctivá namiesto tichej degradácie. Rozpoznáva Code 39 a QR, validuje BCH chránené formátové bity a maskový pattern skôr, než zverejní akékoľvek dáta, a nepokúša sa o obnovu chýb na poškodených symboloch. Ak je vašim vstupom fotografia zakrivenej etikety pod nerovnomerným svetlom, je to iná trieda problémov a chce si špecializovaný engine

// Vymeňte vlastný engine: implementujte IHPDFBarcodeDecoder a podajte ho
// overloadu vediacemu dekodér. HotPDF stále vlastní renderovanie strany,
// rozpočty, mapovanie súradníc a deduplikáciu
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 miesto, kde produkčná pipeline reálne vyniká. RotationAttemptCount a DecoderCallCount vám povedia, či vonkajší retry vôbec bežal, ReceivedResultCount proti AcceptedResultCount odlíši dekodér, ktorý nič nenašiel, od prahu istoty, ktorý zamietol všetko, čo našiel, a RenderedPixels s PeakWorkingBytes je to, čo grafovať, keď dávková úloha začne ávať. Prázdna sada výsledkov plus bdsSucceeded znamená, že strana reálne nemá čitateľný symbol, čo je operačne iný fakt než bdsBudgetExceeded

Polia rozpočtu si zaslúžia zámerné rozhodnutie, nie default. MaxPixels a MaxWorkingBytes existujú preto, že DPI sa násobí kvadraticky: prechod z 300 na 600 DPI na strane A4 zoštvornásobí renderovacie náklady aj špičkovú alokáciu a nedôveryhodný vstup deklarujúci obrovský page box môže zmeniť skenovaciu úlohu na incident out-of-memory. Nastavte stropy na to, čo potrebuje váš najhorší legitímny dokument, a potom nechajte bdsBudgetExceeded smerovať odchýlky na pomalšiu, izolovanú cestu

Ak vaše dokumenty miešajú strojovo čitateľné etikety s tlačeným textom, ktorý plánujete indexovať, barcode dekodér sa prirodzene páruje s rozpoznávacím engineom popísaným v článku OCR porovnávaním šablón vo vnútri HotPDF a generačná strana toho istého príbehu je v článku kreslenie čiarových kódov do PDF s HotPDF. Oba bežia na tej istej renderovacej a rozpočtovej infraštruktúre, takže pipeline, ktorá už nastavila rozumné limity pre jeden, dostane druhý takmer zadarmo

Tolerancia rotácie je jedna z tých funkcií, ktoré sú neviditeľné, keď fungujú, a rozčuľujúce, keď nefungujú, a inžinierske ponaučenie sa zobecňuje za QR: normalizujte čo najbližšie k semantickej reprezentácii, nie na pixelovej vrstve, kde dáta ešte nesú každú nehodu toho, ako boli zachytené. HotPDF dodáva toto ako súčasť HotPDF Delphi PDF komponentu, spolu s časťami na renderovanie, OCR a analýzu strán, ktoré tie isté príjmové pipeline zvyčajne potrebujú