5.28. Coduri QR și AprilTag-uri

Detectoarele prezentate până acum – blob-uri, linii, cercuri, dreptunghiuri – găsesc caracteristici geometrice: poziții și contururi pe care o etapă ulterioară le interpretează. Detectoarele rămase găsesc caracteristici simbolice: tipare imprimate a căror structură vizuală există tocmai pentru a codifica o sarcină utilă. Camera le localizează, decodorul citește biții, iar ceea ce se obține nu este o poziție, ci un șir de caractere (sau un ID) pe care cel care a imprimat simbolul l-a ales în mod deliberat.

Două astfel de familii domină aplicațiile pentru camere mici. Codurile QR poartă text arbitrar, URL-uri, cărți de vizită sau sarcini utile binare – codurile 2D destinate consumatorilor, care apar pe afișe, ambalaje și cărți de îmbarcare. AprilTag-urile poartă un singur ID numeric dintr-un set mic și fix, se decodifică rapid chiar și de la distanță mare și (când sunt furnizați parametrii intrinseci ai obiectivului) raportează o poziție cu 6 grade de libertate (6-DoF) în cadrul camerei – codurile 2D destinate roboticii, care marchează drone, ținte de calibrare și repere fiduciare. Ambele detectoare returnează obiecte de rezultat cu același vocabular de casete de încadrare folosit de detectoarele de blob-uri și de dreptunghiuri, însă sarcina utilă le face cu adevărat diferite de orice s-a tratat până acum.

5.28.1. Coduri QR

find_qrcodes() scanează cadrul pentru coduri QR și returnează o listă de obiecte de rezultat 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)

Detectorul acceptă un singur parametru opțional roi pentru a restrânge căutarea. Are nevoie de o intrare în tonuri de gri – un cadru color este convertit intern înainte de decodare.

Fiecare detectare poartă caseta de încadrare (x, y, w, h, rect), cele patru colțuri detectate (corners, patrulaterul proiectiv pe care îl trasează tiparele de reper ale codului QR) și sarcina utilă decodificată ca șir de caractere. Colțurile sunt elementul potrivit de desenat la adnotarea detectării – un cod QR văzut dintr-un unghi nu este aliniat cu axele, iar caseta de încadrare oferă doar un contur aproximativ.

Metadatele decodorului acoperă tot ce a aflat decodorul QR pe parcurs. version este versiunea codului QR, 1 – 40, care stabilește dimensiunea grilei de module (un cod versiunea 1 are 21 de module lățime, un cod versiunea 40 are 177). ecc_level este nivelul de corecție a erorilor (0 – 3 pentru L / M / Q / H); nivelurile mai mari rezervă mai multe cuvinte de cod pentru corecția erorilor și supraviețuiesc unei deteriorări mai mari, în detrimentul unui spațiu mai mic pentru sarcina utilă. mask este tiparul de mască (0 – 7) pe care codorul l-a ales pentru a minimiza confuzia decodorului. data_type este codificarea pe care a raportat-o decodorul – numerică, alfanumerică, binară sau Kanji – iar indicatorii is_numeric / is_alphanumeric / is_binary / is_kanji expun aceeași valoare sub formă de valori booleene mai prietenoase.

eci este valoarea Extended Channel Interpretation, care identifică codificarea de text în care se află octeții (UTF-8, ISO-8859-1 și așa mai departe). Un cod QR provenit dintr-un material imprimat arbitrar s-ar putea să nu fie garantat UTF-8; o aplicație care trebuie să decodifice corect octeții verifică eci și decodifică în consecință. În special cazul Kanji: MicroPython nu analizează codificarea Kanji, așa că o sarcină utilă is_kanji trebuie tratată ca un tablou de octeți și decodificată de aplicație.

O utilizare tipică: o cameră citește coduri QR de pe o bandă transportoare și raportează sarcina utilă decodificată către o gazdă. Camera rulează find_qrcodes() o dată pe cadru, parcurge lista returnată, alege codurile al căror data_type corespunde cu ceea ce așteaptă aplicația și transmite mai departe c.payload prin UART sau USB. Datele despre caseta de încadrare și colțuri sunt utile pentru previzualizarea din IDE, dar nu sunt ceea ce contează pentru gazdă.

5.28.2. AprilTag-uri

find_apriltags() scanează cadrul pentru AprilTag-uri și returnează o listă de obiecte de rezultat 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-urile diferă de codurile QR prin obiectivele lor de proiectare. Un cod QR este conceput pentru a codifica date arbitrare într-un singur simbol dens pe care utilizatorul îl citește o dată, de aproape. Un AprilTag este conceput pentru a codifica un ID mic într-un simbol rar pe care camera îl citește continuu, de la distanță, cu atâta toleranță la erori cât permite codul Hamming al familiei sale. Compromisul apare în ambele direcții: un cod QR poate purta sute de octeți, dar trebuie citit de aproape; un AprilTag poartă doar câteva sute de ID-uri unice, dar se citește fiabil de la metri distanță.

