5.28. Kode QR dan AprilTag

Detektor yang telah dibahas sejauh ini -- blob, garis, lingkaran, persegi panjang -- menemukan fitur geometris: posisi dan kontur yang ditafsirkan oleh tahap berikutnya. Detektor selanjutnya menemukan fitur simbolis: pola tercetak yang struktur visualnya memang dirancang untuk menyandikan suatu muatan. Kamera menemukan lokasinya, dekoder membaca bitnya, dan yang dikembalikan bukan posisi melainkan sebuah string (atau sebuah ID) yang dipilih secara sengaja oleh pencetak simbol tersebut.

Dua keluarga seperti itu mendominasi aplikasi kamera kecil. Kode QR membawa teks arbitrer, URL, kartu kontak, atau muatan biner -- kode 2D yang menghadap konsumen dan muncul di poster, kemasan, dan tiket pesawat. AprilTag membawa satu ID numerik dari kumpulan tetap yang kecil, didekode dengan cepat bahkan dari jarak jauh, dan (bila intrinsik lensa disediakan) melaporkan pose 6-DoF dalam bingkai kamera -- kode 2D yang menghadap robotika yang menandai drone, target kalibrasi, dan fiducial. Kedua detektor mengembalikan objek hasil dengan kosakata kotak pembatas yang sama seperti yang digunakan detektor blob dan rect, tetapi muatannya membuat keduanya benar-benar berbeda dari apa pun yang telah dibahas sebelumnya.

5.28.1. Kode QR

find_qrcodes() memindai bingkai untuk mencari kode QR dan mengembalikan daftar objek hasil QRCode:

codes = img.find_qrcodes()

for c in codes:
    img.draw_rectangle(c.rect, color=(0, 255, 0))
    for corner in c.corners:
        img.draw_circle((corner[0], corner[1], 4),
                        color=(0, 255, 0))
    print(c.payload)

Detektor menerima satu roi opsional untuk membatasi area pencarian. Detektor membutuhkan input skala abu-abu -- bingkai berwarna dikonversi secara internal sebelum didekode.

Setiap deteksi membawa kotak pembatas (x, y, w, h, rect), empat sudut yang terdeteksi (corners, kuadrilateral proyektif yang ditelusuri oleh pola pencari kode QR), dan muatan yang didekode sebagai string. Sudut-sudut adalah hal yang tepat untuk digambar saat memberi anotasi pada deteksi -- kode QR yang dilihat dari sudut miring tidak sejajar sumbu dan kotak pembatas hanya memberikan garis luar yang longgar.

Metadata dekoder mencakup semua yang dipelajari dekoder QR sepanjang prosesnya. version adalah versi kode QR, 1 -- 40, yang menetapkan ukuran grid modul (kode versi-1 berukuran 21 modul, kode versi-40 berukuran 177). ecc_level adalah tingkat koreksi kesalahan (0 -- 3 untuk L / M / Q / H); tingkat yang lebih tinggi menyediakan lebih banyak codeword untuk koreksi kesalahan dan bertahan dari lebih banyak kerusakan dengan mengorbankan ruang muatan yang lebih sedikit. mask adalah pola mask (0 -- 7) yang dipilih enkoder untuk meminimalkan kebingungan dekoder. data_type adalah enkoding yang dilaporkan dekoder -- numerik, alfanumerik, biner, atau Kanji -- dan flag is_numeric / is_alphanumeric / is_binary / is_kanji mengekspos nilai yang sama sebagai boolean yang lebih ramah.

eci adalah nilai Extended Channel Interpretation, yang mengidentifikasi enkoding teks untuk byte-byte tersebut (UTF-8, ISO-8859-1, dan sebagainya). Kode QR dari materi cetak arbitrer mungkin tidak dijamin berenkoding UTF-8; aplikasi yang perlu mendekode byte dengan benar memeriksa eci dan mendekode sesuai kebutuhan. Kasus Kanji khususnya: MicroPython tidak mengurai enkoding Kanji, sehingga muatan is_kanji harus diperlakukan sebagai array byte dan didekode oleh aplikasi.

