A HotPDF egy betöltött PDF oldal elforgatott QR szimbólumait úgy dekódolja, hogy a mintavételezett modulmátrixot a dekóderen belül végigmormalizálja mind a nyolc D4 tájoláson. A külső forgatási újrapróbálkozás, amely a lineáris szimbológiáknál működik, QR-nél nem működhet, és ennek megértése megment egy napot attól, hogy egy olyan dekódert üldözzön, amely töröttnek látszik, de nem az
A forgatókönyv elég hétköznapi. Szkennelt szállítólevelek érkeznek PDF-ként, minden oldal QR címkét visel, és a szkennelőkezelő abba az irányba etette be a papírkupacot, amelyet a tálca elfogadott. Néhány címke egyenes, néhány negyed fordulattal eltér, néhány fejjel lefelé áll. Meghívja a vonalkód dekódert, az oldalak fele feloldódik, a másik fele üresen tér vissza, hiba nélkül
Miért nem javítja meg a szkennelő maszk forgatása az elforgatott QR-t?
Mert a QR keresőminta-elrendezés szándékosan aszimmetrikus, és a teljes kép forgatása megőrzi azt az aszimmetriát, ahelyett hogy elvenné. A QR Code három keresőnégyzetet helyez a bal felső, jobb felső és bal alsó sarokba, és a jobb alsó sarkot üresen hagyja (ISO/IEC 18004:2015 §6.3.3). Az a hiányzó sarok a tájolási jelzés. Forgassa el az oldal bitmapjét kilencven fokkal, és a rés egyszerűen másik sarokba költözik. Nincs a sík nem triviális forgatása, amely egy háromsarkos elrendezést önmagára képezne vissza, tehát egy csak a kanonikus elrendezést elfogadó dekóder minden próbálkozást sorra elutasít
Ez azért fontos, mert a kézenfekvő javítás a rossz. A természetes ösztön az újrapróbálkozást kívülre akasztja: renderelje az oldalt, adja a maszkot a dekódernek, és ha az elbukik, forgassa el a maszkot, és próbálja újra 90, 180 és 270 fokon. Code 39-nél ez a politika pontosan helyes, mert egy lineáris szimbológia rendelkezik start és stop mintával, amelyet a szkenner megtalál, ha a sávok vízszintesen futnak. QR-nél ez négy garantált bukás, amelyet egy semmit nem talált jelentés követ
A D4 csoport, alkalmazva a modulmátrixra
A normalizálás helyes helye a mintavételezés után van, a boolean modulrácson, nem a pixelmaszkon. Miután a dekóder feloldotta a szimbólumot sötét és világos modulok n mal n nagyságú mátrixába, felsorolhatja a négyzet dihedrális csoportját: négy forgatás szorozva két tükrözéssel, összesen nyolc jelölt tájolás. Minden jelöltnél ellenőrzi a keresőhármasát, és az első jelölt, amelynek három keresője a bal felső, jobb felső és bal alsó pozícióba esik, a valódi tájolás. Onnantól a meglévő folyamatsor változatlanul fut, mert a formátuminformációs bitek, a cikcakk adatelhelyezés és a Reed-Solomon korrekció mind kanonikus mátrixot tételez fel, és most kap is egyet
Két tulajdonság teszi ezt olcsóvá. A mátrix kicsi a renderelt bitmaphez képest, tehát nyolc transzponálás jóval kevésbé kerül, mint nyolc oldalrenderelés. És a mátrix tiszta boolean tömb, amelyet a mintavevő épít, tehát egyetlen átalakítás sem vihet be útközben olyan értékeket, amelyeket sosem mintavételeztek
A verziófelismerés oszthatósági keresés, nem osztás
A modulszám nem származtatható a mintavételezett szélesség feltételezett modulmérettel osztásából, és ennek elrontása a decode hibák finom forrása nagyfelbontású rendereléseknél. Egy v verziójú QR szimbólum 4v + 17 modul széles, tehát az 1-es verzió 21 modul, a 40-es pedig 177. Egy 126 pixel széles maszk egyaránt egyeztethető az 1-es verzióval hat pixel modulonként és több magasabb verzióval kisebb modulméreteknél. A lineáris osztás azok egyikét választja, és általában rosszul
Ami működik, az oszthatósági keresés a jelölt verziókon. Járja a verziókat 40-től 1-ig, tartsa meg azokat a jelölteket, amelyeknek a modulszáma maradék nélkül osztja a mintavételezett szélességet, és modulonként legalább három pixelt hagy, majd válassza a legkisebb fennmaradó verziót. A hárompixeles alsó határ akadályozza meg a keresést abban, hogy egy durva szimbólum abszurdan sűrű olvasatát elfogadja, és a legkisebb verzió szabálya a maradék kétértelműséget arra az olvasatra oldja fel, amelyet egy szkenner ténylegesen adna
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;
A THPDFBarcodeDecodeOptions.Default feltöltött rekordot ad vissza, nem nullázottat, ami azért fontos, mert a nulla DPI vagy a nulla eredményhatár hihető módszernek látszik a semmi visszakapására. A RotationPolicy csak a külső újrapróbálkozást irányítja: a bdrpNone egyszer renderel, a bdrpFallback a sikertelen első menet után próbálja a többi tájolást, a bdrpAll pedig feltétel nélkül renderel minden tájolást. Mivel a QR normalizálás a dekóderen belül történik, a QR oldalak bármelyik politika alatt első próbálkozásra feloldódnak. A politika azoknak a lineáris szimbológiáknak van ott, amelyek ténylegesen igénylik
Hogyan bizonyítható, hogy egy bitmap transzformáció nem talál fel pixeleket?
Számolja meg a festéket mindkét oldalon, és követelje meg, hogy az összegek egyezzenek. Egy forgatás pixelek permutációja, ennél semmi több, tehát a kimenet nem nulla celláinak száma meg kell egyezzen a bemenetében. Amikor a külső újrapróbálkozási út maskrotációja befelé 4800 beállított cellát, kifelé 7439-at jelentett, az az egyetlen összehasonlítás elég volt a transzformáció elítéléséhez anélkül, hogy egy sort olvasott volna a geometriájából
Az ok hétköznapi volt, és érdemes szabályként magával vinni. Egy SetLength-tel méretezett dinamikus tömb nem garantáltan érkezik nullázva, amikor függvényeredményként utazik egy olyan úton, amelyet a futtatókörnyezet nem töröl, és azok a cellák, amelyeket a forgatás sosem ír, hordozzák az ott korábban lévő bájtokat. Néhány elavult bájt nem nulla, a nem nulla pedig festéket jelent. A javítás egy sor: FillChar(Result[0], N, 0) a permutációs ciklus futása előtt, és az általa sugallt fegyelem tágabb: minden maszkot vagy bitmap buffert visszaadó függvény explicit módon törölje a kimenetét, allokációs szemantikába vetett bizalom helyett
Az, amely lehetővé tette, hogy a hiba három kiadást vészeljen át, érdekesebb, mint maga a hiba. Amint a QR áthelyezte a tájoláskezelését a dekóderbe, a QR teljesen leállította a külső maszkforgatás gyakorlását, és annak a kódútnak az egyetlen megmaradó fogyasztója a Code 39 lett. A megosztott infrastruktúra ilyen hibákat rejteget örökké: az egyik funkció lefedettsége teszteltnek láttat egy utat, miközben annak a funkciónak, amely ténylegesen tőle függ, nincs sajátja. Minden útnak, amelyet egy új funkció abbahagy használni, kell egy teszt, amely még mindig használja
Az eredmények visszaolvasása oldal koordinátákban
Minden geometriai értéket, amelyet a dekóder előállít, a próbálkozási bitmap koordinátakerete fejez ki, és a hívónak PDF felhasználói térben van rá szüksége. Az átváltás két szakaszban fut: visszacsinálja a negyed fordulatot, amelyet az újrapróbálkozás alkalmazott, majd a render transzformációt, amely a felhasználói teret a bitmapra képezte. Ami a THPDFDecodedBarcode-ba érkezik, az tengelypárhuzamos befoglaló doboz felhasználói térben, Left, Bottom, Right és Top értékekkel a PDF szerinti konvenció szerint, hogy az Y felfelé nő, plusz egy az óramutató járásával ellentétes OrientationDegrees
Azon a második átváltáson az irányt elrontva a tünet csúnya: a szöveg tökéletesen dekódolódik, de az a doboz, amelyet felülvizsgálati réteghez rajzol, a helyes pozíció tükörképére esik. Aki felülvizsgálati felületet épít a dekóderre, az ismert tesztesettel szemben állítson, egy szimbólummal, amelyet szándékosan az egyik oldalsarok közelébe tettek, hogy a tükrözött Y tengely első ránézésre látható legyen. Ugyanez az érvelés érvényes minden koordinátára, amely átlépi a renderelési határt, és ezért érdemes megérteni a PDF oldal bitmapre renderelését Delphiben, mielőtt a dekóderre épít
Mit tud és mit nem tud a beépített dekóder
A beépített dekóder korlátolt, függőségmentes implementáció, és őszinte a határairól, csendes leromlás helyett. Felismeri a Code 39-et és a QR-t, ellenőrzi a BCH-védett formátumbiteket és a maszkmintát, mielőtt bármilyen adatot közzétenne, és nem próbál hibajavítást sérült szimbólumokon. Ha a bemenete egy hajlított címke fényképe egyenetlen fényben, az más problémakategória, és specializált motort kíván
// Cserélje be a saját motorját: implementálja az IHPDFBarcodeDecoder interfészt,
// és adja át a dekóder-tudatos túlterhelésnek. A HotPDF továbbra is birtokolja
// az oldalrenderelést, a kereteket, a koordinátaleképezést és a deduplikációt
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;
A THPDFBarcodeDecodeInfo az a hely, ahol egy éles folyamatsor megkeresi a munkáját. A RotationAttemptCount és a DecoderCallCount megmondja, futott-e egyáltalán a külső újrapróbálkozás, a ReceivedResultCount a AcceptedResultCount-tal szemben elkülöníti azt a dekódert, amely semmit sem talált attól a magabiztossági küszöbtől, amely mindent elutasított, amit talált, a RenderedPixels pedig a PeakWorkingBytes-szal az, amit akkor rajzol grafikonra, amikor egy kötegelt feladat kavarni kezd. Egy üres eredményhalmaz bdsSucceeded-del azt jelenti, hogy az oldalnak ténylegesen nincs olvasható szimbóluma, ami más működési tény, mint a bdsBudgetExceeded
A keretmezők tudatos döntést érdemelnek, alapértelmezés helyett. A MaxPixels és a MaxWorkingBytes azért létezik, mert a DPI négyzetesen szorz: 300ról 600 DPI-re lépni egy A4 oldalon négyszerezi mind a renderelési költséget, mind a csúcsfoglalást, és egy hatalmas oldaldobozt deklaráló nem megbízható bemenet egy szkennelési feladatot memóriakifutási eseménnyé tehet. Állítsa a határokat arra, amire a legrosszabb legitim dokumentumnak szüksége van, majd hagyja, hogy a bdsBudgetExceeded a kiugrókat egy lassabb, izolált útra terelje
Ha a dokumentumai géppel olvasható címkéket kevernek nyomtatott szöveggel, amelyet indexelni tervez, a vonalkód dekóder természetesen párosul a sablonillesztéses OCR a HotPDF-en belül cikkben tárgyalt felismerőmotorral, és ugyanazon történet generáló oldala a vonalkódok rajzolása PDF-be HotPDF-fel cikkben. Mindkettő ugyanazon renderelési és keretinfrastruktúrán fut, tehát egy folyamatsor, amely már észszerű határokat állít az egyiknek, a másikat majdnem ingyen kapja
A forgatástűrés azoknak a funkcióknak egyike, amely működés közben láthatatlan, és dühítő, amikor nem, és a mérnöki tanulság a QR-n túl általánosítható: normalizáljon a lehető legközelebb a szemantikai ábrázoláshoz, nem a pixelrétegen, ahol az adat még hordozza minden véletlenét annak, ahogyan rögzítették. A HotPDF ezt a HotPDF Delphi PDF komponens részeként szállítja, a renderelési, OCR és oldalanalízis darabok mellett, amelyekre ugyanazok a beérkező folyamatsorok általában szükségesek