5.28. QR-Codes und AprilTags¶
Die bisherigen Detektoren – Blobs, Linien, Kreise, Rechtecke – finden geometrische Merkmale: Positionen und Umrisse, die eine nachgelagerte Stufe interpretiert. Die verbleibenden Detektoren finden symbolische Merkmale: gedruckte Muster, deren visuelle Struktur eigens dafür existiert, eine Nutzlast zu kodieren. Die Kamera lokalisiert sie, der Decoder liest die Bits, und was zurückkommt, ist keine Position, sondern eine Zeichenkette (oder eine ID), die der Drucker des Symbols bewusst gewählt hat.
Zwei solche Familien dominieren Anwendungen mit kleinen Kameras. QR-Codes tragen beliebigen Text, URLs, Kontaktkarten oder binäre Nutzlasten – die verbraucherorientierten 2D-Codes, die auf Plakaten, Verpackungen und Bordkarten erscheinen. AprilTags tragen eine einzelne numerische ID aus einem kleinen festen Satz, dekodieren schnell selbst aus großer Entfernung und melden (sofern die Linsen-Intrinsics geliefert werden) eine 6-DoF-Pose im Kamerakoordinatensystem – die robotikorientierten 2D-Codes, die Drohnen, Kalibrierziele und Fiducials markieren. Beide Detektoren liefern Ergebnisobjekte mit demselben Begrenzungsrahmen-Vokabular zurück, das die Blob- und Rechteckdetektoren verwenden, aber die Nutzlast macht sie wirklich anders als alles bisher Behandelte.
5.28.1. QR-Codes¶
find_qrcodes() durchsucht das Einzelbild nach QR-Codes und gibt eine Liste von QRCode-Ergebnisobjekten zurück:
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)
Der Detektor nimmt eine einzige optionale roi entgegen, um die Suche einzuschränken. Er benötigt Graustufeneingaben – ein Farb-Einzelbild wird vor dem Dekodieren intern konvertiert.
Jede Erkennung trägt den Begrenzungsrahmen (x, y, w, h, rect), die vier erkannten Ecken (corners, das projektive Viereck, das die Suchmuster des QR-Codes nachzeichnen) und die dekodierte Nutzlast als Zeichenkette. Die Ecken sind das Richtige zum Zeichnen, wenn man die Erkennung annotiert – ein schräg betrachteter QR-Code ist nicht achsenparallel, und der Begrenzungsrahmen liefert nur einen groben Umriss.
Die Decoder-Metadaten decken alles ab, was der QR-Decoder unterwegs gelernt hat. version ist die QR-Code-Version, 1 – 40, die die Größe des Modulrasters festlegt (ein Version-1-Code ist 21 Module breit, ein Version-40-Code 177). ecc_level ist die Fehlerkorrekturstufe (0 – 3 für L / M / Q / H); höhere Stufen reservieren mehr Codewörter für die Fehlerkorrektur und überstehen mehr Beschädigung auf Kosten von weniger Platz für die Nutzlast. mask ist das Maskenmuster (0 – 7), das der Encoder wählte, um die Decoder-Verwirrung zu minimieren. data_type ist die vom Decoder gemeldete Kodierung – numerisch, alphanumerisch, binär oder Kanji – und die Flags is_numeric / is_alphanumeric / is_binary / is_kanji stellen denselben Wert als freundlichere boolesche Werte bereit.
eci ist der Extended-Channel-Interpretation-Wert, der die Textkodierung der Bytes angibt (UTF-8, ISO-8859-1 usw.). Ein QR-Code von beliebigem gedrucktem Material ist möglicherweise nicht garantiert UTF-8; eine Anwendung, die die Bytes korrekt dekodieren muss, prüft eci und dekodiert entsprechend. Insbesondere der Kanji-Fall: MicroPython parst die Kanji-Kodierung nicht, daher muss eine is_kanji-Nutzlast als Byte-Array behandelt und von der Anwendung dekodiert werden.
Ein typischer Anwendungsfall: Eine Kamera liest QR-Codes von einem Förderband und meldet die dekodierte Nutzlast an einen Host. Die Kamera führt find_qrcodes() einmal pro Einzelbild aus, iteriert über die zurückgegebene Liste, wählt die Codes, deren data_type dem entspricht, was die Anwendung erwartet, und leitet c.payload über UART oder USB weiter. Die Begrenzungsrahmen- und Eckdaten sind für die IDE-Vorschau nützlich, aber nicht das, was den Host interessiert.
5.28.2. AprilTags¶
find_apriltags() durchsucht das Einzelbild nach AprilTags und gibt eine Liste von AprilTag-Ergebnisobjekten zurück:
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)
AprilTags unterscheiden sich von QR-Codes in ihren Designzielen. Ein QR-Code ist darauf ausgelegt, beliebige Daten in einem einzigen dichten Symbol zu kodieren, das der Benutzer einmal aus der Nähe liest. Ein AprilTag ist darauf ausgelegt, eine kleine ID in einem spärlichen Symbol zu kodieren, das die Kamera kontinuierlich aus der Entfernung liest, mit so viel Fehlertoleranz, wie der Hamming-Code seiner Familie zulässt. Der Kompromiss zeigt sich in beide Richtungen: Ein QR-Code kann Hunderte von Bytes tragen, muss aber aus der Nähe gelesen werden; ein AprilTag trägt nur einige Hundert eindeutige IDs, lässt sich aber zuverlässig aus Metern Entfernung lesen.
Das Schlüsselwort families nimmt eine Bitmaske der zu dekodierenden Tag-Familien entgegen. Die verfügbaren Familien sind image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12 und image.TAGSTANDARD52H13. Jede Familie wägt die Anzahl der IDs gegen die Robustheit ab. Die H-Zahl im Namen ist der minimale Hamming-Abstand zwischen je zwei Codes der Familie – wie viele Bits kippen müssen, bevor aus einem gültigen Code ein anderer wird – TAG16H5 hat 30 IDs bei Abstand 5, TAG25H9 hat 35 IDs bei Abstand 9, und TAG36H11 (der Standard und der häufigste) hat 587 IDs bei Abstand 11. Der Detektor korrigiert unabhängig von der Familie bis zu zwei Bitfehler, daher entscheidet der Abstand, wie riskant diese Korrektur ist: Ein zufälliges Muster in einem verrauschten Einzelbild muss nur innerhalb von zwei Bits eines gültigen Codes landen, um als Fehlerkennung dekodiert zu werden, und die Familien mit höherem Abstand verteilen ihre Codes so viel spärlicher, dass solche Kollisionen selten werden – der Grund, warum TAG36H11 die empfohlene Wahl ist. Die Erkennungszeit skaliert mit der Anzahl der aktivierten Familien, daher aktiviert eine Anwendung nur das, was sie tatsächlich druckt. Die Bitmaske ist das bitweise ODER der Familienkonstanten, wenn mehrere Familien in einem Aufruf benötigt werden.
Jede Erkennung trägt das Begrenzungsrahmen-Vokabular – x, y, w, h, rect, area, ganzzahlige und subpixelgenaue Schwerpunkte (cx, cy, cxf, cyf) – und die vier erkannten Ecken (corners). Es folgen die Identifikationsfelder: id ist die numerische ID innerhalb der Familie (0 – 586 für TAG36H11), family ist die numerische Familienkonstante, und name ist der Familienname als Zeichenkette.
Die Match-Quality-Felder sind das, was eine Anwendung zum Filtern von Erkennungen verwendet. decision_margin ist ein Konfidenzwert von 0.0 – 1.0; höher ist besser, und das Herausfiltern von Erkennungen unterhalb von decision_margin > 0.1 bereinigt die meisten falschen Treffer kostenlos. hamming zählt die Bitfehler, die der Decoder für dieses Tag akzeptiert hat – niedriger ist besser, wobei 0 eine perfekte Dekodierung bedeutet. goodness ist eine historische Bildqualitätsmetrik, die der aktuelle Decoder nicht mehr berechnet; sie ist immer 0.0 und kann ignoriert werden.
5.28.3. Pose aus Intrinsics¶
Das transformative Merkmal von find_apriltags(), dasjenige, das AprilTags als das Fiducial der Wahl in der Robotik rechtfertigt, ist, dass die Methode die 6-DoF-Pose des Tags im Kamerakoordinatensystem direkt aus den erkannten Ecken und einem kleinen Satz von Kalibrierungs-Intrinsics rekonstruieren kann. Die Intrinsics sind die X- und Y-Brennweiten der Kamera in Pixeln (fx, fy) und das optische Zentrum in Pixeln (cx, cy), die die Anwendung alle vier einmal mit einem Kalibrierungsverfahren misst und danach fest verdrahtet.
Wenn die Intrinsics geliefert werden, füllt das zurückgegebene AprilTag seine Felder x_translation, y_translation, z_translation mit der Position des Tags relativ zur Kamera und x_rotation, y_rotation, z_rotation (sowie das doppelte rotation aus Symmetriegründen) mit der Orientierung des Tags. Ohne Intrinsics sind alle sechs Felder 0.0, und die Anwendung ist für jede benötigte Posenschätzung selbst verantwortlich.
Die Translationsfelder werden in Tag-Breiten angegeben: Der Decoder behandelt das Tag als 1 Einheit breit, daher multipliziert die Anwendung jede Translation mit der physischen Breite des gedruckten Tags, um metrische Distanzen zu erhalten. Ein Tag, das mit 100 mm Breite gedruckt ist und z_translation = 8.3 meldet, ist 830 mm von der Kamera entfernt; dasselbe Tag, mit 50 mm Breite bei gleicher Entfernung gedruckt, würde z_translation = 16.6 melden. Die Rotationsfelder sind in Bogenmaß und benötigen keine Skalierung.
Die Posenschätzung ist die Grundlage für eine breite Palette von Robotikanwendungen: das Andocken eines Roboters an eine mit einem Tag markierte Ladestation, das Folgen einer gedruckten Wegpunktspur, das Rekonstruieren der eigenen Pose der Kamera aus mehreren bekannten Tags in der Umgebung. Eine Kamera, die die Intrinsics kennt, ein Tag sieht und eine reale Position für das Tag hat, hat nach derselben Rechnung eine reale Position für sich selbst.
5.28.4. Wann welches wählen¶
QR-Codes und AprilTags lösen unterschiedliche Probleme. Die Wahl zwischen ihnen läuft darauf hinaus, was das gedruckte Symbol trägt.
Wenn die Anwendung beliebige Daten durch das gedruckte Symbol tragen muss – eine URL, eine Seriennummer-Zeichenkette, einen Kontaktdatensatz – ist der QR-Code die richtige Wahl. Hunderte von Bytes passen in einen mäßig großen Code, die Kodierung ist öffentlich und auf jedem Smartphone unterstützt, und der Decoder kommt mit Drehung, mäßiger Beschädigung und schrägen Winkeln zurecht.
Wenn die Anwendung eine kleine ID kontinuierlich aus der Entfernung gelesen mit optionaler Pose benötigt – ein Fiducial auf einem fahrenden Roboter, ein Kalibrierziel in einem Raum, eine Andockmarkierung an einer Ladestation – ist das AprilTag die richtige Wahl. Hunderte von IDs sind für den Anwendungsfall reichlich, der Hamming-Code erholt sich von Bitfehlern, die einen QR-Code besiegen würden, und die Posenschätzung ist gratis, sobald die Intrinsics kalibriert sind.
Manche Anwendungen verwenden beides: Ein AprilTag markiert einen bekannten Ort, und ein zugehöriger QR-Code (daneben gedruckt) trägt die Metadaten darüber, was dieser Ort bedeutet. Die beiden Detektoren laufen unabhängig auf demselben Einzelbild, und die Anwendung korreliert ihre Begrenzungsrahmen, um jedes Tag seinem Begleitcode zuzuordnen.