Tekninen artikkeli

Käännettyjen QR-koodejen dekoodaus PDF-sivuissa HotPDF:llä

HotPDF dekoodaa käännetyt QR-symbolit ladatussa PDF-sivussa normalisoimalla näytteistetyn moduulimatriisin kaikkien kahdeksan D4-suuntauksen läpi dekooderin itsensä sisällä. Ulompi kierto-uudelleenyritys, joka toimii lineaarisille symbologioille, ei voi toimia QR:lle, ja syyn ymmärtäminen säästää sinulta päivän dekooderin jahtaamista, joka näyttää rikki olevan muttei ole

Skenaario on riittävän tavallinen. Skannatut toimitusasiakirjat saapuvat PDF:inä, jokainen sivu kantaa QR-lappua, ja skannaaja syötti pinon arkkeja siinä suunnassa, jonka kasetti hyväksyi. Osa lapuista on pystyssä, osa on neljänneskierroksen pielessä, ja muutama on väärinpäin. Kutsut viivakoodidekooderia, puolet sivuista ratkeaa, ja toinen puoli palaa tyhjänä ilman mitään virhettä

Miksi skannausmaskin kiertäminen ei koskaan korjaa käännettyä QR:ää?

Koska QR:n finder-kaavan asettelu on tarkoituksella epäsymmetrinen, ja kokonaiskuvan kierto säilyttää tuon epäsymmetrian poistamisen sijaan. QR Code sijoittaa kolme finder-neliötä vasempaan ylä-, oikeaan ylä- ja vasempaan alanurkkaan ja jättää oikean alanurkan tyhjäksi (ISO/IEC 18004:2015 §6.3.3). Tuo puuttuva nurkka on suuntausvihje. Kierrä sivubittikartta yhdeksänkymmenellä asteella, ja aukko vain siirtyy toiseen nurkkaan. Ei ole olemassa ei-triviaalia tason kiertoa, joka kuvaisi kolmen nurkan asettelun takaisin itsensä päälle, joten dekooderi, joka hyväksyy vain kanonisen järjestyksen, hylkää jokaisen yrityksen vuorollaan

Tällä on merkitystä, koska ilmeinen korjaus on väärä. Luonteva vaisto on ripustaa uudelleenyritys ulkopuolelle: renderöi sivu, anna maski dekooderille, ja jos se epäonnistuu, kierrä maski ja yritä uudelleen 90, 180 ja 270 asteella. Code 39:lle tuo politiikka on täsmälleen oikein, koska lineaarisella symbologialla on aloitus- ja lopetuskaava, jonka skannaaja löytää, kun palkit kulkevat vaakasuorassa. QR:lle se on neljä taattua epäonnistumista, joita seuraa raportti, jossa ei löytynyt mitään

D4-ryhmä sovellettuna moduulimatriisiin

Normalisoinnin oikea paikka on näytteistyksen jälkeen, boolen moduuliruudukossa pikselimaskin sijaan. Kun dekooderi on ratkaissut symbolin n kertaa n -matriisiksi tummia ja vaaleita moduleita, se voi luetella neliön dieedriryhmän: neljä kiertoa kertaa kaksi peilausta, yhteensä kahdeksan ehdokassuuntausta. Jokaiselle ehdokkaalle se tarkistaa finder-kolmion, ja ensimmäinen ehdokas, jonka kolme finderia laskeutuvat vasempaan ylä-, oikeaan ylä- ja vasempaan alanurkkaan, on oikea suuntaus. Siitä eteenpäin olemassa oleva putki ajaa muuttumattomana, koska formaattitietobitit, zigzag-datansijoitus ja Reed-Solomon-korjaus kaikki olettavat kanonisen matriisin ja saavat nyt sellaisen

