5.28. Códigos QR e AprilTags

Os detetores vistos até agora – manchas, linhas, círculos, retângulos – encontram características geométricas: posições e contornos que uma fase posterior interpreta. Os restantes detetores encontram características simbólicas: padrões impressos cuja estrutura visual existe especificamente para codificar um conteúdo. A câmara localiza-os, o descodificador lê os bits e o que é devolvido não é uma posição, mas uma cadeia de caracteres (ou um ID) escolhida deliberadamente por quem imprimiu o símbolo.

Duas dessas famílias dominam as aplicações de câmaras de pequeno porte. Os códigos QR transportam texto arbitrário, URLs, cartões de contacto ou conteúdos binários – os códigos 2D voltados para o consumidor que aparecem em cartazes, embalagens e cartões de embarque. As AprilTags transportam um único ID numérico de um conjunto fixo pequeno, descodificam rapidamente mesmo a longa distância e (quando fornecidos os intrínsecos da lente) reportam uma pose com 6 DoF no referencial da câmara – os códigos 2D voltados para a robótica que marcam drones, alvos de calibração e fiduciais. Ambos os detetores devolvem objetos de resultado com o mesmo vocabulário de caixa delimitadora que os detetores de manchas e retângulos utilizam, mas o conteúdo torna-os genuinamente diferentes de tudo o que foi abordado até agora.

5.28.1. Códigos QR

find_qrcodes() percorre o fotograma à procura de códigos QR e devolve uma 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)

O detetor aceita um único roi opcional para restringir a pesquisa. Necessita de entrada em escala de cinzentos – um fotograma a cores é convertido internamente antes da descodificação.

Cada deteção inclui a caixa delimitadora (x, y, w, h, rect), os quatro cantos detetados (corners, o quadrilátero projetivo que os padrões de localização do código QR delimitam) e o conteúdo descodificado como cadeia de caracteres. Os cantos são o elemento correto a desenhar ao anotar a deteção – um código QR visto em ângulo oblíquo não está alinhado com os eixos e a caixa delimitadora fornece apenas um contorno aproximado.

Os metadados do descodificador cobrem tudo o que o descodificador QR aprendeu ao longo do processo. version é a versão do código QR, de 1 a 40, que define o tamanho da grelha de módulos (um código de versão 1 tem 21 módulos de largura, um código de versão 40 tem 177). ecc_level é o nível de correção de erros (0 a 3 para L / M / Q / H); níveis mais elevados reservam mais palavras de código para a correção de erros e sobrevivem a mais danos, à custa de menor capacidade de conteúdo. mask é o padrão de máscara (0 a 7) escolhido pelo codificador para minimizar a confusão do descodificador. data_type é a codificação reportada pelo descodificador – numérica, alfanumérica, binária ou Kanji – e os indicadores is_numeric / is_alphanumeric / is_binary / is_kanji expõem o mesmo valor como booleanos mais intuitivos.

eci é o valor de Interpretação de Canal Estendido, que identifica a codificação de texto em que os bytes se encontram (UTF-8, ISO-8859-1, etc.). Um código QR de material impresso arbitrário pode não ser garantidamente UTF-8; uma aplicação que necessite de descodificar os bytes corretamente verifica eci e descodifica em conformidade. O caso Kanji em particular: o MicroPython não analisa a codificação Kanji, pelo que um conteúdo is_kanji tem de ser tratado como um array de bytes e descodificado pela aplicação.

Um caso de utilização típico: uma câmara lê códigos QR numa linha de montagem e reporta o conteúdo descodificado a um anfitrião. A câmara executa find_qrcodes() uma vez por fotograma, itera a lista devolvida, seleciona os códigos cujo data_type corresponde ao esperado pela aplicação e encaminha c.payload via UART ou USB. Os dados da caixa delimitadora e dos cantos são úteis para a pré-visualização na IDE, mas não são o que o anfitrião precisa.

5.28.2. AprilTags

find_apriltags() percorre o fotograma à procura de AprilTags e devolve uma 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)

As AprilTags diferem dos códigos QR nos seus objetivos de conceção. Um código QR é construído para codificar dados arbitrários num único símbolo denso que o utilizador lê uma vez a curta distância. Uma AprilTag é construída para codificar um ID pequeno num símbolo esparso que a câmara lê continuamente a distância, com tanta tolerância a erros quanto o código de Hamming da sua família permite. A troca manifesta-se em ambas as direções: um código QR pode transportar centenas de bytes, mas precisa de ser lido de perto; uma AprilTag transporta apenas algumas centenas de IDs únicos, mas lê de forma fiável a metros de distância.

