HotPDF mendekode simbol QR berotasi di halaman PDF termuat dengan menormalkan matriks modul hasil sampling melalui kedelapan orientasi D4 di dalam decoder itu sendiri. Retry rotasi dari luar yang bekerja untuk symbology linear tidak bisa bekerja untuk QR, dan memahami alasannya menghemat satu hari Anda mengejar decoder yang kelihatan rusak padahal tidak
Skenarionya biasa saja. Surat jalan hasil scan datang sebagai PDF, tiap halaman membawa label QR, dan operator scanner memasukkan tumpukan kertas dengan arah mana pun yang diterima tray. Sebagian label tegak, sebagian meleset seperempat putaran, sebagian lagi terbalik. Anda memanggil decoder barcode, separuh halaman resolve, separuhnya lagi kembali kosong tanpa error sama sekali
Mengapa memutar scan mask tak pernah memperbaiki QR berotasi?
Karena tata letak finder pattern QR sengaja dibuat asimetris, dan rotasi seluruh gambar mempertahankan asimetri itu alih-alih menghapusnya. QR Code menaruh tiga persegi finder di sudut kiri-atas, kanan-atas dan kiri-bawah, dan membiarkan sudut kanan-bawah kosong (ISO/IEC 18004:2015 §6.3.3). Sudut yang hilang itulah penunjuk orientasinya. Putar bitmap halaman sembilan puluh derajat dan celahnya sekadar pindah ke sudut lain. Tidak ada rotasi non-trivial atas bidang yang memetakan tata letak tiga sudut kembali ke dirinya sendiri, jadi decoder yang hanya menerima susunan kanonik akan menolak setiap percobaan bergantian
Ini penting karena solusi yang tampak jelas justru yang salah. Naluri alami adalah menggantung retry di luar: render halaman, serahkan mask ke decoder, dan kalau gagal, putar mask lalu coba lagi pada 90, 180 dan 270 derajat. Untuk Code 39 kebijakan itu tepat sekali, karena symbology linear punya pola start dan stop yang bisa ditemukan scanner begitu batang-batangnya berjalan horizontal. Untuk QR, itu empat kegagalan terjamin yang ditutup laporan tidak ditemukan apa pun
Grup D4, diterapkan pada matriks modul
Tempat yang benar untuk normalisasi adalah setelah sampling, pada grid modul boolean alih-alih pada mask piksel. Begitu decoder menyelesaikan simbol menjadi matriks n kali n modul gelap dan terang, ia bisa mengenumerasi grup dihedral persegi: empat rotasi kali dua refleksi, delapan orientasi kandidat total. Untuk tiap kandidat ia memeriksa segitiga finder, dan kandidat pertama yang ketiga finder-nya mendarat di posisi kiri-atas, kanan-atas dan kiri-bawah adalah orientasi yang sebenarnya. Dari sana pipeline yang sudah ada berjalan tanpa perubahan, karena bit format information, penempatan data zigzag, dan koreksi Reed-Solomon semuanya mengasumsikan matriks kanonik dan kini mendapatkannya
Dua properti membuat ini murah. Matriksnya kecil dibanding bitmap hasil render, jadi delapan transpose jauh lebih murah daripada delapan render halaman. Dan matriksnya array boolean bersih yang dibangun sampler, sehingga transform apa pun di sepanjang jalan tak bisa memperkenalkan nilai yang tidak pernah di-sampling
Deteksi versi adalah pencarian keterbagian, bukan pembagian
Jumlah modul tidak bisa diturunkan dengan membagi lebar hasil sampling dengan ukuran modul yang diasumsikan, dan keliru di sini adalah sumber kegagalan dekode yang halus pada render resolusi tinggi. Simbol QR versi v lebarnya 4v + 17 modul, jadi versi 1 adalah 21 modul dan versi 40 adalah 177. Mask yang terukur 126 piksel sama konsistennya dengan versi 1 pada enam piksel per modul maupun beberapa versi lebih tinggi pada ukuran modul lebih kecil. Pembagian linear memilih salah satunya dan biasanya salah
Yang bekerja adalah pencarian keterbagian atas versi kandidat. Telusuri dari versi 40 turun ke versi 1, simpan kandidat yang jumlah modulnya membagi habis lebar hasil sampling dan menyisakan sekurangnya tiga piksel per modul, dan ambil versi bertahan yang terkecil. Batas bawah tiga piksel itulah yang menghentikan pencarian menerima pembacaan absurdnya padat atas simbol yang kasar, dan aturan versi-terkecil menyelesaikan ambiguitas tersisa demi pembacaan yang benar-benar akan dihasilkan scanner
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 mengembalikan record yang sudah terisi alih-alih yang dinolkan, dan itu penting karena DPI nol atau batas hasil nol adalah cara yang tampak sah untuk mendapatkan kosong. RotationPolicy mengendalikan retry luar saja: bdrpNone merender sekali, bdrpFallback mencoba ulang orientasi lain setelah pass pertama gagal, dan bdrpAll merender setiap orientasi tanpa syarat. Karena normalisasi QR terjadi di dalam decoder, halaman QR resolve pada percobaan pertama di bawah ketiga kebijakan. Kebijakannya ada untuk symbology linear yang benar-benar membutuhkannya
Bagaimana membuktikan transform bitmap tidak mengarang piksel?
Hitung tinta di kedua sisi dan syaratkan totalnya cocok. Rotasi adalah permutasi piksel, tidak lebih, jadi jumlah sel non-nol di output harus sama dengan jumlah di input. Ketika rotasi mask di jalur retry luar melaporkan 4800 sel terisi masuk dan 7439 keluar, satu perbandingan itu cukup untuk menjatuhkan transform itu tanpa membaca satu baris geometrinya
Penyebabnya membosankan dan layak dibawa pulang sebagai aturan. Dynamic array yang diukur dengan SetLength tidak dijamin tiba dalam keadaan nol ketika ia hasil fungsi yang menempuh jalur yang tidak dibersihkan runtime, dan sel yang tak pernah ditulis rotasi membawa byte apa pun yang ada di sana sebelumnya. Sebagian byte basi itu non-nol, dan non-nol berarti tinta. Perbaikannya satu baris, FillChar(Result[0], N, 0) sebelum loop permutasi berjalan, dan disiplin yang tersirat lebih luas: fungsi apa pun yang mengembalikan buffer mask atau bitmap seharusnya membersihkan output-nya secara eksplisit alih-alih mengandalkan semantik alokasi
Apa yang membuat cacat itu selamat tiga rilis lebih menarik daripada cacatnya sendiri. Begitu QR memindahkan penanganan orientasinya ke dalam decoder, QR berhenti menjalankan rotasi mask luar sama sekali, dan satu-satunya konsumen tersisa jalur kode itu adalah Code 39. Infrastruktur bersama menyembunyikan bug semacam ini sepanjang waktu: coverage dari satu fitur membuat sebuah jalur tampak teruji sementara fitur yang benar-benar bergantung padanya tidak punya apa-apa sendiri. Setiap jalur yang berhenti dipakai fitur baru butuh test yang tetap memakainya
Membaca hasil kembali dalam koordinat halaman
Setiap nilai geometris yang dihasilkan decoder dinyatakan dalam frame koordinat bitmap percobaan, dan pemanggil membutuhkannya dalam user space PDF. Konversinya berjalan dua tahap: batalkan seperempat putaran yang diterapkan retry, lalu batalkan transform render yang memetakan user space ke bitmap. Yang tiba di THPDFDecodedBarcode adalah bounding box sejajar sumbu dalam user space, dengan Left, Bottom, Right dan Top mengikuti konvensi PDF bahwa Y bertambah ke atas, plus OrientationDegrees berlawanan arah jarum jam
Keliru arah pada konversi kedua itu dan gejalanya jahat: teks terdekode sempurna, tapi box yang Anda gambar untuk overlay review mendarat di citra cermin posisi yang benar. Siapa pun yang membangun antarmuka review di atas decoder sebaiknya assert terhadap fixture yang diketahui, dengan simbol yang diletakkan sengaja dekat satu sudut halaman sehingga sumbu Y yang terbalik terlihat sekilas. Nalar yang sama berlaku untuk koordinat apa pun yang menyeberangi batas rendering, dan itulah kenapa merender halaman PDF ke bitmap di Delphi layak dipahami sebelum Anda membangun di atas decoder
Apa yang decoder bawaan lakukan dan tidak lakukan
Decoder bawaan adalah implementasi berbatas tanpa dependensi, dan ia jujur soal batasnya alih-alih menurun secara diam-diam. Ia mengenali Code 39 dan QR, memvalidasi bit format yang dilindungi BCH dan pola mask sebelum menerbitkan data apa pun, dan tidak mencoba pemulihan error pada simbol yang rusak. Kalau input Anda foto label melengkung di bawah cahaya tak merata, itu kelas masalah berbeda dan butuh engine terspesialisasi
// Tukar dengan engine Anda sendiri: implementasikan IHPDFBarcodeDecoder dan
// berikan ke overload yang sadar decoder. HotPDF tetap memiliki rendering
// halaman, anggaran, pemetaan koordinat, dan de-duplikasi
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 adalah tempat pipeline produksi membuktikan nilainya. RotationAttemptCount dan DecoderCallCount memberi tahu apakah retry luar berjalan sama sekali, ReceivedResultCount dibanding AcceptedResultCount memisahkan decoder yang tak menemukan apa pun dari threshold confidence yang menolak semua temuannya, dan RenderedPixels berikut PeakWorkingBytes adalah yang Anda grafikkan saat batch job mulai thrashing. Himpunan hasil kosong plus bdsSucceeded berarti halamannya memang tak punya simbol terbaca, fakta operasional yang berbeda dari bdsBudgetExceeded
Field anggaran layak mendapat keputusan yang disengaja, bukan bawaan. MaxPixels dan MaxWorkingBytes ada karena DPI berlipat kuadrat: naik dari 300 ke 600 DPI pada halaman A4 melipatempatkan biaya render dan alokasi puncaknya, dan input tak terpercaya yang mendeklarasikan page box raksasa bisa mengubah pekerjaan scan menjadi insiden out-of-memory. Atur cap sesuai kebutuhan dokumen sah terburuk Anda, lalu biarkan bdsBudgetExceeded mengarahkan pencilan ke jalur yang lebih lambat dan terisolasi
Kalau dokumen Anda mencampur label terbaca mesin dengan teks cetak yang Anda rencanakan indeks, decoder barcode berpasangan alami dengan engine pengenalan yang dibahas di OCR template-matching di dalam HotPDF, dan sisi generasi dari kisah yang sama ada di menggambar barcode ke PDF dengan HotPDF. Keduanya berjalan di atas infrastruktur rendering dan anggaran yang sama, jadi pipeline yang sudah menyetel batas waras untuk yang satu mendapat yang lain hampir gratis
Toleransi rotasi adalah salah satu fitur yang tak terlihat saat bekerja dan menaikkan darah saat tidak, dan pelajaran rekayasanya menggeneralisasi melampaui QR: normalisasilah sedekat mungkin dengan representasi semantik, bukan di lapisan piksel tempat data masih membawa setiap kecelakaan cara ia diambil. HotPDF mengirim ini sebagai bagian dari komponen PDF Delphi HotPDF, berdampingan dengan bagian rendering, OCR dan analisis halaman yang biasanya dibutuhkan pipeline intake yang sama