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ú
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
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
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ú