Cuvântul-cheie families acceptă o mască de biți a familiilor de etichete de decodat. Familiile disponibile sunt image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12 și image.TAGSTANDARD52H13. Fiecare familie face un compromis între numărul de ID-uri și robustețe. Numărul H din nume este distanța Hamming minimă dintre oricare două coduri din familie – câți biți trebuie să se inverseze înainte ca un cod valid să se transforme în altul – TAG16H5 are 30 de ID-uri la distanța 5, TAG25H9 are 35 de ID-uri la distanța 9, iar TAG36H11 (cea implicită și cea mai des folosită) are 587 de ID-uri la distanța 11. Detectorul corectează până la două erori de biți indiferent de familie, așa că distanța decide cât de riscantă este acea corecție: un tipar aleatoriu dintr-un cadru zgomotos trebuie doar să se încadreze la doi biți de un cod valid pentru a se decodifica drept o detectare falsă, iar familiile cu distanță mai mare își dispersează codurile atât de mult mai rar încât astfel de coliziuni devin rare – motivul pentru care TAG36H11 este alegerea recomandată. Timpul de detectare crește odată cu numărul de familii activate, așa că o aplicație activează doar ceea ce imprimă efectiv. Masca de biți este operația SAU pe biți a constantelor de familie atunci când sunt necesare mai multe familii într-un singur apel.

Fiecare detectare poartă vocabularul casetei de încadrare – x, y, w, h, rect, area, centroizi întregi și subpixeli (cx, cy, cxf, cyf) – și cele patru colțuri detectate (corners). Urmează câmpurile de identificare: id este ID-ul numeric din cadrul familiei (0 – 586 pentru TAG36H11), family este constanta numerică a familiei, iar name este numele familiei sub formă de șir de caractere.

Câmpurile de calitate a potrivirii sunt cele pe care le folosește o aplicație pentru a filtra detectările. decision_margin este un scor de încredere de 0.0 – 1.0; mai mare este mai bine, iar filtrarea detectărilor sub decision_margin > 0.1 curăță majoritatea rezultatelor false fără niciun cost. hamming numără erorile de biți pe care decodorul le-a acceptat pentru această etichetă – mai mic este mai bine, 0 însemnând o decodare perfectă. goodness este o metrică istorică a calității imaginii pe care decodorul actual nu o mai calculează; este întotdeauna 0.0 și poate fi ignorată.

5.28.3. Poziția din parametrii intrinseci

Caracteristica transformatoare a find_apriltags(), cea care justifică AprilTag-urile drept reperul fiduciar de elecție în robotică, este faptul că metoda poate recupera poziția cu 6 grade de libertate (6-DoF) a etichetei în cadrul camerei direct din colțurile detectate și un mic set de parametri intrinseci de calibrare. Parametrii intrinseci sunt distanțele focale X și Y ale camerei în pixeli (fx, fy) și centrul optic în pixeli (cx, cy), toate patru fiind măsurate o singură dată de aplicație printr-o procedură de calibrare și apoi codificate fix.

Când sunt furnizați parametrii intrinseci, obiectul AprilTag returnat își completează câmpurile x_translation, y_translation, z_translation cu poziția etichetei față de cameră, și x_rotation, y_rotation, z_rotation (plus duplicatul rotation pentru simetrie) cu orientarea etichetei. Fără parametri intrinseci, toate cele șase câmpuri sunt 0.0, iar aplicația este responsabilă pentru orice estimare de poziție de care are nevoie.

Câmpurile de translație sunt raportate în lățimi de etichetă: decodorul tratează eticheta ca având 1 unitate lățime, așa că aplicația înmulțește fiecare translație cu lățimea fizică a etichetei imprimate pentru a obține distanțe metrice. O etichetă imprimată cu 100 mm lățime și care raportează z_translation = 8.3 se află la 830 mm distanță de cameră; aceeași etichetă imprimată cu 50 mm lățime la aceeași distanță ar raporta z_translation = 16.6. Câmpurile de rotație sunt în radiani și nu necesită scalare.

Estimarea poziției este baza pentru o gamă largă de aplicații robotice: andocarea unui robot la o stație de încărcare marcată cu o etichetă, urmărirea unui traseu imprimat de puncte de trecere, recuperarea propriei poziții a camerei din mai multe etichete cunoscute din mediu. O cameră care cunoaște parametrii intrinseci, vede o etichetă și are o poziție din lumea reală pentru etichetă are, prin aceeași aritmetică, o poziție din lumea reală pentru sine însăși.

5.28.4. Când să alegi care

Codurile QR și AprilTag-urile rezolvă probleme diferite. Alegerea dintre ele se reduce la ce poartă simbolul imprimat.

Când aplicația trebuie să poarte date arbitrare prin simbolul imprimat – un URL, un șir de număr de serie, o înregistrare de contact – codul QR este alegerea potrivită. Sute de octeți încap într-un cod de dimensiune modestă, codificarea este publică și acceptată pe orice smartphone, iar decodorul face față rotației, deteriorării moderate și unghiurilor oblice.

Când aplicația are nevoie de un ID mic citit continuu de la distanță, cu poziție opțională – un reper fiduciar pe un robot în mișcare, o țintă de calibrare într-o încăpere, un marcaj de andocare pe o stație de încărcare – AprilTag-ul este alegerea potrivită. Sute de ID-uri sunt suficiente pentru cazul de utilizare, codul Hamming se recuperează din erori de biți care ar învinge un cod QR, iar estimarea poziției este gratuită odată ce parametrii intrinseci sunt calibrați.

Unele aplicații le folosesc pe ambele: un AprilTag marchează o locație cunoscută, iar un cod QR asociat (imprimat alături) poartă metadatele despre ce înseamnă acea locație. Cele două detectoare rulează independent pe același cadru, iar aplicația corelează casetele lor de încadrare pentru a potrivi fiecare etichetă cu codul ei însoțitor.