A palavra-chave families aceita uma máscara de bits das famílias de etiquetas a descodificar. As famílias disponíveis são image.TAG16H5, image.TAG25H9, image.TAG36H10, image.TAG36H11, image.TAGCIRCLE21H7, image.TAGCIRCLE49H12, image.TAGCUSTOM48H12, image.TAGSTANDARD41H12 e image.TAGSTANDARD52H13. Cada família estabelece uma troca entre contagem de IDs e robustez. O número H no nome é a distância de Hamming mínima entre dois códigos quaisquer da família – quantos bits têm de ser invertidos antes de um código válido se tornar outro – TAG16H5 tem 30 IDs a distância 5, TAG25H9 tem 35 IDs a distância 9 e TAG36H11 (o predefinido e o mais comum) tem 587 IDs a distância 11. O detetor corrige até dois erros de bit independentemente da família, pelo que a distância determina o risco dessa correção: um padrão aleatório num fotograma com ruído apenas precisa de ficar a dois bits de um código válido para ser descodificado como uma falsa deteção, e as famílias de maior distância distribuem os seus códigos de forma tão mais esparsa que tais colisões se tornam raras – razão pela qual TAG36H11 é a opção recomendada. O tempo de deteção escala com o número de famílias ativas, pelo que uma aplicação ativa apenas as que efetivamente imprime. A máscara de bits é o OR bit a bit das constantes de família quando são necessárias várias famílias numa única chamada.

Cada deteção inclui o vocabulário da caixa delimitadora – x, y, w, h, rect, area, centróides inteiro e sub-pixel (cx, cy, cxf, cyf) – e os quatro cantos detetados (corners). Seguem-se os campos de identificação: id é o ID numérico dentro da família (0 a 586 para TAG36H11), family é a constante numérica da família e name é o nome da família como cadeia de caracteres.

Os campos de qualidade de correspondência são os que uma aplicação utiliza para filtrar deteções. decision_margin é uma pontuação de confiança de 0,0 a 1,0; quanto maior, melhor, e filtrar deteções abaixo de decision_margin > 0.1 elimina a maioria dos falsos positivos sem custo. hamming conta os erros de bit que o descodificador aceitou para esta etiqueta – quanto menor, melhor, sendo 0 uma descodificação perfeita. goodness é uma métrica histórica de qualidade de imagem que o descodificador atual já não calcula; é sempre 0,0 e pode ser ignorada.

5.28.3. Pose a partir dos intrínsecos

A característica transformadora de find_apriltags(), aquela que justifica as AprilTags como o fiducial de robótica por excelência, é que o método pode recuperar a pose com 6 DoF da etiqueta no referencial da câmara diretamente a partir dos cantos detetados e de um pequeno conjunto de intrínsecos de calibração. Os intrínsecos são as distâncias focais X e Y da câmara em pixels (fx, fy) e o centro ótico em pixels (cx, cy), todos os quatro dos quais a aplicação mede uma vez com um procedimento de calibração e depois codifica permanentemente.

Quando os intrínsecos são fornecidos, o AprilTag devolvido preenche os seus campos x_translation, y_translation, z_translation com a posição da etiqueta relativamente à câmara, e x_rotation, y_rotation, z_rotation (e o duplicado rotation por simetria) com a orientação da etiqueta. Sem intrínsecos, todos os seis campos são 0,0 e a aplicação é responsável por qualquer estimativa de pose de que necessite.

Os campos de translação são reportados em larguras de etiqueta: o descodificador trata a etiqueta como tendo 1 unidade de largura, pelo que a aplicação multiplica cada translação pela largura física da etiqueta impressa para obter distâncias métricas. Uma etiqueta impressa a 100 mm de largura que reporte z_translation = 8.3 está a 830 mm da câmara; a mesma etiqueta impressa a 50 mm de largura à mesma distância reportaria z_translation = 16.6. Os campos de rotação são em radianos e não necessitam de escalonamento.

A estimativa de pose é a base para uma vasta gama de aplicações robóticas: acoplar um robô a uma estação de carregamento marcada com uma etiqueta, seguir uma trilha de pontos de passagem impressos, recuperar a pose da própria câmara a partir de várias etiquetas conhecidas no ambiente. Uma câmara que conhece os intrínsecos, vê uma etiqueta e tem uma posição no mundo real para essa etiqueta tem, pelo mesmo cálculo, uma posição no mundo real para si própria.

5.28.4. Quando escolher qual

Os códigos QR e as AprilTags resolvem problemas diferentes. A escolha entre eles resume-se ao que o símbolo impresso transporta.

Quando a aplicação precisa de transportar dados arbitrários através do símbolo impresso – um URL, uma cadeia de número de série, um registo de contacto – o código QR é a escolha certa. Centenas de bytes cabem num código de tamanho modesto, a codificação é pública e suportada em todos os smartphones, e o descodificador lida com rotação, danos moderados e ângulos oblíquos.

Quando a aplicação precisa de um ID pequeno lido continuamente a distância com pose opcional – um fiducial num robô em movimento, um alvo de calibração numa sala, um marcador de acoplamento numa estação de carregamento – a AprilTag é a escolha certa. Centenas de IDs são mais do que suficientes para o caso de utilização, o código de Hamming recupera de erros de bit que derrotariam um código QR e a estimativa de pose é gratuita após a calibração dos intrínsecos.

Algumas aplicações utilizam ambos: uma AprilTag marca uma localização conhecida e um código QR associado (impresso ao lado) transporta os metadados sobre o que essa localização significa. Os dois detetores correm independentemente no mesmo fotograma e a aplicação correlaciona as suas caixas delimitadoras para associar cada etiqueta ao código que a acompanha.