Neljä saman HotPDF:n QR-moduulimatriisin esitystä D4-ryhmän kiertotilassa 0, 90, 180 ja 270 asteella, osoittaen kolmen finder-kaavan vaeltavan nurkista tyhjän nurkan liikkuessa mukana, joten vain kanoninen suuntaus esittää finderiä vasemmassa ylä-, oikeassa ylä- ja vasemmassa alanurkassa dekooderille
Pikselimaskin kiertäminen ei voi poistaa QR:n finder-epäsymmetriaa, joten HotPDF luetteloi D4-suuntauksen näytteistetyllä moduulimatriisilla ja pitää ensimmäisen ehdokkaan, jonka finderit laskeutuvat vasemmalle ylös, oikealle ylös ja vasemmalle alas

Kaksi ominaisuutta tekee tästä halpaa. Matriisi on pieni renderöityyn bittikarttaan verrattuna, joten kahdeksan transpoosia maksavat paljon vähemmän kuin kahdeksan sivurenderöintiä. Ja matriisi on siisti bool-taulu, jonka näytteistäjä rakensi, joten mikään transformaatio matkalla ei voi tuoda arvoja, joita ei koskaan näytteistetty

Version tunnistus on jaollisuushaku, ei jakolasku

Moduulimäärää ei voi johtaa jakamalla näytteistetty leveys oletetulla moduulikoolla, ja tämän saaminen väärin on hienovarainen dekoodausvikojen lähde korkean resoluution renderöinneissä. Version v QR-symboli on 4v + 17 moduulia leveä, joten versio 1 on 21 moduulia ja versio 40 on 177. Maski, joka mittaa 126 pikseliä leveäksi, on yhtä lailla yhteensopiva version 1 kanssa kuudella pikselillä moduulia kohden kuin useamman korkeamman version kanssa pienemmillä moduulikoilla. Lineaarinen jakolasku poimii yhden niistä ja on yleensä väärässä

Toimiva tapa on jaollisuushaku ehdokasversioiden yli. Kulje versiosta 40 alas versioon 1, pidä ne ehdokkaat, joiden moduulimäärä jakaa näytteistetyn leveyden tasaisesti ja jättää vähintään kolme pikseliä moduulia kohden, ja ota pienin selviävä versio. Kolmen pikselin lattia on se, mikä estää hakua hyväksymästä karkean symbolin naurettavan tiivistä lukemaa, ja pienimmän version sääntö ratkaisee jäljelle jääneen monitulkintaisuuden sen lukeman hyväksi, jonka skannaaja oikeasti tuottaisi

HotPDF:n version tunnistuksen kävely QR-symbolille 126 pikselin näytteistetyllä maskilla, testaten jokaista ehdokasmoduulimäärää 4v + 17 versiosta 40 alas versioon 1 tasaiseen jaollisuuteen ja kolmen pikselin moduulilattiaan ennen kuin pienin selviävä versio voittaa
QR:n moduulimäärä tulee jaollisuushausta ehdokasversioiden yli, ei maskin leveyden jakamisesta oletetulla moduulikoolla, ja pienin selviävä versio ratkaisee monitulkintaisuuden
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 palauttaa täytetyn tietueen nollatun sijaan, mikä merkitsee, sillä DPI nolla tai tulosten katto nolla on kelvollisen näköinen tapa saada mitään takaisin. RotationPolicy ohjaa vain ulompaa uudelleenyritystä: bdrpNone renderöi kerran, bdrpFallback yrittää muut suuntaukset epäonnistuneen ensimmäisen kierroksen jälkeen, ja bdrpAll renderöi jokaisen suuntauksen ehdottomasti. Koska QR-normalisointi tapahtuu dekooderin sisällä, QR-sivut ratkeavat ensimmäisellä yrityksellä millä tahansa kolmesta politiikasta. Politiikka on siinä niille lineaarisille symbologioille, jotka oikeasti tarvitsevat sitä

Miten todistat, ettei bittikarttatransformaatio keksi pikseleitä?

