Műszaki cikk

Elforgatott QR kódok dekódolása PDF oldalakon a HotPDF-fel

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

Ugyanannak a HotPDF QR modulmátrixnak négy megjelenítése a D4 csoport 0, 90, 180 és 270 fokos forgatásai alatt, megmutatva, hogy a három keresőminta sarkok között vándorol, miközben az üres sarok velük együtt mozog, tehát csak a kanonikus tájolás mutat keresőket a bal felső, jobb felső és bal alsó pozícióban a dekódernek
A pixelmaszk forgatása nem tudja elvenni a QR kereső aszimmetriáját, ezért a HotPDF felsorolja a D4 tájolásokat a mintavételezett modulmátrixon, és megtartja az első jelöltet, amelynek keresői bal felső, jobb felső és bal alsó pozícióba esnek

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

A HotPDF verziófelismerési bejárása egy QR szimbólumhoz egy 126 pixeles mintavételezett maszkon, amely a 4v plus 17 jelölt modulszámokat teszli a 40-es verziótól az 1-ig maradék nélküli oszthatóságra és hárompixeles modul alsó határra, mielőtt a legkisebb fennmaradó verzió nyerne
A QR modulszám oszthatósági keresésből jön a jelölt verziókon, nem a maszk szélességének feltételezett modulmérettel osztásából, és a legkisebb fennmaradó verzió oldja fel a kétértelműséget
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

A HotPDF vonalkód folyamatsor a renderelt oldal bitmaptől a mintavételezésen át a boolean modulmátrixig, D4 normalizálás, oszthatósági verziófelismerés és Reed-Solomon dekódolás, majd a két szakaszos koordinátaátváltás, amely visszacsinálja az újrapróbálkozás negyed fordulatát és a render transzformációt, mielőtt a THPDFDecodedBarcode közzéteszi a Left, Bottom, Right, Top és OrientationDegrees értékeket felhasználói térben
A QR normalizálás a dekóderen belül lehetővé teszi, hogy az oldalak első próbálkozásra oldódjanak fel, míg a két szakaszos koordinátaátváltás a próbálkozási bitmap eredményeit tengelypárhuzamos felhasználói téri dobozokká teszi

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