5.28. Códigos QR y AprilTags¶
Los detectores vistos hasta ahora – manchas (blobs), líneas, círculos, rectángulos – encuentran características geométricas: posiciones y contornos que una etapa posterior interpreta. Los detectores restantes encuentran características simbólicas: patrones impresos cuya estructura visual existe específicamente para codificar una carga útil. La cámara los localiza, el decodificador lee los bits, y lo que se obtiene no es una posición sino una cadena (o un ID) que el impresor del símbolo eligió deliberadamente.
Dos familias de este tipo dominan las aplicaciones de cámaras pequeñas. Los códigos QR llevan texto arbitrario, URL, tarjetas de contacto o cargas útiles binarias – los códigos 2D orientados al consumidor que aparecen en carteles, embalajes y tarjetas de embarque. Los AprilTags llevan un único ID numérico de un pequeño conjunto fijo, se decodifican rápidamente incluso a gran distancia y (cuando se proporcionan los parámetros intrínsecos de la lente) informan de una pose de 6 grados de libertad (6-DoF) en el marco de la cámara – los códigos 2D orientados a la robótica que marcan drones, objetivos de calibración y fiduciales. Ambos detectores devuelven objetos de resultado con el mismo vocabulario de cuadro delimitador que usan los detectores de manchas y rectángulos, pero la carga útil los hace genuinamente diferentes de cualquier cosa vista hasta ahora.
5.28.1. Códigos QR¶
find_qrcodes() examina el fotograma en busca de códigos QR y devuelve una lista de objetos de resultado 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)
El detector admite un único roi opcional para restringir la búsqueda. Necesita una entrada en escala de grises – un fotograma a color se convierte internamente antes de la decodificación.
Cada detección lleva el cuadro delimitador (x, y, w, h, rect), las cuatro esquinas detectadas (corners, el cuadrilátero proyectivo que trazan los patrones localizadores del código QR) y la carga útil decodificada como una cadena. Las esquinas son lo correcto para dibujar al anotar la detección – un código QR visto fuera del eje no está alineado con los ejes y el cuadro delimitador solo ofrece un contorno aproximado.
Los metadatos del decodificador cubren todo lo que el decodificador QR aprendió por el camino. version es la versión del código QR, de 1 a 40, que establece el tamaño de la cuadrícula de módulos (un código de versión 1 tiene 21 módulos de ancho, un código de versión 40 tiene 177). ecc_level es el nivel de corrección de errores (de 0 a 3 para L / M / Q / H); los niveles más altos reservan más palabras de código para la corrección de errores y sobreviven a más daños a costa de menos espacio para la carga útil. mask es el patrón de máscara (de 0 a 7) que el codificador eligió para minimizar la confusión del decodificador. data_type es la codificación que informó el decodificador – numérica, alfanumérica, binaria o Kanji – y los indicadores is_numeric / is_alphanumeric / is_binary / is_kanji exponen el mismo valor como booleanos más cómodos.
eci es el valor de Interpretación de Canal Extendido (Extended Channel Interpretation), que identifica la codificación de texto en la que están los bytes (UTF-8, ISO-8859-1, etc.). Un código QR de material impreso arbitrario puede no estar garantizado como UTF-8; una aplicación que necesite decodificar los bytes correctamente comprueba eci y decodifica en consecuencia. El caso Kanji en particular: MicroPython no analiza la codificación Kanji, por lo que una carga útil is_kanji debe tratarse como una matriz de bytes y ser decodificada por la aplicación.
Un uso típico: una cámara lee códigos QR de una cinta transportadora e informa de la carga útil decodificada a un host. La cámara ejecuta find_qrcodes() una vez por fotograma, itera la lista devuelta, selecciona los códigos cuyo data_type coincide con lo que la aplicación espera y reenvía c.payload por UART o USB. Los datos del cuadro delimitador y de las esquinas son útiles para la vista previa del IDE, pero no son lo que le importa al host.
5.28.2. AprilTags¶
find_apriltags() examina el fotograma en busca de AprilTags y devuelve una lista de objetos de resultado 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)
Los AprilTags difieren de los códigos QR en sus objetivos de diseño. Un código QR está construido para codificar datos arbitrarios en un único símbolo denso que el usuario lee una vez de cerca. Un AprilTag está construido para codificar un ID pequeño en un símbolo disperso que la cámara lee continuamente desde la distancia, con tanta tolerancia a errores como permita el código de Hamming de su familia. El compromiso se manifiesta en ambas direcciones: un código QR puede llevar cientos de bytes pero necesita leerse de cerca; un AprilTag lleva solo unos pocos cientos de IDs únicos pero se lee de forma fiable desde metros de distancia.
La palabra clave families admite una máscara de bits de las familias de etiquetas a decodificar. Las familias disponibles son image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12 y image.TAGSTANDARD52H13. Cada familia equilibra el número de IDs frente a la robustez. El número H del nombre es la distancia de Hamming mínima entre dos códigos cualesquiera de la familia – cuántos bits deben cambiar antes de que un código válido se convierta en otro – TAG16H5 tiene 30 IDs a distancia 5, TAG25H9 tiene 35 IDs a distancia 9, y TAG36H11 (la predeterminada y la más común) tiene 587 IDs a distancia 11. El detector corrige hasta dos errores de bit sin importar la familia, por lo que la distancia decide cuán arriesgada es esa corrección: un patrón aleatorio en un fotograma ruidoso solo tiene que quedar a dos bits de un código válido para decodificarse como una detección falsa, y las familias de mayor distancia dispersan sus códigos de forma tan mucho más escasa que tales colisiones se vuelven raras – la razón por la que TAG36H11 es la opción recomendada. El tiempo de detección escala con el número de familias habilitadas, por lo que una aplicación habilita solo lo que realmente imprime. La máscara de bits es el OR a nivel de bits de las constantes de familia cuando se necesitan varias familias en una sola llamada.
Cada detección lleva el vocabulario de cuadro delimitador – x, y, w, h, rect, area, centroides enteros y subpíxel (cx, cy, cxf, cyf) – y las cuatro esquinas detectadas (corners). A continuación van los campos de identificación: id es el ID numérico dentro de la familia (de 0 a 586 para TAG36H11), family es la constante numérica de familia y name es el nombre de la familia como cadena.
Los campos de calidad de coincidencia son los que usa una aplicación para filtrar detecciones. decision_margin es una puntuación de confianza de 0.0 a 1.0; cuanto mayor mejor, y filtrar las detecciones por debajo de decision_margin > 0.1 limpia la mayoría de los aciertos espurios sin coste alguno. hamming cuenta los errores de bit que el decodificador aceptó para esta etiqueta – cuanto menor mejor, siendo 0 una decodificación perfecta. goodness es una métrica histórica de calidad de imagen que el decodificador actual ya no calcula; siempre vale 0.0 y puede ignorarse.
5.28.3. Pose a partir de parámetros intrínsecos¶
La característica transformadora de find_apriltags(), la que justifica los AprilTags como el fiducial preferido de la robótica, es que el método puede recuperar la pose de 6-DoF de la etiqueta en el marco de la cámara directamente a partir de las esquinas detectadas y un pequeño conjunto de parámetros intrínsecos de calibración. Los parámetros intrínsecos son las distancias focales X e Y de la cámara en píxeles (fx, fy) y el centro óptico en píxeles (cx, cy), los cuatro de los cuales la aplicación mide una vez con un procedimiento de calibración y codifica de forma fija a partir de entonces.
Cuando se proporcionan los parámetros intrínsecos, el AprilTag devuelto rellena sus campos x_translation, y_translation, z_translation con la posición de la etiqueta relativa a la cámara, y x_rotation, y_rotation, z_rotation (y el duplicado rotation por simetría) con la orientación de la etiqueta. Sin parámetros intrínsecos, los seis campos valen 0.0 y la aplicación es responsable de cualquier estimación de pose que necesite.
Los campos de traslación se informan en anchos de etiqueta: el decodificador trata la etiqueta como de 1 unidad de ancho, por lo que la aplicación multiplica cada traslación por el ancho físico de la etiqueta impresa para obtener distancias métricas. Una etiqueta impresa con 100 mm de ancho que informa z_translation = 8.3 está a 830 mm de la cámara; la misma etiqueta impresa con 50 mm de ancho a la misma distancia informaría z_translation = 16.6. Los campos de rotación están en radianes y no necesitan escalado.
La estimación de pose es la base de una amplia gama de aplicaciones robóticas: acoplar un robot a una estación de carga marcada con una etiqueta, seguir un rastro impreso de puntos de paso, recuperar la propia pose de la cámara a partir de varias etiquetas conocidas en el entorno. Una cámara que conoce los parámetros intrínsecos, ve una etiqueta y dispone de una posición en el mundo real para la etiqueta tiene, por la misma aritmética, una posición en el mundo real para sí misma.
5.28.4. Cuándo elegir cada uno¶
Los códigos QR y los AprilTags resuelven problemas diferentes. La elección entre ellos se reduce a qué lleva el símbolo impreso.
Cuando la aplicación necesita llevar datos arbitrarios a través del símbolo impreso – una URL, una cadena de número de serie, un registro de contacto – el código QR es la opción correcta. Cientos de bytes caben en un código de tamaño modesto, la codificación es pública y está soportada en todos los smartphones, y el decodificador hace frente a la rotación, daños moderados y ángulos oblicuos.
Cuando la aplicación necesita un ID pequeño leído continuamente desde la distancia con pose opcional – un fiducial en un robot en movimiento, un objetivo de calibración en una sala, un marcador de acoplamiento en una estación de carga – el AprilTag es la opción correcta. Cientos de IDs son de sobra para el caso de uso, el código de Hamming se recupera de errores de bit que derrotarían a un código QR, y la estimación de pose es gratuita una vez calibrados los parámetros intrínsecos.
Algunas aplicaciones usan ambos: un AprilTag marca una ubicación conocida y un código QR asociado (impreso al lado) lleva los metadatos sobre lo que esa ubicación significa. Los dos detectores se ejecutan de forma independiente sobre el mismo fotograma y la aplicación correlaciona sus cuadros delimitadores para emparejar cada etiqueta con su código acompañante.