Laske muste molemmilta puolilta ja vaadi summien täsmäävän. Kierto on pikselien permutaatio, ei muuta, joten tulosteessa olevien nollasta poikkeavien solujen määrän on oltava yhtä suuri kuin syötteessä. Kun maskin kierto ulommassa uudelleenyrityspolussa raportoi 4800 asetettua solua sisään ja 7439 ulos, se yksi vertailu riitti tuomitsemaan transformaation lukematta riviäkään sen geometriasta

Syy oli arkkinen ja kannattaa viedä mukanaan sääntönä. SetLengthilla kokoonnutty dynaaminen taulu ei ole taattu saapumaan nollattuna, kun se on funktion tulos, joka kulkee polkua, jota ajonaikainen ympäristö ei tyhjennä, ja solut, joita kierto ei koskaan kirjoita, kantavat silloin sitä, mitä tavuja siellä oli ennen. Osa niistä vanhentuneista tavuista on nollasta poikkeavia, ja nollasta poikkeava tarkoittaa mustetta. Korjaus on yksi rivi, FillChar(Result[0], N, 0) ennen kuin permutaatiosilmukka ajaa, ja kurinalaisuus, jonka se esittää, on laajempi: jokaisen maskin tai bittikarttapuskurin palauttavan funktion on tyhjennettävä tulosteensa eksplisiittisesti varauksen semantiikkaan nojaamatta

Se, mikä sai defektin selviämään kolmen julkaisun yli, on kiinnostavampi kuin defekti. Kun QR siirsi suuntauskäsittelynsä dekooderiin, QR lopetti ulomman maskikierron käyttämisen kokonaan, ja ainoa jäljelle jäänyt tuon koodipolun kuluttaja oli Code 39. Jaettu infrastruktuuri kätkee tällaisia bugeja koko ajan: yhden ominaisuuden kattavuus saa polun näyttämään testatulta, sillä aikaa kun todella siitä riippuvalla ominaisuudella ei ole omaa. Jokainen polku, jonka uusi ominaisuus lakkaa käyttämästä, tarvitsee testin, joka yhä käyttää sitä

Tulosten lukeminen takaisin sivukoordinaateissa

Jokainen dekooderin tuottama geometrinen arvo ilmaistaan yritysbittikartan koordinaatikehyksessä, ja kutsuja tarvitsee sen PDF:n käyttäjäavaruudessa. Tuo muunnos ajaa kahdessa vaiheessa: kumoa neljänneskierto, jonka uudelleenyritys sovelsi, ja kumoa sitten renderöintitransformaatio, joka kuvasi käyttäjäavaruuden bittikarttaan. Se, mitä saapuu THPDFDecodedBarcodeissa, on akselisuuntainen rajaava laatikko käyttäjäavaruudessa, Left, Bottom, Right ja Top noudattaen PDF:n käytäntöä, että Y kasvaa ylöspäin, plus vastapäivään menevä OrientationDegrees

HotPDF:n viivakoodiputki renderöidystä sivubittikartasta näytteistyksen kautta boolen moduulimatriisiksi, D4-normalisointi, jaollisuusversiotunnistus ja Reed-Solomon-dekoodaus, sitten kaksivaiheinen koordinaattimuunnos, joka kumoaa uudelleenyrityksen neljänneskierroksen ja renderöintitransformaation ennen kuin THPDFDecodedBarcode julkaisee Left, Bottom, Right, Top ja OrientationDegrees käyttäjäavaruudessa
QR-normalisointi dekooderin sisällä päästää sivut ratkeamaan ensimmäisellä yrityksellä, kun taas kaksivaiheinen koordinaattimuunnos kääntää yritysbittikartan tulokset akselisuuntaisiksi käyttäjäavaruuden laatikoiksi

Saat tuon toisen muunnoksen suunnan väärin, ja oire on ilkeä: teksti dekoodautuu täydellisesti, mutta laatikko, jonka piirrät katselmointipäällekkäisyydelle, laskeutuu oikean sijainnin peilikuvaan. Jokaisen, joka rakentaa katselmointirajapintaa dekooderin päälle, kannattaa väittämä tunnettua kiinnitystä vastaan, symbolilla, joka on sijoitettu tahallisesti yhden sivun nurkan lähelle, niin että käännetty Y-akseli näkyy yhdellä silmäyksellä. Sama päättely pätee mihin tahansa koordinaattiin, joka ylittää renderöintirajan, minkä takia artikkeli PDF-sivun renderöinti bittikartaksi Delphissä kannattaa ymmärtää, ennen kuin rakennat dekooderin päälle