Penggunaan tipikal: kamera membaca kode QR dari konveyor dan melaporkan muatan yang didekode ke host. Cam menjalankan find_qrcodes() sekali per bingkai, mengiterasi daftar yang dikembalikan, memilih kode yang data_type-nya sesuai dengan apa yang diharapkan aplikasi, dan meneruskan c.payload melalui UART atau USB. Data kotak pembatas dan sudut berguna untuk pratinjau IDE tetapi bukan hal yang diperhatikan host.

5.28.2. AprilTag

find_apriltags() memindai bingkai untuk mencari AprilTag dan mengembalikan daftar objek hasil AprilTag:

tags = img.find_apriltags(families=image.TAG36H11)

for t in tags:
    img.draw_rectangle(t.rect, color=(0, 255, 0))
    img.draw_cross(t.cx, t.cy, color=(0, 255, 0))
    print(t.id, t.decision_margin)

AprilTag berbeda dari kode QR dalam tujuan desainnya. Kode QR dirancang untuk menyandikan data arbitrer dalam satu simbol padat yang dibaca pengguna sekali dari jarak dekat. AprilTag dirancang untuk menyandikan ID kecil dalam simbol jarang yang dibaca kamera secara terus-menerus dari jarak jauh, dengan toleransi kesalahan sebesar yang diizinkan oleh kode Hamming dari keluarganya. Kompromi ini terlihat dari kedua sisi: kode QR dapat membawa ratusan byte tetapi perlu dibaca dari jarak dekat; AprilTag hanya membawa beberapa ratus ID unik tetapi dapat dibaca secara andal dari jarak beberapa meter.

Kata kunci families menerima bitmask dari keluarga tag yang akan didekode. Keluarga yang tersedia adalah image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12, dan image.TAGSTANDARD52H13. Setiap keluarga menyeimbangkan antara jumlah ID dan ketangguhan. Angka H dalam nama adalah jarak Hamming minimum antara dua kode manapun dalam keluarga -- berapa banyak bit yang harus berubah sebelum satu kode valid menjadi kode lain -- TAG16H5 memiliki 30 ID pada jarak 5, TAG25H9 memiliki 35 ID pada jarak 9, dan TAG36H11 (default dan yang paling umum) memiliki 587 ID pada jarak 11. Detektor mengoreksi hingga dua kesalahan bit terlepas dari keluarganya, sehingga jarak menentukan seberapa berisiko koreksi tersebut: pola acak dalam bingkai yang bising hanya perlu berada dalam dua bit dari kode valid untuk didekode sebagai deteksi palsu, dan keluarga dengan jarak lebih tinggi menyebarkan kodenya jauh lebih jarang sehingga tabrakan seperti itu menjadi langka -- alasan mengapa TAG36H11 adalah pilihan yang direkomendasikan. Waktu deteksi bertambah seiring dengan jumlah keluarga yang diaktifkan, sehingga aplikasi hanya mengaktifkan apa yang benar-benar dicetak. Bitmask adalah OR bitwise dari konstanta keluarga ketika beberapa keluarga diperlukan dalam satu panggilan.

Setiap deteksi membawa kosakata kotak pembatas -- x, y, w, h, rect, area, sentroid integer dan sub-piksel (cx, cy, cxf, cyf) -- dan empat sudut yang terdeteksi (corners). Bidang identifikasi mengikuti: id adalah ID numerik dalam keluarga (0 -- 586 untuk TAG36H11), family adalah konstanta keluarga numerik, dan name adalah nama keluarga sebagai string.

Bidang kualitas-kecocokan adalah yang digunakan aplikasi untuk memfilter deteksi. decision_margin adalah skor kepercayaan 0.0 -- 1.0; semakin tinggi semakin baik, dan memfilter deteksi di bawah decision_margin > 0.1 membersihkan sebagian besar hit palsu tanpa biaya. hamming menghitung kesalahan bit yang diterima dekoder untuk tag ini -- semakin rendah semakin baik, 0 berarti dekode sempurna. goodness adalah metrik kualitas citra historis yang tidak lagi dihitung oleh dekoder saat ini; nilainya selalu 0.0 dan dapat diabaikan.