Mitä sisäänrakennettu dekooderi tekee ja mitä ei

Sisäänrakennettu dekooderi on rajattu, riippuvuudeton toteutus, ja se on rehellinen rajoistaan hiljaisen heikentymisen sijaan. Se tunnistaa Code 39:n ja QR:n, validoi BCH-suojatut formaattibitit ja maskikaavan ennen kuin julkaisee mitään dataa, eikä se yritä virhepalautusta vaurioituneille symboleille. Jos syötteesi on valokuva kaarevasta lapusta epätasaisessa valossa, kyseessä on eri ongelmaluokka, ja se haluaa erikoistuneen moottorin

// Vaihda oma moottorisi: toteuta IHPDFBarcodeDecoder ja anna se
// dekooderitietoiselle ylikuormalle. HotPDF omistaa yhä sivujen
// renderöinnin, budjetit, koordinaattikuvauksen ja deduplikoinnin
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 on paikka, jossa tuotantoputki ansaitsee paikkansa. RotationAttemptCount ja DecoderCallCount kertovat, ajoi ulompi uudelleenyritys ylipäätään, ReceivedResultCount vasten AcceptedResultCount erottaa dekooderin, joka ei löytänyt mitään, luottamuskynnyksestä, joka hylkäsi kaiken löytämänsä, ja RenderedPixels PeakWorkingBytesin kanssa on se, mitä piirrät kuvaajaan, kun erätyö alkaa puurtaa. Tyhjä tulossarja plus bdsSucceeded tarkoittaa, että sivulla oikeasti ei ole luettavaa symbolia, mikä on eri operatiivinen tosiasia kuin bdsBudgetExceeded

Budjettikentät ansaitsevat tahallisen päätöksen oletuksen sijaan. MaxPixels ja MaxWorkingBytes ovat olemassa, koska DPI kertoutuu neliöllisesti: siirtyminen 300:sta 600 DPI:hin A4-sivulla nelinkertaistaa sekä renderöintikustannuksen että huippuvarauksen, ja epäluotettava syöte, joka julistaa valtavan sivulaatikon, voi kääntää skannaustyön muistin loppumisvälikohtaukseksi. Aseta katot siihen, mitä pahin laillinen dokumenttisi tarvitsee, ja anna sitten bdsBudgetExceededin ohjata poikkeukset hitaampaan, eristettyyn polkuun

Jos dokumenttisi sekoittavat koneellisesti luettavia lappuja painettuun tekstiin, jota aiot indeksoida, viivakoodidekooderi pariutuu luontevasti mallintäsmäys-OCR:n HotPDF:n sisällä käsitellyn tunnistusmoottorin kanssa, ja saman tarinan generointipuoli on artikkelissa viivakoodien piirtäminen PDF:ään HotPDF:llä. Molemmat ajavat samalla renderöinti- ja budjetti-infrastruktuurilla, joten putki, joka jo asettaa järjelliset rajat yhdelle, saa toisen melkein ilmaiseksi

Kiertosieto on yksi niistä ominaisuuksista, jotka ovat näkymättömiä toimiessaan ja raastavia toimiessaan ei, ja insinöörioppi yleistyy QR:n ohi: normalisoi niin lähellä semanttista esitystä kuin pääset, ei pikselikerroksessa, jossa data yhä kantaa jokaisen sattuman siitä, miten se kaadettiin talteen. HotPDF toimittaa tämän osana HotPDF Delphi PDF -komponenttia rinnalla niiden renderöinti-, OCR- ja sivuanalyysipalasten kanssa, joita samat sisääntuloputket yleensä tarvitsevat