5.28.3. Pose dari intrinsik

Fitur transformatif dari find_apriltags(), yang membenarkan AprilTag sebagai fiducial robotika pilihan, adalah bahwa metode ini dapat memulihkan pose 6-DoF tag dalam bingkai kamera langsung dari sudut-sudut yang terdeteksi dan sekumpulan kecil intrinsik kalibrasi. Intrinsik tersebut adalah panjang fokus X dan Y kamera dalam piksel (fx, fy) dan pusat optik dalam piksel (cx, cy), keempatnya diukur sekali oleh aplikasi dengan prosedur kalibrasi dan dikodekan keras setelahnya.

Ketika intrinsik disediakan, AprilTag yang dikembalikan mengisi field x_translation, y_translation, z_translation-nya dengan posisi tag relatif terhadap kamera, dan x_rotation, y_rotation, z_rotation (serta duplikat rotation untuk simetri) dengan orientasi tag. Tanpa intrinsik, semua enam field bernilai 0.0 dan aplikasi bertanggung jawab atas estimasi pose yang diperlukannya.

Field translasi dilaporkan dalam lebar tag: dekoder memperlakukan tag sebagai lebar 1 unit, sehingga aplikasi mengalikan setiap translasi dengan lebar fisik tag yang dicetak untuk mendapatkan jarak metrik. Tag yang dicetak dengan lebar 100 mm dan melaporkan z_translation = 8.3 berada 830 mm dari kamera; tag yang sama dicetak dengan lebar 50 mm pada jarak yang sama akan melaporkan z_translation = 16.6. Field rotasi dalam radian dan tidak memerlukan penskalaan.

Estimasi pose adalah dasar untuk berbagai aplikasi robotika: mendekatkan robot ke stasiun pengisian yang ditandai dengan tag, mengikuti jalur titik jalan yang dicetak, memulihkan pose kamera sendiri dari beberapa tag yang diketahui di lingkungan. Kamera yang mengetahui intrinsik, melihat sebuah tag, dan memiliki posisi dunia nyata untuk tag tersebut memiliki, dengan aritmetika yang sama, posisi dunia nyata untuk dirinya sendiri.

5.28.4. Kapan memilih yang mana

Kode QR dan AprilTag memecahkan masalah yang berbeda. Pilihan di antara keduanya bergantung pada apa yang dibawa simbol tercetak tersebut.

Ketika aplikasi perlu membawa data arbitrer melalui simbol tercetak -- URL, string nomor seri, catatan kontak -- kode QR adalah pilihan yang tepat. Ratusan byte muat dalam kode berukuran sedang, enkodingannya publik dan didukung di setiap smartphone, dan dekoder menangani rotasi, kerusakan sedang, dan sudut miring.

Ketika aplikasi membutuhkan ID kecil yang dibaca terus-menerus dari jarak jauh dengan pose opsional -- fiducial pada robot yang bergerak, target kalibrasi di sebuah ruangan, penanda docking pada stasiun pengisian -- AprilTag adalah pilihan yang tepat. Ratusan ID sudah cukup untuk kasus penggunaan tersebut, kode Hamming pulih dari kesalahan bit yang akan mengalahkan kode QR, dan estimasi pose sudah tersedia begitu intrinsik dikalibrasi.

Beberapa aplikasi menggunakan keduanya: sebuah AprilTag menandai lokasi yang diketahui dan kode QR yang terkait (dicetak berdampingan) membawa metadata tentang apa arti lokasi tersebut. Kedua detektor berjalan secara independen pada bingkai yang sama dan aplikasi mengkorelasikan kotak pembatasnya untuk mencocokkan setiap tag dengan kode pendampingnya.